mirror of
https://git.sanhost.net/sanasol/hytale-f2p.git
synced 2026-04-18 06:02:22 -04:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2b60ba3a0 | ||
|
|
0e50ded8e4 | ||
|
|
c5e5ddbcc4 | ||
|
|
e610072fa6 | ||
|
|
a40465cac9 | ||
|
|
57056e5b7a | ||
|
|
fcf041be39 | ||
|
|
d53ac915f3 | ||
|
|
7347910fe9 | ||
|
|
0b861904ba | ||
|
|
ee53911a06 | ||
|
|
43a2b6d004 | ||
|
|
ce6455314d | ||
|
|
3abdd10cab | ||
|
|
e1a3f919a2 | ||
|
|
e3fe1b6a10 | ||
|
|
7042188696 | ||
|
|
c067efbcea | ||
|
|
b83b728d21 | ||
|
|
66493d35ca | ||
|
|
89b9135523 | ||
|
|
0b1716c168 | ||
|
|
9628363455 |
3
.github/CODE_OF_CONDUCT.md
vendored
3
.github/CODE_OF_CONDUCT.md
vendored
@@ -36,7 +36,8 @@ This Code of Conduct applies within all community spaces, and also applies when
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [Discord Server, message Founders/Devs](https://discord.gg/Fhbb9Yk5WW). All complaints will be reviewed and investigated promptly and fairly.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [Community Chat, message Founders/Devs](https://chat.sanhost.net/invite/Tfz4jCK4).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
||||
|
||||
|
||||
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -1,3 +1,5 @@
|
||||
> **Note:** This project has been superseded by [F2P Evo](https://git.sanhost.net/sanasol/f2p-evo). Please contribute to the new project instead.
|
||||
|
||||
# Contributing to Hytale F2P
|
||||
|
||||
Thank you for your interest in contributing to Hytale F2P! We welcome contributions from everyone. By participating in this project, you agree to abide by our [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||
@@ -65,6 +67,6 @@ Thank you for your interest in contributing to Hytale F2P! We welcome contributi
|
||||
|
||||
## Questions?
|
||||
|
||||
If you have questions about contributing, feel free to ask in our [Discussions](https://github.com/your-username/hytale-f2p/discussions) or create a [Support Request](.github/ISSUE_TEMPLATE/support_request.yml).
|
||||
If you have questions about contributing, join our [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4) or [TG Group](https://t.me/sanhostnet).
|
||||
|
||||
Thank you for contributing to Hytale F2P!
|
||||
2
.github/ISSUE_TEMPLATE/support_request.yml
vendored
2
.github/ISSUE_TEMPLATE/support_request.yml
vendored
@@ -22,7 +22,7 @@ body:
|
||||
value: |
|
||||
If you need help or support with using the launcher, please fill out this support request.
|
||||
Provide as much detail as possible so we can assist you effectively.
|
||||
**Need a quick assistance?** Please Open-A-Ticket in our [Discord Server](https://discord.gg/Fhbb9Yk5WW)!
|
||||
**Need a quick assistance?** Join our [TG Group](https://t.me/sanhostnet) | [TG Channel](https://t.me/hf2p_og) | [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4)!
|
||||
|
||||
- type: textarea
|
||||
id: question
|
||||
|
||||
38
.github/workflows/release.yml
vendored
38
.github/workflows/release.yml
vendored
@@ -52,12 +52,20 @@ jobs:
|
||||
run: |
|
||||
RELEASE_ID=$(curl -s "${FORGEJO_API}/repos/${GITHUB_REPOSITORY}/releases/tags/${{ github.ref_name }}" \
|
||||
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')
|
||||
echo "Release ID: ${RELEASE_ID}"
|
||||
echo "Upload URL: ${FORGEJO_UPLOAD}/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets"
|
||||
for file in dist/*.exe dist/*.exe.blockmap dist/latest.yml; do
|
||||
[ -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)" \
|
||||
echo "Uploading $file ($(stat -c%s "$file" 2>/dev/null || stat -f%z "$file") bytes)..."
|
||||
HTTP_CODE=$(curl -w '%{http_code}' --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"
|
||||
-F "attachment=@${file}" -o /tmp/upload_response.txt 2>/tmp/upload_err.txt)
|
||||
if [ "$HTTP_CODE" = "201" ]; then
|
||||
echo "OK — uploaded $(basename $file)"
|
||||
else
|
||||
echo "FAILED (HTTP $HTTP_CODE): $(cat /tmp/upload_response.txt)"
|
||||
echo "Curl stderr: $(cat /tmp/upload_err.txt)"
|
||||
fi
|
||||
done
|
||||
|
||||
build-macos:
|
||||
@@ -83,12 +91,19 @@ jobs:
|
||||
run: |
|
||||
RELEASE_ID=$(curl -s "${FORGEJO_API}/repos/${GITHUB_REPOSITORY}/releases/tags/${{ github.ref_name }}" \
|
||||
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')
|
||||
echo "Release ID: ${RELEASE_ID}"
|
||||
for file in dist/*.dmg dist/*.zip dist/*.blockmap dist/latest-mac.yml; do
|
||||
[ -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)" \
|
||||
HTTP_CODE=$(curl -w '%{http_code}' --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"
|
||||
-F "attachment=@${file}" -o /tmp/upload_response.txt 2>/tmp/upload_err.txt)
|
||||
if [ "$HTTP_CODE" = "201" ]; then
|
||||
echo "OK — uploaded $(basename $file)"
|
||||
else
|
||||
echo "FAILED (HTTP $HTTP_CODE): $(cat /tmp/upload_response.txt)"
|
||||
echo "Curl stderr: $(cat /tmp/upload_err.txt)"
|
||||
fi
|
||||
done
|
||||
|
||||
build-linux:
|
||||
@@ -113,10 +128,17 @@ jobs:
|
||||
run: |
|
||||
RELEASE_ID=$(curl -s "${FORGEJO_API}/repos/${GITHUB_REPOSITORY}/releases/tags/${{ github.ref_name }}" \
|
||||
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')
|
||||
echo "Release ID: ${RELEASE_ID}"
|
||||
for file in dist/*.AppImage dist/*.AppImage.blockmap dist/*.deb dist/*.rpm dist/*.pacman dist/latest-linux.yml; do
|
||||
[ -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)" \
|
||||
echo "Uploading $file ($(stat -c%s "$file" 2>/dev/null || stat -f%z "$file") bytes)..."
|
||||
HTTP_CODE=$(curl -w '%{http_code}' --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"
|
||||
-F "attachment=@${file}" -o /tmp/upload_response.txt 2>/tmp/upload_err.txt)
|
||||
if [ "$HTTP_CODE" = "201" ]; then
|
||||
echo "OK — uploaded $(basename $file)"
|
||||
else
|
||||
echo "FAILED (HTTP $HTTP_CODE): $(cat /tmp/upload_response.txt)"
|
||||
echo "Curl stderr: $(cat /tmp/upload_err.txt)"
|
||||
fi
|
||||
done
|
||||
|
||||
341
GUI/index.html
341
GUI/index.html
@@ -24,7 +24,7 @@
|
||||
height="100%25" filter="url(%23noiseFilter)" opacity="0.1" /%3E%3C/svg%3E')] opacity-20"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full h-screen relative z-10">
|
||||
<div class="flex w-full h-screen relative z-10" style="isolation: isolate">
|
||||
<nav class="sidebar">
|
||||
<div class="sidebar-logo">
|
||||
<img src="./icon.png" alt="Hytale Logo" />
|
||||
@@ -44,10 +44,6 @@
|
||||
<i class="fas fa-box"></i>
|
||||
<span class="nav-tooltip" data-i18n="nav.mods">Mods</span>
|
||||
</div>
|
||||
<div class="nav-item" data-page="news">
|
||||
<i class="fas fa-newspaper"></i>
|
||||
<span class="nav-tooltip" data-i18n="nav.news">News</span>
|
||||
</div>
|
||||
<div class="nav-item" data-page="settings">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span class="nav-tooltip" data-i18n="nav.settings">Settings</span>
|
||||
@@ -56,9 +52,12 @@
|
||||
<i class="fas fa-terminal"></i>
|
||||
<span class="nav-tooltip">Logs</span>
|
||||
</div>
|
||||
<div class="nav-item" onclick="openDiscordExternal()">
|
||||
<i class="fab fa-discord"></i>
|
||||
<span class="nav-tooltip">Discord</span>
|
||||
<div class="nav-item" id="matchaNavItem">
|
||||
<i class="fas fa-comments" id="matchaNavIcon"></i>
|
||||
<img id="matchaNavAvatar" class="matcha-nav-avatar" style="display:none" />
|
||||
<span class="matcha-nav-status" id="matchaNavStatus" style="display:none"></span>
|
||||
<span class="nav-tooltip">Matcha!</span>
|
||||
<span id="matchaUnreadBadge" class="matcha-unread-badge" style="display:none"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -73,22 +72,41 @@
|
||||
<span id="onlineCount" class="counter-value">0</span>
|
||||
</div>
|
||||
|
||||
<div class="identity-selector" id="identitySelector">
|
||||
<button class="identity-btn" onclick="toggleIdentityDropdown()">
|
||||
<i class="fas fa-id-badge"></i>
|
||||
<span id="currentIdentityName">Player</span>
|
||||
<i id="passwordShieldIcon" class="fas fa-unlock password-shield unprotected" data-tooltip="Click to protect identity" onclick="event.stopPropagation(); openPasswordModal()"></i>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
<div class="identity-dropdown" id="identityDropdown">
|
||||
<div class="identity-list" id="identityList"></div>
|
||||
<div class="identity-divider"></div>
|
||||
<div class="identity-action" onclick="openIdentityManager()">
|
||||
<i class="fas fa-fingerprint"></i>
|
||||
<span data-i18n="header.manageIdentities">Manage</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="header-tooltip" data-i18n="header.identityTooltip">Your player name & UUID used in-game</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-selector" id="profileSelector">
|
||||
<button class="profile-btn" onclick="toggleProfileDropdown()">
|
||||
<i class="fas fa-user-circle"></i>
|
||||
<i class="fas fa-sliders-h"></i>
|
||||
<span id="currentProfileName">Default</span>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
<div class="profile-dropdown" id="profileDropdown">
|
||||
<div class="profile-list" id="profileList">
|
||||
<!-- Profiles populated by JS -->
|
||||
<!-- Configurations populated by JS -->
|
||||
</div>
|
||||
<div class="profile-divider"></div>
|
||||
<div class="profile-action" onclick="openProfileManager()">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span data-i18n="header.manageProfiles">Manage Profiles</span>
|
||||
<span data-i18n="header.manageProfiles">Manage</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="header-tooltip" data-i18n="header.configTooltip">Game config: mods, Java & memory settings</span>
|
||||
</div>
|
||||
|
||||
<div class="window-controls">
|
||||
@@ -199,22 +217,39 @@
|
||||
<i class="fas fa-play"></i>
|
||||
<span data-i18n="play.playButton">PLAY HYTALE</span>
|
||||
</button>
|
||||
|
||||
<div id="game-info-bar" class="game-info-bar">
|
||||
<span id="game-version-info" class="game-info-item game-info-loading">loading...</span>
|
||||
<span class="game-info-sep"></span>
|
||||
<span id="game-branch-info" class="game-info-item game-info-loading">...</span>
|
||||
<span id="game-last-played" class="game-info-item" style="display: none;"></span>
|
||||
</div>
|
||||
|
||||
<div class="community-links">
|
||||
<a href="#" class="community-link" onclick="openDiscordExternal(); return false;" title="Community Chat">
|
||||
<i class="fas fa-comments" style="color: #22c55e;"></i>
|
||||
<span>Chat</span>
|
||||
</a>
|
||||
<a href="#" class="community-link" onclick="window.electronAPI?.openExternal('https://discord.gg/8WVU24XshK'); return false;" title="Discord">
|
||||
<i class="fab fa-discord" style="color: #5865F2;"></i>
|
||||
<span>Discord</span>
|
||||
</a>
|
||||
<a href="#" class="community-link" onclick="window.electronAPI?.openExternal('https://t.me/hf2p_og'); return false;" title="Telegram Channel">
|
||||
<i class="fab fa-telegram" style="color: #26A5E4;"></i>
|
||||
<span>Channel</span>
|
||||
</a>
|
||||
<a href="#" class="community-link" onclick="window.electronAPI?.openExternal('https://t.me/sanhostnet'); return false;" title="Telegram Group">
|
||||
<i class="fab fa-telegram" style="color: #26A5E4;"></i>
|
||||
<span>Group</span>
|
||||
</a>
|
||||
<a href="#" class="community-link" onclick="window.electronAPI?.openExternal('https://git.sanhost.net/sanasol/hytale-f2p'); return false;" title="Source Code">
|
||||
<i class="fab fa-git-alt" style="color: #e2e8f0;"></i>
|
||||
<span>Source</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="news-section">
|
||||
<div class="news-header">
|
||||
<h2 class="news-title">
|
||||
<i class="fas fa-star mr-2"></i>
|
||||
<span data-i18n="play.latestNews">LATEST NEWS</span>
|
||||
</h2>
|
||||
<button class="view-all-btn" onclick="navigateToPage('news')">
|
||||
<span data-i18n="play.viewAll">VIEW ALL</span> <i
|
||||
class="fas fa-arrow-right ml-1"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="newsGrid" class="news-grid-horizontal"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="featured-page" class="page">
|
||||
@@ -267,15 +302,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="news-page" class="page">
|
||||
<div class="news-header">
|
||||
<h2 class="news-title">
|
||||
<i class="fas fa-newspaper mr-2"></i>
|
||||
<span data-i18n="news.title">ALL NEWS</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="allNewsGrid" class="news-grid-full"></div>
|
||||
</div>
|
||||
|
||||
<div id="settings-page" class="page">
|
||||
<div class="settings-container">
|
||||
@@ -559,6 +585,18 @@
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="settings-option">
|
||||
<label class="settings-checkbox">
|
||||
<input type="checkbox" id="allowMultiInstanceCheck" />
|
||||
<span class="checkmark"></span>
|
||||
<div class="checkbox-content">
|
||||
<div class="checkbox-title" data-i18n="settings.allowMultiInstance">Allow multiple game instances</div>
|
||||
<div class="checkbox-description" data-i18n="settings.allowMultiInstanceDescription">
|
||||
Allow running multiple game clients at the same time (useful for mod development)
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="settings-option">
|
||||
<label class="settings-checkbox">
|
||||
<input type="checkbox" id="launcherHwAccelCheck" />
|
||||
@@ -652,6 +690,29 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Matcha! Panel -->
|
||||
<div id="matchaPanelBackdrop" class="matcha-panel-backdrop"></div>
|
||||
<div id="matchaPanel" class="matcha-panel">
|
||||
<div class="matcha-panel-header">
|
||||
<div id="matchaPanelHeaderContent" class="matcha-panel-header-content">
|
||||
<span class="matcha-header-title">Matcha!</span>
|
||||
</div>
|
||||
<button class="matcha-panel-close" id="matchaPanelCloseBtn">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="matchaPanelBody" class="matcha-panel-body"></div>
|
||||
</div>
|
||||
|
||||
<!-- Matcha User Profile Popup -->
|
||||
<div id="matchaUserProfileOverlay" class="matcha-user-profile-overlay" style="display:none">
|
||||
<div class="matcha-user-profile-card" id="matchaUserProfileCard">
|
||||
<div class="matcha-user-profile-body" id="matchaUserProfileBody">
|
||||
<div class="matcha-loading"><i class="fas fa-spinner fa-spin"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="myModsModal" class="mods-modal">
|
||||
<div class="mods-modal-content">
|
||||
<div class="mods-modal-header">
|
||||
@@ -721,28 +782,40 @@
|
||||
</div>
|
||||
|
||||
<div class="uuid-modal-body">
|
||||
<div class="uuid-current-section">
|
||||
<h3 class="uuid-section-title" data-i18n="uuid.currentUserUUID">Current User UUID</h3>
|
||||
<div class="uuid-current-display">
|
||||
<input type="text" id="modalCurrentUuid" class="uuid-display-input" readonly />
|
||||
<button id="modalCopyUuidBtn" class="uuid-action-btn copy-btn" title="Copy UUID">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
<button id="modalRegenerateUuidBtn" class="uuid-action-btn regenerate-btn"
|
||||
title="Generate New UUID">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uuid-list-section">
|
||||
<div class="uuid-list-header">
|
||||
<h3 class="uuid-section-title" data-i18n="uuid.allPlayerUUIDs">All Player UUIDs</h3>
|
||||
<button id="generateNewUuidBtn" class="uuid-generate-btn">
|
||||
<button id="addIdentityBtn" class="uuid-generate-btn">
|
||||
<i class="fas fa-plus"></i>
|
||||
<span data-i18n="uuid.generateNew">Generate New UUID</span>
|
||||
<span data-i18n="uuid.addIdentity">Add Identity</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="uuidAddForm" class="uuid-add-form" style="display: none;">
|
||||
<div class="uuid-add-form-row">
|
||||
<input type="text" id="addIdentityUsername" class="uuid-input"
|
||||
data-i18n-placeholder="uuid.usernamePlaceholder"
|
||||
placeholder="Username" maxlength="16" />
|
||||
</div>
|
||||
<div class="uuid-add-form-row">
|
||||
<input type="text" id="addIdentityUuid" class="uuid-input"
|
||||
data-i18n-placeholder="uuid.customPlaceholder"
|
||||
placeholder="UUID (auto-generated)" maxlength="36" />
|
||||
<button id="addIdentityRegenerateBtn" class="uuid-action-btn regenerate-btn"
|
||||
title="Generate new UUID">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="uuid-add-form-actions">
|
||||
<button id="addIdentityConfirmBtn" class="uuid-set-btn">
|
||||
<i class="fas fa-check"></i>
|
||||
<span data-i18n="uuid.add">Add</span>
|
||||
</button>
|
||||
<button id="addIdentityCancelBtn" class="uuid-cancel-btn">
|
||||
<i class="fas fa-times"></i>
|
||||
<span data-i18n="uuid.cancel">Cancel</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="uuidList" class="uuid-list">
|
||||
<div class="uuid-loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
@@ -751,21 +824,62 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uuid-custom-section">
|
||||
<h3 class="uuid-section-title" data-i18n="uuid.setCustomUUID">Set Custom UUID</h3>
|
||||
<div class="uuid-custom-form">
|
||||
<input type="text" id="customUuidInput" class="uuid-input"
|
||||
data-i18n-placeholder="uuid.customPlaceholder" maxlength="36" />
|
||||
<button id="setCustomUuidBtn" class="uuid-set-btn">
|
||||
<i class="fas fa-check"></i>
|
||||
<span data-i18n="uuid.setUUID">Set UUID</span>
|
||||
</button>
|
||||
<div class="uuid-advanced-section">
|
||||
<button id="uuidAdvancedToggle" class="uuid-advanced-toggle">
|
||||
<i class="fas fa-chevron-right uuid-advanced-chevron"></i>
|
||||
<span data-i18n="uuid.advanced">Advanced</span>
|
||||
</button>
|
||||
<div id="uuidAdvancedContent" class="uuid-advanced-content" style="display: none;">
|
||||
<h3 class="uuid-section-title" data-i18n="uuid.setCustomUUID">Set Custom UUID</h3>
|
||||
<div class="uuid-custom-form">
|
||||
<input type="text" id="customUuidInput" class="uuid-input"
|
||||
data-i18n-placeholder="uuid.customPlaceholder" maxlength="36" />
|
||||
<button id="setCustomUuidBtn" class="uuid-set-btn">
|
||||
<i class="fas fa-check"></i>
|
||||
<span data-i18n="uuid.setUUID">Set UUID</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="uuid-custom-hint">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span data-i18n="uuid.warning">Warning: Setting a custom UUID will change your current player
|
||||
identity</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uuid-advanced-section" style="margin-top: 12px;">
|
||||
<button id="passwordSectionToggle" class="uuid-advanced-toggle">
|
||||
<i class="fas fa-chevron-right uuid-advanced-chevron"></i>
|
||||
<span>Password Protection</span>
|
||||
</button>
|
||||
<div id="passwordSectionContent" class="uuid-advanced-content" style="display: none;">
|
||||
<h3 class="uuid-section-title">Protect Your Identity</h3>
|
||||
<p class="uuid-custom-hint" style="margin-bottom: 12px;">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
<span>Set a password to prevent others from using your UUID</span>
|
||||
</p>
|
||||
<div id="passwordStatusMsg" class="uuid-custom-hint" style="margin-bottom: 8px; color: #93a3b8;"></div>
|
||||
<div id="passwordSetForm">
|
||||
<div class="uuid-custom-form" style="margin-bottom: 8px;">
|
||||
<input type="password" id="currentPasswordInput" class="uuid-input"
|
||||
placeholder="Current password" style="display: none;" />
|
||||
</div>
|
||||
<div class="uuid-custom-form" style="margin-bottom: 8px;">
|
||||
<input type="password" id="newPasswordInput" class="uuid-input"
|
||||
placeholder="New password (min 6 chars)" />
|
||||
</div>
|
||||
<div class="uuid-custom-form">
|
||||
<button id="setPasswordBtn" class="uuid-set-btn" onclick="handleSetPassword()">
|
||||
<i class="fas fa-lock"></i>
|
||||
<span>Set Password</span>
|
||||
</button>
|
||||
<button id="removePasswordBtn" class="uuid-cancel-btn" onclick="handleRemovePassword()" style="display: none;">
|
||||
<i class="fas fa-unlock"></i>
|
||||
<span>Remove Password</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="uuid-custom-hint">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span data-i18n="uuid.warning">Warning: Setting a custom UUID will change your current player
|
||||
identity</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -776,8 +890,8 @@
|
||||
<div class="profile-modal-content">
|
||||
<div class="profile-modal-header">
|
||||
<h2 class="profile-modal-title">
|
||||
<i class="fas fa-users-cog mr-2"></i>
|
||||
<span data-i18n="profiles.modalTitle">Manage Profiles</span>
|
||||
<i class="fas fa-sliders-h mr-2"></i>
|
||||
<span data-i18n="configurations.modalTitle">Manage Configurations</span>
|
||||
</h2>
|
||||
<button class="modal-close-btn" onclick="closeProfileManager()">
|
||||
<i class="fas fa-times"></i>
|
||||
@@ -788,46 +902,105 @@
|
||||
<!-- Populated by JS -->
|
||||
</div>
|
||||
<div class="profile-create-section">
|
||||
<input type="text" id="newProfileName" data-i18n-placeholder="profiles.newProfilePlaceholder"
|
||||
<input type="text" id="newProfileName" data-i18n-placeholder="configurations.newProfilePlaceholder"
|
||||
class="profile-input" maxlength="20">
|
||||
<button class="profile-create-btn" onclick="createNewProfile()">
|
||||
<i class="fas fa-plus"></i> <span data-i18n="profiles.createProfile">Create Profile</span>
|
||||
<i class="fas fa-plus"></i> <span data-i18n="configurations.createProfile">Create Configuration</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="version-display-bottom">
|
||||
<!-- Password Protection Modal -->
|
||||
<div id="passwordModal" class="uuid-modal" style="display: none;">
|
||||
<div class="uuid-modal-content" style="max-width: 420px;">
|
||||
<div class="uuid-modal-header">
|
||||
<h2 class="uuid-modal-title">
|
||||
<i class="fas fa-shield-alt mr-2"></i>
|
||||
<span>Identity Protection</span>
|
||||
</h2>
|
||||
<button class="modal-close-btn" onclick="closePasswordModal()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="uuid-modal-body" style="padding: 16px 20px;">
|
||||
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px; padding: 12px; background: rgba(255,255,255,0.03); border-radius: 8px; border: 1px solid rgba(255,255,255,0.06);">
|
||||
<i class="fas fa-user" style="color: #22c55e; font-size: 1.2em;"></i>
|
||||
<div style="flex:1; min-width:0;">
|
||||
<div id="pwModalName" style="font-weight: 600; font-size: 1em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">Player</div>
|
||||
<div id="pwModalUuid" style="font-size: 0.7em; color: #6b7280; font-family: monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"></div>
|
||||
</div>
|
||||
<div id="pwModalStatusBadge" style="flex-shrink:0;"></div>
|
||||
</div>
|
||||
|
||||
<div id="pwModalStatusText" style="margin-bottom: 14px; font-size: 0.85em; color: #93a3b8; text-align: center;"></div>
|
||||
|
||||
<div id="pwModalSetForm">
|
||||
<div style="margin-bottom: 10px;">
|
||||
<input type="password" id="pwModalCurrentPassword" class="uuid-input"
|
||||
placeholder="Current password" style="display: none; width: 100%;" />
|
||||
</div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<input type="password" id="pwModalNewPassword" class="uuid-input"
|
||||
placeholder="New password (min 6 chars)" style="width: 100%;" />
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button id="pwModalSetBtn" class="uuid-set-btn" style="flex:1;" onclick="handlePasswordModalSet()">
|
||||
<i class="fas fa-lock"></i>
|
||||
<span>Set Password</span>
|
||||
</button>
|
||||
<button id="pwModalRemoveBtn" class="uuid-cancel-btn" style="display: none;" onclick="handlePasswordModalRemove()">
|
||||
<i class="fas fa-unlock"></i>
|
||||
<span>Remove</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pwModalUsernameInfo" style="margin-top: 14px; padding: 10px; background: rgba(34,197,94,0.06); border-radius: 6px; border: 1px solid rgba(34,197,94,0.15); font-size: 0.8em; color: #93a3b8; display: none;">
|
||||
<i class="fas fa-info-circle" style="color: #22c55e;"></i>
|
||||
<span>Setting a password also reserves your username — no one else can use it.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="version-display-bottom" onclick="window.electronAPI?.openExternal('https://git.sanhost.net/sanasol/hytale-f2p/releases')" style="cursor: pointer;" title="View releases">
|
||||
<i class="fas fa-code-branch"></i>
|
||||
<span id="launcherVersion"></span>
|
||||
</div>
|
||||
|
||||
<footer class="fixed bottom-0 left-0 right-0 z-50 bg-black/80 backdrop-blur-sm px-4 py-2">
|
||||
<div class="flex items-center justify-center text-xs text-gray-400">
|
||||
<span>Made by <a href="https://github.com/amiayweb" target="_blank"
|
||||
<span>Made by <a href="#" onclick="window.electronAPI?.openExternal('https://github.com/amiayweb'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@amiayweb</a> & <a
|
||||
href="https://github.com/Relyz1993" target="_blank"
|
||||
href="#" onclick="window.electronAPI?.openExternal('https://github.com/Relyz1993'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@Relyz</a></span>
|
||||
<span class="mx-2">|</span>
|
||||
<span>Contributors:
|
||||
<a href="https://github.com/chasem-dev" target="_blank"
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://github.com/chasem-dev'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@chasem-dev</a>,
|
||||
<a href="https://github.com/crimera" target="_blank"
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://github.com/crimera'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@crimera</a>,
|
||||
<a href="https://github.com/sanasol" target="_blank"
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://github.com/sanasol'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@sanasol</a>,
|
||||
<a href="https://github.com/Terromur" target="_blank"
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://github.com/Terromur'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@terromur</a>,
|
||||
<a href="https://github.com/ericiskoolbeans" target="_blank"
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://github.com/ericiskoolbeans'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@ericiskoolbeans</a>,
|
||||
<a href="https://github.com/fazrigading" target="_blank"
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://github.com/fazrigading'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@fazrigading</a>,
|
||||
<a href="https://github.com/Rahul-Sahani04" target="_blank"
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://github.com/Rahul-Sahani04'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@Rahul-Sahani04</a>,
|
||||
<a href="https://github.com/xSamiVS" target="_blank"
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://github.com/xSamiVS'); return false;"
|
||||
class="text-blue-400 hover:text-blue-300 transition-colors">@xSamiVS</a>
|
||||
</span>
|
||||
<span class="mx-2">|</span>
|
||||
<span>Social powered by <a href="#" onclick="window.electronAPI?.openExternal('https://github.com/vZylev/Butter-Launcher'); return false;"
|
||||
class="text-purple-400 hover:text-purple-300 transition-colors">Butter Launcher</a> &
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://butterlauncher.tech/'); return false;"
|
||||
class="text-purple-400 hover:text-purple-300 transition-colors">Matcha!</a>
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -837,14 +1010,14 @@
|
||||
<div class="modal-content discord-popup-modal">
|
||||
<div class="modal-header">
|
||||
<div class="discord-popup-header">
|
||||
<i class="fab fa-discord"></i>
|
||||
<h2 class="modal-title">Join Our Discord Community</h2>
|
||||
<i class="fas fa-comments"></i>
|
||||
<h2 class="modal-title">Join Our Community</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="discord-popup-body">
|
||||
<p class="discord-popup-text">
|
||||
Join our community of over <strong>5000 members</strong> and stay connected!
|
||||
Join our community and stay connected!
|
||||
</p>
|
||||
<p class="discord-popup-text">
|
||||
Get the latest news, updates, and announcements about the launcher.
|
||||
@@ -852,11 +1025,11 @@
|
||||
<p class="discord-popup-text">
|
||||
Find help, report bugs, share your feedback, and connect with other players.
|
||||
</p>
|
||||
|
||||
|
||||
<div class="discord-popup-actions">
|
||||
<button class="discord-popup-btn primary" onclick="joinDiscord()">
|
||||
<i class="fab fa-discord"></i>
|
||||
Join Discord
|
||||
<i class="fas fa-comments"></i>
|
||||
Join Community Chat
|
||||
</button>
|
||||
<button class="discord-popup-btn secondary" onclick="closeDiscordPopup()">
|
||||
Maybe Later
|
||||
|
||||
@@ -35,13 +35,21 @@ export function setupLauncher() {
|
||||
// Initial Profile Load
|
||||
loadProfiles();
|
||||
|
||||
// Close dropdown on outside click
|
||||
// Initial Identity Load
|
||||
loadIdentities();
|
||||
|
||||
// Close dropdowns on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
const selector = document.getElementById('profileSelector');
|
||||
if (selector && !selector.contains(e.target)) {
|
||||
const profileSelector = document.getElementById('profileSelector');
|
||||
if (profileSelector && !profileSelector.contains(e.target)) {
|
||||
const dropdown = document.getElementById('profileDropdown');
|
||||
if (dropdown) dropdown.classList.remove('show');
|
||||
}
|
||||
const identitySelector = document.getElementById('identitySelector');
|
||||
if (identitySelector && !identitySelector.contains(e.target)) {
|
||||
const dropdown = document.getElementById('identityDropdown');
|
||||
if (dropdown) dropdown.classList.remove('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -83,7 +91,7 @@ function renderProfileList(profiles, activeProfile) {
|
||||
managerList.innerHTML = profiles.map(p => `
|
||||
<div class="profile-manager-item ${p.id === activeProfile.id ? 'active' : ''}">
|
||||
<div class="flex items-center gap-3">
|
||||
<i class="fas fa-user-circle text-xl text-gray-400"></i>
|
||||
<i class="fas fa-sliders-h text-xl text-gray-400"></i>
|
||||
<div>
|
||||
<div class="font-bold">${p.name}</div>
|
||||
<div class="text-xs text-gray-500">ID: ${p.id.substring(0, 8)}...</div>
|
||||
@@ -106,13 +114,6 @@ function updateCurrentProfileUI(profile) {
|
||||
}
|
||||
}
|
||||
|
||||
window.toggleProfileDropdown = () => {
|
||||
const dropdown = document.getElementById('profileDropdown');
|
||||
if (dropdown) {
|
||||
dropdown.classList.toggle('show');
|
||||
}
|
||||
};
|
||||
|
||||
window.openProfileManager = () => {
|
||||
const modal = document.getElementById('profileManagerModal');
|
||||
if (modal) {
|
||||
@@ -146,7 +147,7 @@ window.createNewProfile = async () => {
|
||||
};
|
||||
|
||||
window.deleteProfile = async (id) => {
|
||||
if (!confirm('Are you sure you want to delete this profile? parameters and mods configuration will be lost.')) return;
|
||||
if (!confirm('Are you sure you want to delete this configuration? Mod settings will be lost.')) return;
|
||||
|
||||
try {
|
||||
await window.electronAPI.profile.delete(id);
|
||||
@@ -160,7 +161,7 @@ window.deleteProfile = async (id) => {
|
||||
window.switchProfile = async (id) => {
|
||||
try {
|
||||
if (window.LauncherUI) window.LauncherUI.showProgress();
|
||||
const switchingMsg = window.i18n ? window.i18n.t('progress.switchingProfile') : 'Switching Profile...';
|
||||
const switchingMsg = window.i18n ? window.i18n.t('progress.switchingProfile') : 'Switching configuration...';
|
||||
if (window.LauncherUI) window.LauncherUI.updateProgress({ message: switchingMsg });
|
||||
|
||||
await window.electronAPI.profile.activate(id);
|
||||
@@ -168,6 +169,16 @@ window.switchProfile = async (id) => {
|
||||
// Refresh UI
|
||||
await loadProfiles();
|
||||
|
||||
// Refresh branch radio buttons in settings
|
||||
if (window.SettingsAPI?.reloadBranch) {
|
||||
await window.SettingsAPI.reloadBranch();
|
||||
}
|
||||
|
||||
// Refresh game info bar on play page
|
||||
if (window.LauncherUI?.loadGameInfoBar) {
|
||||
window.LauncherUI.loadGameInfoBar();
|
||||
}
|
||||
|
||||
// Refresh Mods
|
||||
if (window.modsManager) {
|
||||
if (window.modsManager.loadInstalledMods) await window.modsManager.loadInstalledMods();
|
||||
@@ -179,7 +190,7 @@ window.switchProfile = async (id) => {
|
||||
if (dropdown) dropdown.classList.remove('show');
|
||||
|
||||
if (window.LauncherUI) {
|
||||
const switchedMsg = window.i18n ? window.i18n.t('progress.profileSwitched') : 'Profile Switched!';
|
||||
const switchedMsg = window.i18n ? window.i18n.t('progress.profileSwitched') : 'Configuration switched!';
|
||||
window.LauncherUI.updateProgress({ message: switchedMsg });
|
||||
setTimeout(() => window.LauncherUI.hideProgress(), 1000);
|
||||
}
|
||||
@@ -192,7 +203,8 @@ window.switchProfile = async (id) => {
|
||||
};
|
||||
|
||||
export async function launch() {
|
||||
if (isDownloading || (playBtn && playBtn.disabled)) return;
|
||||
const btn = homePlayBtn || playBtn;
|
||||
if (isDownloading || (btn && btn.disabled)) return;
|
||||
|
||||
// ==========================================================================
|
||||
// STEP 1: Check launch readiness from backend (single source of truth)
|
||||
@@ -270,11 +282,7 @@ export async function launch() {
|
||||
// STEP 3: Start launch process
|
||||
// ==========================================================================
|
||||
if (window.LauncherUI) window.LauncherUI.showProgress();
|
||||
isDownloading = true;
|
||||
if (playBtn) {
|
||||
playBtn.disabled = true;
|
||||
playText.textContent = 'LAUNCHING...';
|
||||
}
|
||||
lockPlayButton('LAUNCHING...');
|
||||
|
||||
try {
|
||||
const startingMsg = window.i18n ? window.i18n.t('progress.startingGame') : 'Starting game...';
|
||||
@@ -284,20 +292,92 @@ export async function launch() {
|
||||
// Pass playerName from config - backend will validate again
|
||||
const result = await window.electronAPI.launchGame(playerName, javaPath, '', gpuPreference);
|
||||
|
||||
isDownloading = false;
|
||||
|
||||
if (window.LauncherUI) {
|
||||
window.LauncherUI.hideProgress();
|
||||
if (result.usernameTaken) {
|
||||
// Username reserved by another player
|
||||
isDownloading = false;
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
resetPlayButton();
|
||||
if (window.LauncherUI && window.LauncherUI.showError) {
|
||||
window.LauncherUI.showError('This username is reserved by another player. Please change your player name in Identity settings.');
|
||||
} else {
|
||||
showNotification('This username is reserved by another player. Please change your player name.', 'error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.nameLocked) {
|
||||
// UUID is password-protected and locked to a specific name
|
||||
isDownloading = false;
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
resetPlayButton();
|
||||
const msg = result.registeredName
|
||||
? `This UUID is locked to username "${result.registeredName}". Change your identity name to "${result.registeredName}" in Settings.`
|
||||
: 'This UUID is locked to a different username. Check your identity settings.';
|
||||
if (window.LauncherUI && window.LauncherUI.showError) {
|
||||
window.LauncherUI.showError(msg);
|
||||
} else {
|
||||
showNotification(msg, 'error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.passwordRequired) {
|
||||
// Check for saved password first
|
||||
let savedPw = null;
|
||||
try {
|
||||
const cfg = await window.electronAPI.loadConfig();
|
||||
const uuid = result.uuid || '';
|
||||
savedPw = cfg && cfg.savedPasswords && cfg.savedPasswords[uuid] ? cfg.savedPasswords[uuid] : null;
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
if (savedPw) {
|
||||
// Try saved password silently
|
||||
if (window.LauncherUI) window.LauncherUI.showProgress();
|
||||
lockPlayButton('LAUNCHING...');
|
||||
const autoResult = await window.electronAPI.launchGameWithPassword(playerName, javaPath, '', gpuPreference, savedPw);
|
||||
if (autoResult.success) {
|
||||
isDownloading = false;
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
resetPlayButton();
|
||||
if (window.electronAPI.minimizeWindow) setTimeout(() => { window.electronAPI.minimizeWindow(); }, 500);
|
||||
return;
|
||||
}
|
||||
// Saved password failed — clear it and show popup
|
||||
isDownloading = false;
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
resetPlayButton();
|
||||
try {
|
||||
const cfg2 = await window.electronAPI.loadConfig();
|
||||
const sp = cfg2.savedPasswords || {};
|
||||
delete sp[result.uuid || ''];
|
||||
await window.electronAPI.saveConfig({ savedPasswords: sp });
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
// Show interactive password dialog
|
||||
const launchResult = await promptForPasswordAndLaunch(playerName, javaPath, gpuPreference, result.uuid);
|
||||
if (launchResult && launchResult.success) {
|
||||
if (window.electronAPI.minimizeWindow) setTimeout(() => { window.electronAPI.minimizeWindow(); }, 500);
|
||||
}
|
||||
return;
|
||||
}
|
||||
resetPlayButton();
|
||||
|
||||
if (result.success) {
|
||||
// Keep button locked so user can't double-launch
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
lockPlayButton('GAME RUNNING');
|
||||
setTimeout(() => {
|
||||
resetPlayButton();
|
||||
}, 10000); // Reset after 10s (game should be visible by then)
|
||||
if (window.electronAPI.minimizeWindow) {
|
||||
setTimeout(() => {
|
||||
window.electronAPI.minimizeWindow();
|
||||
}, 500);
|
||||
}
|
||||
} else {
|
||||
isDownloading = false;
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
resetPlayButton();
|
||||
console.error('[Launcher] Launch failed:', result.error);
|
||||
|
||||
// Handle specific error cases
|
||||
@@ -353,6 +433,187 @@ export async function launch() {
|
||||
}
|
||||
}
|
||||
|
||||
function promptForPasswordAndLaunch(playerName, javaPath, gpuPreference, uuid) {
|
||||
return new Promise((resolve) => {
|
||||
// Remove any existing password prompt
|
||||
const existing = document.querySelector('.custom-confirm-modal');
|
||||
if (existing) existing.remove();
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'custom-confirm-modal';
|
||||
overlay.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
backdrop-filter: blur(4px);
|
||||
z-index: 20000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const dialog = document.createElement('div');
|
||||
dialog.style.cssText = `
|
||||
background: #1f2937;
|
||||
border-radius: 12px;
|
||||
padding: 0;
|
||||
min-width: 380px;
|
||||
max-width: 420px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
`;
|
||||
|
||||
dialog.innerHTML = `
|
||||
<div style="padding: 20px 24px; border-bottom: 1px solid rgba(255,255,255,0.1);">
|
||||
<div style="display: flex; align-items: center; gap: 10px; color: #f59e0b;">
|
||||
<i class="fas fa-lock" style="font-size: 20px;"></i>
|
||||
<h3 style="margin: 0; font-size: 1.1rem; font-weight: 600; color: #e5e7eb;">Password Required</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 20px 24px;">
|
||||
<p style="margin: 0 0 12px 0; color: #9ca3af; font-size: 0.9rem; line-height: 1.5;">This identity is password-protected. Enter your password to continue.</p>
|
||||
<div id="pwErrorMsg" style="display: none; margin-bottom: 12px; padding: 8px 12px; background: rgba(239,68,68,0.15); border: 1px solid rgba(239,68,68,0.3); border-radius: 6px; color: #f87171; font-size: 0.85rem;"></div>
|
||||
<input type="password" id="launchPasswordInput" style="
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 14px;
|
||||
background: rgba(0,0,0,0.3);
|
||||
border: 1px solid rgba(255,255,255,0.15);
|
||||
border-radius: 8px;
|
||||
color: #e5e7eb;
|
||||
font-size: 0.95rem;
|
||||
outline: none;
|
||||
" placeholder="Password" autofocus />
|
||||
<label style="display: flex; align-items: center; gap: 8px; margin-top: 12px; cursor: pointer; color: #9ca3af; font-size: 0.85rem; user-select: none;">
|
||||
<input type="checkbox" id="pwRememberCheck" style="accent-color: #f59e0b; width: 16px; height: 16px; cursor: pointer;" />
|
||||
Remember password
|
||||
</label>
|
||||
</div>
|
||||
<div style="padding: 16px 24px; display: flex; gap: 10px; justify-content: flex-end; border-top: 1px solid rgba(255,255,255,0.1);">
|
||||
<button id="pwCancelBtn" style="
|
||||
background: transparent;
|
||||
color: #9ca3af;
|
||||
border: 1px solid rgba(156,163,175,0.3);
|
||||
padding: 9px 18px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
">Cancel</button>
|
||||
<button id="pwConfirmBtn" style="
|
||||
background: #f59e0b;
|
||||
color: #000;
|
||||
border: none;
|
||||
padding: 9px 18px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
">Login</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
overlay.appendChild(dialog);
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
const input = overlay.querySelector('#launchPasswordInput');
|
||||
const confirmBtn = overlay.querySelector('#pwConfirmBtn');
|
||||
const cancelBtn = overlay.querySelector('#pwCancelBtn');
|
||||
const errorMsg = overlay.querySelector('#pwErrorMsg');
|
||||
const rememberCheck = overlay.querySelector('#pwRememberCheck');
|
||||
|
||||
let busy = false;
|
||||
|
||||
const close = (result) => {
|
||||
overlay.remove();
|
||||
isDownloading = false;
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
resetPlayButton();
|
||||
resolve(result);
|
||||
};
|
||||
|
||||
const showError = (msg) => {
|
||||
errorMsg.textContent = msg;
|
||||
errorMsg.style.display = 'block';
|
||||
input.style.borderColor = 'rgba(239,68,68,0.5)';
|
||||
input.value = '';
|
||||
input.focus();
|
||||
};
|
||||
|
||||
const tryLogin = async () => {
|
||||
const password = input.value;
|
||||
if (!password) {
|
||||
showError('Please enter your password.');
|
||||
return;
|
||||
}
|
||||
if (busy) return;
|
||||
busy = true;
|
||||
|
||||
// Show loading state
|
||||
confirmBtn.disabled = true;
|
||||
confirmBtn.textContent = 'Logging in...';
|
||||
errorMsg.style.display = 'none';
|
||||
input.style.borderColor = 'rgba(255,255,255,0.15)';
|
||||
|
||||
try {
|
||||
if (window.LauncherUI) window.LauncherUI.showProgress();
|
||||
lockPlayButton('LAUNCHING...');
|
||||
|
||||
const result = await window.electronAPI.launchGameWithPassword(playerName, javaPath, '', gpuPreference, password);
|
||||
|
||||
if (result.success) {
|
||||
// Save password if "Remember" checked
|
||||
if (rememberCheck.checked && uuid) {
|
||||
try {
|
||||
const cfg = await window.electronAPI.loadConfig();
|
||||
const sp = cfg.savedPasswords || {};
|
||||
sp[uuid] = password;
|
||||
await window.electronAPI.saveConfig({ savedPasswords: sp });
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
overlay.remove();
|
||||
isDownloading = false;
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
resetPlayButton();
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrong password
|
||||
if (result.passwordRequired) {
|
||||
showError(result.error || 'Incorrect password. Please try again.');
|
||||
} else {
|
||||
showError(result.error || 'Launch failed.');
|
||||
}
|
||||
|
||||
isDownloading = false;
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
resetPlayButton();
|
||||
} catch (err) {
|
||||
showError(err.message || 'An error occurred.');
|
||||
isDownloading = false;
|
||||
if (window.LauncherUI) window.LauncherUI.hideProgress();
|
||||
resetPlayButton();
|
||||
} finally {
|
||||
busy = false;
|
||||
confirmBtn.disabled = false;
|
||||
confirmBtn.textContent = 'Login';
|
||||
}
|
||||
};
|
||||
|
||||
confirmBtn.addEventListener('click', tryLogin);
|
||||
cancelBtn.addEventListener('click', () => close(null));
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') tryLogin();
|
||||
if (e.key === 'Escape') close(null);
|
||||
});
|
||||
|
||||
setTimeout(() => input.focus(), 100);
|
||||
});
|
||||
}
|
||||
|
||||
function showCustomConfirm(message, title, onConfirm, onCancel = null, confirmText, cancelText) {
|
||||
// Apply defaults with i18n support
|
||||
title = title || (window.i18n ? window.i18n.t('confirm.defaultTitle') : 'Confirm Action');
|
||||
@@ -588,9 +849,21 @@ async function performRepair() {
|
||||
|
||||
function resetPlayButton() {
|
||||
isDownloading = false;
|
||||
if (playBtn) {
|
||||
playBtn.disabled = false;
|
||||
playText.textContent = window.i18n ? window.i18n.t('play.play') : 'PLAY';
|
||||
const btn = homePlayBtn || playBtn;
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
const textEl = btn.querySelector('span');
|
||||
if (textEl) textEl.textContent = window.i18n ? window.i18n.t('play.playButton') : 'PLAY HYTALE';
|
||||
}
|
||||
}
|
||||
|
||||
function lockPlayButton(text) {
|
||||
isDownloading = true;
|
||||
const btn = homePlayBtn || playBtn;
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
const textEl = btn.querySelector('span');
|
||||
if (textEl) textEl.textContent = text || 'LAUNCHING...';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,6 +949,142 @@ async function loadCustomJavaPath() {
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// IDENTITY SWITCHER
|
||||
// ==========================================
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
async function loadIdentities() {
|
||||
try {
|
||||
if (!window.electronAPI) return;
|
||||
|
||||
const nameEl = document.getElementById('currentIdentityName');
|
||||
|
||||
// Load current username
|
||||
let currentUsername = 'Player';
|
||||
if (window.electronAPI.loadUsername) {
|
||||
const name = await window.electronAPI.loadUsername();
|
||||
if (name) currentUsername = name;
|
||||
}
|
||||
if (nameEl) nameEl.textContent = currentUsername;
|
||||
|
||||
// Load all identities for dropdown
|
||||
const list = document.getElementById('identityList');
|
||||
if (!list || !window.electronAPI.getAllUuidMappings) return;
|
||||
|
||||
const mappings = await window.electronAPI.getAllUuidMappings();
|
||||
renderIdentityList(mappings, currentUsername);
|
||||
} catch (error) {
|
||||
console.error('Failed to load identities:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function renderIdentityList(mappings, currentUsername) {
|
||||
const list = document.getElementById('identityList');
|
||||
if (!list) return;
|
||||
|
||||
if (!mappings || mappings.length === 0) {
|
||||
list.innerHTML = '<div class="identity-empty">No identities</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Check password status for all identities in parallel
|
||||
const statusChecks = mappings.map(async m => {
|
||||
try {
|
||||
if (m.uuid && window.electronAPI?.checkPasswordStatus) {
|
||||
const s = await window.electronAPI.checkPasswordStatus(m.uuid);
|
||||
return s?.hasPassword || false;
|
||||
}
|
||||
} catch {}
|
||||
return false;
|
||||
});
|
||||
const statuses = await Promise.all(statusChecks);
|
||||
|
||||
list.innerHTML = mappings.map((m, i) => {
|
||||
const safe = escapeHtml(m.username);
|
||||
const isActive = m.username === currentUsername;
|
||||
const hasPassword = statuses[i];
|
||||
const pwBadge = hasPassword
|
||||
? '<span class="pw-badge locked"><i class="fas fa-lock"></i></span>'
|
||||
: '<span class="pw-badge unlocked"><i class="fas fa-unlock"></i></span>';
|
||||
return `
|
||||
<div class="identity-item ${isActive ? 'active' : ''}"
|
||||
onclick="switchIdentity('${safe.replace(/'/g, "'")}')">
|
||||
<span>${safe}</span>
|
||||
${pwBadge}
|
||||
${isActive ? '<i class="fas fa-check" style="margin-left:4px;"></i>' : ''}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
window.toggleIdentityDropdown = () => {
|
||||
const dropdown = document.getElementById('identityDropdown');
|
||||
if (dropdown) {
|
||||
dropdown.classList.toggle('show');
|
||||
// Close profile dropdown
|
||||
const profileDropdown = document.getElementById('profileDropdown');
|
||||
if (profileDropdown) profileDropdown.classList.remove('show');
|
||||
}
|
||||
};
|
||||
|
||||
window.openIdentityManager = () => {
|
||||
// Close dropdown
|
||||
const dropdown = document.getElementById('identityDropdown');
|
||||
if (dropdown) dropdown.classList.remove('show');
|
||||
// Open UUID modal from settings
|
||||
if (window.openUuidModal) {
|
||||
window.openUuidModal();
|
||||
}
|
||||
};
|
||||
|
||||
window.switchIdentity = async (username) => {
|
||||
try {
|
||||
if (!window.electronAPI || !window.electronAPI.saveUsername) return;
|
||||
|
||||
const result = await window.electronAPI.saveUsername(username);
|
||||
if (result && result.success === false) {
|
||||
throw new Error(result.error || 'Failed to switch identity');
|
||||
}
|
||||
|
||||
// Refresh identity dropdown
|
||||
await loadIdentities();
|
||||
|
||||
// Close dropdown
|
||||
const dropdown = document.getElementById('identityDropdown');
|
||||
if (dropdown) dropdown.classList.remove('show');
|
||||
|
||||
// Update settings page username field and UUID display
|
||||
const settingsInput = document.getElementById('settingsPlayerName');
|
||||
if (settingsInput) settingsInput.value = username;
|
||||
if (window.loadCurrentUuid) window.loadCurrentUuid();
|
||||
|
||||
// Update password shield icon for new identity
|
||||
if (window.updatePasswordShieldIcon) window.updatePasswordShieldIcon();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to switch identity:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Make loadIdentities available globally for settings.js to call
|
||||
window.loadIdentities = loadIdentities;
|
||||
|
||||
window.toggleProfileDropdown = () => {
|
||||
const dropdown = document.getElementById('profileDropdown');
|
||||
if (dropdown) {
|
||||
dropdown.classList.toggle('show');
|
||||
// Close identity dropdown
|
||||
const identityDropdown = document.getElementById('identityDropdown');
|
||||
if (identityDropdown) identityDropdown.classList.remove('show');
|
||||
}
|
||||
};
|
||||
|
||||
window.launch = launch;
|
||||
window.uninstallGame = uninstallGame;
|
||||
window.repairGame = repairGame;
|
||||
|
||||
1920
GUI/js/matcha.js
Normal file
1920
GUI/js/matcha.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,11 @@
|
||||
import './ui.js';
|
||||
import './install.js';
|
||||
import './launcher.js';
|
||||
import './news.js';
|
||||
import { initModsManager } from './mods.js';
|
||||
import './players.js';
|
||||
import './settings.js';
|
||||
import './logs.js';
|
||||
import { initMatcha } from './matcha.js';
|
||||
|
||||
let i18nInitialized = false;
|
||||
(async () => {
|
||||
@@ -16,10 +16,12 @@ let i18nInitialized = false;
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
updateLanguageSelector();
|
||||
initModsManager();
|
||||
initMatcha();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
updateLanguageSelector();
|
||||
initModsManager();
|
||||
initMatcha();
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -59,7 +61,8 @@ window.closeDiscordPopup = function() {
|
||||
};
|
||||
|
||||
window.joinDiscord = async function() {
|
||||
await window.electronAPI?.openExternal('https://discord.gg/Fhbb9Yk5WW');
|
||||
// await window.electronAPI?.openExternal('https://discord.gg/Fhbb9Yk5WW');
|
||||
await window.electronAPI?.openExternal('https://chat.sanhost.net/invite/Tfz4jCK4');
|
||||
|
||||
try {
|
||||
await window.electronAPI?.saveConfig({ discordPopup: true });
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
117
GUI/js/ui.js
117
GUI/js/ui.js
@@ -79,12 +79,18 @@ function setupWindowControls() {
|
||||
const header = document.querySelector('.header');
|
||||
|
||||
const profileSelector = document.querySelector('.profile-selector');
|
||||
const identitySelector = document.querySelector('.identity-selector');
|
||||
|
||||
if (profileSelector) {
|
||||
profileSelector.style.pointerEvents = 'auto';
|
||||
profileSelector.style.zIndex = '10000';
|
||||
}
|
||||
|
||||
if (identitySelector) {
|
||||
identitySelector.style.pointerEvents = 'auto';
|
||||
identitySelector.style.zIndex = '10000';
|
||||
}
|
||||
|
||||
if (windowControls) {
|
||||
windowControls.style.pointerEvents = 'auto';
|
||||
windowControls.style.zIndex = '10000';
|
||||
@@ -98,6 +104,9 @@ function setupWindowControls() {
|
||||
if (profileSelector) {
|
||||
profileSelector.style.webkitAppRegion = 'no-drag';
|
||||
}
|
||||
if (identitySelector) {
|
||||
identitySelector.style.webkitAppRegion = 'no-drag';
|
||||
}
|
||||
}
|
||||
|
||||
if (window.electronAPI) {
|
||||
@@ -129,6 +138,7 @@ function showLauncherOrInstall(isInstalled) {
|
||||
if (gameTitle) gameTitle.style.display = '';
|
||||
showPage('play-page');
|
||||
setActiveNav('play');
|
||||
loadGameInfoBar();
|
||||
} else {
|
||||
if (launcher) launcher.style.display = 'none';
|
||||
if (install) {
|
||||
@@ -729,13 +739,69 @@ async function checkGameInstallation() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadGameInfoBar() {
|
||||
try {
|
||||
const info = await window.electronAPI.getGameInfo();
|
||||
const bar = document.getElementById('game-info-bar');
|
||||
if (!bar) return;
|
||||
|
||||
const versionEl = document.getElementById('game-version-info');
|
||||
const branchEl = document.getElementById('game-branch-info');
|
||||
const lastPlayedEl = document.getElementById('game-last-played');
|
||||
|
||||
if (versionEl) {
|
||||
versionEl.classList.remove('game-info-loading');
|
||||
if (info.version) {
|
||||
const display = info.readableVersion
|
||||
? `${info.version} - ${info.readableVersion}`
|
||||
: info.version;
|
||||
versionEl.textContent = display;
|
||||
} else {
|
||||
versionEl.textContent = 'Not installed';
|
||||
}
|
||||
}
|
||||
if (branchEl) {
|
||||
branchEl.classList.remove('game-info-loading');
|
||||
const isPreRelease = info.branch === 'pre-release';
|
||||
branchEl.textContent = isPreRelease ? 'Pre-Release' : 'Release';
|
||||
branchEl.style.color = isPreRelease ? '#f59e0b' : '';
|
||||
}
|
||||
|
||||
if (lastPlayedEl && info.lastPlayed) {
|
||||
lastPlayedEl.style.display = '';
|
||||
if (!lastPlayedEl.previousElementSibling?.classList?.contains('game-info-sep')) {
|
||||
const sep = document.createElement('span');
|
||||
sep.className = 'game-info-sep';
|
||||
lastPlayedEl.before(sep);
|
||||
}
|
||||
lastPlayedEl.textContent = formatTimeAgo(info.lastPlayed);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Could not load game info:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function formatTimeAgo(timestamp) {
|
||||
const diff = Date.now() - timestamp;
|
||||
const minutes = Math.floor(diff / 60000);
|
||||
const hours = Math.floor(diff / 3600000);
|
||||
const days = Math.floor(diff / 86400000);
|
||||
|
||||
if (minutes < 1) return 'Just played';
|
||||
if (minutes < 60) return `Played ${minutes}m ago`;
|
||||
if (hours < 24) return `Played ${hours}h ago`;
|
||||
if (days < 30) return `Played ${days}d ago`;
|
||||
return `Played ${Math.floor(days / 30)}mo ago`;
|
||||
}
|
||||
|
||||
window.LauncherUI = {
|
||||
showPage,
|
||||
setActiveNav,
|
||||
showLauncherOrInstall,
|
||||
showProgress,
|
||||
hideProgress,
|
||||
updateProgress
|
||||
updateProgress,
|
||||
loadGameInfoBar
|
||||
};
|
||||
|
||||
// Make installation effects globally available
|
||||
@@ -1103,9 +1169,56 @@ function getRetryContextMessage() {
|
||||
}
|
||||
|
||||
window.openDiscordExternal = function() {
|
||||
window.electronAPI?.openExternal('https://discord.gg/Fhbb9Yk5WW');
|
||||
// window.electronAPI?.openExternal('https://discord.gg/Fhbb9Yk5WW');
|
||||
window.electronAPI?.openExternal('https://chat.sanhost.net/invite/Tfz4jCK4');
|
||||
};
|
||||
|
||||
window.toggleMaximize = toggleMaximize;
|
||||
|
||||
// Global Escape key handler for closing popups/modals/dropdowns
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key !== 'Escape') return;
|
||||
|
||||
// Custom confirm dialogs handle their own Escape — skip if one is open
|
||||
if (document.querySelector('.custom-confirm-modal')) return;
|
||||
|
||||
// Close modals (highest priority)
|
||||
const profileModal = document.getElementById('profileManagerModal');
|
||||
if (profileModal && profileModal.style.display !== 'none') {
|
||||
if (window.closeProfileManager) window.closeProfileManager();
|
||||
return;
|
||||
}
|
||||
|
||||
const uuidModal = document.getElementById('uuidModal');
|
||||
if (uuidModal && uuidModal.style.display !== 'none') {
|
||||
if (window.closeUuidModal) window.closeUuidModal();
|
||||
return;
|
||||
}
|
||||
|
||||
const discordModal = document.getElementById('discordPopupModal');
|
||||
if (discordModal && discordModal.style.display !== 'none') {
|
||||
discordModal.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const versionModal = document.getElementById('versionSelectModal');
|
||||
if (versionModal && versionModal.style.display !== 'none') {
|
||||
versionModal.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
// Close dropdowns (lower priority)
|
||||
const identityDropdown = document.getElementById('identityDropdown');
|
||||
if (identityDropdown && identityDropdown.classList.contains('show')) {
|
||||
identityDropdown.classList.remove('show');
|
||||
return;
|
||||
}
|
||||
|
||||
const profileDropdown = document.getElementById('profileDropdown');
|
||||
if (profileDropdown && profileDropdown.classList.contains('show')) {
|
||||
profileDropdown.classList.remove('show');
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', setupUI);
|
||||
|
||||
@@ -16,6 +16,8 @@ class ClientUpdateManager {
|
||||
});
|
||||
|
||||
window.electronAPI.onUpdateDownloadProgress((progress) => {
|
||||
console.log('📊 download-progress event:', progress);
|
||||
this.downloadProgressReceived = true;
|
||||
this.updateDownloadProgress(progress);
|
||||
});
|
||||
|
||||
@@ -49,7 +51,7 @@ class ClientUpdateManager {
|
||||
|
||||
const popupHTML = `
|
||||
<div id="update-popup-overlay">
|
||||
<div class="update-popup-container update-popup-pulse">
|
||||
<div class="update-popup-container">
|
||||
<div class="update-popup-header">
|
||||
<div class="update-popup-icon">
|
||||
<i class="fas fa-download"></i>
|
||||
@@ -118,11 +120,29 @@ class ClientUpdateManager {
|
||||
this.blockInterface();
|
||||
|
||||
// Show progress container immediately (auto-download is enabled)
|
||||
this.downloadProgressReceived = false;
|
||||
const progressContainer = document.getElementById('update-progress-container');
|
||||
if (progressContainer) {
|
||||
progressContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
// If no progress events arrive within 2 seconds, show indeterminate animation
|
||||
setTimeout(() => {
|
||||
if (!this.downloadProgressReceived && progressContainer) {
|
||||
const bar = document.getElementById('update-progress-bar');
|
||||
const speed = document.getElementById('update-progress-speed');
|
||||
const percent = document.getElementById('update-progress-percent');
|
||||
if (bar) {
|
||||
bar.style.width = '100%';
|
||||
bar.style.animation = 'indeterminateProgress 1.5s ease-in-out infinite';
|
||||
bar.style.opacity = '0.7';
|
||||
}
|
||||
if (speed) speed.textContent = '';
|
||||
if (percent) percent.textContent = 'Downloading...';
|
||||
console.log('⏳ No download-progress events received, showing indeterminate progress');
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
const installBtn = document.getElementById('update-install-btn');
|
||||
if (installBtn) {
|
||||
installBtn.addEventListener('click', async (e) => {
|
||||
@@ -248,6 +268,10 @@ class ClientUpdateManager {
|
||||
const progressSize = document.getElementById('update-progress-size');
|
||||
|
||||
if (progressBar && progress) {
|
||||
// Stop indeterminate animation if it was running
|
||||
progressBar.style.animation = 'none';
|
||||
progressBar.style.opacity = '1';
|
||||
|
||||
const percent = Math.round(progress.percent || 0);
|
||||
progressBar.style.width = `${percent}%`;
|
||||
|
||||
@@ -273,57 +297,35 @@ class ClientUpdateManager {
|
||||
showUpdateDownloaded(updateInfo) {
|
||||
const statusText = document.getElementById('update-status-text');
|
||||
const progressContainer = document.getElementById('update-progress-container');
|
||||
const progressBar = document.getElementById('update-progress-bar');
|
||||
const progressPercent = document.getElementById('update-progress-percent');
|
||||
const progressSpeed = document.getElementById('update-progress-speed');
|
||||
const progressSize = document.getElementById('update-progress-size');
|
||||
const buttonsContainer = document.getElementById('update-buttons-container');
|
||||
const installBtn = document.getElementById('update-install-btn');
|
||||
const downloadBtn = document.getElementById('update-download-btn');
|
||||
const skipBtn = document.getElementById('update-skip-btn');
|
||||
const footerText = document.getElementById('update-footer-text');
|
||||
const popupContainer = document.querySelector('.update-popup-container');
|
||||
|
||||
// Remove breathing/pulse animation when download is complete
|
||||
if (popupContainer) {
|
||||
popupContainer.classList.remove('update-popup-pulse');
|
||||
// Stop indeterminate animation and show 100%
|
||||
if (progressBar) {
|
||||
progressBar.style.animation = 'none';
|
||||
progressBar.style.opacity = '1';
|
||||
progressBar.style.width = '100%';
|
||||
}
|
||||
if (progressPercent) progressPercent.textContent = '100%';
|
||||
if (progressSpeed) progressSpeed.textContent = 'Complete';
|
||||
if (progressContainer) progressContainer.style.display = 'block';
|
||||
|
||||
// Hide progress after a short delay so user sees 100%
|
||||
setTimeout(() => {
|
||||
if (progressContainer) progressContainer.style.display = 'none';
|
||||
}, 1500);
|
||||
|
||||
if (statusText) {
|
||||
statusText.textContent = 'Update downloaded! Ready to install.';
|
||||
}
|
||||
|
||||
if (progressContainer) {
|
||||
progressContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
// Use platform info from main process if available, fallback to browser detection
|
||||
const autoInstallSupported = updateInfo.autoInstallSupported !== undefined
|
||||
? updateInfo.autoInstallSupported
|
||||
: navigator.platform.toUpperCase().indexOf('MAC') < 0;
|
||||
|
||||
if (!autoInstallSupported) {
|
||||
// macOS: Show manual download as primary since auto-update doesn't work
|
||||
if (statusText) {
|
||||
statusText.textContent = 'Update downloaded but auto-install may not work on macOS.';
|
||||
}
|
||||
|
||||
if (installBtn) {
|
||||
// Still show install button but as secondary option
|
||||
installBtn.classList.add('update-download-btn-secondary');
|
||||
installBtn.innerHTML = '<i class="fas fa-check" style="margin-right: 0.5rem;"></i>Try Install & Restart';
|
||||
}
|
||||
|
||||
if (downloadBtn) {
|
||||
// Make manual download primary
|
||||
downloadBtn.classList.remove('update-download-btn-secondary');
|
||||
downloadBtn.innerHTML = '<i class="fas fa-external-link-alt" style="margin-right: 0.5rem;"></i>Download Manually (Recommended)';
|
||||
}
|
||||
|
||||
if (footerText) {
|
||||
footerText.textContent = 'Auto-install often fails on macOS:';
|
||||
}
|
||||
} else {
|
||||
// Windows/Linux: Auto-install should work
|
||||
if (statusText) {
|
||||
statusText.textContent = 'Update downloaded! Ready to install.';
|
||||
}
|
||||
|
||||
if (footerText) {
|
||||
footerText.textContent = 'Click to install the update:';
|
||||
}
|
||||
if (footerText) {
|
||||
footerText.textContent = 'Click to install the update:';
|
||||
}
|
||||
|
||||
if (buttonsContainer) {
|
||||
@@ -338,7 +340,7 @@ class ClientUpdateManager {
|
||||
console.error('❌ Skip button not found in DOM!');
|
||||
}
|
||||
|
||||
console.log('✅ Update downloaded, ready to install. autoInstallSupported:', autoInstallSupported);
|
||||
console.log('✅ Update downloaded, ready to install');
|
||||
}
|
||||
|
||||
handleUpdateError(errorInfo) {
|
||||
@@ -366,9 +368,6 @@ class ClientUpdateManager {
|
||||
|
||||
if (errorMessage && errorText) {
|
||||
let message = errorInfo.message || 'An error occurred during the update process.';
|
||||
if (errorInfo.isMacSigningError) {
|
||||
message = 'Auto-update requires code signing. Please download manually.';
|
||||
}
|
||||
errorText.textContent = message;
|
||||
errorMessage.style.display = 'block';
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "اللاعبون:",
|
||||
"manageProfiles": "إدارة الملفات الشخصية",
|
||||
"manageProfiles": "إدارة",
|
||||
"manageIdentities": "إدارة",
|
||||
"identityTooltip": "اسم اللاعب ومعرّف UUID المستخدمان في اللعبة",
|
||||
"configTooltip": "إعدادات اللعبة: المودات، Java والذاكرة",
|
||||
"defaultProfile": "الافتراضي"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "سلوك المشغل",
|
||||
"closeOnStart": "إغلاق المشغل عند بدء اللعبة",
|
||||
"closeOnStartDescription": "إغلاق المشغل تلقائياً بعد تشغيل Hytale",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "تسريع الأجهزة (Hardware Acceleration)",
|
||||
"hwAccelDescription": "تفعيل تسريع الأجهزة للمشغل",
|
||||
"gameBranch": "فرع اللعبة",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "إدارة UUID",
|
||||
"currentUserUUID": "UUID المستخدم الحالي",
|
||||
"allPlayerUUIDs": "جميع معرفات UUID للاعبين",
|
||||
"generateNew": "إنشاء UUID جديد",
|
||||
"addIdentity": "إضافة هوية",
|
||||
"usernamePlaceholder": "اسم المستخدم",
|
||||
"add": "إضافة",
|
||||
"cancel": "إلغاء",
|
||||
"advanced": "متقدم",
|
||||
"loadingUUIDs": "جاري تحميل الـ UUIDs...",
|
||||
"setCustomUUID": "تعيين UUID مخصص",
|
||||
"customPlaceholder": "أدخل UUID مخصص (الصيغة: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "نسخ UUID",
|
||||
"regenerateTooltip": "إنشاء UUID جديد"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "إدارة الملفات الشخصية",
|
||||
"newProfilePlaceholder": "اسم الملف الشخصي الجديد",
|
||||
"createProfile": "إنشاء ملف شخصي"
|
||||
"configurations": {
|
||||
"modalTitle": "إدارة التكوينات",
|
||||
"newProfilePlaceholder": "اسم التكوين الجديد",
|
||||
"createProfile": "إنشاء تكوين"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "انضم إلى مجتمعنا على ديسكورد!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "تم حفظ إعداد تسريع الأجهزة",
|
||||
"hwAccelSaveFailed": "فشل حفظ إعداد تسريع الأجهزة",
|
||||
"noUsername": "لم يتم تهيئة اسم مستخدم. يرجى حفظ اسم المستخدم أولاً.",
|
||||
"identityAdded": "تمت إضافة الهوية بنجاح!",
|
||||
"identityAddFailed": "فشل في إضافة الهوية",
|
||||
"switchUsernameSuccess": "تم التبديل إلى المستخدم \"{username}\" بنجاح!",
|
||||
"switchUsernameFailed": "فشل تبديل اسم المستخدم",
|
||||
"playerNameTooLong": "يجب أن يكون اسم اللاعب 16 حرفاً أو أقل"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "جاري التثبيت...",
|
||||
"extracting": "جاري الاستخراج...",
|
||||
"verifying": "جاري التحقق...",
|
||||
"switchingProfile": "جاري تبديل الملف الشخصي...",
|
||||
"profileSwitched": "تم تبديل الملف الشخصي!",
|
||||
"switchingProfile": "جاري تبديل التكوين...",
|
||||
"profileSwitched": "تم تبديل التكوين!",
|
||||
"startingGame": "جاري بدء اللعبة...",
|
||||
"launching": "جاري التشغيل...",
|
||||
"uninstallingGame": "جاري إلغاء تثبيت اللعبة...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Spieler:",
|
||||
"manageProfiles": "Profile verwalten",
|
||||
"manageProfiles": "Verwalten",
|
||||
"manageIdentities": "Verwalten",
|
||||
"identityTooltip": "Dein Spielername und UUID im Spiel",
|
||||
"configTooltip": "Spielkonfiguration: Mods, Java- und Speichereinstellungen",
|
||||
"defaultProfile": "Standard"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Launcher-Verhalten",
|
||||
"closeOnStart": "Launcher beim Spielstart schließen",
|
||||
"closeOnStartDescription": "Schließe den Launcher automatisch, nachdem Hytale gestartet wurde",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "Hardware-Beschleunigung",
|
||||
"hwAccelDescription": "Hardware-Beschleunigung für den Launcher aktivieren",
|
||||
"gameBranch": "Spiel-Branch",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "UUID-Verwaltung",
|
||||
"currentUserUUID": "Aktuelle Benutzer-UUID",
|
||||
"allPlayerUUIDs": "Alle Spieler-UUIDs",
|
||||
"generateNew": "Neue UUID generieren",
|
||||
"addIdentity": "Identität hinzufügen",
|
||||
"usernamePlaceholder": "Benutzername",
|
||||
"add": "Hinzufügen",
|
||||
"cancel": "Abbrechen",
|
||||
"advanced": "Erweitert",
|
||||
"loadingUUIDs": "UUIDs werden geladen...",
|
||||
"setCustomUUID": "Benutzerdefinierte UUID festlegen",
|
||||
"customPlaceholder": "Benutzerdefinierte UUID eingeben (Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "UUID kopieren",
|
||||
"regenerateTooltip": "Neue UUID generieren"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Profile verwalten",
|
||||
"newProfilePlaceholder": "Neuer Profilname",
|
||||
"createProfile": "Profil erstellen"
|
||||
"configurations": {
|
||||
"modalTitle": "Konfigurationen verwalten",
|
||||
"newProfilePlaceholder": "Neuer Konfigurationsname",
|
||||
"createProfile": "Konfiguration erstellen"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Tritt unserer Discord-Community bei!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "Hardware-Beschleunigungseinstellung gespeichert",
|
||||
"hwAccelSaveFailed": "Hardware-Beschleunigungseinstellung konnte nicht gespeichert werden",
|
||||
"noUsername": "Kein Benutzername konfiguriert. Bitte speichere zuerst deinen Benutzernamen.",
|
||||
"identityAdded": "Identität erfolgreich hinzugefügt!",
|
||||
"identityAddFailed": "Fehler beim Hinzufügen der Identität",
|
||||
"switchUsernameSuccess": "Erfolgreich zu \"{username}\" gewechselt!",
|
||||
"switchUsernameFailed": "Benutzername konnte nicht gewechselt werden",
|
||||
"playerNameTooLong": "Spielername darf maximal 16 Zeichen haben"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "Installiere...",
|
||||
"extracting": "Entpacke...",
|
||||
"verifying": "Überprüfe...",
|
||||
"switchingProfile": "Profil wird gewechselt...",
|
||||
"profileSwitched": "Profil gewechselt!",
|
||||
"switchingProfile": "Konfiguration wird gewechselt...",
|
||||
"profileSwitched": "Konfiguration gewechselt!",
|
||||
"startingGame": "Spiel wird gestartet...",
|
||||
"launching": "STARTET...",
|
||||
"uninstallingGame": "Spiel wird deinstalliert...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Players:",
|
||||
"manageProfiles": "Manage Profiles",
|
||||
"manageProfiles": "Manage",
|
||||
"manageIdentities": "Manage",
|
||||
"identityTooltip": "Your player name & UUID used in-game",
|
||||
"configTooltip": "Game config: mods, Java & memory settings",
|
||||
"defaultProfile": "Default"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Launcher Behavior",
|
||||
"closeOnStart": "Close Launcher on game start",
|
||||
"closeOnStartDescription": "Automatically close the launcher after Hytale has launched",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "Hardware Acceleration",
|
||||
"hwAccelDescription": "Enable hardware acceleration for the launcher",
|
||||
"gameBranch": "Game Branch",
|
||||
@@ -162,21 +167,24 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "UUID Management",
|
||||
"currentUserUUID": "Current User UUID",
|
||||
"allPlayerUUIDs": "All Player UUIDs",
|
||||
"generateNew": "Generate New UUID",
|
||||
"addIdentity": "Add Identity",
|
||||
"usernamePlaceholder": "Username",
|
||||
"add": "Add",
|
||||
"cancel": "Cancel",
|
||||
"advanced": "Advanced",
|
||||
"loadingUUIDs": "Loading UUIDs...",
|
||||
"setCustomUUID": "Set Custom UUID",
|
||||
"setCustomUUID": "Set Custom UUID for Current User",
|
||||
"customPlaceholder": "Enter custom UUID (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
"setUUID": "Set UUID",
|
||||
"warning": "Warning: Setting a custom UUID will change your current player identity",
|
||||
"copyTooltip": "Copy UUID",
|
||||
"regenerateTooltip": "Generate New UUID"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Manage Profiles",
|
||||
"newProfilePlaceholder": "New Profile Name",
|
||||
"createProfile": "Create Profile"
|
||||
"configurations": {
|
||||
"modalTitle": "Manage Configurations",
|
||||
"newProfilePlaceholder": "New Configuration Name",
|
||||
"createProfile": "Create Configuration"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Join our Discord community!",
|
||||
@@ -230,6 +238,8 @@
|
||||
"hwAccelSaved": "Hardware acceleration setting saved",
|
||||
"hwAccelSaveFailed": "Failed to save hardware acceleration setting",
|
||||
"noUsername": "No username configured. Please save your username first.",
|
||||
"identityAdded": "Identity added successfully!",
|
||||
"identityAddFailed": "Failed to add identity",
|
||||
"switchUsernameSuccess": "Switched to \"{username}\" successfully!",
|
||||
"switchUsernameFailed": "Failed to switch username",
|
||||
"playerNameTooLong": "Player name must be 16 characters or less",
|
||||
@@ -266,8 +276,8 @@
|
||||
"installing": "Installing...",
|
||||
"extracting": "Extracting...",
|
||||
"verifying": "Verifying...",
|
||||
"switchingProfile": "Switching profile...",
|
||||
"profileSwitched": "Profile switched!",
|
||||
"switchingProfile": "Switching configuration...",
|
||||
"profileSwitched": "Configuration switched!",
|
||||
"startingGame": "Starting game...",
|
||||
"launching": "LAUNCHING...",
|
||||
"uninstallingGame": "Uninstalling game...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Jugadores:",
|
||||
"manageProfiles": "Gestionar Perfiles",
|
||||
"manageProfiles": "Gestionar",
|
||||
"manageIdentities": "Gestionar",
|
||||
"identityTooltip": "Tu nombre de jugador y UUID usados en el juego",
|
||||
"configTooltip": "Configuración del juego: mods, Java y memoria",
|
||||
"defaultProfile": "Predeterminado"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Comportamiento del Launcher",
|
||||
"closeOnStart": "Cerrar Launcher al iniciar el juego",
|
||||
"closeOnStartDescription": "Cierra automáticamente el launcher después de que Hytale se haya iniciado",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "Aceleración por Hardware",
|
||||
"hwAccelDescription": "Habilitar aceleración por hardware para el launcher",
|
||||
"gameBranch": "Rama del Juego",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "Gestión de UUID",
|
||||
"currentUserUUID": "UUID del usuario actual",
|
||||
"allPlayerUUIDs": "Todos los UUIDs de jugadores",
|
||||
"generateNew": "Generar nuevo UUID",
|
||||
"addIdentity": "Añadir identidad",
|
||||
"usernamePlaceholder": "Nombre de usuario",
|
||||
"add": "Añadir",
|
||||
"cancel": "Cancelar",
|
||||
"advanced": "Avanzado",
|
||||
"loadingUUIDs": "Cargando UUIDs...",
|
||||
"setCustomUUID": "Establecer UUID personalizado",
|
||||
"customPlaceholder": "Ingresa un UUID personalizado (formato: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "Copiar UUID",
|
||||
"regenerateTooltip": "Generar nuevo UUID"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Gestionar perfiles",
|
||||
"newProfilePlaceholder": "Nombre del nuevo perfil",
|
||||
"createProfile": "Crear perfil"
|
||||
"configurations": {
|
||||
"modalTitle": "Gestionar Configuraciones",
|
||||
"newProfilePlaceholder": "Nombre de la nueva configuración",
|
||||
"createProfile": "Crear Configuración"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "¡Únete a nuestra comunidad de Discord!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "Configuración de aceleración por hardware guardada",
|
||||
"hwAccelSaveFailed": "Error al guardar la configuración de aceleración por hardware",
|
||||
"noUsername": "No hay nombre de usuario configurado. Por favor, guarda tu nombre de usuario primero.",
|
||||
"identityAdded": "¡Identidad añadida con éxito!",
|
||||
"identityAddFailed": "Error al añadir identidad",
|
||||
"switchUsernameSuccess": "¡Cambiado a \"{username}\" con éxito!",
|
||||
"switchUsernameFailed": "Error al cambiar nombre de usuario",
|
||||
"playerNameTooLong": "El nombre del jugador debe tener 16 caracteres o menos"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "Instalando...",
|
||||
"extracting": "Extrayendo...",
|
||||
"verifying": "Verificando...",
|
||||
"switchingProfile": "Cambiando perfil...",
|
||||
"profileSwitched": "¡Perfil cambiado!",
|
||||
"switchingProfile": "Cambiando configuración...",
|
||||
"profileSwitched": "¡Configuración cambiada!",
|
||||
"startingGame": "Iniciando juego...",
|
||||
"launching": "INICIANDO...",
|
||||
"uninstallingGame": "Desinstalando juego...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Joueurs:",
|
||||
"manageProfiles": "Gérer les Profils",
|
||||
"manageProfiles": "Gérer",
|
||||
"manageIdentities": "Gérer",
|
||||
"identityTooltip": "Votre nom de joueur et UUID utilisés en jeu",
|
||||
"configTooltip": "Configuration du jeu : mods, Java et mémoire",
|
||||
"defaultProfile": "Par défaut"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Comportement du Launcher",
|
||||
"closeOnStart": "Fermer le Launcher au démarrage du jeu",
|
||||
"closeOnStartDescription": "Fermer automatiquement le launcher après le lancement d'Hytale",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "Accélération Matérielle",
|
||||
"hwAccelDescription": "Activer l'accélération matérielle pour le launcher",
|
||||
"gameBranch": "Branche du Jeu",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "Gestion UUID",
|
||||
"currentUserUUID": "UUID Utilisateur Actuel",
|
||||
"allPlayerUUIDs": "Tous les UUIDs Joueurs",
|
||||
"generateNew": "Générer Nouvel UUID",
|
||||
"addIdentity": "Ajouter une identité",
|
||||
"usernamePlaceholder": "Nom d'utilisateur",
|
||||
"add": "Ajouter",
|
||||
"cancel": "Annuler",
|
||||
"advanced": "Avancé",
|
||||
"loadingUUIDs": "Chargement des UUIDs...",
|
||||
"setCustomUUID": "Définir UUID Personnalisé",
|
||||
"customPlaceholder": "Entrez UUID personnalisé (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "Copier UUID",
|
||||
"regenerateTooltip": "Générer Nouvel UUID"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Gérer les Profils",
|
||||
"newProfilePlaceholder": "Nom du Nouveau Profil",
|
||||
"createProfile": "Créer un Profil"
|
||||
"configurations": {
|
||||
"modalTitle": "Gérer les Configurations",
|
||||
"newProfilePlaceholder": "Nom de la Nouvelle Configuration",
|
||||
"createProfile": "Créer une Configuration"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Rejoignez notre communauté Discord!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "Paramètre d'accélération matérielle sauvegardé",
|
||||
"hwAccelSaveFailed": "Échec de la sauvegarde du paramètre d'accélération matérielle",
|
||||
"noUsername": "Aucun nom d'utilisateur configuré. Veuillez d'abord enregistrer votre nom d'utilisateur.",
|
||||
"identityAdded": "Identité ajoutée avec succès !",
|
||||
"identityAddFailed": "Échec de l'ajout de l'identité",
|
||||
"switchUsernameSuccess": "Basculé vers \"{username}\" avec succès!",
|
||||
"switchUsernameFailed": "Échec du changement de nom d'utilisateur",
|
||||
"playerNameTooLong": "Le nom du joueur doit comporter 16 caractères ou moins"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "Installation...",
|
||||
"extracting": "Extraction...",
|
||||
"verifying": "Vérification...",
|
||||
"switchingProfile": "Changement de profil...",
|
||||
"profileSwitched": "Profil changé!",
|
||||
"switchingProfile": "Changement de configuration...",
|
||||
"profileSwitched": "Configuration changée !",
|
||||
"startingGame": "Démarrage du jeu...",
|
||||
"launching": "LANCEMENT...",
|
||||
"uninstallingGame": "Désinstallation du jeu...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Pemain:",
|
||||
"manageProfiles": "Kelola Profil",
|
||||
"manageProfiles": "Kelola",
|
||||
"manageIdentities": "Kelola",
|
||||
"identityTooltip": "Nama pemain & UUID yang digunakan dalam game",
|
||||
"configTooltip": "Konfigurasi game: mod, Java & pengaturan memori",
|
||||
"defaultProfile": "Default"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Perilaku Launcher",
|
||||
"closeOnStart": "Tutup launcher saat game dimulai",
|
||||
"closeOnStartDescription": "Tutup launcher secara otomatis setelah Hytale diluncurkan",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "Akselerasi Perangkat Keras",
|
||||
"hwAccelDescription": "Aktifkan akselerasi perangkat keras untuk launcher`",
|
||||
"gameBranch": "Cabang Game",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "Manajemen UUID",
|
||||
"currentUserUUID": "UUID Pengguna Saat Ini",
|
||||
"allPlayerUUIDs": "Semua UUID Pemain",
|
||||
"generateNew": "Hasilkan UUID Baru",
|
||||
"addIdentity": "Tambah Identitas",
|
||||
"usernamePlaceholder": "Nama Pengguna",
|
||||
"add": "Tambah",
|
||||
"cancel": "Batal",
|
||||
"advanced": "Lanjutan",
|
||||
"loadingUUIDs": "Memuat UUID...",
|
||||
"setCustomUUID": "Setel UUID Kustom",
|
||||
"customPlaceholder": "Masukkan UUID kustom (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "Salin UUID",
|
||||
"regenerateTooltip": "Hasilkan UUID Baru"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Kelola Profil",
|
||||
"newProfilePlaceholder": "Nama Profil Baru",
|
||||
"createProfile": "Buat Profil"
|
||||
"configurations": {
|
||||
"modalTitle": "Kelola Konfigurasi",
|
||||
"newProfilePlaceholder": "Nama Konfigurasi Baru",
|
||||
"createProfile": "Buat Konfigurasi"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Gabung komunitas Discord kami!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "Pengaturan akselerasi perangkat keras disimpan",
|
||||
"hwAccelSaveFailed": "Gagal menyimpan pengaturan akselerasi perangkat keras",
|
||||
"noUsername": "Nama pengguna belum dikonfigurasi. Silakan simpan nama pengguna terlebih dahulu.",
|
||||
"identityAdded": "Identitas berhasil ditambahkan!",
|
||||
"identityAddFailed": "Gagal menambahkan identitas",
|
||||
"switchUsernameSuccess": "Berhasil beralih ke \"{username}\"!",
|
||||
"switchUsernameFailed": "Gagal beralih nama pengguna",
|
||||
"playerNameTooLong": "Nama pemain harus 16 karakter atau kurang"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "Menginstal...",
|
||||
"extracting": "Mengekstrak...",
|
||||
"verifying": "Memverifikasi...",
|
||||
"switchingProfile": "Beralih profil...",
|
||||
"profileSwitched": "Profil dialihkan!",
|
||||
"switchingProfile": "Beralih konfigurasi...",
|
||||
"profileSwitched": "Konfigurasi dialihkan!",
|
||||
"startingGame": "Memulai game...",
|
||||
"launching": "MELUNCURKAN...",
|
||||
"uninstallingGame": "Menghapus instalasi game...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Graczy:",
|
||||
"manageProfiles": "Zarządzaj Profilami",
|
||||
"manageProfiles": "Zarządzaj",
|
||||
"manageIdentities": "Zarządzaj",
|
||||
"identityTooltip": "Twoja nazwa gracza i UUID używane w grze",
|
||||
"configTooltip": "Konfiguracja gry: mody, Java i ustawienia pamięci",
|
||||
"defaultProfile": "Domyślny"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Zachowanie Launchera",
|
||||
"closeOnStart": "Zamknij Launcher przy starcie gry",
|
||||
"closeOnStartDescription": "Automatycznie zamknij launcher po uruchomieniu Hytale",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "Przyspieszenie Sprzętowe",
|
||||
"hwAccelDescription": "Włącz przyspieszenie sprzętowe dla launchera",
|
||||
"gameBranch": "Gałąź Gry",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "Zarządzanie UUID",
|
||||
"currentUserUUID": "Aktualny UUID użytkownika",
|
||||
"allPlayerUUIDs": "Wszystkie identyfikatory UUID graczy",
|
||||
"generateNew": "Wygeneruj nowy UUID",
|
||||
"addIdentity": "Dodaj tożsamość",
|
||||
"usernamePlaceholder": "Nazwa użytkownika",
|
||||
"add": "Dodaj",
|
||||
"cancel": "Anuluj",
|
||||
"advanced": "Zaawansowane",
|
||||
"loadingUUIDs": "Ładowanie UUID...",
|
||||
"setCustomUUID": "Ustaw niestandardowy UUID",
|
||||
"customPlaceholder": "Wprowadź niestandardowy UUID (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "Kopiuj UUID",
|
||||
"regenerateTooltip": "Wygeneruj nowy UUID"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Zarządzaj Profilami",
|
||||
"newProfilePlaceholder": "Nowa Nazwa Profilu",
|
||||
"createProfile": "Utwórz Profil"
|
||||
"configurations": {
|
||||
"modalTitle": "Zarządzaj Konfiguracjami",
|
||||
"newProfilePlaceholder": "Nazwa Nowej Konfiguracji",
|
||||
"createProfile": "Utwórz Konfigurację"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Dołącz do naszej społeczności Discord!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "Zapisano ustawienie przyspieszenia sprzętowego",
|
||||
"hwAccelSaveFailed": "Nie udało się zapisać ustawienia przyspieszenia sprzętowego",
|
||||
"noUsername": "Nie skonfigurowano nazwy użytkownika. Najpierw zapisz swoją nazwę użytkownika.",
|
||||
"identityAdded": "Tożsamość dodana pomyślnie!",
|
||||
"identityAddFailed": "Nie udało się dodać tożsamości",
|
||||
"switchUsernameSuccess": "Pomyślnie przełączono na \"{username}\"!",
|
||||
"switchUsernameFailed": "Nie udało się przełączyć nazwy użytkownika",
|
||||
"playerNameTooLong": "Nazwa gracza musi mieć 16 znaków lub mniej"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "Instalowanie...",
|
||||
"extracting": "Ekstraktowanie...",
|
||||
"verifying": "Weryfikowanie...",
|
||||
"switchingProfile": "Przełączanie profilu...",
|
||||
"profileSwitched": "Profil zmieniony!",
|
||||
"switchingProfile": "Przełączanie konfiguracji...",
|
||||
"profileSwitched": "Konfiguracja zmieniona!",
|
||||
"startingGame": "Uruchamianie gry...",
|
||||
"launching": "URUCHAMIANIE...",
|
||||
"uninstallingGame": "Odinstalowywanie gry...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Jogadores:",
|
||||
"manageProfiles": "Gerenciar Perfis",
|
||||
"manageProfiles": "Gerenciar",
|
||||
"manageIdentities": "Gerenciar",
|
||||
"identityTooltip": "Seu nome de jogador e UUID usados no jogo",
|
||||
"configTooltip": "Configuração do jogo: mods, Java e memória",
|
||||
"defaultProfile": "Padrão"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Comportamento do Lançador",
|
||||
"closeOnStart": "Fechar Lançador ao iniciar o jogo",
|
||||
"closeOnStartDescription": "Fechar automaticamente o lançador após o Hytale ter sido iniciado",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "Aceleração de Hardware",
|
||||
"hwAccelDescription": "Ativar aceleração de hardware para o lançador",
|
||||
"gameBranch": "Versão do Jogo",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "Gerenciamento de UUID",
|
||||
"currentUserUUID": "UUID do usuário atual",
|
||||
"allPlayerUUIDs": "Todos os UUIDs de jogadores",
|
||||
"generateNew": "Gerar novo UUID",
|
||||
"addIdentity": "Adicionar identidade",
|
||||
"usernamePlaceholder": "Nome de usuário",
|
||||
"add": "Adicionar",
|
||||
"cancel": "Cancelar",
|
||||
"advanced": "Avançado",
|
||||
"loadingUUIDs": "Carregando UUIDs...",
|
||||
"setCustomUUID": "Definir UUID personalizado",
|
||||
"customPlaceholder": "Digite um UUID personalizado (formato: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "Copiar UUID",
|
||||
"regenerateTooltip": "Gerar novo UUID"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Gerenciar perfis",
|
||||
"newProfilePlaceholder": "Nome do novo perfil",
|
||||
"createProfile": "Criar perfil"
|
||||
"configurations": {
|
||||
"modalTitle": "Gerenciar Configurações",
|
||||
"newProfilePlaceholder": "Nome da Nova Configuração",
|
||||
"createProfile": "Criar Configuração"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Junte-se à nossa comunidade do Discord!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "Configuração de aceleração de hardware salva",
|
||||
"hwAccelSaveFailed": "Falha ao salvar configuração de aceleração de hardware",
|
||||
"noUsername": "Nenhum nome de usuário configurado. Por favor, salve seu nome de usuário primeiro.",
|
||||
"identityAdded": "Identidade adicionada com sucesso!",
|
||||
"identityAddFailed": "Falha ao adicionar identidade",
|
||||
"switchUsernameSuccess": "Alterado para \"{username}\" com sucesso!",
|
||||
"switchUsernameFailed": "Falha ao trocar nome de usuário",
|
||||
"playerNameTooLong": "O nome do jogador deve ter 16 caracteres ou menos"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "Instalando...",
|
||||
"extracting": "Extraindo...",
|
||||
"verifying": "Verificando...",
|
||||
"switchingProfile": "Alternando perfil...",
|
||||
"profileSwitched": "Perfil alternado!",
|
||||
"switchingProfile": "Alternando configuração...",
|
||||
"profileSwitched": "Configuração alternada!",
|
||||
"startingGame": "Iniciando jogo...",
|
||||
"launching": "INICIANDO...",
|
||||
"uninstallingGame": "Desinstalando jogo...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Игроки:",
|
||||
"manageProfiles": "Управлять профилями:",
|
||||
"manageProfiles": "Управление",
|
||||
"manageIdentities": "Управление",
|
||||
"identityTooltip": "Ваш игровой ник и UUID, используемые в игре",
|
||||
"configTooltip": "Конфигурация игры: моды, Java и настройки памяти",
|
||||
"defaultProfile": "По умолчанию"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Поведение лаунчера",
|
||||
"closeOnStart": "Закрыть лаунчер при старте игры",
|
||||
"closeOnStartDescription": "Автоматически закрыть лаунчер после запуска Hytale",
|
||||
"allowMultiInstance": "Несколько копий игры",
|
||||
"allowMultiInstanceDescription": "Разрешить запуск нескольких клиентов одновременно (полезно для разработки модов)",
|
||||
"hwAccel": "Аппаратное ускорение",
|
||||
"hwAccelDescription": "Включить аппаратное ускорение для лаунчера",
|
||||
"gameBranch": "Ветка игры",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "Управление UUID",
|
||||
"currentUserUUID": "UUID текущего пользователя",
|
||||
"allPlayerUUIDs": "UUID всех игроков",
|
||||
"generateNew": "Сгенерировать новый UUID",
|
||||
"addIdentity": "Добавить личность",
|
||||
"usernamePlaceholder": "Имя пользователя",
|
||||
"add": "Добавить",
|
||||
"cancel": "Отмена",
|
||||
"advanced": "Дополнительно",
|
||||
"loadingUUIDs": "Загрузка UUID...",
|
||||
"setCustomUUID": "Установить кастомный UUID",
|
||||
"customPlaceholder": "Ввести кастомный UUID (форматы: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "Скопировать UUID",
|
||||
"regenerateTooltip": "Сгенерировать новый UUID"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Управление профилями",
|
||||
"newProfilePlaceholder": "Новое имя профиля",
|
||||
"createProfile": "Создать профиль"
|
||||
"configurations": {
|
||||
"modalTitle": "Управление конфигурациями",
|
||||
"newProfilePlaceholder": "Название новой конфигурации",
|
||||
"createProfile": "Создать конфигурацию"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Присоединитесь к нашему сообществу в Discord!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "Настройка аппаратного ускорения сохранена!",
|
||||
"hwAccelSaveFailed": "Не удалось сохранить настройку аппаратного ускорения",
|
||||
"noUsername": "Имя пользователя не настроено. Пожалуйста, сначала сохраните имя пользователя.",
|
||||
"identityAdded": "Личность успешно добавлена!",
|
||||
"identityAddFailed": "Не удалось добавить личность",
|
||||
"switchUsernameSuccess": "Успешно переключено на \"{username}\"!",
|
||||
"switchUsernameFailed": "Не удалось переключить имя пользователя",
|
||||
"playerNameTooLong": "Имя игрока должно быть не более 16 символов"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "Установка...",
|
||||
"extracting": "Извлечение...",
|
||||
"verifying": "Проверка...",
|
||||
"switchingProfile": "Смена профиля...",
|
||||
"profileSwitched": "Профиль сменён!",
|
||||
"switchingProfile": "Смена конфигурации...",
|
||||
"profileSwitched": "Конфигурация изменена!",
|
||||
"startingGame": "Запуск игры...",
|
||||
"launching": "ЗАПУСК...",
|
||||
"uninstallingGame": "Удаление игры...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Spelare:",
|
||||
"manageProfiles": "Hantera profiler",
|
||||
"manageProfiles": "Hantera",
|
||||
"manageIdentities": "Hantera",
|
||||
"identityTooltip": "Ditt spelarnamn och UUID som används i spelet",
|
||||
"configTooltip": "Spelkonfiguration: moddar, Java- och minnesinställningar",
|
||||
"defaultProfile": "Standard"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Launcher-beteende",
|
||||
"closeOnStart": "Stäng launcher vid spelstart",
|
||||
"closeOnStartDescription": "Stäng automatiskt launcher efter att Hytale har startats",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "Hårdvaruacceleration",
|
||||
"hwAccelDescription": "Aktivera hårdvaruacceleration för launchern",
|
||||
"gameBranch": "Spelgren",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "UUID-hantering",
|
||||
"currentUserUUID": "Nuvarande användar-UUID",
|
||||
"allPlayerUUIDs": "Alla spelare-UUID:er",
|
||||
"generateNew": "Generera ny UUID",
|
||||
"addIdentity": "Lägg till identitet",
|
||||
"usernamePlaceholder": "Användarnamn",
|
||||
"add": "Lägg till",
|
||||
"cancel": "Avbryt",
|
||||
"advanced": "Avancerat",
|
||||
"loadingUUIDs": "Laddar UUID:er...",
|
||||
"setCustomUUID": "Ange anpassad UUID",
|
||||
"customPlaceholder": "Ange anpassad UUID (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "Kopiera UUID",
|
||||
"regenerateTooltip": "Generera ny UUID"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Hantera profiler",
|
||||
"newProfilePlaceholder": "Nytt profilnamn",
|
||||
"createProfile": "Skapa profil"
|
||||
"configurations": {
|
||||
"modalTitle": "Hantera konfigurationer",
|
||||
"newProfilePlaceholder": "Nytt konfigurationsnamn",
|
||||
"createProfile": "Skapa konfiguration"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Gå med i vår Discord-gemenskap!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "Hårdvaruaccelerationsinställning sparad",
|
||||
"hwAccelSaveFailed": "Misslyckades med att spara hårdvaruaccelerationsinställning",
|
||||
"noUsername": "Inget användarnamn konfigurerat. Vänligen spara ditt användarnamn först.",
|
||||
"identityAdded": "Identitet tillagd!",
|
||||
"identityAddFailed": "Kunde inte lägga till identitet",
|
||||
"switchUsernameSuccess": "Bytte till \"{username}\" framgångsrikt!",
|
||||
"switchUsernameFailed": "Misslyckades med att byta användarnamn",
|
||||
"playerNameTooLong": "Spelarnamnet måste vara 16 tecken eller mindre"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "Installerar...",
|
||||
"extracting": "Extraherar...",
|
||||
"verifying": "Verifierar...",
|
||||
"switchingProfile": "Byter profil...",
|
||||
"profileSwitched": "Profil bytt!",
|
||||
"switchingProfile": "Byter konfiguration...",
|
||||
"profileSwitched": "Konfiguration bytt!",
|
||||
"startingGame": "Startar spel...",
|
||||
"launching": "STARTAR...",
|
||||
"uninstallingGame": "Avinstallerar spel...",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "Oyuncular:",
|
||||
"manageProfiles": "Profilleri Yönet",
|
||||
"manageProfiles": "Yönet",
|
||||
"manageIdentities": "Yönet",
|
||||
"identityTooltip": "Oyun içinde kullanılan oyuncu adınız ve UUID'niz",
|
||||
"configTooltip": "Oyun yapılandırması: modlar, Java ve bellek ayarları",
|
||||
"defaultProfile": "Varsayılan"
|
||||
},
|
||||
"install": {
|
||||
@@ -137,6 +140,8 @@
|
||||
"closeLauncher": "Başlatıcı Davranışı",
|
||||
"closeOnStart": "Oyun başlatıldığında Başlatıcıyı Kapat",
|
||||
"closeOnStartDescription": "Hytale başlatıldıktan sonra başlatıcıyı otomatik olarak kapatın",
|
||||
"allowMultiInstance": "Allow multiple game instances",
|
||||
"allowMultiInstanceDescription": "Allow running multiple game clients at the same time (useful for mod development)",
|
||||
"hwAccel": "Donanım Hızlandırma",
|
||||
"hwAccelDescription": "Başlatıcı için donanım hızlandırmasını etkinleştir",
|
||||
"gameBranch": "Oyun Dalı",
|
||||
@@ -151,9 +156,12 @@
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "UUID Yönetimi",
|
||||
"currentUserUUID": "Geçerli Kullanıcı UUID",
|
||||
"allPlayerUUIDs": "Tüm Oyuncu UUID'leri",
|
||||
"generateNew": "Yeni UUID Oluştur",
|
||||
"addIdentity": "Kimlik Ekle",
|
||||
"usernamePlaceholder": "Kullanıcı Adı",
|
||||
"add": "Ekle",
|
||||
"cancel": "İptal",
|
||||
"advanced": "Gelişmiş",
|
||||
"loadingUUIDs": "UUID'ler yükleniyor...",
|
||||
"setCustomUUID": "Özel UUID Ayarla",
|
||||
"customPlaceholder": "Özel UUID girin (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
@@ -162,10 +170,10 @@
|
||||
"copyTooltip": "UUID'yi Kopyala",
|
||||
"regenerateTooltip": "Yeni UUID Oluştur"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Profilleri Yönet",
|
||||
"newProfilePlaceholder": "Yeni Profil Adı",
|
||||
"createProfile": "Profil Oluştur"
|
||||
"configurations": {
|
||||
"modalTitle": "Yapılandırmaları Yönet",
|
||||
"newProfilePlaceholder": "Yeni Yapılandırma Adı",
|
||||
"createProfile": "Yapılandırma Oluştur"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Discord topluluğumuza katılın!",
|
||||
@@ -219,6 +227,8 @@
|
||||
"hwAccelSaved": "Donanım hızlandırma ayarı kaydedildi",
|
||||
"hwAccelSaveFailed": "Donanım hızlandırma ayarı kaydedilemedi",
|
||||
"noUsername": "Kullanıcı adı yapılandırılmadı. Lütfen önce kullanıcı adınızı kaydedin.",
|
||||
"identityAdded": "Kimlik başarıyla eklendi!",
|
||||
"identityAddFailed": "Kimlik eklenemedi",
|
||||
"switchUsernameSuccess": "\"{username}\" adına başarıyla geçildi!",
|
||||
"switchUsernameFailed": "Kullanıcı adı değiştirilemedi",
|
||||
"playerNameTooLong": "Oyuncu adı 16 karakter veya daha az olmalıdır"
|
||||
@@ -247,8 +257,8 @@
|
||||
"installing": "Kuruluyur...",
|
||||
"extracting": "Ayıklanıyor...",
|
||||
"verifying": "Doğrulanıyor...",
|
||||
"switchingProfile": "Profil değiştiriliyor...",
|
||||
"profileSwitched": "Profil değiştirildi!",
|
||||
"switchingProfile": "Yapılandırma değiştiriliyor...",
|
||||
"profileSwitched": "Yapılandırma değiştirildi!",
|
||||
"startingGame": "Oyun başlatılıyor...",
|
||||
"launching": "BAŞLATILIYOR...",
|
||||
"uninstallingGame": "Oyun kaldırılıyor...",
|
||||
|
||||
1933
GUI/style.css
1933
GUI/style.css
File diff suppressed because it is too large
Load Diff
503
README.md
503
README.md
@@ -1,495 +1,20 @@
|
||||
<div align="center">
|
||||
# Hytale F2P Launcher (Deprecated)
|
||||
|
||||
<header>
|
||||
<h1>🎮 Hytale F2P Launcher 🚀</h1>
|
||||
<h2>💻 Cross-Platform Multiplayer 🖥️</h2>
|
||||
<h3>Available for Windows 🪟, macOS 🍎, and Linux 🐧</h3>
|
||||
<p><small>An unofficial cross-platform launcher for Hytale with automatic updates and multiplayer support!</small></p>
|
||||
</header>
|
||||
> **This project has been superseded by [F2P Evo](https://git.sanhost.net/sanasol/f2p-evo).**
|
||||
> Download the new launcher: https://git.sanhost.net/sanasol/f2p-evo/releases/latest
|
||||
|
||||
[](https://github.com/amiayweb/Hytale-F2P/releases)
|
||||
[](https://github.com/amiayweb/Hytale-F2P/releases)
|
||||
[](https://github.com/amiayweb/Hytale-F2P/releases)
|
||||
F2P Evo is a complete rewrite with Tauri 2 + Vue 3 + Rust, featuring:
|
||||
- Multi-instance and multi-identity management
|
||||
- Built-in mod manager with CurseForge integration
|
||||
- Matcha social chat with friends and game invites
|
||||
- Differential updates, password-protected accounts, 12 languages
|
||||
|
||||
[](https://github.com/amiayweb/Hytale-F2P/stargazers)
|
||||
[](https://github.com/amiayweb/Hytale-F2P/network/members)
|
||||
[](https://github.com/amiayweb/Hytale-F2P/issues)
|
||||

|
||||
## Community
|
||||
|
||||
### ⚠️ **WARNING: READ [QUICK START](#-quick-start) before Downloading & Installing the Launcher!** ⚠️
|
||||
|
||||
#### 🛑 **Found a problem? [Join the HF2P Discord](https://discord.gg/Fhbb9Yk5WW) and head to `#-⚠️-community-help`** 🛑
|
||||
|
||||
<p>
|
||||
👍 If you like the project, <b>feel free to support us via Buy Me a Coffee!</b> ☕<br>
|
||||
Any support is appreciated and helps keep the project going.
|
||||
</p>
|
||||
|
||||
<a href="https://buymeacoffee.com/hf2p">
|
||||
<img src="https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExem14OW1tanN3eHlyYmR4NW1sYmJkOTZmbmJxejdjZXB6MXY5cW12MSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9cw/TDQOtnWgsBx99cNoyH/giphy.gif" width="120">
|
||||
</a>
|
||||
|
||||
|
||||
⭐ **If you find this project useful, please give it a STAR!** ⭐
|
||||
|
||||
[](https://www.star-history.com/#amiayweb/Hytale-F2P&type=date&legend=top-left)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📸 Screenshots
|
||||
|
||||
<div align="center">
|
||||
<img src="https://i.imgur.com/wwuuMUf.png" alt="Hytale F2P Launcher" width="1000">
|
||||
<details>
|
||||
<summary><b>View Gallery</b></summary>
|
||||
<table style="width: 100%; border-spacing: 15px; border-collapse: separate;">
|
||||
<tr>
|
||||
<td align="center" style="vertical-align: top; width: 50%;">
|
||||
<b>Featured Servers 🆕</b><br>
|
||||
<img src="https://i.imgur.com/fEu9y3Z.png" alt="Hytale F2P Featured Servers" width="100%">
|
||||
</td>
|
||||
<td align="center" style="vertical-align: top; width: 50%;">
|
||||
<b>Settings Page ⚙️</b><br>
|
||||
<img src="https://i.imgur.com/l5iBzxc.png" alt="Hytale F2P Settings Page" width="100%">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="vertical-align: top; width: 50%;">
|
||||
<b>Downloadable Mods from CurseForge 🛠️</b><br>
|
||||
<img src="https://i.imgur.com/QIDbqYn.png" alt="Hytale F2P Mods Download" width="100%">
|
||||
</td>
|
||||
<td align="center" style="vertical-align: top; width: 50%;">
|
||||
<b>My Mods Menu 🔧</b><br>
|
||||
<img src="https://i.imgur.com/rjvwUfq.png" alt="Hytale F2P My Mods Menu" width="100%">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="vertical-align: top; width: 50%;">
|
||||
<b>In-Game Screenshot - Spawn Point 🎮</b><br>
|
||||
<img src="https://i.imgur.com/X8lNFQ7.png" alt="Hytale F2P In-Game Screenshot-1" width="100%">
|
||||
</td>
|
||||
<td align="center" style="vertical-align: top; width: 50%;">
|
||||
<b>In-Game Screenshot - Gameplay Terrain 🌳</b><br>
|
||||
<img src="https://i.imgur.com/3iRScPa.png" alt="Hytale F2P In-Game Screenshot-2" width="100%">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
---
|
||||
## ✨ Features
|
||||
|
||||
🎯 **Core Features**
|
||||
- 🔄 **Automatic Updates** - Smart version checking and seamless game updates
|
||||
- 💾 **Data Preservation** - Intelligent UserData backup and restoration during updates
|
||||
- 🌐 **Cross-Platform** - Full support for Windows x64, Linux x64 (X11/Wayland, SteamDeck), and macOS Silicon
|
||||
- ☕ **Java Management** - Automatic Java runtime detection and installation
|
||||
- 🎮 **Multiplayer Support** - Automatic multiplayer client installation (Windows, macOS & Linux !)
|
||||
|
||||
🛡️ **Advanced Features**
|
||||
- 📁 **Custom Installation** - Choose your own installation directory
|
||||
- 🔍 **Smart Detection** - Automatic game and dependency detection
|
||||
- 🗂️ **Mod Support** - Built-in mod management system
|
||||
- 📰 **News Feed** - Stay updated with the latest Hytale news
|
||||
- 🎨 **Modern UI** - Clean, responsive interface with dark theme
|
||||
|
||||
---
|
||||
|
||||
# 🚀 Quick Start
|
||||
|
||||
## 🖥️ System Requirements
|
||||
|
||||
### 🎮 Hytale Hardware Requirements
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Hytale is designed to be accessible while scaling for high-end performance.
|
||||
> Below are the [official system requirements for the Early Access](https://hytale.com/news/2025/12/hytale-hardware-requirements) release.
|
||||
|
||||
<div align="center">
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Component</th>
|
||||
<th>🥉 Minimum (1080p @ 30 FPS)</th>
|
||||
<th>🥈 Recommended (1080p @ 60 FPS)</th>
|
||||
<th>🥇 Best (1440p @ 60 FPS)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>🖥️ OS</b></td>
|
||||
<td colspan="3" align="center">
|
||||
Windows 10/11 (64-bit X64) | Linux (x64) | macOS (ARM64/Apple Silicon)
|
||||
<br />
|
||||
<small><i>⚠️ Note: ARM64 (Windows & Linux), macOS (x86/Intel) <b>are not supported!</b> ⚠️</i></small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>⚙️ CPU</b></td>
|
||||
<td>Intel i5-7500 / Ryzen 3 1200 / Apple M1</td>
|
||||
<td>Intel i5-10400 / Ryzen 5 3600 / Apple M2</td>
|
||||
<td>Intel i7-10700K / Ryzen 9 3800X / Apple M3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>🧠 RAM</b></td>
|
||||
<td>8GB (dGPU) / 12GB (iGPU)<sup><a href="#fn1" id="ref1">1</a></sup></td>
|
||||
<td>16 GB</td>
|
||||
<td>32 GB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>🎮 GPU</b></td>
|
||||
<td>GTX 900 / RX 400 / UHD 620</td>
|
||||
<td>GTX 1060 / RX 580 / Iris Xe</td>
|
||||
<td>RTX 30 Series / RX 7000 Series</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>💾 Storage</b></td>
|
||||
<td>20 GB (SATA SSD)</td>
|
||||
<td>20 GB (NVMe SSD)</td>
|
||||
<td>50 GB+ (NVMe SSD)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>🌐 Network</b></td>
|
||||
<td>2 Mbit/s</td>
|
||||
<td>8 Mbit/s</td>
|
||||
<td>10+ Mbit/s</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p id="fn1"><sup>Note 1</sup> Using Discrete/Dedicated GPU (dGPU) must have 8 GB RAM minimum, while using Integrated GPU (iGPU) must have 12 GB RAM.</p>
|
||||
|
||||
> [!WARNING]
|
||||
> Our launcher has **not yet** supported Offline Mode (playing Hytale without internet).
|
||||
> We will surely add the feature as soon as possible. Kindly wait for the update.
|
||||
|
||||
---
|
||||
|
||||
### 🪟 Windows Prequisites
|
||||
* **Java JDK 25:**
|
||||
* [Oracle](https://www.oracle.com/java/technologies/downloads/#jdk25-windows)
|
||||
* or [Alt 1: Adoptium](https://adoptium.net/temurin/releases/?version=25)
|
||||
* or [Alt 2: Microsoft](https://learn.microsoft.com/en-us/java/openjdk/download).
|
||||
* **Latest Visual Studio Redist:**
|
||||
* Download via [All-in-One by Techpowerup](https://www.techpowerup.com/download/visual-c-redistributable-runtime-package-all-in-one/)
|
||||
* Or [Microsoft Visual C++ Redistributable](https://aka.ms/vc14/vc_redist.x64.exe)
|
||||
|
||||
### 🐧 Linux Prequisites
|
||||
|
||||
* Make sure you have already installed newest **GPU driver** especially proprietary NVIDIA, consult your distro docs or wiki.
|
||||
* Also make sure that your GPU can be connected to EGL, try checking it first (again, consult your distro docs or wiki) before installing Hytale game via our launcher.
|
||||
* [Not needed in update v2.2.0+] Install `libpng` package to avoid `SDL3_Image` error:
|
||||
* `libpng16-16 libpng-dev` for Ubuntu/Debian-based Distro
|
||||
* `libpng libpng-devel` for Fedora/RHEL-based Distro
|
||||
* `libpng` for Arch-based Distro
|
||||
|
||||
---
|
||||
|
||||
## 📥 Installation
|
||||
|
||||
### 🪟 Windows Installation
|
||||
|
||||
1. **Prerequisites:** Ensure you have installed all [**Windows Prerequisites**](https://github.com/amiayweb/Hytale-F2P/tree/main?tab=readme-ov-file#-windows-prequisites) listed above.
|
||||
2. **Download:** Get the latest `Hytale-F2P-Launcher.exe` from the [**Releases**](https://github.com/amiayweb/Hytale-F2P/releases/latest/) page.
|
||||
3. **SmartScreen Note:** Since the executable is currently unsigned, Windows may show a "Windows protected your PC" popup.
|
||||
* Click **More info**, then click **Run anyway**.
|
||||
4. **Launch:** Once installed, you can launch the app directly from your Desktop or the Start menu.
|
||||
5. **Whitelist in Windows Firewall** [#192](https://github.com/amiayweb/Hytale-F2P/issues/192#issuecomment-3803042908)
|
||||
* Open the Windows Start Menu and search for `Allow an app through Windows Firewall`
|
||||
* Click "Change settings" (you may need Admin privileges) and Locate `HytaleClient.exe` in the list.
|
||||
* Ensure both the Private and Public checkboxes are checked. Click OK to save.
|
||||
|
||||
### 🐧 Linux Installation
|
||||
|
||||
1. **Prerequisites:** Ensure you have installed all [**Linux Prerequisites**](https://github.com/amiayweb/Hytale-F2P/tree/main?tab=readme-ov-file#-linux-prequisites) above.
|
||||
2. **Download:** Choose the package that fits your distribution from the [**Releases**](https://github.com/amiayweb/Hytale-F2P/releases/latest/) page:
|
||||
* **Universal:** `.AppImage`
|
||||
* **Arch Linux:** `.pkg.tar.zst`
|
||||
* **Fedora/RHEL/openSUSE:** `.rpm`
|
||||
* **Debian/Ubuntu:** `.deb`
|
||||
3. **Permissions & Execution:**
|
||||
* **AppImage:** Make the file executable and run it:
|
||||
```bash
|
||||
chmod +x hytale-f2p-launcher.AppImage
|
||||
./hytale-f2p-launcher.AppImage
|
||||
```
|
||||
* **Ubuntu/Debian-based or Fedora/RHEL-based:** Install the DEB/RPM:
|
||||
```bash
|
||||
# Fedora/RHEL-based
|
||||
sudo dnf install hytale-f2p-launcher.rpm
|
||||
# Debian/Ubuntu
|
||||
sudo apt install -y libasound2 libpng16-16 libpng-dev libicu76 # Not needed in v2.2.0+
|
||||
sudo dpkg -i hytale-f2p-launcher.deb
|
||||
```
|
||||
* **Arch Linux (pacman):** Install the package using:
|
||||
```bash
|
||||
# Stable Build
|
||||
sudo pacman -U hytale-f2p-launcher.pkg.tar.zst
|
||||
# Development Build
|
||||
yay -S hytale-f2p-git # or
|
||||
paru -S hytale-f2p-git
|
||||
# Manual Build
|
||||
git clone https://aur.archlinux.org/hytale-f2p-git.git
|
||||
cd hytale-f2p-git
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Make sure to adjust the filename correctly with the version and the architecture type. TIP: Use `cd` command to the package location.
|
||||
|
||||
### 🍎 macOS Installation
|
||||
|
||||
> [!NOTE]
|
||||
> Apple Silicon Users: If you are on an M1, M2, or M3 Mac, you may be prompted to install Rosetta 2 the first time you run the launcher. This is normal and required for compatibility.
|
||||
|
||||
1. **Download:** Get the latest `.dmg` file from the [**Releases**](https://github.com/amiayweb/Hytale-F2P/releases/latest/) page.
|
||||
2. **Mount:** Double-click the `.dmg` file to open it.
|
||||
3. **Install:** Drag the **Hytale F2P Launcher** icon into your **Applications** folder.
|
||||
4. **First Run:** If macOS prevents the app from opening because it is from an "unidentified developer":
|
||||
* Open **System Settings** > **Privacy & Security**.
|
||||
* Scroll down to the **Security** section.
|
||||
* Look for the message regarding "Hytale F2P Launcher" and click **Open Anyway**.
|
||||
* Authenticate with your password and click **Open**.
|
||||
|
||||
#### **Advanced macOS: Manual Installation (.zip)**
|
||||
The `.zip` version is useful for users who prefer a portable installation or need to bypass specific permission issues.
|
||||
|
||||
1. **Extract:** Download and unzip the file to your desired location (e.g., `~/Applications`).
|
||||
2. **Remove Quarantine:** macOS often "quarantines" apps downloaded via browser. If the app won't open, open **Terminal** and run:
|
||||
```bash
|
||||
xattr -rd com.apple.quarantine /path/to/Hytale-F2P-Launcher.app
|
||||
```
|
||||
> [!TIP]
|
||||
> Type the first part of the command, then drag the app icon into the Terminal window to auto-fill the path.
|
||||
|
||||
---
|
||||
|
||||
# 📢 How to Host a Server
|
||||
|
||||
## 🌐 Host your Singleplayer Server (Online-Play Feature)
|
||||
|
||||
> [!NOTE]
|
||||
> You have to play the game to host the server. See Dedicated Server section below if you want to host it without you playing as the host.
|
||||
|
||||
1. Open your Singleplayer World
|
||||
2. Pause the game (Esc) > select Online Play > Turn on `Allow Other Players to Join` > Set password if needed > Press `Save`.
|
||||
3. Check the status `Connected via UPnP`, it means you can use the Invite Codes for your friends.
|
||||
4. If your friends can't connect to your hosted Online-Play feature OR if it's showing `"Restricted (no UPnP)`, please follow the Tailscale/Playit.gg/Radmin tutorial in [SERVER.md](SERVER.md).
|
||||
|
||||
## 🖧 Host a Dedicated Server
|
||||
|
||||
> [!NOTE]
|
||||
> If you already have the patched `HytaleServer.jar` in `HytaleF2P/{release/pre-release}/package/game/latest/Server`, you can use it to host local dedicated server. Put the `.bat`/`.sh` script from our Discord server inside your `.../latest/Server` folder.
|
||||
|
||||
> [!TIP]
|
||||
> Use services like Playit.gg, Tailscale, Radmin VPN to share UDP connection if setting up router as an admin is not possible.
|
||||
|
||||
> [!WARNING]
|
||||
> `HytaleServer.rar` file is needed to set up a server on non-playing hardware (such as VPS/server hosting). Additional: **Linux ARM64** is supported for server only, not client.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> See detailed information of setting up a server here: [SERVER.md](SERVER.md).
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for detailed Troubleshooting guide.
|
||||
|
||||
---
|
||||
|
||||
## 🔨 Building from Source
|
||||
|
||||
See [BUILD.md](docs/BUILD.md) for comprehensive build instructions.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Changelog
|
||||
|
||||
### 🆕 v2.2.1
|
||||
- 👚 **Avatar Not Saving Bug Fix:** FINALLY, the long-awaited avatar saves is now working! 🙌 Show off your avatar skin in our Discord `#-media` text channel! 👀
|
||||
- 🚀 **HytaleClient Fails to Launch and Persists in Task Manager Bug Fix:** Major bug fix for all affected Windows users! No more ghost processes of `HytaleClient.exe` in Task Manager! And no more launch fail, that's hella one of an achievement 🔥 (If problem persists please create issue on Github 😢)
|
||||
- 🚦 **EPERM Bug Fix in 'Repair Game' Button:** Repair game will not produce Error Permission (EPERM) any more.
|
||||
- 🚨 **'Server Failed to Boot' Bug Fix:** Happy news for internet-limited countries (e.g. 🇷🇺 Russia, 🇹🇷 Turkey, 🇧🇷 Brazil, etc.)! The launcher now using proxy to access our patched JAR & check game version release status!🎉 Make sure you're already allow the `HytaleClient.exe` on Public & Private Windows Firewall 😉!
|
||||
- ⚡ **GPU Detection System Enhancements:** The detection system will now detect your GPU with `CimInstance` instead of `WmicObject`, which deprecated for most Windows 11 updates. Also, it's show how much your VRAM on each iGPU and dGPU! 🔍
|
||||
- ⚠️ **Failed to Deserialize Packets Bug Fix:** Shared `libzstd` library didn't get detected in Fedora/Bazzite/RHEL-based Linux Distros due to incorrect checking library order. 📑
|
||||
- 📟 **UUID Persistence Bug Fix:** Correlates to the avatar not saving bug, this fixes the persistence UUID when changing username. 🔖
|
||||
- 🌐 **Turkish Translation Fix:** 🇹🇷 Turkey players should feel at home now. 🏠
|
||||
|
||||
### 🔄 v2.2.0
|
||||
- 🔃 **Game Patches Auto-Update Improvement:** No need to install 1.5GB for every updates! Game updates now reduced to almost **~90%** (Hytale Game Update 3 to 4 only take ~150MB).
|
||||
- 🩹 **Improved Patch System Pre-Release JAR:** In previous version, only Release JAR could be patched. Now it also can be used for Pre-Release JAR!
|
||||
- 🔗 **Fix Mods Manager Issue:** Mods now can be downloaded seamlessly from the launcher, use Profiles to install your preferred mod. It will also automatically copy from selected `Profile/<profilename>` to the `Mods` folder.
|
||||
- 💾 **New User Data Location:** UserData Migration to Centralized Location. User data now preserves in `HytaleSaves` located beside `HytaleF2P` folder.
|
||||
- 🎮 **SteamDeck and Ubuntu/Debian-based Library Fix:** Replace bundled `libzstd.so` with system version to fix `glibc 2.41+` crash.
|
||||
- 🍎 **Launcher auto-update Improvement for macOS:** Fix auto-install fails on unsigned app. Added option to download the new launcher version on Github website.
|
||||
- 🌎 **New Translations**: Added France 🇲🇫, German 🇩🇪, Indonesian 🇮🇩, Russia 🇷🇺, and Swedish 🇸🇪 translations to the launcher.
|
||||
- 🔐 **Fixes Tar Vulnerability:** Updates `tar` from version `6.2.1` to `7.5.7` for vulnerability issue.
|
||||
- ⚙️ **Improved Settings Pane UI:** Settings are now shown in two columns instead of one. No more doom scrolling just to change your language.
|
||||
- ⭐ **Added Features Servers:** Don't know which one to play? Join our Featured Servers!
|
||||
- 💬 **Removed Chat Pane and Add Discord Feature:** Useless chat feature, we got Discord. Join it, NOW. Also added Discord RPC features to Github and our Discord Server. SHOW OFF TO YOUR FRIENDS.
|
||||
- 🔍 **Investigation on Avatar Not Saving Bug:** We are currently investigating this issue.
|
||||
|
||||
<details><summary>Click here to see older Changelogs</summary>
|
||||
|
||||
### 🔄 v2.1.1
|
||||
- 🛠️ **Fix Bug EPERM**: EPERM or Error Permission in creating/removing process in reinstalling is now fixed.
|
||||
- 🅰️ **Adds .pkg.tar.zst Build for Arch Users**: This Arch-package has been needed since the first release.
|
||||
- ❎ **Removes .pacman Build for Arch**: Based on the established conventions within the Arch Linux community, the file extension .pacman should not be used for package files.
|
||||
- 🌎 **New Translation**: New Polish 🇵🇱 Translation added to the Launcher.
|
||||
|
||||
### 🔄 v2.1.0
|
||||
- 🚨 **Auto-Retry Downloads and Auto-Patch Files** —
|
||||
- ⚡ **Hardware Acceleration** —
|
||||
- 🔎 **Browse CurseForge Mods** — Browsing mods now easier with our dedicated CurseForge API Key.
|
||||
- 🌎 **Fixes and Release New Translation** — Fixed 🇪🇸 🇧🇷 and added more translation for current build. Turkish 🇹🇷 language now added.
|
||||
|
||||
### 🔄 v2.0.2b *(Minor Update: Performance & Utilities)*
|
||||
- 🌎 **Language Translation** — A big welcome for Spanish 🇪🇸 and Portuguese (Brazil) 🇧🇷 players! **Language setting can be found in the bottom part of Settings pane.**
|
||||
- 💻 **Laptop/Hybrid GPU Performance Issue Fix** — Added automatic GPU detection system and options to choose which GPU will be used for the game, *specifically for Linux users*.
|
||||
- 👨💻 **In-App Logging** — Reporting bugs and issues to `Github Issues` tab or `Open A Ticket` channel in our Discord Server has been made easier for players, no more finding logs file manually.
|
||||
- 🛠️ **Repair Button** — Your game's broken? One button will fix them, go to Settings pane to Repair your game in one-click, **without losing any data**. If doing so did not fix your issue, please report it to us immediately!
|
||||
- 🐛 **Fixed Bugs** — Fixed issue [#84](https://github.com/amiayweb/Hytale-F2P/issues/84) where mods disappearing when game starts in previous launcher (v2.0.2a).
|
||||
|
||||
|
||||
### 🔄 v2.0.2a *(Minor Update)*
|
||||
- 🧑🚀 **Profiles System** — Added proper profile management: create, switch, and delete profiles. Each profile now has its own **isolated mod list**.
|
||||
- 🔒 **Mod Isolation** — Fixed ModManager so mods are **strictly scoped to the active profile**. Browsing and installing now only affects the selected profile.
|
||||
- 🚨 **Critical Path Fix** — Resolved a macOS bug where mods were being saved to a Windows path (`~/AppData/Local`) instead of `~/Library/Application Support`.
|
||||
- 🛡️ **Stability Improvements** — Added an **auto-sync step before every launch** to ensure the physical mods folder always matches the active profile.
|
||||
- 🎨 **UI Enhancements** — Added a **profile selector dropdown** and a **profile management modal**.
|
||||
|
||||
### 🔄 v2.0.2
|
||||
- 🎮 **Discord RPC Integration** - Added Discord Rich Presence with toggle in settings (enabled by default)
|
||||
- 🌐 **Cross-Platform Multiplayer** - Added multiplayer patch support for Windows, Linux, and macOS
|
||||
- 🎨 **Chat Improvements** - Simplified chat color system
|
||||
- 🏆 **Badge System Expansion** - Added new FOUNDER UUID to the badge system
|
||||
- 🔧 **Progress Bar Fix** - Resolved issue where download progress bar stayed active after game launch
|
||||
- 🐛 **Bug Fixes**: General fixes
|
||||
|
||||
### 🔄 v2.0.1
|
||||
- 📊 **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
|
||||
- 🐧 **Enhanced Linux Support** - Full Wayland and X11 compatibility
|
||||
- 🔄 **Multiplayer Auto-Install** - Automatic multiplayer client setup on updates (Windows)
|
||||
- 📡 **API Integration** - Real-time version checking and client management
|
||||
- 🎨 **UI Improvements** - Added contributor credits footer
|
||||
- 🔄 **Complete Launcher Overhaul** - Total redesign of the launcher architecture and interface
|
||||
- 🗂️ **Integrated Mod Manager** - Built-in mod installation, management
|
||||
- 💬 **Community Chat System** - Real-time chat for launcher users to connect and communicate
|
||||
|
||||
### 🔧 v1.0.1
|
||||
- 📁 **Custom Installation** - Choose installation directory with file browser
|
||||
- 🏠 **Always on Top** - Launcher stays visible during installation
|
||||
- 🧠 **Smart Detection** - Automatic game detection and UI adaptation
|
||||
- 🗑️ **Uninstall Feature** - Easy game removal with one click
|
||||
- 🔄 **Dynamic UI** - "INSTALL" vs "PLAY" button based on game state
|
||||
- 🛠️ **Path Management** - Proper custom directory handling
|
||||
- 💫 **UI Polish** - Improved layout and overflow prevention
|
||||
|
||||
### 🎉 v1.0.0 *(Initial Release)*
|
||||
- 🎮 **Offline Gameplay** - Play Hytale without internet connection
|
||||
- ⚡ **Auto Installation** - One-click game setup
|
||||
- ☕ **Java Management** - Automatic Java runtime handling
|
||||
- 🎨 **Modern Interface** - Clean, intuitive design
|
||||
- 🌟 **First Release** - Core launcher functionality
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 👥 Contributors
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Made with ❤️ by the community**
|
||||
|
||||
[](https://github.com/amiayweb/Hytale-F2P/graphs/contributors)
|
||||
|
||||
</div>
|
||||
|
||||
### 🏆 Project Creator
|
||||
- [**@amiayweb**](https://github.com/amiayweb) - *Lead Developer & Project Creator*
|
||||
- [**@Relyz1993**](https://github.com/Relyz1993) - *Server Helper & Second Developer & Project Creator*
|
||||
|
||||
### 🌟 Main Contributors
|
||||
- [**@sanasol**](https://github.com/sanasol) - *Main Issues fixer | Multiplayer Patcher*
|
||||
- [**@Terromur**](https://github.com/Terromur) - *Main Issues fixer | Beta tester*
|
||||
- [**@fazrigading**](https://github.com/fazrigading) - *Main Issues fixer | Beta tester | Github Release Maintainer*
|
||||
- [**@ericiskoolbeans**](https://github.com/ericiskoolbeans) - *Beta Tester*
|
||||
- [**@chasem-dev**](https://github.com/chasem-dev) - *Issues fixer*
|
||||
- [**@Rahul-Sahani04**](https://github.com/Rahul-Sahani04) - *Issues fixer*
|
||||
- [**@xSamiVS**](https://github.com/xSamiVS) - *Issues fixer | Language Translator*
|
||||
|
||||
#### 🎟️ Fresh Contributors
|
||||
- [**@GreenKod**](https://github.com/GreenKod) - *Code refractor*
|
||||
- [**@Citeli-py**](https://github.com/Citeli-py) - *Linux fix & packages version in early release*
|
||||
- [**@crimera**](https://github.com/crimera) - *Generate new UUID for new username string feature*
|
||||
- [**@letha11**](https://github.com/letha11) - *CSS filename typo fix*
|
||||
- [**@colbster937**](https://github.com/colbster937) - *Icon upscaler*
|
||||
- [**@ArnavSingh77**](https://github.com/ArnavSingh77) - *Close game launcher on start feature, improve app termination behavior*
|
||||
- [**@TalesAmaral**](https://github.com/TalesAmaral) - *BUILD.md link fix in README.md*
|
||||
|
||||
#### 🌐 Language Translators
|
||||
- [**@BlackSystemCoder**](https://github.com/BlackSystemCoder) - *Russian Language Translator*
|
||||
- [**@walti0**](https://github.com/walti0) - *Polish Language Translator*
|
||||
|
||||
---
|
||||
|
||||
## 📞 Contact Information
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Questions? Ads? Collaboration? Endorsement? Other business-related?**
|
||||
Message the founders at https://discord.gg/Fhbb9Yk5WW
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ⚖️ Legal Disclaimer
|
||||
|
||||
<div align="center">
|
||||
|
||||
⚠️ **Important Notice** ⚠️
|
||||
|
||||
</div>
|
||||
|
||||
This launcher is created for **educational purposes only**.
|
||||
|
||||
🏛️ **Not Official** - This is an independent fan project **not affiliated with, endorsed by, or associated with** Hypixel Studios or Hytale.
|
||||
|
||||
🛡️ **No Warranty** - This software is provided **"as is"** without any warranty of any kind.
|
||||
|
||||
📝 **Responsibility** - The authors take no responsibility for how this software is used.
|
||||
|
||||
🛑 **Takedown Policy** - If Hypixel Studios or Hytale requests removal, this project will be taken down immediately.
|
||||
|
||||
❤️ **Support Official** - Please support the official game by **purchasing** it legally when available.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**⭐ Star this project if you found it helpful! ⭐**
|
||||
|
||||
*Made with ❤️ by [@amiayweb](https://github.com/amiayweb) and the legendary contributors with amazing community*
|
||||
|
||||
[](https://www.star-history.com/#amiayweb/Hytale-F2P&type=date&legend=top-left)
|
||||
</div>
|
||||
- Community Chat: https://chat.sanhost.net/invite/Tfz4jCK4
|
||||
- Telegram Group: https://t.me/sanhostnet
|
||||
- Telegram Channel: https://t.me/hf2p_og
|
||||
|
||||
## Credits
|
||||
|
||||
Original launcher by [@amiayweb](https://github.com/amiayweb) and contributors.
|
||||
|
||||
67
SERVER.md
67
SERVER.md
@@ -1,8 +1,10 @@
|
||||
# 🎮 Hytale F2P Server Guide
|
||||
> **Deprecated:** This guide is for the legacy Hytale F2P launcher. For the new F2P Evo launcher, see [F2P Evo Server Guide](https://git.sanhost.net/sanasol/f2p-evo/src/branch/main/SERVER.md).
|
||||
|
||||
# Hytale F2P Server Guide
|
||||
|
||||
Play with friends online! This guide covers both easy in-game hosting and advanced dedicated server setup.
|
||||
|
||||
### **DOWNLOAD SERVER FILES (JAR/RAR/SCRIPTS) HERE: https://discord.gg/Fhbb9Yk5WW**
|
||||
**Need server files?** Download from [F2P Evo releases](https://git.sanhost.net/sanasol/f2p-evo/releases/latest) or join [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4).
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
@@ -33,43 +35,7 @@ Play with friends online! This guide covers both easy in-game hosting and advanc
|
||||
* [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 🆕
|
||||
|
||||
@@ -100,8 +66,7 @@ Enjoy **20% OFF** all new game servers, **available now for a limited time!** Do
|
||||
|
||||
"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.
|
||||
|
||||
Kindly support us via [our Buy Me a Coffee link](https://buymeacoffee.com/hf2p) if you think our launcher took a big part of developing this Hytale community for the love of the game itself.
|
||||
**We will always advertise, always pushing, and always asking, to every users of this launcher to purchase the original game to help the official development of Hytale**.
|
||||
**We encourage all users to purchase the official game to support its development.**
|
||||
|
||||
### Server Directory Location
|
||||
|
||||
@@ -247,7 +212,7 @@ 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
|
||||
* 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.
|
||||
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`
|
||||
6. Download `start.bat` (Windows) or `start.sh` (Linux/macOS) from the [server/ directory](server/)
|
||||
7. 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. 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.
|
||||
@@ -282,9 +247,8 @@ For 24/7 servers, custom configurations, or hosting on a VPS/dedicated machine.
|
||||
2. `Assets.zip`
|
||||
3. `run_scripts_with_token.bat` for Windows or `run_scripts_with_token.sh` for macOS/Linux
|
||||
|
||||
> [!NOTE]
|
||||
> The `HytaleServer.rar` available on our Discord Server (`#open-public-server` channel; typo on the Discord, not `zip`) includes all of the prequisites.
|
||||
> Unfortunately, the JAR inside the RAR isn't updated so you'll need to download the JAR from the link on Discord.
|
||||
> [!TIP]
|
||||
> The easiest way is to use the one-click scripts from the [server/](server/) directory. They handle downloading everything automatically.
|
||||
|
||||
> [!TIP]
|
||||
> You can copy `Assets.zip` generated from the launcher to be used for the dedicated server. It's located in `HytaleF2P/release/package/game/latest`.
|
||||
@@ -557,10 +521,10 @@ docker run -d \
|
||||
-e HYTALE_AUTH_DOMAIN=auth.sanasol.ws \
|
||||
-e HYTALE_SERVER_NAME="My Server" \
|
||||
-e JVM_XMX=8G \
|
||||
ghcr.io/hybrowse/hytale-server-docker:latest
|
||||
ghcr.io/sanasol/hytale-server-docker:latest
|
||||
```
|
||||
|
||||
See [Docker documentation](https://github.com/Hybrowse/hytale-server-docker) for details.
|
||||
See [Docker documentation](https://github.com/sanasol/hytale-server-docker) for details.
|
||||
|
||||
---
|
||||
|
||||
@@ -575,9 +539,12 @@ See [Docker documentation](https://github.com/Hybrowse/hytale-server-docker) for
|
||||
|
||||
# Credits
|
||||
|
||||
- Hytale F2P Project
|
||||
- [Hybrowse Docker Image](https://github.com/Hybrowse/hytale-server-docker)
|
||||
- Auth Server: sanasol.ws
|
||||
- F2P Evo Project
|
||||
- [Server Docker Image](https://github.com/sanasol/hytale-server-docker)
|
||||
- [Auth Server](https://github.com/sanasol/hytale-auth-server)
|
||||
- Auth domain: auth.sanasol.ws
|
||||
|
||||
**Need help?** [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4) | [TG Group](https://t.me/sanhostnet) | [TG Channel](https://t.me/hf2p_og)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
> **Deprecated:** This guide is for the legacy Hytale F2P launcher. For the new F2P Evo launcher, see [F2P Evo Troubleshooting](https://git.sanhost.net/sanasol/f2p-evo/src/branch/main/TROUBLESHOOTING.md).
|
||||
|
||||
# Hytale F2P Launcher - Troubleshooting Guide
|
||||
|
||||
This guide covers common issues and their solutions. If your issue isn't listed here, please check [existing issues](https://github.com/amiayweb/Hytale-F2P/issues) or join our [Discord](https://discord.gg/Fhbb9Yk5WW).
|
||||
This guide covers common issues and their solutions. If your issue isn't listed here, please check [existing issues](https://git.sanhost.net/sanasol/f2p-evo/issues) or join [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4) | [TG Group](https://t.me/sanhostnet) | [TG Channel](https://t.me/hf2p_og).
|
||||
|
||||
---
|
||||
|
||||
@@ -205,7 +207,7 @@ domain: 'SQRLCodeSignatureErrorDomain'
|
||||
```
|
||||
|
||||
**Solution - Manual update:**
|
||||
1. Download the latest version manually from [Releases](https://github.com/amiayweb/Hytale-F2P/releases/latest)
|
||||
1. Download the latest version manually from [Releases](https://git.sanhost.net/sanasol/f2p-evo/releases/latest)
|
||||
2. Backup your data first (see [Backup Locations](#backup-locations))
|
||||
3. Install the fresh download
|
||||
|
||||
@@ -436,8 +438,8 @@ Game sessions have a 10-hour TTL. This is by design for security.
|
||||
|
||||
If your issue isn't resolved by this guide:
|
||||
|
||||
1. **Check existing issues:** [GitHub Issues](https://github.com/amiayweb/Hytale-F2P/issues)
|
||||
2. **Join Discord:** [discord.gg/Fhbb9Yk5WW](https://discord.gg/Fhbb9Yk5WW)
|
||||
1. **Check existing issues:** [Issues](https://git.sanhost.net/sanasol/f2p-evo/issues)
|
||||
2. **Join the community:** [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4) | [TG Group](https://t.me/sanhostnet) | [TG Channel](https://t.me/hf2p_og)
|
||||
3. **Open a new issue** with:
|
||||
- Your operating system and version
|
||||
- Launcher version
|
||||
|
||||
@@ -529,7 +529,7 @@ function getAllUuidMappingsArray() {
|
||||
* Validates UUID format before saving
|
||||
* Preserves original case of username
|
||||
*/
|
||||
function setUuidForUser(username, uuid) {
|
||||
function setUuidForUser(username, uuid, { force = false } = {}) {
|
||||
const { validate: validateUuid } = require('uuid');
|
||||
|
||||
if (!username || typeof username !== 'string' || !username.trim()) {
|
||||
@@ -543,15 +543,29 @@ function setUuidForUser(username, uuid) {
|
||||
const displayName = username.trim();
|
||||
const normalizedLookup = displayName.toLowerCase();
|
||||
|
||||
// 1. Update UUID store (source of truth)
|
||||
// 1. Check for existing entries — reject overwrite unless forced
|
||||
migrateUuidStoreIfNeeded();
|
||||
const uuidStore = loadUuidStore();
|
||||
const storeKey = Object.keys(uuidStore).find(k => k.toLowerCase() === normalizedLookup);
|
||||
if (storeKey && uuidStore[storeKey] !== uuid && !force) {
|
||||
console.log(`[Config] Rejected UUID overwrite for "${displayName}": existing ${uuidStore[storeKey]}, attempted ${uuid}`);
|
||||
return { success: false, error: 'duplicate', existingUuid: uuidStore[storeKey] };
|
||||
}
|
||||
// Check if UUID already used by a different name
|
||||
if (!force) {
|
||||
const existingByUuid = Object.entries(uuidStore).find(([k, v]) => v.toLowerCase() === uuid.toLowerCase() && k.toLowerCase() !== normalizedLookup);
|
||||
if (existingByUuid) {
|
||||
console.log(`[Config] Rejected duplicate UUID for "${displayName}": UUID ${uuid} already used by "${existingByUuid[0]}"`);
|
||||
return { success: false, error: 'uuid_in_use', existingUsername: existingByUuid[0] };
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Update UUID store (source of truth)
|
||||
if (storeKey) delete uuidStore[storeKey];
|
||||
uuidStore[displayName] = uuid;
|
||||
saveUuidStore(uuidStore);
|
||||
|
||||
// 2. Update config.json (backward compat)
|
||||
// 3. Update config.json (backward compat)
|
||||
const config = loadConfig();
|
||||
const userUuids = config.userUuids || {};
|
||||
const existingKey = Object.keys(userUuids).find(k => k.toLowerCase() === normalizedLookup);
|
||||
@@ -560,7 +574,7 @@ function setUuidForUser(username, uuid) {
|
||||
saveConfig({ userUuids });
|
||||
|
||||
console.log(`[Config] UUID set for "${displayName}": ${uuid}`);
|
||||
return uuid;
|
||||
return { success: true, uuid };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -619,7 +633,7 @@ function resetCurrentUserUuid() {
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const newUuid = uuidv4();
|
||||
|
||||
return setUuidForUser(username, newUuid);
|
||||
return setUuidForUser(username, newUuid, { force: true });
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -708,6 +722,15 @@ function loadLauncherHardwareAcceleration() {
|
||||
return config.launcherHardwareAcceleration !== undefined ? config.launcherHardwareAcceleration : true;
|
||||
}
|
||||
|
||||
function saveAllowMultiInstance(enabled) {
|
||||
saveConfig({ allowMultiInstance: !!enabled });
|
||||
}
|
||||
|
||||
function loadAllowMultiInstance() {
|
||||
const config = loadConfig();
|
||||
return config.allowMultiInstance !== undefined ? config.allowMultiInstance : false;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MODS MANAGEMENT
|
||||
// =============================================================================
|
||||
@@ -863,7 +886,7 @@ function checkLaunchReady() {
|
||||
// =============================================================================
|
||||
|
||||
const DEFAULT_WRAPPER_CONFIG = {
|
||||
stripFlags: ['-XX:+UseCompactObjectHeaders'],
|
||||
stripFlags: [],
|
||||
injectArgs: [
|
||||
{ arg: '--disable-sentry', condition: 'server' }
|
||||
]
|
||||
@@ -1064,6 +1087,48 @@ function _generateWindowsWrapper(stripFlags, alwaysArgs, serverArgs) {
|
||||
return lines.join('\r\n');
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MATCHA SOCIAL AUTH
|
||||
// =============================================================================
|
||||
|
||||
function saveMatchaToken(token) {
|
||||
saveConfig({ matchaToken: token || null });
|
||||
}
|
||||
|
||||
function loadMatchaToken() {
|
||||
const config = loadConfig();
|
||||
return config.matchaToken || null;
|
||||
}
|
||||
|
||||
function saveMatchaHandle(handle) {
|
||||
saveConfig({ matchaHandle: handle || null });
|
||||
}
|
||||
|
||||
function loadMatchaHandle() {
|
||||
const config = loadConfig();
|
||||
return config.matchaHandle || null;
|
||||
}
|
||||
|
||||
function saveMatchaUserId(id) {
|
||||
saveConfig({ matchaUserId: id || null });
|
||||
}
|
||||
|
||||
function loadMatchaUserId() {
|
||||
const config = loadConfig();
|
||||
return config.matchaUserId || null;
|
||||
}
|
||||
|
||||
function clearMatchaAuth() {
|
||||
const config = loadConfig();
|
||||
delete config.matchaToken;
|
||||
delete config.matchaUserId;
|
||||
delete config.matchaHandle;
|
||||
const data = JSON.stringify(config, null, 2);
|
||||
fs.writeFileSync(CONFIG_TEMP, data, 'utf8');
|
||||
if (fs.existsSync(CONFIG_FILE)) fs.copyFileSync(CONFIG_FILE, CONFIG_BACKUP);
|
||||
fs.renameSync(CONFIG_TEMP, CONFIG_FILE);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// EXPORTS
|
||||
// =============================================================================
|
||||
@@ -1105,6 +1170,8 @@ module.exports = {
|
||||
loadCloseLauncherOnStart,
|
||||
saveLauncherHardwareAcceleration,
|
||||
loadLauncherHardwareAcceleration,
|
||||
saveAllowMultiInstance,
|
||||
loadAllowMultiInstance,
|
||||
|
||||
// Mods
|
||||
saveModsToConfig,
|
||||
@@ -1137,6 +1204,15 @@ module.exports = {
|
||||
resetWrapperConfig,
|
||||
generateWrapperScript,
|
||||
|
||||
// Matcha Social
|
||||
saveMatchaToken,
|
||||
loadMatchaToken,
|
||||
saveMatchaHandle,
|
||||
loadMatchaHandle,
|
||||
saveMatchaUserId,
|
||||
loadMatchaUserId,
|
||||
clearMatchaAuth,
|
||||
|
||||
// Constants
|
||||
CONFIG_FILE,
|
||||
UUID_STORE_FILE
|
||||
|
||||
@@ -20,6 +20,8 @@ const {
|
||||
|
||||
saveLauncherHardwareAcceleration,
|
||||
loadLauncherHardwareAcceleration,
|
||||
saveAllowMultiInstance,
|
||||
loadAllowMultiInstance,
|
||||
|
||||
loadConfig,
|
||||
saveConfig,
|
||||
@@ -151,6 +153,10 @@ module.exports = {
|
||||
saveLauncherHardwareAcceleration,
|
||||
loadLauncherHardwareAcceleration,
|
||||
|
||||
// Multi-instance functions
|
||||
saveAllowMultiInstance,
|
||||
loadAllowMultiInstance,
|
||||
|
||||
// Config functions
|
||||
loadConfig,
|
||||
saveConfig,
|
||||
|
||||
@@ -30,6 +30,7 @@ const { syncModsForCurrentProfile } = require('./modManager');
|
||||
const { getUserDataPath } = require('../utils/userDataMigration');
|
||||
const { syncServerList } = require('../utils/serverListSync');
|
||||
const { killGameProcesses } = require('./gameManager');
|
||||
const { loadAllowMultiInstance } = require('../core/config');
|
||||
|
||||
// Client patcher for custom auth server (sanasol.ws)
|
||||
let clientPatcher = null;
|
||||
@@ -42,24 +43,51 @@ try {
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
// Fetch tokens from the auth server (properly signed with server's Ed25519 key)
|
||||
async function fetchAuthTokens(uuid, name) {
|
||||
async function fetchAuthTokens(uuid, name, password) {
|
||||
const authServerUrl = getAuthServerUrl();
|
||||
try {
|
||||
console.log(`Fetching auth tokens from ${authServerUrl}/game-session/child`);
|
||||
|
||||
const bodyData = {
|
||||
uuid: uuid,
|
||||
name: name,
|
||||
scopes: ['hytale:server', 'hytale:client']
|
||||
};
|
||||
if (password) bodyData.password = password;
|
||||
|
||||
const response = await fetch(`${authServerUrl}/game-session/child`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
uuid: uuid,
|
||||
name: name,
|
||||
scopes: ['hytale:server', 'hytale:client']
|
||||
})
|
||||
body: JSON.stringify(bodyData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errBody = await response.json().catch(() => ({}));
|
||||
if (response.status === 401 && errBody.password_required) {
|
||||
const err = new Error('Password required');
|
||||
err.passwordRequired = true;
|
||||
err.attemptsRemaining = errBody.attemptsRemaining;
|
||||
throw err;
|
||||
}
|
||||
if (response.status === 429) {
|
||||
const err = new Error('Too many failed attempts. Try again later.');
|
||||
err.lockedOut = true;
|
||||
err.lockoutSeconds = errBody.lockoutSeconds;
|
||||
throw err;
|
||||
}
|
||||
if (response.status === 403 && errBody.username_taken) {
|
||||
const err = new Error('This username is reserved by another player who has set a password. Please use a different name.');
|
||||
err.usernameTaken = true;
|
||||
throw err;
|
||||
}
|
||||
if (response.status === 403 && errBody.name_locked) {
|
||||
const err = new Error(`This UUID is locked to username "${errBody.registeredName}". Change your identity name to match.`);
|
||||
err.nameLocked = true;
|
||||
err.registeredName = errBody.registeredName;
|
||||
throw err;
|
||||
}
|
||||
throw new Error(`Auth server returned ${response.status}`);
|
||||
}
|
||||
|
||||
@@ -76,10 +104,12 @@ async function fetchAuthTokens(uuid, name) {
|
||||
if (payload.username && payload.username !== name && name !== 'Player') {
|
||||
console.warn(`[Auth] Token username mismatch: token has "${payload.username}", expected "${name}". Retrying...`);
|
||||
// Retry once with explicit name
|
||||
const retryBody = { uuid: uuid, name: name, scopes: ['hytale:server', 'hytale:client'] };
|
||||
if (password) retryBody.password = password;
|
||||
const retryResponse = await fetch(`${authServerUrl}/game-session/child`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ uuid: uuid, name: name, scopes: ['hytale:server', 'hytale:client'] })
|
||||
body: JSON.stringify(retryBody)
|
||||
});
|
||||
if (retryResponse.ok) {
|
||||
const retryData = await retryResponse.json();
|
||||
@@ -98,6 +128,10 @@ async function fetchAuthTokens(uuid, name) {
|
||||
console.log('Auth tokens received from server');
|
||||
return { identityToken, sessionToken };
|
||||
} catch (error) {
|
||||
// Re-throw authentication errors — must not fall back to local tokens
|
||||
if (error.passwordRequired || error.lockedOut || error.usernameTaken || error.nameLocked) {
|
||||
throw error;
|
||||
}
|
||||
console.error('Failed to fetch auth tokens:', error.message);
|
||||
// Fallback to local generation if server unavailable
|
||||
return generateLocalTokens(uuid, name);
|
||||
@@ -146,7 +180,7 @@ function generateLocalTokens(uuid, name) {
|
||||
};
|
||||
}
|
||||
|
||||
async function launchGame(playerNameOverride = null, progressCallback, javaPathOverride, installPathOverride, gpuPreference = 'auto', branchOverride = null) {
|
||||
async function launchGame(playerNameOverride = null, progressCallback, javaPathOverride, installPathOverride, gpuPreference = 'auto', branchOverride = null, options = {}) {
|
||||
// ==========================================================================
|
||||
// CACHE INVALIDATION: Clear proxyClient module cache to force fresh .env load
|
||||
// This prevents stale cached values from affecting multiple launch attempts
|
||||
@@ -255,11 +289,12 @@ async function launchGame(playerNameOverride = null, progressCallback, javaPathO
|
||||
const uuid = getUuidForUser(playerName);
|
||||
console.log(`[Launcher] UUID for "${playerName}": ${uuid} (verify this stays constant across launches)`);
|
||||
|
||||
// Fetch tokens from auth server
|
||||
// Fetch tokens from auth server (with password if provided)
|
||||
if (progressCallback) {
|
||||
progressCallback('Fetching authentication tokens...', null, null, null, null);
|
||||
}
|
||||
const { identityToken, sessionToken } = await fetchAuthTokens(uuid, playerName);
|
||||
const launchPassword = options?.password || null;
|
||||
const { identityToken, sessionToken } = await fetchAuthTokens(uuid, playerName, launchPassword);
|
||||
|
||||
// Patch client and server binaries to use custom auth server (BEFORE signing on macOS)
|
||||
// FORCE patch on every launch to ensure consistency
|
||||
@@ -410,6 +445,17 @@ async function launchGame(playerNameOverride = null, progressCallback, javaPathO
|
||||
|
||||
const env = { ...process.env };
|
||||
|
||||
// Linux: Add Client directory to LD_LIBRARY_PATH so the dynamic linker can find
|
||||
// bundled native libraries (e.g. libSDL3_image.so.0). The .NET DllImport only tries
|
||||
// bare names like "SDL3_image.so" which don't match versioned .so.0 files.
|
||||
// LD_LIBRARY_PATH lets dlopen() find them via standard library resolution.
|
||||
if (process.platform === 'linux') {
|
||||
const clientDir = path.dirname(clientPath);
|
||||
const existing = env.LD_LIBRARY_PATH || '';
|
||||
env.LD_LIBRARY_PATH = existing ? `${clientDir}:${existing}` : clientDir;
|
||||
console.log(`Linux: LD_LIBRARY_PATH includes ${clientDir}`);
|
||||
}
|
||||
|
||||
const waylandEnv = setupWaylandEnvironment();
|
||||
Object.assign(env, waylandEnv);
|
||||
const gpuEnv = setupGpuEnvironment(gpuPreference);
|
||||
@@ -464,8 +510,10 @@ async function launchGame(playerNameOverride = null, progressCallback, javaPathO
|
||||
}
|
||||
|
||||
// Kill any stalled game processes from a previous launch to prevent file locks
|
||||
// and "game already running" issues
|
||||
await killGameProcesses();
|
||||
// and "game already running" issues (skip if multi-instance mode is enabled)
|
||||
if (!loadAllowMultiInstance()) {
|
||||
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.
|
||||
@@ -575,7 +623,7 @@ async function launchGame(playerNameOverride = null, progressCallback, javaPathO
|
||||
}
|
||||
}
|
||||
|
||||
async function launchGameWithVersionCheck(playerNameOverride = null, progressCallback, javaPathOverride, installPathOverride, gpuPreference = 'auto', branchOverride = null) {
|
||||
async function launchGameWithVersionCheck(playerNameOverride = null, progressCallback, javaPathOverride, installPathOverride, gpuPreference = 'auto', branchOverride = null, options = {}) {
|
||||
try {
|
||||
// ==========================================================================
|
||||
// PRE-LAUNCH VALIDATION: Check username is configured
|
||||
@@ -648,7 +696,7 @@ async function launchGameWithVersionCheck(playerNameOverride = null, progressCal
|
||||
progressCallback('Launching game...', 80, null, null, null);
|
||||
}
|
||||
|
||||
const launchResult = await launchGame(playerNameOverride, progressCallback, javaPathOverride, installPathOverride, gpuPreference, branch);
|
||||
const launchResult = await launchGame(playerNameOverride, progressCallback, javaPathOverride, installPathOverride, gpuPreference, branch, options);
|
||||
|
||||
// Ensure we always return a result
|
||||
if (!launchResult) {
|
||||
@@ -662,6 +710,10 @@ async function launchGameWithVersionCheck(playerNameOverride = null, progressCal
|
||||
if (progressCallback) {
|
||||
progressCallback(`Error: ${error.message}`, -1, null, null, null);
|
||||
}
|
||||
// Re-throw authentication errors so IPC handler can return proper flags
|
||||
if (error.passwordRequired || error.lockedOut || error.usernameTaken || error.nameLocked) {
|
||||
throw error;
|
||||
}
|
||||
// Always return an error response instead of throwing
|
||||
return { success: false, error: error.message || 'Unknown launch error' };
|
||||
}
|
||||
|
||||
@@ -106,6 +106,23 @@ function getBundledJavaPath(jreDir = JRE_DIR) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: check for nested JRE directory (e.g. jdk-25.0.2+10-jre/bin/java)
|
||||
// This happens when flattenJREDir fails due to EPERM/EACCES on Windows
|
||||
try {
|
||||
if (fs.existsSync(jreDir)) {
|
||||
const entries = fs.readdirSync(jreDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory() && entry.name !== 'bin' && entry.name !== 'lib') {
|
||||
const nestedCandidate = path.join(jreDir, entry.name, 'bin', JAVA_EXECUTABLE);
|
||||
if (fs.existsSync(nestedCandidate)) {
|
||||
console.log(`[JRE] Using nested Java path: ${nestedCandidate}`);
|
||||
return nestedCandidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) { /* ignore */ }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -409,7 +426,7 @@ function extractTarGz(tarGzPath, dest) {
|
||||
function flattenJREDir(jreLatest) {
|
||||
try {
|
||||
const entries = fs.readdirSync(jreLatest, { withFileTypes: true });
|
||||
|
||||
|
||||
if (entries.length !== 1 || !entries[0].isDirectory()) {
|
||||
return;
|
||||
}
|
||||
@@ -420,12 +437,48 @@ function flattenJREDir(jreLatest) {
|
||||
for (const file of files) {
|
||||
const oldPath = path.join(nested, file.name);
|
||||
const newPath = path.join(jreLatest, file.name);
|
||||
fs.renameSync(oldPath, newPath);
|
||||
try {
|
||||
fs.renameSync(oldPath, newPath);
|
||||
} catch (renameErr) {
|
||||
if (renameErr.code === 'EPERM' || renameErr.code === 'EACCES' || renameErr.code === 'EBUSY') {
|
||||
console.log(`[JRE] Rename failed for ${file.name} (${renameErr.code}), using copy fallback`);
|
||||
copyRecursiveSync(oldPath, newPath);
|
||||
} else {
|
||||
throw renameErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs.rmSync(nested, { recursive: true, force: true });
|
||||
try {
|
||||
fs.rmSync(nested, { recursive: true, force: true });
|
||||
} catch (rmErr) {
|
||||
console.log('[JRE] Could not remove nested JRE dir (non-critical):', rmErr.message);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Notice: could not restructure Java directory:', err.message);
|
||||
console.error('[JRE] Failed to restructure Java directory:', err.message);
|
||||
// Last resort: check if java exists in a nested subdir and skip flatten
|
||||
try {
|
||||
const entries = fs.readdirSync(jreLatest, { withFileTypes: true });
|
||||
const nestedDir = entries.find(e => e.isDirectory() && e.name !== 'bin' && e.name !== 'lib');
|
||||
if (nestedDir) {
|
||||
const nestedBin = path.join(jreLatest, nestedDir.name, 'bin', process.platform === 'win32' ? 'java.exe' : 'java');
|
||||
if (fs.existsSync(nestedBin)) {
|
||||
console.log(`[JRE] Java found in nested dir: ${nestedDir.name}, leaving structure as-is`);
|
||||
}
|
||||
}
|
||||
} catch (_) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
function copyRecursiveSync(src, dest) {
|
||||
const stat = fs.statSync(src);
|
||||
if (stat.isDirectory()) {
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
for (const child of fs.readdirSync(src)) {
|
||||
copyRecursiveSync(path.join(src, child), path.join(dest, child));
|
||||
}
|
||||
} else {
|
||||
fs.copyFileSync(src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const {
|
||||
loadConfig,
|
||||
saveConfig,
|
||||
getModsPath
|
||||
const {
|
||||
loadConfig,
|
||||
saveConfig,
|
||||
getModsPath,
|
||||
loadVersionBranch,
|
||||
saveVersionBranch,
|
||||
loadVersionClient,
|
||||
saveVersionClient
|
||||
} = require('../core/config');
|
||||
|
||||
// Lazy-load modManager to avoid circular deps, or keep imports structured.
|
||||
@@ -39,11 +43,13 @@ class ProfileManager {
|
||||
name: 'Default',
|
||||
created: new Date().toISOString(),
|
||||
lastUsed: new Date().toISOString(),
|
||||
|
||||
|
||||
// settings specific to this profile
|
||||
// If global settings existed, we copy them here
|
||||
mods: config.installedMods || [], // Legacy mods are now part of default profile
|
||||
javaPath: config.javaPath || '',
|
||||
versionBranch: config.version_branch || 'release',
|
||||
versionClient: config.version_client || null,
|
||||
gameOptions: {
|
||||
minMemory: '1G',
|
||||
maxMemory: '4G',
|
||||
@@ -73,13 +79,16 @@ class ProfileManager {
|
||||
const config = loadConfig();
|
||||
const id = uuidv4();
|
||||
|
||||
// New profiles inherit the current branch/version
|
||||
const newProfile = {
|
||||
id,
|
||||
name: name.trim(),
|
||||
created: new Date().toISOString(),
|
||||
lastUsed: null,
|
||||
mods: [], // Start with no mods enabled
|
||||
javaPath: '',
|
||||
javaPath: '',
|
||||
versionBranch: loadVersionBranch(),
|
||||
versionClient: loadVersionClient(),
|
||||
gameOptions: {
|
||||
minMemory: '1G',
|
||||
maxMemory: '4G',
|
||||
@@ -128,20 +137,35 @@ class ProfileManager {
|
||||
}
|
||||
|
||||
console.log(`[ProfileManager] Switching to profile: ${config.profiles[id].name} (${id})`);
|
||||
|
||||
// 1. Update config first
|
||||
|
||||
// Save current branch/version to the outgoing profile
|
||||
const oldId = config.activeProfileId;
|
||||
if (oldId && config.profiles[oldId]) {
|
||||
config.profiles[oldId].versionBranch = loadVersionBranch();
|
||||
config.profiles[oldId].versionClient = loadVersionClient();
|
||||
}
|
||||
|
||||
// 1. Update config
|
||||
config.profiles[id].lastUsed = new Date().toISOString();
|
||||
saveConfig({
|
||||
saveConfig({
|
||||
activeProfileId: id,
|
||||
profiles: config.profiles
|
||||
profiles: config.profiles
|
||||
});
|
||||
|
||||
// 2. Trigger Mod Sync
|
||||
// We need to require this here to ensure it uses the *newly saved* active profile ID
|
||||
// 2. Restore branch/version from the new profile
|
||||
const newProfile = config.profiles[id];
|
||||
if (newProfile.versionBranch) {
|
||||
saveVersionBranch(newProfile.versionBranch);
|
||||
}
|
||||
if (newProfile.versionClient !== undefined) {
|
||||
saveVersionClient(newProfile.versionClient);
|
||||
}
|
||||
|
||||
// 3. Trigger Mod Sync
|
||||
const { syncModsForCurrentProfile } = require('./modManager');
|
||||
await syncModsForCurrentProfile();
|
||||
|
||||
return config.profiles[id];
|
||||
return newProfile;
|
||||
}
|
||||
|
||||
deleteProfile(id) {
|
||||
@@ -177,7 +201,7 @@ class ProfileManager {
|
||||
}
|
||||
|
||||
// Safety checks on updates
|
||||
const allowedFields = ['name', 'javaPath', 'gameOptions', 'mods'];
|
||||
const allowedFields = ['name', 'javaPath', 'gameOptions', 'mods', 'versionBranch', 'versionClient'];
|
||||
const sanitizedUpdates = {};
|
||||
|
||||
Object.keys(updates).forEach(key => {
|
||||
|
||||
529
backend/services/matchaService.js
Normal file
529
backend/services/matchaService.js
Normal file
@@ -0,0 +1,529 @@
|
||||
const axios = require('axios');
|
||||
const WebSocket = require('ws');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { loadConfig, saveConfig } = require('../core/config');
|
||||
|
||||
const MATCHA_BASE = 'https://butter.lat';
|
||||
const MATCHA_API = `${MATCHA_BASE}/api/matcha`;
|
||||
const MATCHA_WS = 'wss://butter.lat/api/matcha/ws';
|
||||
|
||||
class MatchaService {
|
||||
constructor() {
|
||||
this.token = null;
|
||||
this.user = null;
|
||||
this.ws = null;
|
||||
this.wsConnected = false;
|
||||
this.mainWindow = null;
|
||||
this.heartbeatInterval = null;
|
||||
this.reconnectTimeout = null;
|
||||
this.reconnectAttempts = 0;
|
||||
this.maxReconnectAttempts = 5;
|
||||
this.lastMessageSentAt = 0;
|
||||
this.gameRunning = false;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// LIFECYCLE
|
||||
// =========================================================================
|
||||
|
||||
init(mainWindow) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.token = this._loadToken();
|
||||
if (this.token) {
|
||||
this._connectWs();
|
||||
this._startHeartbeat();
|
||||
// Load cached user info
|
||||
const config = loadConfig();
|
||||
if (config.matchaUserId && config.matchaHandle) {
|
||||
this.user = { id: config.matchaUserId, handle: config.matchaHandle };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// Best-effort offline heartbeat
|
||||
if (this.token) {
|
||||
this.sendHeartbeat('offline').catch(() => {});
|
||||
}
|
||||
this._stopHeartbeat();
|
||||
this._disconnectWs();
|
||||
this.mainWindow = null;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// TOKEN MANAGEMENT
|
||||
// =========================================================================
|
||||
|
||||
_loadToken() {
|
||||
const config = loadConfig();
|
||||
return config.matchaToken || null;
|
||||
}
|
||||
|
||||
_saveToken(token) {
|
||||
this.token = token;
|
||||
saveConfig({ matchaToken: token });
|
||||
}
|
||||
|
||||
_saveUser(user) {
|
||||
this.user = user;
|
||||
if (user) {
|
||||
saveConfig({ matchaUserId: user.id, matchaHandle: user.handle });
|
||||
}
|
||||
}
|
||||
|
||||
_clearAuth() {
|
||||
this.token = null;
|
||||
this.user = null;
|
||||
this._stopHeartbeat();
|
||||
this._disconnectWs();
|
||||
// Set to null — saveConfig merges, and JSON.stringify preserves null values,
|
||||
// but this effectively marks them as cleared for _loadToken() checks
|
||||
saveConfig({ matchaToken: null, matchaUserId: null, matchaHandle: null });
|
||||
}
|
||||
|
||||
_authHeaders() {
|
||||
return this.token ? { Authorization: `Bearer ${this.token}` } : {};
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// AUTH
|
||||
// =========================================================================
|
||||
|
||||
async register(username, password, password2) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/register`, {
|
||||
username, password, password2, deferCreate: true
|
||||
});
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async confirmRegistration(pendingId, proofId) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/register/confirm`, {
|
||||
pendingId, proofId
|
||||
});
|
||||
if (res.data.token) {
|
||||
this._saveToken(res.data.token);
|
||||
if (res.data.user) this._saveUser(res.data.user);
|
||||
this._connectWs();
|
||||
this._startHeartbeat();
|
||||
}
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async login(handle, password) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/login`, { handle, password });
|
||||
if (res.data.token) {
|
||||
this._saveToken(res.data.token);
|
||||
if (res.data.user) this._saveUser(res.data.user);
|
||||
this._connectWs();
|
||||
this._startHeartbeat();
|
||||
}
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this.sendHeartbeat('offline').catch(() => {});
|
||||
this._clearAuth();
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
getAuthState() {
|
||||
return {
|
||||
authenticated: !!this.token,
|
||||
user: this.user,
|
||||
wsConnected: this.wsConnected
|
||||
};
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// PROFILE
|
||||
// =========================================================================
|
||||
|
||||
async getMe() {
|
||||
try {
|
||||
const res = await axios.get(`${MATCHA_API}/me`, { headers: this._authHeaders() });
|
||||
if (res.data?.user) this._saveUser({ id: res.data.user.id, handle: res.data.user.handle });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async getUser(userId) {
|
||||
try {
|
||||
const res = await axios.get(`${MATCHA_API}/users/${encodeURIComponent(userId)}`, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// FRIENDS
|
||||
// =========================================================================
|
||||
|
||||
async getFriends() {
|
||||
try {
|
||||
const res = await axios.get(`${MATCHA_API}/friends`, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async sendFriendRequest(handle) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/friends/request`, { toHandle: handle }, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async acceptFriend(requestId) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/friends/request/accept`, { id: requestId }, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async rejectFriend(requestId) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/friends/request/reject`, { id: requestId }, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async cancelFriendRequest(requestId) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/friends/request/cancel`, { id: requestId }, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async removeFriend(friendId) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/friends/remove`, { friendId }, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// MESSAGES
|
||||
// =========================================================================
|
||||
|
||||
async getMessages(withTarget, cursor, after) {
|
||||
try {
|
||||
const params = { with: withTarget };
|
||||
if (cursor) params.cursor = cursor;
|
||||
if (after) params.after = after;
|
||||
const res = await axios.get(`${MATCHA_API}/messages`, { headers: this._authHeaders(), params });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async sendMessage(to, body, replyTo) {
|
||||
// Enforce 800ms throttle
|
||||
const now = Date.now();
|
||||
const elapsed = now - this.lastMessageSentAt;
|
||||
if (elapsed < 800) {
|
||||
return { ok: false, error: 'Please wait before sending another message' };
|
||||
}
|
||||
this.lastMessageSentAt = now;
|
||||
|
||||
// Try WebSocket first
|
||||
if (this.wsConnected && this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
const msg = { type: 'send', to, body };
|
||||
if (replyTo) msg.replyTo = replyTo;
|
||||
this.ws.send(JSON.stringify(msg));
|
||||
return { ok: true, data: { sent: true, via: 'ws' } };
|
||||
}
|
||||
|
||||
// Fallback to HTTP
|
||||
try {
|
||||
const payload = { to, body };
|
||||
if (replyTo) payload.replyTo = replyTo;
|
||||
const res = await axios.post(`${MATCHA_API}/messages/send`, payload, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteMessage(messageId) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/messages/${messageId}/delete`, {}, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// UNREAD
|
||||
// =========================================================================
|
||||
|
||||
async getUnread() {
|
||||
try {
|
||||
const res = await axios.get(`${MATCHA_API}/unread`, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async clearUnread(withTarget) {
|
||||
try {
|
||||
const res = await axios.post(`${MATCHA_API}/unread/clear`, { with: withTarget }, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// HEARTBEAT
|
||||
// =========================================================================
|
||||
|
||||
async sendHeartbeat(state) {
|
||||
if (!this.token) return;
|
||||
try {
|
||||
await axios.post(`${MATCHA_API}/heartbeat`, { state: state || 'online' }, { headers: this._authHeaders() });
|
||||
} catch (err) {
|
||||
console.log('[Matcha] Heartbeat failed:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
_startHeartbeat() {
|
||||
this._stopHeartbeat();
|
||||
this.heartbeatInterval = setInterval(() => {
|
||||
const state = this.gameRunning ? 'in_game' : 'online';
|
||||
this.sendHeartbeat(state);
|
||||
}, 30000);
|
||||
// Send initial heartbeat
|
||||
this.sendHeartbeat('online');
|
||||
}
|
||||
|
||||
_stopHeartbeat() {
|
||||
if (this.heartbeatInterval) {
|
||||
clearInterval(this.heartbeatInterval);
|
||||
this.heartbeatInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
setGameRunning(running) {
|
||||
this.gameRunning = running;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// AVATAR
|
||||
// =========================================================================
|
||||
|
||||
async uploadAvatar(filePath, mode) {
|
||||
try {
|
||||
const crypto = require('crypto');
|
||||
const fileBuffer = fs.readFileSync(filePath);
|
||||
if (fileBuffer.length > 1024 * 1024) {
|
||||
return { ok: false, error: 'Avatar too large (max 1MB)' };
|
||||
}
|
||||
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
||||
const endpoint = mode === 'custom' ? `${MATCHA_API}/avatar/custom` : `${MATCHA_API}/avatar`;
|
||||
const res = await axios.post(endpoint, fileBuffer, {
|
||||
headers: {
|
||||
...this._authHeaders(),
|
||||
'Content-Type': 'image/png',
|
||||
'x-avatar-hash': hash,
|
||||
'x-avatar-enable': '1',
|
||||
'x-avatar-force': '1',
|
||||
'Cache-Control': 'no-store'
|
||||
},
|
||||
maxContentLength: 1024 * 1024
|
||||
});
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAvatar() {
|
||||
try {
|
||||
const res = await axios.delete(`${MATCHA_API}/avatar`, { headers: this._authHeaders() });
|
||||
return { ok: true, data: res.data };
|
||||
} catch (err) {
|
||||
return this._handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// WEBSOCKET
|
||||
// =========================================================================
|
||||
|
||||
_connectWs() {
|
||||
if (this.ws) this._disconnectWs();
|
||||
|
||||
try {
|
||||
this.ws = new WebSocket(MATCHA_WS);
|
||||
|
||||
this.ws.on('open', () => {
|
||||
console.log('[Matcha] WebSocket connected');
|
||||
this.reconnectAttempts = 0;
|
||||
// Authenticate
|
||||
if (this.token) {
|
||||
this.ws.send(JSON.stringify({ type: 'auth', token: this.token }));
|
||||
}
|
||||
});
|
||||
|
||||
this.ws.on('message', (raw) => {
|
||||
try {
|
||||
const data = JSON.parse(raw.toString());
|
||||
this._handleWsMessage(data);
|
||||
} catch (err) {
|
||||
console.error('[Matcha] WS parse error:', err.message);
|
||||
}
|
||||
});
|
||||
|
||||
this.ws.on('close', (code) => {
|
||||
console.log('[Matcha] WebSocket closed:', code);
|
||||
this.wsConnected = false;
|
||||
this._sendToRenderer('matcha:ws:disconnected');
|
||||
|
||||
// Handle ban
|
||||
if (code === 4003) {
|
||||
this._sendToRenderer('matcha:ws:banned', { reason: 'Account banned' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto-reconnect if we have a token
|
||||
if (this.token && code !== 4003) {
|
||||
this._scheduleReconnect();
|
||||
}
|
||||
});
|
||||
|
||||
this.ws.on('error', (err) => {
|
||||
console.error('[Matcha] WS error:', err.message);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[Matcha] WS connect failed:', err.message);
|
||||
this._scheduleReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
_disconnectWs() {
|
||||
if (this.reconnectTimeout) {
|
||||
clearTimeout(this.reconnectTimeout);
|
||||
this.reconnectTimeout = null;
|
||||
}
|
||||
if (this.ws) {
|
||||
try {
|
||||
this.ws.close();
|
||||
} catch (e) {}
|
||||
this.ws = null;
|
||||
}
|
||||
this.wsConnected = false;
|
||||
}
|
||||
|
||||
_scheduleReconnect() {
|
||||
if (this.reconnectTimeout) return;
|
||||
// Exponential backoff capped at 30s, no hard limit (matches Butter's infinite reconnect)
|
||||
const delay = Math.min(2000 * Math.pow(2, this.reconnectAttempts), 30000);
|
||||
this.reconnectAttempts++;
|
||||
// Notify renderer after several failures so it can show a banner
|
||||
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||||
this._sendToRenderer('matcha:ws:max-retries');
|
||||
}
|
||||
console.log(`[Matcha] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
||||
this.reconnectTimeout = setTimeout(() => {
|
||||
this.reconnectTimeout = null;
|
||||
if (this.token) this._connectWs();
|
||||
}, delay);
|
||||
}
|
||||
|
||||
_handleWsMessage(data) {
|
||||
switch (data.type) {
|
||||
case 'authed':
|
||||
this.wsConnected = true;
|
||||
if (data.user) this._saveUser(data.user);
|
||||
this._sendToRenderer('matcha:ws:connected', { user: this.user });
|
||||
break;
|
||||
case 'message':
|
||||
console.log('[Matcha] WS message received:', JSON.stringify(data).substring(0, 200));
|
||||
this._sendToRenderer('matcha:ws:message', data);
|
||||
break;
|
||||
case 'message_deleted':
|
||||
this._sendToRenderer('matcha:ws:message-deleted', data);
|
||||
break;
|
||||
case 'avatar_updated':
|
||||
this._sendToRenderer('matcha:ws:avatar-updated', data);
|
||||
break;
|
||||
case 'banned':
|
||||
this._sendToRenderer('matcha:ws:banned', data);
|
||||
break;
|
||||
case 'announcement':
|
||||
this._sendToRenderer('matcha:ws:announcement', data);
|
||||
break;
|
||||
case 'error':
|
||||
console.log('[Matcha] WS error:', data.message || data.error || JSON.stringify(data));
|
||||
// If auth error, treat as ban/disconnect
|
||||
if (data.message === 'Not authed' || data.error === 'Not authed') {
|
||||
this._clearAuth();
|
||||
this._sendToRenderer('matcha:ws:disconnected');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log('[Matcha] Unknown WS message type:', data.type);
|
||||
}
|
||||
}
|
||||
|
||||
manualReconnect() {
|
||||
this.reconnectAttempts = 0;
|
||||
if (this.token) this._connectWs();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// HELPERS
|
||||
// =========================================================================
|
||||
|
||||
_sendToRenderer(channel, data) {
|
||||
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||
this.mainWindow.webContents.send(channel, data);
|
||||
}
|
||||
}
|
||||
|
||||
_handleError(err) {
|
||||
if (err.response) {
|
||||
const status = err.response.status;
|
||||
const msg = err.response.data?.error || err.response.data?.message || err.message;
|
||||
if (status === 401) {
|
||||
// Token expired or invalid
|
||||
this._clearAuth();
|
||||
this._sendToRenderer('matcha:ws:disconnected');
|
||||
}
|
||||
return { ok: false, error: msg, status };
|
||||
}
|
||||
return { ok: false, error: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new MatchaService();
|
||||
36
backend/utils/__tests__/clientPatcher.test.js
Normal file
36
backend/utils/__tests__/clientPatcher.test.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const assert = require('assert');
|
||||
const clientPatcher = require('../clientPatcher');
|
||||
|
||||
function bytesAreZero(buffer) {
|
||||
for (const byte of buffer) {
|
||||
if (byte !== 0x00) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const directStrategy = clientPatcher.getDomainStrategy('authws.net');
|
||||
assert.strictEqual(directStrategy.mode, 'direct');
|
||||
assert.strictEqual(directStrategy.mainDomain, 'authws.net');
|
||||
assert.strictEqual(directStrategy.subdomainPrefix, '');
|
||||
|
||||
const splitStrategy = clientPatcher.getDomainStrategy('auth.sanasol.ws');
|
||||
assert.strictEqual(splitStrategy.mode, 'split');
|
||||
assert.strictEqual(splitStrategy.subdomainPrefix, 'auth.s');
|
||||
assert.strictEqual(splitStrategy.mainDomain, 'anasol.ws');
|
||||
|
||||
const oldSubdomain = clientPatcher.stringToLengthPrefixed('https://tools.');
|
||||
const newSubdomain = clientPatcher.stringToLengthPrefixed('https://');
|
||||
const replaceResult = clientPatcher.replaceBytes(oldSubdomain, oldSubdomain, newSubdomain);
|
||||
const padding = replaceResult.buffer.slice(newSubdomain.length, oldSubdomain.length);
|
||||
assert.ok(bytesAreZero(padding), 'Expected null padding after shorter replacement');
|
||||
|
||||
const oldDomainUtf16 = clientPatcher.stringToUtf16LE('hytale.com');
|
||||
const smartResult = clientPatcher.findAndReplaceDomainSmart(oldDomainUtf16, 'hytale.com', 'auth.ws');
|
||||
const newDomainUtf16 = clientPatcher.stringToUtf16LE('auth.ws');
|
||||
assert.ok(smartResult.buffer.slice(0, newDomainUtf16.length).equals(newDomainUtf16));
|
||||
const smartPadding = smartResult.buffer.slice(newDomainUtf16.length, oldDomainUtf16.length);
|
||||
assert.ok(bytesAreZero(smartPadding), 'Expected null padding in smart replacement');
|
||||
|
||||
console.log('clientPatcher tests passed');
|
||||
@@ -132,9 +132,10 @@ class ClientPatcher {
|
||||
/**
|
||||
* Replace bytes in buffer - only overwrites the length of new bytes
|
||||
*/
|
||||
replaceBytes(buffer, oldBytes, newBytes) {
|
||||
replaceBytes(buffer, oldBytes, newBytes, options = {}) {
|
||||
let count = 0;
|
||||
const result = Buffer.from(buffer);
|
||||
const padNulls = options.padNulls !== false;
|
||||
|
||||
if (newBytes.length > oldBytes.length) {
|
||||
console.warn(` Warning: New pattern (${newBytes.length}) longer than old (${oldBytes.length}), skipping`);
|
||||
@@ -144,6 +145,9 @@ class ClientPatcher {
|
||||
const positions = this.findAllOccurrences(result, oldBytes);
|
||||
for (const pos of positions) {
|
||||
newBytes.copy(result, pos);
|
||||
if (padNulls && newBytes.length < oldBytes.length) {
|
||||
result.fill(0x00, pos + newBytes.length, pos + oldBytes.length);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
@@ -173,14 +177,21 @@ class ClientPatcher {
|
||||
|
||||
if (lastCharFirstByte === oldLastCharByte) {
|
||||
newUtf16NoLast.copy(result, pos);
|
||||
result[lastCharPos] = newLastCharByte;
|
||||
const newLastCharPos = pos + newUtf16NoLast.length;
|
||||
const secondByte = lastCharPos + 1 < result.length ? result[lastCharPos + 1] : 0x00;
|
||||
const isUtf16 = secondByte === 0x00;
|
||||
if (newUtf16NoLast.length < oldUtf16NoLast.length) {
|
||||
const padEnd = isUtf16 ? lastCharPos + 2 : pos + oldUtf16NoLast.length;
|
||||
result.fill(0x00, newLastCharPos + 1, padEnd);
|
||||
}
|
||||
result[newLastCharPos] = newLastCharByte;
|
||||
|
||||
if (lastCharPos + 1 < result.length) {
|
||||
const secondByte = result[lastCharPos + 1];
|
||||
if (secondByte === 0x00) {
|
||||
if (newLastCharPos + 1 < result.length) {
|
||||
const nextByte = result[newLastCharPos + 1];
|
||||
if (nextByte === 0x00) {
|
||||
console.log(` Patched UTF-16LE occurrence at offset 0x${pos.toString(16)}`);
|
||||
} else {
|
||||
console.log(` Patched length-prefixed occurrence at offset 0x${pos.toString(16)} (metadata: 0x${secondByte.toString(16)})`);
|
||||
console.log(` Patched length-prefixed occurrence at offset 0x${pos.toString(16)} (metadata: 0x${nextByte.toString(16)})`);
|
||||
}
|
||||
}
|
||||
count++;
|
||||
@@ -190,6 +201,51 @@ class ClientPatcher {
|
||||
return { buffer: result, count };
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all domain patches using legacy UTF-16LE format
|
||||
*/
|
||||
applyLegacyPatches(data, domain, protocol = 'https://') {
|
||||
let result = Buffer.from(data);
|
||||
let totalCount = 0;
|
||||
const strategy = this.getDomainStrategy(domain);
|
||||
|
||||
console.log(` Legacy strategy: ${strategy.description}`);
|
||||
|
||||
const oldSentry = 'https://ca900df42fcf57d4dd8401a86ddd7da2@sentry.hytale.com/2';
|
||||
const newSentry = `${protocol}t@${domain}/2`;
|
||||
const sentryResult = this.replaceBytes(
|
||||
result,
|
||||
this.stringToUtf16LE(oldSentry),
|
||||
this.stringToUtf16LE(newSentry)
|
||||
);
|
||||
result = sentryResult.buffer;
|
||||
if (sentryResult.count > 0) {
|
||||
console.log(` Replaced ${sentryResult.count} legacy sentry occurrence(s)`);
|
||||
totalCount += sentryResult.count;
|
||||
}
|
||||
|
||||
const domainResult = this.findAndReplaceDomainSmart(result, ORIGINAL_DOMAIN, strategy.mainDomain);
|
||||
result = domainResult.buffer;
|
||||
if (domainResult.count > 0) {
|
||||
console.log(` Replaced ${domainResult.count} legacy domain occurrence(s)`);
|
||||
totalCount += domainResult.count;
|
||||
}
|
||||
|
||||
const subdomains = ['https://tools.', 'https://sessions.', 'https://account-data.', 'https://telemetry.'];
|
||||
const newSubdomainPrefix = protocol + strategy.subdomainPrefix;
|
||||
|
||||
for (const sub of subdomains) {
|
||||
const subResult = this.findAndReplaceDomainSmart(result, sub, newSubdomainPrefix);
|
||||
result = subResult.buffer;
|
||||
if (subResult.count > 0) {
|
||||
console.log(` Replaced ${subResult.count} legacy subdomain occurrence(s)`);
|
||||
totalCount += subResult.count;
|
||||
}
|
||||
}
|
||||
|
||||
return { buffer: result, count: totalCount };
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all domain patches using length-prefixed format
|
||||
*/
|
||||
@@ -472,7 +528,7 @@ class ClientPatcher {
|
||||
if (count === 0 && discordCount === 0) {
|
||||
console.log('No occurrences found - trying legacy UTF-16LE format...');
|
||||
|
||||
const legacyResult = this.findAndReplaceDomainSmart(data, ORIGINAL_DOMAIN, strategy.mainDomain);
|
||||
const legacyResult = this.applyLegacyPatches(data, newDomain);
|
||||
if (legacyResult.count > 0) {
|
||||
console.log(`Found ${legacyResult.count} occurrences with legacy format`);
|
||||
fs.writeFileSync(clientPath, legacyResult.buffer);
|
||||
@@ -645,23 +701,64 @@ class ClientPatcher {
|
||||
* Find client binary path based on platform
|
||||
*/
|
||||
findClientPath(gameDir) {
|
||||
const candidates = this.findClientBinaries(gameDir);
|
||||
return candidates.length > 0 ? candidates[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all client binaries that may contain patchable strings
|
||||
*/
|
||||
findClientBinaries(gameDir) {
|
||||
const candidates = [];
|
||||
const seen = new Set();
|
||||
const clientDir = path.join(gameDir, 'Client');
|
||||
|
||||
const addCandidate = (candidate) => {
|
||||
if (!candidate || seen.has(candidate)) {
|
||||
return;
|
||||
}
|
||||
if (fs.existsSync(candidate)) {
|
||||
candidates.push(candidate);
|
||||
seen.add(candidate);
|
||||
}
|
||||
};
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
candidates.push(path.join(gameDir, 'Client', 'Hytale.app', 'Contents', 'MacOS', 'HytaleClient'));
|
||||
candidates.push(path.join(gameDir, 'Client', 'HytaleClient'));
|
||||
const appBundle = path.join(clientDir, 'Hytale.app');
|
||||
const appMacosDir = path.join(appBundle, 'Contents', 'MacOS');
|
||||
|
||||
addCandidate(path.join(appMacosDir, 'HytaleClient'));
|
||||
addCandidate(path.join(clientDir, 'HytaleClient'));
|
||||
|
||||
if (fs.existsSync(appMacosDir)) {
|
||||
const entries = fs.readdirSync(appMacosDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isFile()) {
|
||||
addCandidate(path.join(appMacosDir, entry.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (process.platform === 'win32') {
|
||||
candidates.push(path.join(gameDir, 'Client', 'HytaleClient.exe'));
|
||||
addCandidate(path.join(clientDir, 'HytaleClient.exe'));
|
||||
addCandidate(path.join(clientDir, 'HytaleClient.dll'));
|
||||
|
||||
if (fs.existsSync(clientDir)) {
|
||||
const entries = fs.readdirSync(clientDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile()) {
|
||||
continue;
|
||||
}
|
||||
const lowerName = entry.name.toLowerCase();
|
||||
if (lowerName.endsWith('.dll')) {
|
||||
addCandidate(path.join(clientDir, entry.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
candidates.push(path.join(gameDir, 'Client', 'HytaleClient'));
|
||||
addCandidate(path.join(clientDir, 'HytaleClient'));
|
||||
}
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return candidates;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -691,17 +788,34 @@ class ClientPatcher {
|
||||
success: true
|
||||
};
|
||||
|
||||
const clientPath = this.findClientPath(gameDir);
|
||||
if (clientPath) {
|
||||
if (progressCallback) progressCallback('Patching client binary...', 10);
|
||||
results.client = await this.patchClient(clientPath, (msg, pct) => {
|
||||
if (progressCallback) {
|
||||
progressCallback(`Client: ${msg}`, pct ? pct / 2 : null);
|
||||
}
|
||||
});
|
||||
const clientPaths = this.findClientBinaries(gameDir);
|
||||
if (clientPaths.length > 0) {
|
||||
if (progressCallback) progressCallback('Patching client binaries...', 10);
|
||||
const clientResults = [];
|
||||
let totalPatchCount = 0;
|
||||
|
||||
for (const clientPath of clientPaths) {
|
||||
const result = await this.patchClient(clientPath, (msg, pct) => {
|
||||
if (progressCallback) {
|
||||
progressCallback(`Client: ${path.basename(clientPath)}: ${msg}`, pct ? pct / 2 : null);
|
||||
}
|
||||
});
|
||||
clientResults.push({ path: clientPath, ...result });
|
||||
totalPatchCount += result.patchCount || 0;
|
||||
}
|
||||
|
||||
const allSuccess = clientResults.every((entry) => entry.success);
|
||||
const allAlreadyPatched = clientResults.every((entry) => entry.alreadyPatched);
|
||||
|
||||
results.client = {
|
||||
success: allSuccess,
|
||||
patchCount: totalPatchCount,
|
||||
alreadyPatched: allAlreadyPatched,
|
||||
binaries: clientResults
|
||||
};
|
||||
} else {
|
||||
console.warn('Could not find HytaleClient binary');
|
||||
results.client = { success: false, error: 'Client binary not found' };
|
||||
console.warn('Could not find HytaleClient binaries');
|
||||
results.client = { success: false, error: 'Client binaries not found' };
|
||||
}
|
||||
|
||||
// Download DualAuth ByteBuddy Agent (runtime patching, no JAR modification)
|
||||
|
||||
405
backend/utils/clientPatcher.localhost.js.bak
Normal file
405
backend/utils/clientPatcher.localhost.js.bak
Normal file
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* Localhost/Local Development Code Backup
|
||||
*
|
||||
* This file contains code removed from clientPatcher.js that was used for local development.
|
||||
* To re-enable local dev mode, merge this code back into clientPatcher.js.
|
||||
*
|
||||
* Backed up: 2026-01-28
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// LOCAL PATCHER PATHS (was in ensurePatcherDownloaded method)
|
||||
// =============================================================================
|
||||
|
||||
// Check for local patcher (for local development)
|
||||
const localPatcherPaths = [
|
||||
path.join(__dirname, '..', '..', '..', 'hytale-auth-server', 'patcher', 'DualAuthPatcher.java'),
|
||||
path.join(__dirname, '..', '..', '..', '..', 'hytale-auth-server', 'patcher', 'DualAuthPatcher.java'),
|
||||
'/Users/sanasol/code/pterodactyl-hytale/hytale-auth-server/patcher/DualAuthPatcher.java'
|
||||
];
|
||||
|
||||
// Check if we should use local patcher (localhost domain = local dev)
|
||||
const domain = getTargetDomain();
|
||||
const isLocalDev = domain.startsWith('localhost') || domain.startsWith('127.0.0.1');
|
||||
|
||||
if (isLocalDev) {
|
||||
for (const localPath of localPatcherPaths) {
|
||||
if (fs.existsSync(localPath)) {
|
||||
console.log(`Using local DualAuthPatcher: ${localPath}`);
|
||||
// Always copy fresh for local dev to pick up changes
|
||||
fs.copyFileSync(localPath, patcherJava);
|
||||
// Delete compiled class to force recompile
|
||||
const patcherClass = path.join(patcherDir, 'DualAuthPatcher.class');
|
||||
if (fs.existsSync(patcherClass)) {
|
||||
fs.unlinkSync(patcherClass);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.log('Local patcher not found, falling back to download...');
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// LEGACY SERVER PATCHER (was patchServerLegacy method)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Legacy server patcher (simple domain replacement, no dual auth)
|
||||
* Use patchServer() for full dual auth support
|
||||
*/
|
||||
async patchServerLegacy(serverPath, progressCallback) {
|
||||
const newDomain = this.getNewDomain();
|
||||
const strategy = this.getDomainStrategy(newDomain);
|
||||
|
||||
console.log('=== Legacy Server Patcher ===');
|
||||
console.log(`Target: ${serverPath}`);
|
||||
console.log(`Domain: ${newDomain} (${newDomain.length} chars)`);
|
||||
|
||||
if (!fs.existsSync(serverPath)) {
|
||||
return { success: false, error: `Server JAR not found: ${serverPath}` };
|
||||
}
|
||||
|
||||
if (progressCallback) progressCallback('Patching server...', 20);
|
||||
|
||||
console.log('Opening server JAR...');
|
||||
const zip = new AdmZip(serverPath);
|
||||
const entries = zip.getEntries();
|
||||
|
||||
let totalCount = 0;
|
||||
const oldUtf8 = this.stringToUtf8(ORIGINAL_DOMAIN);
|
||||
|
||||
for (const entry of entries) {
|
||||
const name = entry.entryName;
|
||||
if (name.endsWith('.class') || name.endsWith('.properties') ||
|
||||
name.endsWith('.json') || name.endsWith('.xml') || name.endsWith('.yml')) {
|
||||
const data = entry.getData();
|
||||
if (data.includes(oldUtf8)) {
|
||||
const { buffer: patchedData, count } = this.findAndReplaceDomainUtf8(data, ORIGINAL_DOMAIN, strategy.mainDomain);
|
||||
if (count > 0) {
|
||||
zip.updateFile(entry.entryName, patchedData);
|
||||
totalCount += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalCount > 0) {
|
||||
zip.writeZip(serverPath);
|
||||
}
|
||||
|
||||
if (progressCallback) progressCallback('Complete', 100);
|
||||
return { success: true, patchCount: totalCount };
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// DUALAUTHPATCHER FLOW FOR NON-STANDARD DOMAINS (was in patchServer method)
|
||||
// =============================================================================
|
||||
|
||||
// For non-standard domains, use DualAuthPatcher for proper bytecode patching
|
||||
const isStandardDomain = newDomain === 'auth.sanasol.ws' || newDomain === 'sanasol.ws';
|
||||
|
||||
if (!isStandardDomain) {
|
||||
console.log(`Non-standard domain "${newDomain}" - using DualAuthPatcher`);
|
||||
|
||||
// Find Java
|
||||
const java = this.findJava();
|
||||
if (!java) {
|
||||
console.error('Java not found - cannot run DualAuthPatcher');
|
||||
console.error('Please install Java or use the bundled JRE');
|
||||
return { success: false, error: 'Java not found for DualAuthPatcher' };
|
||||
}
|
||||
console.log(` Using Java: ${java}`);
|
||||
|
||||
// Setup patcher directory
|
||||
const patcherDir = path.join(path.dirname(serverPath), '.patcher');
|
||||
const libDir = path.join(patcherDir, 'lib');
|
||||
|
||||
try {
|
||||
// Download patcher and libraries
|
||||
if (progressCallback) progressCallback('Downloading DualAuthPatcher...', 20);
|
||||
await this.ensurePatcherDownloaded(patcherDir);
|
||||
|
||||
if (progressCallback) progressCallback('Downloading ASM libraries...', 30);
|
||||
await this.ensureAsmLibraries(libDir);
|
||||
|
||||
// Compile patcher
|
||||
if (progressCallback) progressCallback('Compiling patcher...', 40);
|
||||
const compileResult = await this.compileDualAuthPatcher(java, patcherDir, libDir);
|
||||
if (!compileResult.success) {
|
||||
return { success: false, error: compileResult.error };
|
||||
}
|
||||
|
||||
// Build classpath
|
||||
const classpath = [
|
||||
patcherDir,
|
||||
path.join(libDir, 'asm-9.6.jar'),
|
||||
path.join(libDir, 'asm-tree-9.6.jar'),
|
||||
path.join(libDir, 'asm-util-9.6.jar')
|
||||
].join(process.platform === 'win32' ? ';' : ':');
|
||||
|
||||
// Run DualAuthPatcher with custom domain
|
||||
if (progressCallback) progressCallback('Running DualAuthPatcher...', 60);
|
||||
console.log(` Running DualAuthPatcher with domain: ${newDomain}`);
|
||||
|
||||
const patchResult = await this.runDualAuthPatcher(java, classpath, serverPath, newDomain);
|
||||
|
||||
if (patchResult.success) {
|
||||
// Mark as patched
|
||||
fs.writeFileSync(patchFlagFile, JSON.stringify({
|
||||
domain: newDomain,
|
||||
patchedAt: new Date().toISOString(),
|
||||
patcher: 'DualAuthPatcher',
|
||||
output: patchResult.stdout
|
||||
}, null, 2));
|
||||
|
||||
if (progressCallback) progressCallback('Server patching complete', 100);
|
||||
console.log('=== Server Patching Complete (DualAuthPatcher) ===');
|
||||
return { success: true, patchCount: 1 };
|
||||
} else {
|
||||
console.error('DualAuthPatcher failed:', patchResult.error);
|
||||
return { success: false, error: patchResult.error };
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to run DualAuthPatcher:', err.message);
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// HELPER METHODS FOR DUALAUTHPATCHER (keep if re-enabling non-standard domains)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Find Java executable - uses bundled JRE first (same as game uses)
|
||||
* Falls back to system Java if bundled not available
|
||||
*/
|
||||
findJava() {
|
||||
// 1. Try bundled JRE first (comes with the game)
|
||||
try {
|
||||
const bundled = getBundledJavaPath(JRE_DIR);
|
||||
if (bundled && fs.existsSync(bundled)) {
|
||||
console.log(`Using bundled Java: ${bundled}`);
|
||||
return bundled;
|
||||
}
|
||||
} catch (e) {
|
||||
// Bundled not available
|
||||
}
|
||||
|
||||
// 2. Try javaManager's getJavaExec (handles all fallbacks)
|
||||
try {
|
||||
const javaExec = getJavaExec(JRE_DIR);
|
||||
if (javaExec && fs.existsSync(javaExec)) {
|
||||
console.log(`Using Java from javaManager: ${javaExec}`);
|
||||
return javaExec;
|
||||
}
|
||||
} catch (e) {
|
||||
// Not available
|
||||
}
|
||||
|
||||
// 3. Check JAVA_HOME
|
||||
if (process.env.JAVA_HOME) {
|
||||
const javaHome = process.env.JAVA_HOME;
|
||||
const javaBin = path.join(javaHome, 'bin', process.platform === 'win32' ? 'java.exe' : 'java');
|
||||
if (fs.existsSync(javaBin)) {
|
||||
console.log(`Using Java from JAVA_HOME: ${javaBin}`);
|
||||
return javaBin;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Try 'java' from PATH
|
||||
try {
|
||||
execSync('java -version 2>&1', { encoding: 'utf8' });
|
||||
console.log('Using Java from PATH');
|
||||
return 'java';
|
||||
} catch (e) {
|
||||
// Not in PATH
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get DualAuthPatcher - downloads from GitHub
|
||||
*/
|
||||
async ensurePatcherDownloaded(patcherDir) {
|
||||
const patcherJava = path.join(patcherDir, 'DualAuthPatcher.java');
|
||||
const patcherUrl = 'https://raw.githubusercontent.com/sanasol/hytale-auth-server/master/patcher/DualAuthPatcher.java';
|
||||
|
||||
if (!fs.existsSync(patcherDir)) {
|
||||
fs.mkdirSync(patcherDir, { recursive: true });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(patcherJava)) {
|
||||
console.log('Downloading DualAuthPatcher from hytale-auth-server...');
|
||||
try {
|
||||
const https = require('https');
|
||||
await new Promise((resolve, reject) => {
|
||||
const file = fs.createWriteStream(patcherJava);
|
||||
https.get(patcherUrl, (response) => {
|
||||
if (response.statusCode === 302 || response.statusCode === 301) {
|
||||
https.get(response.headers.location, (redirectResponse) => {
|
||||
redirectResponse.pipe(file);
|
||||
file.on('finish', () => {
|
||||
file.close();
|
||||
resolve();
|
||||
});
|
||||
}).on('error', reject);
|
||||
} else {
|
||||
response.pipe(file);
|
||||
file.on('finish', () => {
|
||||
file.close();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}).on('error', (err) => {
|
||||
fs.unlink(patcherJava, () => {});
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
console.log(' Downloaded DualAuthPatcher.java');
|
||||
} catch (e) {
|
||||
console.error(` Failed to download DualAuthPatcher: ${e.message}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download ASM libraries if not present
|
||||
*/
|
||||
async ensureAsmLibraries(libDir) {
|
||||
if (!fs.existsSync(libDir)) {
|
||||
fs.mkdirSync(libDir, { recursive: true });
|
||||
}
|
||||
|
||||
const libs = [
|
||||
{ name: 'asm-9.6.jar', url: 'https://repo1.maven.org/maven2/org/ow2/asm/asm/9.6/asm-9.6.jar' },
|
||||
{ name: 'asm-tree-9.6.jar', url: 'https://repo1.maven.org/maven2/org/ow2/asm/asm-tree/9.6/asm-tree-9.6.jar' },
|
||||
{ name: 'asm-util-9.6.jar', url: 'https://repo1.maven.org/maven2/org/ow2/asm/asm-util/9.6/asm-util-9.6.jar' }
|
||||
];
|
||||
|
||||
for (const lib of libs) {
|
||||
const libPath = path.join(libDir, lib.name);
|
||||
if (!fs.existsSync(libPath)) {
|
||||
console.log(`Downloading ${lib.name}...`);
|
||||
try {
|
||||
const https = require('https');
|
||||
await new Promise((resolve, reject) => {
|
||||
const file = fs.createWriteStream(libPath);
|
||||
https.get(lib.url, (response) => {
|
||||
response.pipe(file);
|
||||
file.on('finish', () => {
|
||||
file.close();
|
||||
resolve();
|
||||
});
|
||||
}).on('error', (err) => {
|
||||
fs.unlink(libPath, () => {});
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
console.log(` Downloaded ${lib.name}`);
|
||||
} catch (e) {
|
||||
console.error(` Failed to download ${lib.name}: ${e.message}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile DualAuthPatcher if needed
|
||||
*/
|
||||
async compileDualAuthPatcher(java, patcherDir, libDir) {
|
||||
const patcherClass = path.join(patcherDir, 'DualAuthPatcher.class');
|
||||
const patcherJava = path.join(patcherDir, 'DualAuthPatcher.java');
|
||||
|
||||
if (fs.existsSync(patcherClass)) {
|
||||
const classTime = fs.statSync(patcherClass).mtime;
|
||||
const javaTime = fs.statSync(patcherJava).mtime;
|
||||
if (classTime > javaTime) {
|
||||
console.log('DualAuthPatcher already compiled');
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Compiling DualAuthPatcher...');
|
||||
|
||||
const javac = java.replace(/java(\.exe)?$/, 'javac$1');
|
||||
const classpath = [
|
||||
path.join(libDir, 'asm-9.6.jar'),
|
||||
path.join(libDir, 'asm-tree-9.6.jar'),
|
||||
path.join(libDir, 'asm-util-9.6.jar')
|
||||
].join(process.platform === 'win32' ? ';' : ':');
|
||||
|
||||
try {
|
||||
const execOptions = {
|
||||
stdio: 'pipe',
|
||||
cwd: patcherDir,
|
||||
env: { ...process.env }
|
||||
};
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const systemRoot = process.env.SystemRoot || 'C:\\WINDOWS';
|
||||
const systemPath = `${systemRoot}\\system32;${systemRoot};${systemRoot}\\System32\\Wbem`;
|
||||
execOptions.env.PATH = execOptions.env.PATH
|
||||
? `${systemPath};${execOptions.env.PATH}`
|
||||
: systemPath;
|
||||
execOptions.shell = true;
|
||||
}
|
||||
|
||||
execSync(`"${javac}" -cp "${classpath}" -d "${patcherDir}" "${patcherJava}"`, execOptions);
|
||||
console.log(' Compilation successful');
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
const error = `Failed to compile DualAuthPatcher: ${e.message}`;
|
||||
console.error(error);
|
||||
if (e.stderr) console.error(e.stderr.toString());
|
||||
return { success: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run DualAuthPatcher on the server JAR
|
||||
*/
|
||||
async runDualAuthPatcher(java, classpath, serverPath, domain) {
|
||||
return new Promise((resolve) => {
|
||||
const args = ['-cp', classpath, 'DualAuthPatcher', serverPath];
|
||||
const env = { ...process.env, HYTALE_AUTH_DOMAIN: domain };
|
||||
|
||||
console.log(`Running: java ${args.join(' ')}`);
|
||||
console.log(` HYTALE_AUTH_DOMAIN=${domain}`);
|
||||
|
||||
const proc = spawn(java, args, { env, stdio: ['pipe', 'pipe', 'pipe'] });
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
proc.stdout.on('data', (data) => {
|
||||
const str = data.toString();
|
||||
stdout += str;
|
||||
console.log(str.trim());
|
||||
});
|
||||
|
||||
proc.stderr.on('data', (data) => {
|
||||
const str = data.toString();
|
||||
stderr += str;
|
||||
console.error(str.trim());
|
||||
});
|
||||
|
||||
proc.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve({ success: true, stdout });
|
||||
} else {
|
||||
resolve({ success: false, error: `Patcher exited with code ${code}: ${stderr}` });
|
||||
}
|
||||
});
|
||||
|
||||
proc.on('error', (err) => {
|
||||
resolve({ success: false, error: `Failed to run patcher: ${err.message}` });
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -594,6 +594,10 @@ function setupGpuEnvironment(gpuPreference) {
|
||||
if (detected.vendor === 'nvidia') {
|
||||
envVars.__NV_PRIME_RENDER_OFFLOAD = '1';
|
||||
envVars.__GLX_VENDOR_LIBRARY_NAME = 'nvidia';
|
||||
// Prevent Wayland explicit sync crashes on NVIDIA (Hyprland, etc.)
|
||||
if (isWaylandSession()) {
|
||||
envVars.__NV_DISABLE_EXPLICIT_SYNC = '1';
|
||||
}
|
||||
const nvidiaEglFile = '/usr/share/glvnd/egl_vendor.d/10_nvidia.json';
|
||||
if (fs.existsSync(nvidiaEglFile)) {
|
||||
envVars.__EGL_VENDOR_LIBRARY_FILENAMES = nvidiaEglFile;
|
||||
|
||||
732
docs/CLIENT_BINARY_ANALYSIS.md
Normal file
732
docs/CLIENT_BINARY_ANALYSIS.md
Normal file
@@ -0,0 +1,732 @@
|
||||
# Hytale Client Binary Analysis
|
||||
|
||||
CSC_LINK="/Users/sanasol/Downloads/Certificates-hytale.p12" CSC_KEY_PASSWORD="YieocpBVP68Rih*" APPLE_ID="sanasol2008rs@gmail.com" APPLE_APP_SPECIFIC_PASSWORD="ihah-lbta-movj-iqni" APPLE_TEAM_ID="9WVL8YG95H" npm run build:mac
|
||||
CSC_LINK="/Users/sanasol/Downloads/Certificates-hytale.p12" CSC_KEY_PASSWORD="YieocpBVP68Rih*" APPLE_ID="sanasol2008rs@gmail.com" APPLE_APP_SPECIFIC_PASSWORD="ihah-lbta-movj-iqni" APPLE_TEAM_ID="9WVL8YG95H" npx electron-builder --mac --arm64
|
||||
|
||||
|
||||
password ihah-lbta-movj-iqni
|
||||
team id 9WVL8YG95H
|
||||
cert pass YieocpBVP68Rih*
|
||||
|
||||
## Overview
|
||||
|
||||
This document contains a comprehensive analysis of the HytaleClient binary, documenting all discovered URLs, API endpoints, service domains, patchable strings, and internal functionality.
|
||||
|
||||
**Binary Analyzed:** `HytaleClient` (macOS .NET AOT compiled)
|
||||
**Analysis Date:** 2026-01-27
|
||||
**String Encoding:** UTF-16LE (Windows .NET string format)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Service URLs](#1-service-urls)
|
||||
2. [API Endpoints](#2-api-endpoints)
|
||||
3. [World Tools & Builder Tools](#3-world-tools--builder-tools)
|
||||
4. [External Service URLs](#4-external-service-urls)
|
||||
5. [Patchable Strings](#5-patchable-strings)
|
||||
6. [Sentry Error Tracking](#6-sentry-error-tracking)
|
||||
7. [Internal Class References](#7-internal-class-references)
|
||||
8. [Binary Offsets Reference](#8-binary-offsets-reference)
|
||||
9. [Implementation Notes](#9-implementation-notes)
|
||||
|
||||
---
|
||||
|
||||
## 1. Service URLs
|
||||
|
||||
### 1.1 Primary Hytale Services
|
||||
|
||||
The client connects to four main service subdomains:
|
||||
|
||||
| Service | URL Pattern | Purpose | Status |
|
||||
|---------|-------------|---------|--------|
|
||||
| **Sessions** | `https://sessions.{domain}` | Authentication, JWT tokens, session management | Implemented in auth-server |
|
||||
| **Account Data** | `https://account-data.{domain}` | Player profiles, skins, account information | Implemented in auth-server |
|
||||
| **Telemetry** | `https://telemetry.{domain}` | Analytics, error reporting, usage statistics | Implemented (accepts/discards) |
|
||||
| **Tools** | `https://tools.{domain}` | Asset editor, prefab management, world tools | **Not implemented** |
|
||||
|
||||
### 1.2 URL Construction
|
||||
|
||||
The client constructs URLs by combining:
|
||||
1. Protocol: `https://`
|
||||
2. Subdomain: `sessions.`, `account-data.`, `telemetry.`, `tools.`
|
||||
3. Base domain: `hytale.com`
|
||||
|
||||
**Example:** `https://` + `sessions.` + `hytale.com` = `https://sessions.hytale.com`
|
||||
|
||||
The client patcher replaces these components to redirect traffic to the F2P auth server.
|
||||
|
||||
### 1.3 F2P Domain Routing
|
||||
|
||||
For F2P mode, all subdomains route to a single endpoint:
|
||||
- `sessions.{f2p_domain}` → `https://{f2p_domain}`
|
||||
- `account-data.{f2p_domain}` → `https://{f2p_domain}`
|
||||
- `telemetry.{f2p_domain}` → `https://{f2p_domain}`
|
||||
- `tools.{f2p_domain}` → `https://{f2p_domain}`
|
||||
|
||||
---
|
||||
|
||||
## 2. API Endpoints
|
||||
|
||||
### 2.1 Session Management Endpoints
|
||||
|
||||
#### POST /game-session/new
|
||||
Create a new game session.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"clientVersion": "string",
|
||||
"platform": "string"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"token": "JWT token",
|
||||
"refreshToken": "refresh token",
|
||||
"expiresIn": 36000
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /game-session/refresh
|
||||
Refresh an existing session token.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"refreshToken": "string"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"token": "new JWT token",
|
||||
"refreshToken": "new refresh token",
|
||||
"expiresIn": 36000
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /game-session/child
|
||||
Create a child session (for server connections).
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"parentToken": "string",
|
||||
"audience": "server identifier"
|
||||
}
|
||||
```
|
||||
|
||||
#### DELETE /game-session
|
||||
Notify server of session end (player disconnect).
|
||||
|
||||
### 2.2 Server Join Endpoints
|
||||
|
||||
#### POST /server-join/auth-grant
|
||||
Request authorization grant for connecting to a game server.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"serverAddress": "string",
|
||||
"serverPort": number
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"grant": "authorization grant string"
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /server-join/auth-token
|
||||
Exchange authorization grant for server-specific token with certificate binding.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"grant": "authorization grant",
|
||||
"clientCertHash": "SHA256 hash of client certificate"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"token": "server-specific JWT with cnf claim"
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 Account Endpoints
|
||||
|
||||
#### GET /my-account/game-profile
|
||||
Get the player's game profile.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"uuid": "player UUID",
|
||||
"username": "display name",
|
||||
"createdAt": "timestamp"
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /my-account/game-profile
|
||||
Update the player's game profile.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"username": "new display name"
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /my-account/cosmetics
|
||||
Get list of unlocked cosmetics for the player.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"cosmetics": [
|
||||
{
|
||||
"id": "cosmetic_id",
|
||||
"category": "category_name",
|
||||
"unlockedAt": "timestamp"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /my-account/skin
|
||||
Save player's skin/character customization preferences.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"skinTone": "SkinTone_01",
|
||||
"bodyType": "Default",
|
||||
"parts": {
|
||||
"haircut": "Haircut_ShortMessy.Blue",
|
||||
"eyes": "Eyes_Default.Green",
|
||||
"eyebrows": "Eyebrows_Default",
|
||||
"face": "Face_Default"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 JWKS Endpoint
|
||||
|
||||
#### GET /.well-known/jwks.json
|
||||
Get JSON Web Key Set for JWT verification.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"keys": [
|
||||
{
|
||||
"kty": "OKP",
|
||||
"crv": "Ed25519",
|
||||
"x": "base64url-encoded-public-key",
|
||||
"kid": "key-id",
|
||||
"use": "sig",
|
||||
"alg": "EdDSA"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 Profile Lookup Endpoints
|
||||
|
||||
#### GET /profile/uuid/{uuid}
|
||||
Lookup player profile by UUID.
|
||||
|
||||
#### GET /profile/username/{username}
|
||||
Lookup player profile by username (server-scoped).
|
||||
|
||||
---
|
||||
|
||||
## 3. World Tools & Builder Tools
|
||||
|
||||
### 3.1 World Tools (worldtools.*)
|
||||
|
||||
These are in-game tools for world creation and editing in builder/creative mode.
|
||||
|
||||
| Tool | Command | Description |
|
||||
|------|---------|-------------|
|
||||
| **Change Model** | `worldtools.changeModel` | Change the model of an entity in the world |
|
||||
| **Import Image** | `worldtools.importImage` | Import image files into the world as textures |
|
||||
| **Import OBJ** | `worldtools.importObj` | Import 3D OBJ model files into the world |
|
||||
| **Instance** | `worldtools.instance` | Manage world instances and copies |
|
||||
| **Play Sound** | `worldtools.playSound` | Play sound effects in the world |
|
||||
| **Prefab Editor** | `worldtools.prefabEditor` | Open the prefab editor interface |
|
||||
| **Prefab List** | `worldtools.prefabList` | List and manage saved prefabs |
|
||||
| **Spawn Entity** | `worldtools.spawnEntity` | Spawn entities at specified locations |
|
||||
| **Spawn Particle** | `worldtools.spawnParticle` | Spawn particle effects |
|
||||
| **Tint Chunk** | `worldtools.tintChunk` | Apply color tinting to world chunks |
|
||||
|
||||
### 3.2 Builder Tools (buildertools.*)
|
||||
|
||||
Additional tools for the asset editor and builder mode.
|
||||
|
||||
| Tool | Class | Description |
|
||||
|------|-------|-------------|
|
||||
| **Image Import** | `buildertools.imageimport.ImageImportPage` | UI page for importing images |
|
||||
| **OBJ Import** | `buildertools.objimport.ObjImportPage` | UI page for importing OBJ models |
|
||||
| **Prefab Editor** | `buildertools.prefabeditor.ui.PrefabEditorLoadSettings` | Prefab editor with load/save |
|
||||
| **Prefab List** | `buildertools.prefablist.PrefabPage` | Prefab listing and management |
|
||||
|
||||
### 3.3 Machinima Tool
|
||||
|
||||
- **Purpose:** In-game cinematic/video recording tool
|
||||
- **Access:** Available via hotbar slot
|
||||
- **Message:** "Hotbar is full. Clear a slot to receive the Machinima tool."
|
||||
|
||||
### 3.4 Asset Editor
|
||||
|
||||
The client includes an asset editor with these features:
|
||||
- `AssetEditorDownload` - Download assets from tools service
|
||||
- `assetEditor.exportModal` - Export modal for assets
|
||||
- `assetEditor.fileSaveState` - File save state management
|
||||
- `assetEditor.property.tooltip` - Property tooltips
|
||||
|
||||
### 3.5 tools.hytale.com API Requirements
|
||||
|
||||
To fully support builder mode, the tools service would need:
|
||||
|
||||
```
|
||||
POST /assets/upload
|
||||
- Upload asset files (images, models, sounds)
|
||||
- Returns asset ID/URL
|
||||
|
||||
GET /assets/{assetId}
|
||||
- Download asset by ID
|
||||
- Returns asset binary data
|
||||
|
||||
POST /prefabs/save
|
||||
- Save prefab definition
|
||||
- Returns prefab ID
|
||||
|
||||
GET /prefabs/{prefabId}
|
||||
- Load prefab by ID
|
||||
- Returns prefab JSON
|
||||
|
||||
GET /prefabs/list
|
||||
- List user's saved prefabs
|
||||
- Returns array of prefab metadata
|
||||
|
||||
DELETE /prefabs/{prefabId}
|
||||
- Delete a prefab
|
||||
```
|
||||
|
||||
**Note:** The game functions without tools.hytale.com - it's only needed for cloud-based asset sharing in builder mode.
|
||||
|
||||
---
|
||||
|
||||
## 4. External Service URLs
|
||||
|
||||
### 4.1 Hytale Official URLs
|
||||
|
||||
| URL | Purpose | Patchable |
|
||||
|-----|---------|-----------|
|
||||
| `https://store.hytale.com/?upgrade=` | In-game store for purchases | Yes |
|
||||
| `https://hytale.com/help/joining-friends` | Help documentation | Yes |
|
||||
| `https://discord.gg/hytale` | Official Discord invite | Yes |
|
||||
|
||||
### 4.2 Third-Party Service URLs
|
||||
|
||||
| URL | Purpose | Notes |
|
||||
|-----|---------|-------|
|
||||
| `https://blockbench.net/downloads` | Blockbench download page | 3D model editor |
|
||||
| `https://blockbench.net/plugins/hytale_plugin` | Hytale Blockbench plugin | For asset creation |
|
||||
| `https://docs.sentry.io/platforms/dotnet/*` | Sentry documentation | Error tracking docs |
|
||||
| `https://aka.ms/*` | Microsoft .NET documentation | Runtime docs |
|
||||
| `https://learn.microsoft.com/*` | Microsoft Learn | .NET API docs |
|
||||
| `https://go.microsoft.com/*` | Microsoft redirects | Various docs |
|
||||
|
||||
### 4.3 Graphics/Rendering References
|
||||
|
||||
| URL | Purpose |
|
||||
|-----|---------|
|
||||
| `https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)` | GLSL interface blocks |
|
||||
| `https://www.khronos.org/opengl/wiki/Sampler_(GLSL)` | GLSL texture samplers |
|
||||
| `https://www.shadertoy.com/view/Xd23Dh` | Shader reference |
|
||||
| `https://www.shadertoy.com/view/ltlSRj` | Shader reference |
|
||||
| `https://aras-p.info/texts/CompactNormalStorage.html` | Normal map compression |
|
||||
| `https://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/` | Depth reconstruction |
|
||||
| `https://briansharpe.files.wordpress.com/2018/07/moment-transparency-supp-av.pdf` | Transparency rendering |
|
||||
| `https://www.pmavridis.com/research/fbcompression/` | Frame buffer compression |
|
||||
| `https://jcgt.org/published/0002/02/09/` | Graphics technique |
|
||||
| `https://jcgt.org/published/0006/01/03/` | Graphics technique |
|
||||
| `https://graphics.cs.williams.edu/papers/CSSM/` | Graphics paper |
|
||||
| `http://www.humus.name/Articles/Persson_LowLevelThinking.pdf` | Low-level graphics |
|
||||
|
||||
### 4.4 GitHub References
|
||||
|
||||
| URL | Purpose |
|
||||
|-----|---------|
|
||||
| `https://github.com/NLog/NLog.git` | Logging framework |
|
||||
| `https://github.com/Noesis/Managed/tree/master/Src/Noesis/Core` | NoesisGUI core |
|
||||
| `https://github.com/Noesis/Managed/tree/master/Src/NoesisApp/Core` | NoesisGUI app |
|
||||
| `https://github.com/dotnet/dotnet` | .NET runtime |
|
||||
| `https://github.com/ektrah/nsec.git` | NSec cryptography |
|
||||
| `https://github.com/getsentry/sentry-dotnet` | Sentry .NET SDK |
|
||||
|
||||
---
|
||||
|
||||
## 5. Patchable Strings
|
||||
|
||||
### 5.1 Domain Strings
|
||||
|
||||
| Original | Replacement | Purpose |
|
||||
|----------|-------------|---------|
|
||||
| `hytale.com` | `{f2p_domain}` | Base domain (4-16 chars) |
|
||||
| `sessions.` | Stripped or replaced | Session service subdomain |
|
||||
| `account-data.` | Stripped or replaced | Account service subdomain |
|
||||
| `telemetry.` | Stripped or replaced | Telemetry subdomain |
|
||||
| `tools.` | Stripped or replaced | Tools service subdomain |
|
||||
|
||||
### 5.2 URL Strings
|
||||
|
||||
| Original | Can Replace With | Notes |
|
||||
|----------|------------------|-------|
|
||||
| `https://store.hytale.com/?upgrade=` | Custom store URL | In-game purchases |
|
||||
| `https://discord.gg/hytale` | Custom Discord | Community link |
|
||||
| `https://hytale.com/help/joining-friends` | Custom help docs | Help system |
|
||||
| `sentry.hytale.com` | Own Sentry or disable | Error tracking |
|
||||
|
||||
### 5.3 String Encoding Details
|
||||
|
||||
**.NET UTF-16LE Format:**
|
||||
- Each character is 2 bytes (little-endian)
|
||||
- Example: "hytale" = `68 00 79 00 74 00 61 00 6c 00 65 00`
|
||||
- Strings are length-prefixed in the binary
|
||||
|
||||
**Length Prefix Format:**
|
||||
- 1 byte for strings < 128 chars
|
||||
- 2 bytes (varint) for longer strings
|
||||
- Followed by UTF-16LE character data
|
||||
|
||||
### 5.4 Current Patcher Behavior
|
||||
|
||||
The `clientPatcher.js` patches:
|
||||
1. `sessions.hytale.com` → `{f2p_domain}` (single endpoint)
|
||||
2. `account-data.hytale.com` → `{f2p_domain}`
|
||||
3. `telemetry.hytale.com` → `{f2p_domain}`
|
||||
|
||||
**Not currently patched:**
|
||||
- `tools.hytale.com` (builder mode assets)
|
||||
- `store.hytale.com` (in-game store)
|
||||
- `sentry.hytale.com` (error tracking)
|
||||
|
||||
---
|
||||
|
||||
## 6. Sentry Error Tracking
|
||||
|
||||
### 6.1 Sentry Configuration
|
||||
|
||||
**DSN Found in Binary:**
|
||||
```
|
||||
https://ca900df42fcf57d4dd8401a86ddd7da2@sentry.hytale.com/
|
||||
```
|
||||
|
||||
**DSN Components:**
|
||||
- Protocol: `https`
|
||||
- Public Key: `ca900df42fcf57d4dd8401a86ddd7da2`
|
||||
- Host: `sentry.hytale.com`
|
||||
- Project ID: (after trailing slash)
|
||||
|
||||
### 6.2 Sentry Integration
|
||||
|
||||
The client uses the official Sentry .NET SDK:
|
||||
- Package: `sentry-dotnet`
|
||||
- Documentation refs found in binary
|
||||
|
||||
### 6.3 Patching Options
|
||||
|
||||
**Option 1: Disable Sentry**
|
||||
- Replace DSN with invalid string
|
||||
- Errors won't be reported
|
||||
|
||||
**Option 2: Redirect to Own Sentry**
|
||||
- Replace `sentry.hytale.com` with own Sentry host
|
||||
- Requires same-length domain or binary patching
|
||||
|
||||
**Option 3: Leave As-Is**
|
||||
- Errors still report to Hypixel
|
||||
- May expose F2P server information
|
||||
|
||||
### 6.4 Sentry Environment Variables
|
||||
|
||||
Found configuration references:
|
||||
- `SENTRY_DSN` - DSN override
|
||||
- `SENTRY_ENVIRONMENT` - Environment name
|
||||
- Docs: `https://docs.sentry.io/platforms/dotnet/configuration/environments`
|
||||
|
||||
---
|
||||
|
||||
## 7. Internal Class References
|
||||
|
||||
### 7.1 Package Structure
|
||||
|
||||
```
|
||||
com.hypixel.hytale/
|
||||
├── builtin/
|
||||
│ ├── buildertools/
|
||||
│ │ ├── imageimport/
|
||||
│ │ │ └── ImageImportPage
|
||||
│ │ ├── objimport/
|
||||
│ │ │ └── ObjImportPage
|
||||
│ │ ├── prefabeditor/
|
||||
│ │ │ └── ui/PrefabEditorLoadSettings
|
||||
│ │ └── prefablist/
|
||||
│ │ └── PrefabPage
|
||||
│ ├── instances/
|
||||
│ │ └── page/InstanceListPage
|
||||
│ └── model/
|
||||
│ └── pages/ChangeModelPage
|
||||
├── server/
|
||||
│ └── core/
|
||||
│ └── asset/
|
||||
│ └── type/
|
||||
│ └── particle/
|
||||
│ └── pages/ParticleSpawn*
|
||||
└── Creation/
|
||||
└── navigation/
|
||||
├── buildertools/
|
||||
└── worldtools/
|
||||
├── changeModel
|
||||
├── importImage
|
||||
├── importObj
|
||||
├── instance
|
||||
├── playSound
|
||||
├── prefabEditor
|
||||
├── prefabList
|
||||
├── spawnEntity
|
||||
├── spawnParticle
|
||||
└── tintChunk
|
||||
```
|
||||
|
||||
### 7.2 UI Components
|
||||
|
||||
| Component | Path | Purpose |
|
||||
|-----------|------|---------|
|
||||
| GameLoading | `/GameLoading.u` | Loading screen |
|
||||
| GamePageNavigation | `/GamePageNavigation.u` | Main navigation |
|
||||
| ServerButton | `/ServerButton.u` | Server list button |
|
||||
| ServerModal | `/ServerModal.u` | Server details modal |
|
||||
| ServersPage | `/Servers/ServersPage.u` | Server browser |
|
||||
| DirectConnectPopup | `/Servers/DirectConnectPopup.u` | Direct connect dialog |
|
||||
| EditServerPopup | `/Servers/EditServerPopup.u` | Edit server dialog |
|
||||
| JoinViaCodePopup | `/Servers/JoinViaCodePopup.u` | Join via code dialog |
|
||||
| MinigamesPage | `/Minigames/MinigamesPage.u` | Minigames browser |
|
||||
|
||||
### 7.3 Configuration Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `/GameplayConfigs/Default.json` | Default gameplay settings |
|
||||
| `hytale_plugin.js` | Blockbench plugin script |
|
||||
| `hytale_assets` | Asset reference |
|
||||
|
||||
---
|
||||
|
||||
## 8. Binary Offsets Reference
|
||||
|
||||
### 8.1 URL String Offsets (macOS binary)
|
||||
|
||||
| Offset | Content | Length |
|
||||
|--------|---------|--------|
|
||||
| `0x1bf0098` | `https://account-data` | ~21 chars |
|
||||
| `0x1bf00c9` | `https://aka.ms/dotnet-warnings/{0}` | ~35 chars |
|
||||
| `0x1bf0114` | `https://blockbench.net/downloads` | ~33 chars |
|
||||
| `0x1bf015d` | `https://blockbench.net/plugins/hytale_plugin` | ~45 chars |
|
||||
| `0x1bf01bc` | `https://...@sentry.hytale.com/` | ~60 chars |
|
||||
| `0x1bf023b` | `https://discord.gg/hytale` | ~26 chars |
|
||||
| `0x1bf0274` | `https://hytale.com/help/joining-friends` | ~40 chars |
|
||||
| `0x1bf02c9` | `https://sessions` | ~17 chars |
|
||||
| `0x1bf02f2` | `https://store.hytale.com/?upgrade=` | ~35 chars |
|
||||
| `0x1bf033d` | `https://telemetry` | ~18 chars |
|
||||
| `0x1bf0368` | `https://tools` | ~14 chars |
|
||||
|
||||
### 8.2 API Endpoint Offsets
|
||||
|
||||
| Offset | Endpoint |
|
||||
|--------|----------|
|
||||
| `0x1b115d2` | `/game-session/child` |
|
||||
| `0x1b115ff` | `/game-session/refresh` |
|
||||
| `0x1b117c2` | `/server-join/auth-grant` |
|
||||
| `0x1b117f7` | `/server-join/auth-token` |
|
||||
| `0x1b11689` | `/my-account/cosmetics` |
|
||||
| `0x1b116ba` | `/my-account/game-profile` |
|
||||
| `0x1b116f1` | `/my-account/skin` |
|
||||
| `0x1b10d8c` | `/.well-known/jwks.json` |
|
||||
|
||||
### 8.3 Notes on Offsets
|
||||
|
||||
- Offsets are for the macOS binary
|
||||
- Windows/Linux binaries will have different offsets
|
||||
- Offsets may change between game versions
|
||||
- Always verify offsets before patching
|
||||
|
||||
---
|
||||
|
||||
## 9. Implementation Notes
|
||||
|
||||
### 9.1 Current Auth Server Implementation
|
||||
|
||||
The auth server (`hytale-auth-server`) currently implements:
|
||||
|
||||
**Fully Implemented:**
|
||||
- [x] `/game-session/new` - Session creation
|
||||
- [x] `/game-session/refresh` - Token refresh
|
||||
- [x] `/server-join/auth-grant` - Auth grants
|
||||
- [x] `/server-join/auth-token` - Token exchange with cert binding
|
||||
- [x] `/my-account/cosmetics` - Cosmetic list
|
||||
- [x] `/my-account/game-profile` - Profile get/update
|
||||
- [x] `/my-account/skin` - Skin save
|
||||
- [x] `/.well-known/jwks.json` - JWKS endpoint
|
||||
- [x] `/profile/uuid/{uuid}` - UUID lookup
|
||||
- [x] `/profile/username/{username}` - Username lookup
|
||||
- [x] Telemetry endpoints (accept and discard)
|
||||
|
||||
**Not Implemented:**
|
||||
- [ ] `tools.hytale.com` API (asset upload/download)
|
||||
- [ ] Prefab cloud storage
|
||||
- [ ] Asset sharing between players
|
||||
|
||||
### 9.2 Tools Service Implementation (Future)
|
||||
|
||||
If implementing tools.hytale.com functionality:
|
||||
|
||||
```javascript
|
||||
// Suggested endpoints for auth-server
|
||||
|
||||
// Asset upload
|
||||
app.post('/tools/assets/upload', async (req, res) => {
|
||||
// Handle multipart file upload
|
||||
// Store in local filesystem or S3
|
||||
// Return asset ID
|
||||
});
|
||||
|
||||
// Asset download
|
||||
app.get('/tools/assets/:assetId', async (req, res) => {
|
||||
// Retrieve asset by ID
|
||||
// Stream file to client
|
||||
});
|
||||
|
||||
// Prefab operations
|
||||
app.post('/tools/prefabs', async (req, res) => {
|
||||
// Save prefab JSON
|
||||
// Associate with user
|
||||
});
|
||||
|
||||
app.get('/tools/prefabs/:prefabId', async (req, res) => {
|
||||
// Get prefab by ID
|
||||
});
|
||||
|
||||
app.get('/tools/prefabs', async (req, res) => {
|
||||
// List user's prefabs
|
||||
});
|
||||
|
||||
app.delete('/tools/prefabs/:prefabId', async (req, res) => {
|
||||
// Delete prefab
|
||||
});
|
||||
```
|
||||
|
||||
### 9.3 Patching Recommendations
|
||||
|
||||
**Essential (Already Done):**
|
||||
1. Patch `sessions.hytale.com` → F2P domain
|
||||
2. Patch `account-data.hytale.com` → F2P domain
|
||||
3. Patch `telemetry.hytale.com` → F2P domain (or disable)
|
||||
|
||||
**Optional Enhancements:**
|
||||
1. Patch `tools.hytale.com` → F2P domain (if implementing tools API)
|
||||
2. Patch `sentry.hytale.com` → Own Sentry or disable
|
||||
3. Patch `discord.gg/hytale` → Community Discord
|
||||
4. Patch `store.hytale.com` → Custom store (if applicable)
|
||||
|
||||
**Not Recommended to Patch:**
|
||||
- Blockbench URLs (useful for modding)
|
||||
- Microsoft documentation URLs
|
||||
- Graphics reference URLs
|
||||
|
||||
### 9.4 Security Considerations
|
||||
|
||||
1. **Sentry DSN Exposure**
|
||||
- Current: Errors report to Hypixel's Sentry
|
||||
- Risk: May expose F2P server details
|
||||
- Recommendation: Disable or redirect
|
||||
|
||||
2. **Telemetry Data**
|
||||
- Current: Accepted but discarded
|
||||
- Alternative: Log for analytics
|
||||
- Risk: Privacy concerns
|
||||
|
||||
3. **Asset Upload (if implemented)**
|
||||
- Validate file types
|
||||
- Limit file sizes
|
||||
- Scan for malicious content
|
||||
- Rate limit uploads
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: String Extraction Commands
|
||||
|
||||
### Extract UTF-16LE Strings
|
||||
```bash
|
||||
python3 << 'EOF'
|
||||
with open("HytaleClient", "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
pattern = b'h\x00t\x00t\x00p\x00s\x00:\x00/\x00/\x00'
|
||||
idx = 0
|
||||
while True:
|
||||
idx = data.find(pattern, idx)
|
||||
if idx == -1:
|
||||
break
|
||||
end = idx
|
||||
chars = []
|
||||
while end < len(data) - 1:
|
||||
char = data[end] | (data[end+1] << 8)
|
||||
if 0x20 <= char <= 0x7e:
|
||||
chars.append(chr(char))
|
||||
end += 2
|
||||
else:
|
||||
break
|
||||
print(f"{hex(idx)}: {''.join(chars)}")
|
||||
idx += 2
|
||||
EOF
|
||||
```
|
||||
|
||||
### Search for Specific Pattern
|
||||
```bash
|
||||
xxd HytaleClient | grep "h.y.t.a.l.e"
|
||||
```
|
||||
|
||||
### Extract Context Around Offset
|
||||
```bash
|
||||
dd if=HytaleClient bs=1 skip=$((0x1bf0000)) count=2048 | xxd
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: Version History
|
||||
|
||||
| Date | Changes |
|
||||
|------|---------|
|
||||
| 2026-01-27 | Initial analysis of macOS client binary |
|
||||
|
||||
---
|
||||
|
||||
## Appendix C: Related Documentation
|
||||
|
||||
- `CLAUDE.md` - Project overview and architecture
|
||||
- `DUAL_AUTH_FLOW.md` - Dual authentication flow diagrams
|
||||
- `STEAMDECK_CRASH_INVESTIGATION.md` - libzstd crash fix
|
||||
- `PLAYER_PASSWORD_FEATURE.md` - Planned password authentication
|
||||
- `backend/utils/clientPatcher.js` - Client patcher implementation
|
||||
90
docs/FASTUTIL_CLASSLOADER_ISSUE.md
Normal file
90
docs/FASTUTIL_CLASSLOADER_ISSUE.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Singleplayer Server Crash: fastutil ClassNotFoundException
|
||||
|
||||
## Status: Open — likely outdated HytaleServer.jar (Feb 24-28 2026)
|
||||
|
||||
## Symptom
|
||||
|
||||
Singleplayer server crashes immediately on boot:
|
||||
|
||||
```
|
||||
Exception in thread "main" java.lang.NoClassDefFoundError: it/unimi/dsi/fastutil/objects/ObjectArrayList
|
||||
at com.hypixel.hytale.plugin.early.EarlyPluginLoader.<clinit>(EarlyPluginLoader.java:34)
|
||||
at com.hypixel.hytale.Main.main(Main.java:36)
|
||||
Caused by: java.lang.ClassNotFoundException: it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
|
||||
```
|
||||
|
||||
Server exits with code 1. Multiplayer works fine for the same user.
|
||||
|
||||
## Affected Users
|
||||
|
||||
1. **ヅ𝚃 JAYED !** (Feb 24) — Windows x86_64, had AOT cache errors before fastutil crash
|
||||
2. **Asentrix** (Feb 27) — Windows x86_64 (NT 10.0.26200.0), RTX 4060, Launcher v2.4.4, NO AOT cache errors
|
||||
3. **7645754** (Feb 28) — Standalone server on localhost, **FIXED by updating HytaleServer.jar**
|
||||
|
||||
- Reproduces 100% on singleplayer, every attempt (users 1-2)
|
||||
- Multiplayer works fine for users 1-2
|
||||
- macOS/Linux users are NOT affected
|
||||
|
||||
## Ruled Out (confirmed via debug builds)
|
||||
|
||||
| Suspect | Tested | Result |
|
||||
|---------|--------|--------|
|
||||
| **DualAuth Agent** | Debug build with agent completely disabled (`debug-no-agent` tag) | **Same crash.** Agent is innocent. |
|
||||
| **`-Xshare:off` (CDS)** | Added to `JAVA_TOOL_OPTIONS` in launcher code (`debug-xshare-off` tag) | **Did not help.** CDS is not the cause. |
|
||||
| **`-XX:+UseCompactObjectHeaders`** | Stripped via wrapper | **Did not help.** Server has `-XX:+IgnoreUnrecognizedVMOptions` anyway. |
|
||||
| **Corrupted game files** | User did repair + full reinstall | **Same crash.** |
|
||||
| **Java wrapper** | Logs confirm wrapper works correctly | Not the cause. |
|
||||
| **ARM64/Parallels** | User is on standard Windows x86_64 | Not applicable. |
|
||||
| **AOT cache** | Asentrix has no AOT errors (JAYED did), both crash the same way | Not the root cause. |
|
||||
|
||||
## Key Finding: Outdated HytaleServer.jar (Feb 28)
|
||||
|
||||
User `7645754` had the **exact same error** on their standalone localhost server but NOT on their VPS. **Fixed by replacing `HytaleServer.jar` with the current version.** The old JAR used to work but stopped — likely the bundled JRE was updated and is now incompatible with older JAR versions.
|
||||
|
||||
This strongly suggests the root cause for F2P launcher users is also a **stale/mismatched `HytaleServer.jar`**. The launcher may report the correct version but the actual file on disk could be from an older download.
|
||||
|
||||
## What We Know
|
||||
|
||||
- `fastutil` is bundled inside `HytaleServer.jar` (fat/shaded JAR)
|
||||
- JVM's `BuiltinClassLoader` cannot find `it.unimi.dsi.fastutil.objects.ObjectArrayList` despite it being in the JAR
|
||||
- Crash happens at `EarlyPluginLoader` static initializer (line 34) which imports `ObjectArrayList`
|
||||
- **Replacing `HytaleServer.jar` with a fresh copy fixes the issue** (confirmed by user 3)
|
||||
- The issue is NOT caused by the DualAuth agent or any launcher modification
|
||||
|
||||
## Fix for Users
|
||||
|
||||
### F2P Launcher users (Asentrix, JAYED)
|
||||
1. **Delete the entire game folder**: `%LOCALAPPDATA%\HytaleF2P\release\package\game\`
|
||||
2. Relaunch — launcher will re-download everything fresh
|
||||
3. NOT just "repair" — full delete to ensure no stale files remain
|
||||
|
||||
### Standalone server users
|
||||
1. Download fresh `HytaleServer.jar` from current game version
|
||||
2. Replace the old JAR file
|
||||
|
||||
## Update History
|
||||
|
||||
### Feb 24: First report (JAYED)
|
||||
User reported singleplayer crash. Initial investigation found AOT cache errors + fastutil ClassNotFoundException. Stripping `-XX:+UseCompactObjectHeaders` did not help.
|
||||
|
||||
### Feb 27: Second report (Asentrix), extensive debugging
|
||||
- Asentrix hit same crash, no AOT errors — ruled out AOT as root cause
|
||||
- Built `debug-xshare-off`: added `-Xshare:off` to `JAVA_TOOL_OPTIONS` — **did not help**
|
||||
- Built `debug-no-agent`: completely disabled DualAuth agent — **same crash**
|
||||
- **Conclusion**: Neither the agent nor CDS is the cause. The JVM itself cannot load classes from the fat JAR on these specific Windows systems.
|
||||
- Note: wrapper `injectArgs` append AFTER `-jar`, so they cannot inject JVM flags — only `JAVA_TOOL_OPTIONS` works for JVM flags
|
||||
|
||||
### Feb 28: Third user (7645754) — FIXED by replacing HytaleServer.jar
|
||||
- Standalone server user had same crash on localhost, VPS worked fine
|
||||
- **Fixed by updating `HytaleServer.jar` to match VPS version**
|
||||
- Root cause likely: outdated JAR incompatible with current/updated JRE
|
||||
- For F2P launcher users: need to delete game folder and force fresh re-download
|
||||
|
||||
## Related
|
||||
|
||||
- Java wrapper config: `backend/core/config.js` (stripFlags / injectArgs)
|
||||
- DualAuth Agent: v1.1.12, package `ws.sanasol.dualauth`
|
||||
- Game version at time of reports: `2026.02.19-1a311a592`
|
||||
- Debug tags: `debug-xshare-off`, `debug-no-agent`
|
||||
- Log submission IDs: `c88e7b71` (Asentrix initial), `0445e4dc` (xshare test), `748dceeb` (no-agent test)
|
||||
48
docs/KULVIN_RAM_PRESSURE_CRASH.md
Normal file
48
docs/KULVIN_RAM_PRESSURE_CRASH.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Client Crash: RAM Pressure on Low-End Hardware
|
||||
|
||||
## Status: Resolved (Feb 24, 2026)
|
||||
|
||||
## Symptom
|
||||
|
||||
Game launches but crashes with exit code 1 after ~32 seconds. Launcher logs show stale `java.exe(HytaleServer)` killed on every relaunch. Earlier sessions showed game running for ~4 minutes before `System.OutOfMemoryException`.
|
||||
|
||||
## Affected User
|
||||
|
||||
- Discord: KULVIN
|
||||
- Hardware: Intel UHD 620 + NVIDIA MX150 (2GB VRAM), low-end laptop
|
||||
- 22 mods installed
|
||||
- Platform: Windows x64
|
||||
|
||||
## Root Cause
|
||||
|
||||
RAM pressure from background apps + 22 mods. The game's .NET client ran out of memory, and the embedded singleplayer server JVM (no `-Xmx` cap) competed for the same limited RAM.
|
||||
|
||||
## Timeline
|
||||
|
||||
1. Morning session: Game ran ~4 minutes, then `System.OutOfMemoryException` crashed client, server JVM crashed with `EXCEPTION_ACCESS_VIOLATION`
|
||||
2. Evening sessions: Game started crashing in ~32 seconds (system still degraded from earlier OOM)
|
||||
3. Server JVM orphaned every time (cleaned up by `killGameProcesses()` on next launch)
|
||||
|
||||
## Resolution
|
||||
|
||||
User fixed by:
|
||||
1. Closing background applications to free RAM
|
||||
2. Reinstalling the game
|
||||
|
||||
## Additional Issues Found
|
||||
|
||||
- `WeaponStatsViewer` mod left a directory (not a .jar) in `HytaleSaves\Mods\`, causing EPERM on every mod sync
|
||||
- Stale AOT cache (`HytaleServer.aot`) cleaned up automatically by launcher
|
||||
|
||||
## Debugging Steps (for similar cases)
|
||||
|
||||
1. Check RAM usage in Task Manager before launching
|
||||
2. Windows Event Viewer (Win+R → `eventvwr.msc` → Application) for crash module details
|
||||
3. Try with all mods disabled
|
||||
4. Reboot to clear degraded memory state
|
||||
5. Close background apps (browsers, Discord, etc.)
|
||||
|
||||
## Recommendations
|
||||
|
||||
- Low-end hardware: reduce mod count (10 or fewer)
|
||||
- Consider adding `-Xmx` cap to singleplayer server JVM to prevent unbounded memory growth
|
||||
138
docs/UPDATE_SYSTEM_FIXES.md
Normal file
138
docs/UPDATE_SYSTEM_FIXES.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# Update System Fixes Summary
|
||||
|
||||
## Overview
|
||||
This document summarizes the fixes made to the launcher auto-update system to improve UX and fix macOS-specific issues.
|
||||
|
||||
## Issues Fixed
|
||||
|
||||
### 1. Duplicate Update Popups
|
||||
**Problem:** Two different update UI files (`update.js` and `updater.js`) were both listening for update events, causing duplicate popups to appear.
|
||||
|
||||
**Solution:**
|
||||
- Disabled `updater.js` in `index.html` (commented out the script tag)
|
||||
- Now only `update.js` handles all update UI with improved features
|
||||
|
||||
### 2. Missing Skip Button
|
||||
**Problem:** Users were soft-locked on the update screen with no way to dismiss it, especially problematic on macOS where auto-install often fails.
|
||||
|
||||
**Solution:**
|
||||
- Added "Skip for now (not recommended)" button to update popup
|
||||
- Skip button appears:
|
||||
- After 30 seconds (timeout fallback)
|
||||
- Immediately on any update error
|
||||
- After install attempt fails (5 second timeout)
|
||||
- Always visible once download completes
|
||||
|
||||
### 3. macOS Auto-Install Failure
|
||||
**Problem:** `quitAndInstall()` silently fails on unsigned macOS apps, leaving users stuck.
|
||||
|
||||
**Solution:**
|
||||
- Detect macOS in `main.js` and send `autoInstallSupported: false` flag
|
||||
- On macOS, show "Download Manually (Recommended)" as primary action
|
||||
- "Try Install & Restart" shown as secondary option
|
||||
- Added force quit fallback in `install-update` handler for macOS
|
||||
- Clear messaging: "Update downloaded but auto-install may not work on macOS"
|
||||
|
||||
### 4. Missing IPC Handlers
|
||||
**Problem:** `open-download-page` IPC handler was not registered, causing errors when clicking manual download.
|
||||
|
||||
**Solution:**
|
||||
- Added `open-download-page` handler in `main.js` that opens GitHub releases page
|
||||
- Added `quitAndInstallUpdate` alias in `preload.js` for compatibility
|
||||
|
||||
### 5. Interface Blocking Not Removed
|
||||
**Problem:** `unblockInterface()` method was called but never defined, leaving the UI blurred after closing popup.
|
||||
|
||||
**Solution:**
|
||||
- Added complete `unblockInterface()` method that:
|
||||
- Removes `interface-blocked` class from main content
|
||||
- Removes `no-select` class from body
|
||||
- Properly removes event listeners using stored bound function references
|
||||
|
||||
### 6. Breathing Animation on Downloaded State
|
||||
**Problem:** The pulse/breathing animation continued after download completed, which felt inappropriate for a "ready to install" state.
|
||||
|
||||
**Solution:**
|
||||
- Remove `update-popup-pulse` class in `showUpdateDownloaded()` method
|
||||
|
||||
### 7. Player Name Not Synced on First Install
|
||||
**Problem:** Player name entered during installation wasn't synced to settings page input.
|
||||
|
||||
**Solution:**
|
||||
- In `install.js`, sync player name to both `playerName` and `settingsPlayerName` inputs after installation completes
|
||||
|
||||
## Files Modified
|
||||
|
||||
### `GUI/index.html`
|
||||
- Commented out `updater.js` script tag (duplicate update UI)
|
||||
|
||||
### `GUI/js/update.js`
|
||||
- Removed legacy `onUpdatePopup` listener
|
||||
- Added `closeUpdatePopup()` method
|
||||
- Added `unblockInterface()` method
|
||||
- Added skip button to popup HTML
|
||||
- Added skip button visibility logic (30s timeout, on error, after download)
|
||||
- Added macOS detection and alternative UI (manual download as primary)
|
||||
- Removed pulse animation when download completes
|
||||
- Added console logging for debugging
|
||||
- Added extra DOM check to prevent duplicate popups
|
||||
- Fixed manual download button to show "Opened in browser" and close popup
|
||||
|
||||
### `main.js`
|
||||
- Changed `autoUpdater.autoDownload` from `false` to `true`
|
||||
- Added macOS error handling with `requiresManualDownload` flag
|
||||
- Added `autoInstallSupported` flag to `update-downloaded` event
|
||||
- Added `open-download-page` IPC handler
|
||||
- Enhanced `install-update` handler with macOS force quit fallback
|
||||
|
||||
### `preload.js`
|
||||
- Added `quitAndInstallUpdate` alias for `install-update` IPC
|
||||
|
||||
### `GUI/js/install.js`
|
||||
- Sync player name to `settingsPlayerName` input after installation
|
||||
|
||||
### `backend/utils/clientPatcher.js`
|
||||
- Removed server patching code (server uses pre-patched JAR from CDN)
|
||||
- Simplified to client-only patching
|
||||
- Removed unused imports: `crypto`, `AdmZip`, `execSync`, `spawn`, `getJavaExec`, `getBundledJavaPath`, `JRE_DIR`
|
||||
- Removed unused methods: `stringToUtf8()`, `findAndReplaceDomainUtf8()`
|
||||
- Cleaned up comments and documentation
|
||||
- Localhost/local dev code moved to `clientPatcher.localhost.js.bak` for reference
|
||||
|
||||
## Testing
|
||||
|
||||
To test the update popup manually, you can temporarily add this debug code to `update.js` init():
|
||||
|
||||
```javascript
|
||||
// DEBUG: Simulate update available popup after 2 seconds
|
||||
setTimeout(() => {
|
||||
this.showUpdatePopup({
|
||||
currentVersion: '2.0.0',
|
||||
newVersion: '2.1.0',
|
||||
releaseNotes: 'Debug test update'
|
||||
});
|
||||
|
||||
// Simulate download complete after 3 more seconds
|
||||
setTimeout(() => {
|
||||
this.showUpdateDownloaded({
|
||||
version: '2.1.0',
|
||||
platform: 'darwin',
|
||||
autoInstallSupported: false // Simulate macOS
|
||||
});
|
||||
}, 3000);
|
||||
}, 2000);
|
||||
```
|
||||
|
||||
## Platform-Specific Behavior
|
||||
|
||||
### Windows/Linux
|
||||
- Auto-download enabled
|
||||
- "Install & Restart" as primary action
|
||||
- Skip button available as fallback
|
||||
|
||||
### macOS
|
||||
- Auto-download enabled (download works, install doesn't)
|
||||
- "Download Manually (Recommended)" as primary action
|
||||
- "Try Install & Restart" as secondary option
|
||||
- Skip button always visible after download
|
||||
- Force quit fallback if quitAndInstall fails
|
||||
21
local-dev/Dockerfile.debug
Normal file
21
local-dev/Dockerfile.debug
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
# Install openssl for SSL certificate generation
|
||||
RUN apk add --no-cache openssl
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files from hytale-auth-server
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Copy source files
|
||||
COPY src ./src
|
||||
COPY assets ./assets
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# Use debug wrapper as entry point (will fall back to normal app.js if DEBUG_MODE!=true)
|
||||
CMD ["node", "src/debug-wrapper.js"]
|
||||
252
local-dev/README.md
Normal file
252
local-dev/README.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# Hytale F2P Local Development Environment
|
||||
|
||||
Local development setup for researching and testing the Hytale client. Uses the **full hytale-auth-server** with a debug wrapper for capturing all requests.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# 1. Start the local auth server
|
||||
cd local-dev
|
||||
chmod +x start.sh
|
||||
./start.sh
|
||||
|
||||
# 2. In another terminal, start the launcher
|
||||
cd ..
|
||||
HYTALE_AUTH_DOMAIN=localhost:3000 npm start
|
||||
```
|
||||
|
||||
That's it! The auth server will capture all requests for analysis.
|
||||
|
||||
## What's Included
|
||||
|
||||
### Full Auth Server with Debug Wrapper
|
||||
- Uses the complete `hytale-auth-server` (not a minimal implementation)
|
||||
- All endpoints work exactly as in production
|
||||
- Debug wrapper captures ALL requests with full details
|
||||
- Web-based debug dashboard at `/debug`
|
||||
- Requests logged to `data/auth/debug-requests.jsonl`
|
||||
- Unknown endpoints captured for research
|
||||
|
||||
### Services
|
||||
- **Auth Server** (port 3000) - Full hytale-auth-server with debug
|
||||
- **Kvrocks** (port 6666) - Redis-compatible storage for sessions
|
||||
|
||||
## Debug Dashboard
|
||||
|
||||
Access at `http://localhost:3000/debug`
|
||||
|
||||
Features:
|
||||
- Real-time request viewer with auto-refresh
|
||||
- Filter by subdomain (sessions, account-data, telemetry, tools)
|
||||
- View full request/response details (headers, body, timing)
|
||||
- Subdomain summary statistics
|
||||
- Color-coded by request type and status
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
local-dev/
|
||||
├── docker-compose.yml # Docker services config
|
||||
├── Dockerfile.debug # Dockerfile using debug entry point
|
||||
├── debug-wrapper.js # Debug wrapper for auth server
|
||||
├── start.sh # Start script
|
||||
├── README.md # This file
|
||||
└── data/
|
||||
├── auth/
|
||||
│ ├── jwt_keys.json # Generated JWT keys
|
||||
│ └── debug-requests.jsonl # Request log
|
||||
└── kvrocks/ # Redis data
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Debug Endpoints
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/debug` | GET | Web-based debug dashboard |
|
||||
| `/debug/requests` | GET | JSON list of captured requests |
|
||||
| `/debug/requests?subdomain=X` | GET | Filter by subdomain |
|
||||
| `/debug/subdomains` | GET | Request summary by subdomain |
|
||||
| `/debug/requests` | DELETE | Clear captured requests |
|
||||
|
||||
### Auth Server Endpoints
|
||||
All standard hytale-auth-server endpoints work:
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/health` | GET | Health check |
|
||||
| `/.well-known/jwks.json` | GET | JWT public keys |
|
||||
| `/game-session/new` | POST | Create session |
|
||||
| `/game-session/refresh` | POST | Refresh token |
|
||||
| `/game-session/child` | POST | Child session |
|
||||
| `/game-session` | DELETE | End session |
|
||||
| `/server-join/auth-grant` | POST | Auth grant |
|
||||
| `/server-join/auth-token` | POST | Token exchange |
|
||||
| `/my-account/game-profile` | GET/POST | Profile |
|
||||
| `/my-account/cosmetics` | GET | Cosmetics |
|
||||
| `/my-account/skin` | POST | Save skin |
|
||||
| `/profile/uuid/:uuid` | GET | Profile lookup |
|
||||
| `/profile/username/:username` | GET | Username lookup |
|
||||
| `/avatar/:uuid` | GET | Avatar viewer |
|
||||
| `/customizer/:uuid` | GET | Avatar customizer |
|
||||
| `/admin` | GET | Admin dashboard (password: localdev) |
|
||||
|
||||
### Catch-All
|
||||
Unknown endpoints return a debug response with tokens for research.
|
||||
|
||||
## Researching tools.hytale.com
|
||||
|
||||
The `tools.` subdomain is used for cloud-based asset management in builder/editor modes.
|
||||
|
||||
### How to Trigger Tools API
|
||||
|
||||
**Method 1: Builder Mode**
|
||||
1. Start local dev environment
|
||||
2. Launch game
|
||||
3. Look for Creative/Builder mode in game menu
|
||||
4. Use asset import/export features
|
||||
5. Check debug dashboard for captured `tools.` requests
|
||||
|
||||
**Method 2: Server Configuration**
|
||||
Builder mode may need server-side enablement:
|
||||
- Server config for `builderToolsEnabled`
|
||||
- Game mode settings
|
||||
- Player permissions
|
||||
|
||||
**Method 3: Console Commands**
|
||||
Try these worldtools commands in-game:
|
||||
```
|
||||
worldtools.changeModel
|
||||
worldtools.importImage
|
||||
worldtools.importObj
|
||||
worldtools.prefabEditor
|
||||
worldtools.prefabList
|
||||
worldtools.spawnEntity
|
||||
```
|
||||
|
||||
### Expected tools.hytale.com Endpoints
|
||||
|
||||
Based on binary analysis:
|
||||
```
|
||||
POST /assets/upload - Upload asset files
|
||||
GET /assets/{id} - Download asset by ID
|
||||
POST /prefabs - Save prefab
|
||||
GET /prefabs/{id} - Load prefab
|
||||
GET /prefabs - List prefabs
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### Start Services
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
docker compose logs -f auth-server
|
||||
```
|
||||
|
||||
### Stop Services
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### View Captured Requests (CLI)
|
||||
```bash
|
||||
# All requests
|
||||
curl http://localhost:3000/debug/requests | jq
|
||||
|
||||
# Filter by subdomain
|
||||
curl "http://localhost:3000/debug/requests?subdomain=tools" | jq
|
||||
|
||||
# Subdomain summary
|
||||
curl http://localhost:3000/debug/subdomains | jq
|
||||
|
||||
# From log file
|
||||
cat data/auth/debug-requests.jsonl | jq -s '.'
|
||||
```
|
||||
|
||||
### Clear Request Log
|
||||
```bash
|
||||
curl -X DELETE http://localhost:3000/debug/requests
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `HYTALE_AUTH_DOMAIN` | - | Set to `localhost:3000` for local dev |
|
||||
| `PORT` | `3000` | Auth server port |
|
||||
| `DATA_DIR` | `/app/data` | Data directory |
|
||||
| `DEBUG_MODE` | `true` | Enable debug features |
|
||||
| `ADMIN_PASSWORD` | `localdev` | Admin dashboard password |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Client won't connect
|
||||
1. Verify auth server is running: `curl http://localhost:3000/health`
|
||||
2. Check environment variable: `echo $HYTALE_AUTH_DOMAIN`
|
||||
3. Check Docker logs: `docker compose logs auth-server`
|
||||
|
||||
### JWT/Token errors
|
||||
1. Check JWKS endpoint: `curl http://localhost:3000/.well-known/jwks.json`
|
||||
2. Ensure `data/auth/jwt_keys.json` exists
|
||||
3. Try restarting: `docker compose restart auth-server`
|
||||
|
||||
### Port 3000 in use
|
||||
Change the port in docker-compose.yml:
|
||||
```yaml
|
||||
ports:
|
||||
- "3001:3000"
|
||||
```
|
||||
Then use `HYTALE_AUTH_DOMAIN=localhost:3001`
|
||||
|
||||
### Server won't start
|
||||
Check for build errors:
|
||||
```bash
|
||||
docker compose logs auth-server
|
||||
docker compose build --no-cache auth-server
|
||||
```
|
||||
|
||||
## Request Log Format
|
||||
|
||||
Each line in `debug-requests.jsonl` is a JSON object:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-01-27T12:00:00.000Z",
|
||||
"method": "POST",
|
||||
"path": "/game-session/new",
|
||||
"host": "localhost:3000",
|
||||
"subdomain": null,
|
||||
"headers": {"content-type": "application/json", ...},
|
||||
"query": {},
|
||||
"body": {"uuid": "...", "name": "Player"},
|
||||
"response": {
|
||||
"statusCode": 200,
|
||||
"duration": 15,
|
||||
"body": {"identityToken": "...", ...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Docker Compose** starts Kvrocks (Redis) and the auth server
|
||||
2. **Dockerfile.debug** builds the auth server with `debug-wrapper.js` as entry point
|
||||
3. **debug-wrapper.js** wraps the original server:
|
||||
- Intercepts all requests before they reach handlers
|
||||
- Captures request details (headers, body, timing)
|
||||
- Intercepts responses to capture response body
|
||||
- Stores everything in memory and log file
|
||||
- Adds `/debug/*` routes for the dashboard
|
||||
|
||||
4. When `DEBUG_MODE=true`:
|
||||
- All requests are logged to console with colors
|
||||
- Debug dashboard available at `/debug`
|
||||
- Unknown endpoints return debug info with valid tokens
|
||||
|
||||
5. When `DEBUG_MODE=false`:
|
||||
- Server runs normally (no debug overhead)
|
||||
- Same as production auth server
|
||||
19
local-dev/data/auth/certs/server.crt
Normal file
19
local-dev/data/auth/certs/server.crt
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDJTCCAg2gAwIBAgIUPhxbMZRuz3jUe6EzJf6yh9dazkcwDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDEyNzIxMzU0OVoXDTI3MDEy
|
||||
NzIxMzU0OVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEAzkqaar2i/RFFkmXjcVC6G8Dvf9vYTMDJXq47qVmE0nDb
|
||||
MsGSZCk4f5a9aG8BIEK/LN1csaGo3C7995R4+tqhEmFvRjHBsY2eJNeamHTEZsiw
|
||||
UqWC98/Mq2EdT/mRVWks00XQAR0yobnPz7nphmxDv3LxddHfvYv1Va010UHO4SrW
|
||||
1twNvM5UwCT2r5shpyzJAqF8U35RmaWSVzkEN7OMZbcg7aYsumdW2jSn539MKpAC
|
||||
Eyr8pRXoDqGh4zp2lEuPryKtPyh2ljNmpYIQYaXQWlyjXDsHbs9U0sHFP2fw6k1X
|
||||
sp4dUcPYzyt7WC6XfbUq2Ynz2no2sOJV1hruKY1niwIDAQABo28wbTAdBgNVHQ4E
|
||||
FgQU/RBh6UbVrO9qkFVNmo+5AnM6mMgwHwYDVR0jBBgwFoAU/RBh6UbVrO9qkFVN
|
||||
mo+5AnM6mMgwDwYDVR0TAQH/BAUwAwEB/zAaBgNVHREEEzARgglsb2NhbGhvc3SH
|
||||
BH8AAAEwDQYJKoZIhvcNAQELBQADggEBAIr5gJ2gb079dF/tHeyqx6BxCg9+hsUg
|
||||
38Fsxde0a6lEjSkntla+p20Bl2mdeWEeKFw5wc1mnMQkEJjixEhE5iA6gdswvpgf
|
||||
07bYFRddCUYLrkQjWbo6u7UrkOeJfgZdHFTihMPguZ2tEPcMXitY7Ct24JRC3RRZ
|
||||
BKCp88ix+ns61vAJfgdBZPbrtM0Ky2fIS+m9fWmKHvxag2/TOB67/avFzBkYXVgV
|
||||
Xy79xxcICgIUELAQ8Hz0lXYfW/k+QnSAAtgLRxGh3eONaA/0Ij9sqzSnQzLM5fLd
|
||||
omUKx+p2gzCpprSZGfV221ZtiAm6IvwKpQJMyM2YSbdCJfYAC0RjVyQ=
|
||||
-----END CERTIFICATE-----
|
||||
28
local-dev/data/auth/certs/server.key
Normal file
28
local-dev/data/auth/certs/server.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDOSppqvaL9EUWS
|
||||
ZeNxULobwO9/29hMwMlerjupWYTScNsywZJkKTh/lr1obwEgQr8s3VyxoajcLv33
|
||||
lHj62qESYW9GMcGxjZ4k15qYdMRmyLBSpYL3z8yrYR1P+ZFVaSzTRdABHTKhuc/P
|
||||
uemGbEO/cvF10d+9i/VVrTXRQc7hKtbW3A28zlTAJPavmyGnLMkCoXxTflGZpZJX
|
||||
OQQ3s4xltyDtpiy6Z1baNKfnf0wqkAITKvylFegOoaHjOnaUS4+vIq0/KHaWM2al
|
||||
ghBhpdBaXKNcOwduz1TSwcU/Z/DqTVeynh1Rw9jPK3tYLpd9tSrZifPaejaw4lXW
|
||||
Gu4pjWeLAgMBAAECggEAOXySsYItJGamw5g/HHnJkyhd1XyXNzRWKVtWZuf6WoZ2
|
||||
nxtQRzcxdmS0XaDpaGsRSVhal2mcW9eAkHjAie9ZCX07fA0rk+YKFuw6OZf4j0gH
|
||||
0tAqwhIXT/7dI0dB19JaWnnO8DCJxoW4QoPlbr9G1dgbL6EPv4t8D6cYIzs6goZk
|
||||
LZcj5niASmkPgNoSi6yiBTcvOUEalJm3b+u5BMKL5jaodmo0pcBRa+w2jPdrFB9X
|
||||
9s2ZdztLXdn2JVxS7N0IN9KNJzOhip/0FMiv9MAxQ+rSxsNgaZRKQ5iI8nzCRRv8
|
||||
VDI6FW4+hUFIcieeEqzfORJRmtj2N99AzVZMOomkkQKBgQD3rkRiIB7Q5mFIoRRN
|
||||
YWiTOlGT3o4tYRWBE/p6eFEBTxPlvbezKyZ5OdwsUVMVbWe2ZqAUjUgQQ53VtRZq
|
||||
3VAQbsvsm08IbY9Zrj2zSqhfhm/pyT63fqWqdqHMmJfXCkL1tZJWaijyzmsUA5sO
|
||||
j7zGJtxdb7a0DFD0WYhFQ9nbeQKBgQDVOHES0+UCQsq7XEmGhYBJNnVFtV8bCy8q
|
||||
HuGBX1f+M+mE4DDuNo067EFKlEfiMTrMIKQT/3WNim6YpdCcMIrA/roCmG2cqjmQ
|
||||
gkuEItxU1WGbO3VvK2eN9lG5uiWjg5P/5F4Y6U+DdBqGmNqvH3dvQP1AyEeqYnDv
|
||||
YGkXFygWIwKBgQDUV0k1PwhsXDanR8HaHVrEbkkmFrWZ3hPLl880VBZOovcSDbaC
|
||||
GspfP+Ws8QPj6OnzjMRNGlrf5rhYUWoosBhGHlciQHxfY150qlcncSgszVsA+ZGV
|
||||
SzTIkfBhMalrqNaDROlywIzerW1LuVOkBkL3NrXSPUZL0gtNkbysdWE/MQKBgFLB
|
||||
IUHJc+y4t66YVwEa93ty43k2t77rTFbwvV1U//XteAjWaqdKDO59m6mye2PS75si
|
||||
YAxS7fENdXdRg/Ha9T+Kne878e8IMmdf6qdSUGmsl8GEBkQreHmkzHtlQA6ClwKO
|
||||
Q+cvRmkiutjaoqWtdNF9S83E7eu3YVXG+YK4vho5AoGBANt5fp1In+BLcToV+MoK
|
||||
gf6gNfwq6N3as5V4/JsB5Uem53dNE1E37rKNZqpeKHgBfrRi4FcfXNQFtNsk1Hty
|
||||
tLLuNJtt6kTFKuurccCtxvexU7oItR7Jph6U7pWjKV9zcoReX817QZQeVjq2ZYAx
|
||||
bGmSCyK9LMAI/TGYwu9G2DMA
|
||||
-----END PRIVATE KEY-----
|
||||
488
local-dev/data/auth/debug-requests.jsonl
Normal file
488
local-dev/data/auth/debug-requests.jsonl
Normal file
File diff suppressed because one or more lines are too long
5
local-dev/data/auth/jwt_keys.json
Normal file
5
local-dev/data/auth/jwt_keys.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"privateKey": "MC4CAQAwBQYDK2VwBCIEID/rEs35asZTitPu/73KBIoI4mb3dzH4QoBElz8IX29x",
|
||||
"publicKey": "MCowBQYDK2VwAyEAg1HEi1XUdUZAYc+/9ioijFkkGdulbxT3eVpM+i8Mdr4=",
|
||||
"createdAt": "2026-01-27T23:22:58.667Z"
|
||||
}
|
||||
19
local-dev/data/auth/ssl/server.crt
Normal file
19
local-dev/data/auth/ssl/server.crt
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIUHQdNViKmIqxQLoob3FV9wsVi7EYwDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDEyNzIyMDA0MFoXDTI3MDEy
|
||||
NzIyMDA0MFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEAtt+6Fk7s1tunvn1rCB4HislzRl38OCHw8OS6LcJQgqat
|
||||
qJMtfm/UQd2i97URmX6/RAimAEKAPd2njZXhtZ5pyGkURW9K99PhKx1yW7YPVnDs
|
||||
bkyC3TvIFG6kBtUqwdawSHigUMKaOUMlDnHLlrobIoxGt1EplyPXRykLfzWO37Wo
|
||||
3WNp/3tXcSvL7Osq/82f6jJhyTlo6UngUInMXpm3bq/k2ikEsIge7yJUqoLJwdCL
|
||||
lIkm+Q0jI+p0YSiA+b17iLJBpvQHKkhLASKfK78lggZdcZJIc0J7Oqflzy3mG9e2
|
||||
qqeSSCUKEpvvh95ep5kk5AkP0wYlgVO9bh+y9MEA9wIDAQABo1MwUTAdBgNVHQ4E
|
||||
FgQUeK7lS9jHG9wkYTasyg14fNLo3bQwHwYDVR0jBBgwFoAUeK7lS9jHG9wkYTas
|
||||
yg14fNLo3bQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4p1
|
||||
wxBa1j156zE6ZAQAZT9axh+nuHjA6JyKPO82npOrjjMRDgkvvmvEjMQDL+YSwngD
|
||||
RA5sJGwKiU9boaTYQEuM2uaqlNzBENv3GpWzkbfzKbqcgOOA/WFmXbogDjkknlee
|
||||
/jgirTl1hM8Kih0avDjKLwRvSSAUZB2Z0tjpK1ZdLp7L8jIGzYdke0Qt4wDyucxS
|
||||
C1BIPe8r12cSnNeP9krbdj7+P3YEWi9UgZWKAY8HbSjZSzXZvVlxcUO4ViSvF+lh
|
||||
kGfAg22af46U0TKtZB2X++xO7aNrcRbqKNSEpibG510BN9ym4P/D1+QhFcz5guco
|
||||
tvenNmwdgwY9viSnIw==
|
||||
-----END CERTIFICATE-----
|
||||
28
local-dev/data/auth/ssl/server.key
Normal file
28
local-dev/data/auth/ssl/server.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC237oWTuzW26e+
|
||||
fWsIHgeKyXNGXfw4IfDw5LotwlCCpq2oky1+b9RB3aL3tRGZfr9ECKYAQoA93aeN
|
||||
leG1nmnIaRRFb0r30+ErHXJbtg9WcOxuTILdO8gUbqQG1SrB1rBIeKBQwpo5QyUO
|
||||
ccuWuhsijEa3USmXI9dHKQt/NY7ftajdY2n/e1dxK8vs6yr/zZ/qMmHJOWjpSeBQ
|
||||
icxembdur+TaKQSwiB7vIlSqgsnB0IuUiSb5DSMj6nRhKID5vXuIskGm9AcqSEsB
|
||||
Ip8rvyWCBl1xkkhzQns6p+XPLeYb17aqp5JIJQoSm++H3l6nmSTkCQ/TBiWBU71u
|
||||
H7L0wQD3AgMBAAECggEANC/zjHM4jnY/0BI9jaL4NwiXP4EJvcEd37j+Upmt3SMQ
|
||||
0tcxd/AU7DkCTVtcaufrUFCBgvh3XXBYZQTdVTWgbYNjOA9zHvdVvjaYkIiLpvjx
|
||||
1+Y4wEbTqdULNTH3EjsgsoXOBk4KsgWx2QXrehehL8JFWgIs8sdVI6cYc1SG8dsK
|
||||
ekWB14zwaA4bBMejpGtDV0q2/bgECq6R9vU9pMD46LXkjHli+xSnYdwrimMlckg/
|
||||
VX94Tedi23mdEmxZBZ+m3fhzrSqs8C0JmTezGEO7awDl3NnAhK/t1cSfLz0o1YJv
|
||||
1SOC/JSLET4rMDMHxRlUfhM5CI9NeSXS3n4HWKcBRQKBgQD3drS+sZ/pqUxjUbv+
|
||||
LPUCLHar6ax0ge9bw3nS6LPRyp1uqmYiVRfruEWWZnCGIU+ST3zokZrroQA5jGPG
|
||||
DB5PLsopqoE217G2514WEW8hZzW3395VtZqphA+o9SCmIA1logEYdN+ZDG6KQtiS
|
||||
Ml1BdRTuyrUAC4QG4PNkb8xmSwKBgQC9LqTP7rU7p8RttN/f6q9kJCjsVrfj8rIs
|
||||
fSPzYhVDHSNwJufwMjqLJxmNtrg0yIZfPfXKki8yX5ms28xh7d2je61rBBajI/lY
|
||||
8cYsgP7bebruvClb4Rgyi9//bACWaEca/hQUte/8OG37wCSvotWMe0wZst1vLXrY
|
||||
4i1Qo3YUhQKBgCIy3H2yDHh9NhpPtFxHGEEJpgjSDUw4nLygwhg8ooUurB0cHWtH
|
||||
OTwRRkSnuYs/1UBSDeASJ0fDA0XwueUnzZSB0dx6PMs4ec3eIamJFUOXgNv9azuL
|
||||
Emm033kpDy8GJPPTtLUNic0b47bl0Ao8PZkLNi5eAy+TZ9aPhfNuY+ALAoGBAILT
|
||||
Y8QrlocHkK4xO/R0LhA5dVdi7M4lApQDgj4IIR4FY4FmVlTj11ptkp1Os3pFBa+N
|
||||
gJEyoJLcS8CfS2qZfQFWQOnVrPXqpb+diucx2YzbVKtN0ego9HvYAPJ4tLtOp4WC
|
||||
GU7tNmWcfGpxSK1xFO6OQWUwLTN6Jw8e8ilmsPylAoGBAK89GUz02qJrdbN8TMsI
|
||||
vXBVpJX27wjFFwLHcsQ09CJ1Fppa8tZrqO/BtYTPhOncsmiOSjuFCnh6d3meEUHQ
|
||||
BSYSjQWtJ+pilEMp2EiO55s4k/iFhKUeRsCjrTSGor21HqgRNaxsEfoieNWWWFv+
|
||||
rcDAyW1WdJaydbVfXisAtzOL
|
||||
-----END PRIVATE KEY-----
|
||||
BIN
local-dev/data/kvrocks/db/000014.sst
Normal file
BIN
local-dev/data/kvrocks/db/000014.sst
Normal file
Binary file not shown.
BIN
local-dev/data/kvrocks/db/000023.sst
Normal file
BIN
local-dev/data/kvrocks/db/000023.sst
Normal file
Binary file not shown.
BIN
local-dev/data/kvrocks/db/000032.sst
Normal file
BIN
local-dev/data/kvrocks/db/000032.sst
Normal file
Binary file not shown.
BIN
local-dev/data/kvrocks/db/000033.sst
Normal file
BIN
local-dev/data/kvrocks/db/000033.sst
Normal file
Binary file not shown.
BIN
local-dev/data/kvrocks/db/000034.log
Normal file
BIN
local-dev/data/kvrocks/db/000034.log
Normal file
Binary file not shown.
1
local-dev/data/kvrocks/db/CURRENT
Normal file
1
local-dev/data/kvrocks/db/CURRENT
Normal file
@@ -0,0 +1 @@
|
||||
MANIFEST-000035
|
||||
1
local-dev/data/kvrocks/db/IDENTITY
Normal file
1
local-dev/data/kvrocks/db/IDENTITY
Normal file
@@ -0,0 +1 @@
|
||||
7a0fd32b-70d3-447e-90d3-74fc57ad6651
|
||||
0
local-dev/data/kvrocks/db/LOCK
Normal file
0
local-dev/data/kvrocks/db/LOCK
Normal file
2434
local-dev/data/kvrocks/db/LOG
Normal file
2434
local-dev/data/kvrocks/db/LOG
Normal file
File diff suppressed because it is too large
Load Diff
1440
local-dev/data/kvrocks/db/LOG.old.1769550932829617
Normal file
1440
local-dev/data/kvrocks/db/LOG.old.1769550932829617
Normal file
File diff suppressed because it is too large
Load Diff
1448
local-dev/data/kvrocks/db/LOG.old.1769551238558585
Normal file
1448
local-dev/data/kvrocks/db/LOG.old.1769551238558585
Normal file
File diff suppressed because it is too large
Load Diff
391
local-dev/data/kvrocks/db/LOG.old.1769551238561527
Normal file
391
local-dev/data/kvrocks/db/LOG.old.1769551238561527
Normal file
@@ -0,0 +1,391 @@
|
||||
2026/01/27-22:00:38.559438 1 RocksDB version: 10.6.2
|
||||
2026/01/27-22:00:38.559502 1 Git sha 0
|
||||
2026/01/27-22:00:38.559503 1 Compile date 2025-11-08 14:59:16
|
||||
2026/01/27-22:00:38.559504 1 DB SUMMARY
|
||||
2026/01/27-22:00:38.559511 1 Host name (Env): 50e4104391a6
|
||||
2026/01/27-22:00:38.559513 1 DB Session ID: Y90KK2H3WYG203E6436J
|
||||
2026/01/27-22:00:38.559609 1 CURRENT file: CURRENT
|
||||
2026/01/27-22:00:38.559610 1 IDENTITY file: IDENTITY
|
||||
2026/01/27-22:00:38.559618 1 MANIFEST file: MANIFEST-000011 size: 751 Bytes
|
||||
2026/01/27-22:00:38.559619 1 SST files in /data/db dir, Total Num: 0, files:
|
||||
2026/01/27-22:00:38.559620 1 Write Ahead Log file in /data/db: 000010.log size: 1329 ;
|
||||
2026/01/27-22:00:38.559621 1 Options.error_if_exists: 0
|
||||
2026/01/27-22:00:38.559621 1 Options.create_if_missing: 1
|
||||
2026/01/27-22:00:38.559622 1 Options.paranoid_checks: 1
|
||||
2026/01/27-22:00:38.559623 1 Options.flush_verify_memtable_count: 1
|
||||
2026/01/27-22:00:38.559623 1 Options.compaction_verify_record_count: 1
|
||||
2026/01/27-22:00:38.559624 1 Options.track_and_verify_wals_in_manifest: 0
|
||||
2026/01/27-22:00:38.559624 1 Options.track_and_verify_wals: 0
|
||||
2026/01/27-22:00:38.559625 1 Options.verify_sst_unique_id_in_manifest: 1
|
||||
2026/01/27-22:00:38.559625 1 Options.env: 0xffffa561d0c0
|
||||
2026/01/27-22:00:38.559626 1 Options.fs: PosixFileSystem
|
||||
2026/01/27-22:00:38.559627 1 Options.info_log: 0xffffa570f600
|
||||
2026/01/27-22:00:38.559627 1 Options.max_file_opening_threads: 16
|
||||
2026/01/27-22:00:38.559628 1 Options.statistics: 0xffffa56135b0
|
||||
2026/01/27-22:00:38.559628 1 Options.statistics stats level: 3
|
||||
2026/01/27-22:00:38.559629 1 Options.use_fsync: 0
|
||||
2026/01/27-22:00:38.559629 1 Options.max_log_file_size: 268435456
|
||||
2026/01/27-22:00:38.559630 1 Options.max_manifest_file_size: 67108864
|
||||
2026/01/27-22:00:38.559630 1 Options.log_file_time_to_roll: 0
|
||||
2026/01/27-22:00:38.559631 1 Options.keep_log_file_num: 12
|
||||
2026/01/27-22:00:38.559631 1 Options.recycle_log_file_num: 0
|
||||
2026/01/27-22:00:38.559632 1 Options.allow_fallocate: 1
|
||||
2026/01/27-22:00:38.559632 1 Options.allow_mmap_reads: 0
|
||||
2026/01/27-22:00:38.559633 1 Options.allow_mmap_writes: 0
|
||||
2026/01/27-22:00:38.559633 1 Options.use_direct_reads: 0
|
||||
2026/01/27-22:00:38.559634 1 Options.use_direct_io_for_flush_and_compaction: 0
|
||||
2026/01/27-22:00:38.559634 1 Options.create_missing_column_families: 1
|
||||
2026/01/27-22:00:38.559634 1 Options.db_log_dir:
|
||||
2026/01/27-22:00:38.559635 1 Options.wal_dir:
|
||||
2026/01/27-22:00:38.559636 1 Options.table_cache_numshardbits: 6
|
||||
2026/01/27-22:00:38.559636 1 Options.WAL_ttl_seconds: 10800
|
||||
2026/01/27-22:00:38.559637 1 Options.WAL_size_limit_MB: 16384
|
||||
2026/01/27-22:00:38.559637 1 Options.max_write_batch_group_size_bytes: 1048576
|
||||
2026/01/27-22:00:38.559638 1 Options.manifest_preallocation_size: 4194304
|
||||
2026/01/27-22:00:38.559638 1 Options.is_fd_close_on_exec: 1
|
||||
2026/01/27-22:00:38.559639 1 Options.advise_random_on_open: 1
|
||||
2026/01/27-22:00:38.559639 1 Options.db_write_buffer_size: 0
|
||||
2026/01/27-22:00:38.559640 1 Options.write_buffer_manager: 0xffffa566bfc0
|
||||
2026/01/27-22:00:38.559640 1 Options.use_adaptive_mutex: 0
|
||||
2026/01/27-22:00:38.559640 1 Options.rate_limiter: 0xffffa5636000
|
||||
2026/01/27-22:00:38.559641 1 Options.sst_file_manager.rate_bytes_per_sec: 0
|
||||
2026/01/27-22:00:38.559642 1 Options.wal_recovery_mode: 2
|
||||
2026/01/27-22:00:38.559642 1 Options.enable_thread_tracking: 0
|
||||
2026/01/27-22:00:38.559643 1 Options.enable_pipelined_write: 0
|
||||
2026/01/27-22:00:38.559643 1 Options.unordered_write: 0
|
||||
2026/01/27-22:00:38.559644 1 Options.allow_concurrent_memtable_write: 1
|
||||
2026/01/27-22:00:38.559644 1 Options.enable_write_thread_adaptive_yield: 1
|
||||
2026/01/27-22:00:38.559645 1 Options.write_thread_max_yield_usec: 100
|
||||
2026/01/27-22:00:38.559645 1 Options.write_thread_slow_yield_usec: 3
|
||||
2026/01/27-22:00:38.559646 1 Options.row_cache: None
|
||||
2026/01/27-22:00:38.559646 1 Options.wal_filter: None
|
||||
2026/01/27-22:00:38.559647 1 Options.avoid_flush_during_recovery: 0
|
||||
2026/01/27-22:00:38.559647 1 Options.allow_ingest_behind: 0
|
||||
2026/01/27-22:00:38.559648 1 Options.two_write_queues: 0
|
||||
2026/01/27-22:00:38.559648 1 Options.manual_wal_flush: 0
|
||||
2026/01/27-22:00:38.559648 1 Options.wal_compression: 0
|
||||
2026/01/27-22:00:38.559649 1 Options.background_close_inactive_wals: 0
|
||||
2026/01/27-22:00:38.559649 1 Options.atomic_flush: 0
|
||||
2026/01/27-22:00:38.559650 1 Options.avoid_unnecessary_blocking_io: 1
|
||||
2026/01/27-22:00:38.559650 1 Options.prefix_seek_opt_in_only: 0
|
||||
2026/01/27-22:00:38.559651 1 Options.persist_stats_to_disk: 0
|
||||
2026/01/27-22:00:38.559651 1 Options.write_dbid_to_manifest: 1
|
||||
2026/01/27-22:00:38.559652 1 Options.write_identity_file: 1
|
||||
2026/01/27-22:00:38.559652 1 Options.log_readahead_size: 0
|
||||
2026/01/27-22:00:38.559653 1 Options.file_checksum_gen_factory: Unknown
|
||||
2026/01/27-22:00:38.559653 1 Options.best_efforts_recovery: 0
|
||||
2026/01/27-22:00:38.559654 1 Options.max_bgerror_resume_count: 2147483647
|
||||
2026/01/27-22:00:38.559654 1 Options.bgerror_resume_retry_interval: 1000000
|
||||
2026/01/27-22:00:38.559655 1 Options.allow_data_in_errors: 0
|
||||
2026/01/27-22:00:38.559655 1 Options.db_host_id: __hostname__
|
||||
2026/01/27-22:00:38.559656 1 Options.enforce_single_del_contracts: true
|
||||
2026/01/27-22:00:38.559657 1 Options.metadata_write_temperature: kUnknown
|
||||
2026/01/27-22:00:38.559657 1 Options.wal_write_temperature: kUnknown
|
||||
2026/01/27-22:00:38.559658 1 Options.max_background_jobs: 4
|
||||
2026/01/27-22:00:38.559658 1 Options.max_background_compactions: -1
|
||||
2026/01/27-22:00:38.559659 1 Options.max_subcompactions: 2
|
||||
2026/01/27-22:00:38.559659 1 Options.avoid_flush_during_shutdown: 0
|
||||
2026/01/27-22:00:38.559660 1 Options.writable_file_max_buffer_size: 1048576
|
||||
2026/01/27-22:00:38.559661 1 Options.delayed_write_rate : 536870912000
|
||||
2026/01/27-22:00:38.559661 1 Options.max_total_wal_size: 536870912
|
||||
2026/01/27-22:00:38.559662 1 Options.delete_obsolete_files_period_micros: 21600000000
|
||||
2026/01/27-22:00:38.559663 1 Options.stats_dump_period_sec: 0
|
||||
2026/01/27-22:00:38.559663 1 Options.stats_persist_period_sec: 600
|
||||
2026/01/27-22:00:38.559664 1 Options.stats_history_buffer_size: 1048576
|
||||
2026/01/27-22:00:38.559664 1 Options.max_open_files: 8096
|
||||
2026/01/27-22:00:38.559665 1 Options.bytes_per_sync: 1048576
|
||||
2026/01/27-22:00:38.559665 1 Options.wal_bytes_per_sync: 0
|
||||
2026/01/27-22:00:38.559666 1 Options.strict_bytes_per_sync: 0
|
||||
2026/01/27-22:00:38.559666 1 Options.compaction_readahead_size: 2097152
|
||||
2026/01/27-22:00:38.559667 1 Options.max_background_flushes: -1
|
||||
2026/01/27-22:00:38.559667 1 Options.daily_offpeak_time_utc:
|
||||
2026/01/27-22:00:38.559668 1 Compression algorithms supported:
|
||||
2026/01/27-22:00:38.559669 1 kCustomCompressionFE supported: 0
|
||||
2026/01/27-22:00:38.559669 1 kCustomCompressionFC supported: 0
|
||||
2026/01/27-22:00:38.559670 1 kCustomCompressionF8 supported: 0
|
||||
2026/01/27-22:00:38.559670 1 kCustomCompressionF7 supported: 0
|
||||
2026/01/27-22:00:38.559671 1 kCustomCompressionB2 supported: 0
|
||||
2026/01/27-22:00:38.559671 1 kLZ4Compression supported: 1
|
||||
2026/01/27-22:00:38.559672 1 kCustomCompression88 supported: 0
|
||||
2026/01/27-22:00:38.559673 1 kCustomCompressionD8 supported: 0
|
||||
2026/01/27-22:00:38.559673 1 kCustomCompression9F supported: 0
|
||||
2026/01/27-22:00:38.559674 1 kCustomCompressionD6 supported: 0
|
||||
2026/01/27-22:00:38.559674 1 kCustomCompressionA9 supported: 0
|
||||
2026/01/27-22:00:38.559675 1 kCustomCompressionEC supported: 0
|
||||
2026/01/27-22:00:38.559675 1 kCustomCompressionA3 supported: 0
|
||||
2026/01/27-22:00:38.559676 1 kCustomCompressionCB supported: 0
|
||||
2026/01/27-22:00:38.559676 1 kCustomCompression90 supported: 0
|
||||
2026/01/27-22:00:38.559677 1 kCustomCompressionA0 supported: 0
|
||||
2026/01/27-22:00:38.559677 1 kCustomCompressionC6 supported: 0
|
||||
2026/01/27-22:00:38.559678 1 kCustomCompression9D supported: 0
|
||||
2026/01/27-22:00:38.559678 1 kCustomCompression8B supported: 0
|
||||
2026/01/27-22:00:38.559679 1 kCustomCompressionA8 supported: 0
|
||||
2026/01/27-22:00:38.559679 1 kCustomCompression8D supported: 0
|
||||
2026/01/27-22:00:38.559680 1 kCustomCompression97 supported: 0
|
||||
2026/01/27-22:00:38.559680 1 kCustomCompression98 supported: 0
|
||||
2026/01/27-22:00:38.559681 1 kCustomCompressionAC supported: 0
|
||||
2026/01/27-22:00:38.559681 1 kCustomCompressionE9 supported: 0
|
||||
2026/01/27-22:00:38.559682 1 kCustomCompression96 supported: 0
|
||||
2026/01/27-22:00:38.559682 1 kCustomCompressionB1 supported: 0
|
||||
2026/01/27-22:00:38.559683 1 kCustomCompression95 supported: 0
|
||||
2026/01/27-22:00:38.559683 1 kCustomCompression84 supported: 0
|
||||
2026/01/27-22:00:38.559684 1 kCustomCompression91 supported: 0
|
||||
2026/01/27-22:00:38.559686 1 kCustomCompressionAB supported: 0
|
||||
2026/01/27-22:00:38.559687 1 kCustomCompressionB3 supported: 0
|
||||
2026/01/27-22:00:38.559687 1 kCustomCompression81 supported: 0
|
||||
2026/01/27-22:00:38.559688 1 kCustomCompressionDC supported: 0
|
||||
2026/01/27-22:00:38.559689 1 kBZip2Compression supported: 0
|
||||
2026/01/27-22:00:38.559689 1 kCustomCompressionBB supported: 0
|
||||
2026/01/27-22:00:38.559690 1 kCustomCompression9C supported: 0
|
||||
2026/01/27-22:00:38.559690 1 kCustomCompressionC9 supported: 0
|
||||
2026/01/27-22:00:38.559691 1 kCustomCompressionCC supported: 0
|
||||
2026/01/27-22:00:38.559691 1 kCustomCompression92 supported: 0
|
||||
2026/01/27-22:00:38.559692 1 kCustomCompressionB9 supported: 0
|
||||
2026/01/27-22:00:38.559694 1 kCustomCompression8F supported: 0
|
||||
2026/01/27-22:00:38.559695 1 kCustomCompression8A supported: 0
|
||||
2026/01/27-22:00:38.559695 1 kCustomCompression9B supported: 0
|
||||
2026/01/27-22:00:38.559696 1 kZSTD supported: 1
|
||||
2026/01/27-22:00:38.559696 1 kCustomCompressionAA supported: 0
|
||||
2026/01/27-22:00:38.559697 1 kCustomCompressionA2 supported: 0
|
||||
2026/01/27-22:00:38.559697 1 kZlibCompression supported: 1
|
||||
2026/01/27-22:00:38.559698 1 kXpressCompression supported: 0
|
||||
2026/01/27-22:00:38.559699 1 kCustomCompressionFD supported: 0
|
||||
2026/01/27-22:00:38.559699 1 kCustomCompressionE2 supported: 0
|
||||
2026/01/27-22:00:38.559700 1 kLZ4HCCompression supported: 1
|
||||
2026/01/27-22:00:38.559700 1 kCustomCompressionA6 supported: 0
|
||||
2026/01/27-22:00:38.559701 1 kCustomCompression85 supported: 0
|
||||
2026/01/27-22:00:38.559701 1 kCustomCompressionA4 supported: 0
|
||||
2026/01/27-22:00:38.559702 1 kCustomCompression86 supported: 0
|
||||
2026/01/27-22:00:38.559702 1 kCustomCompression83 supported: 0
|
||||
2026/01/27-22:00:38.559703 1 kCustomCompression87 supported: 0
|
||||
2026/01/27-22:00:38.559703 1 kCustomCompression89 supported: 0
|
||||
2026/01/27-22:00:38.559704 1 kCustomCompression8C supported: 0
|
||||
2026/01/27-22:00:38.559704 1 kCustomCompressionDB supported: 0
|
||||
2026/01/27-22:00:38.559705 1 kCustomCompressionF3 supported: 0
|
||||
2026/01/27-22:00:38.559705 1 kCustomCompressionE6 supported: 0
|
||||
2026/01/27-22:00:38.559705 1 kCustomCompression8E supported: 0
|
||||
2026/01/27-22:00:38.559706 1 kCustomCompressionDA supported: 0
|
||||
2026/01/27-22:00:38.559706 1 kCustomCompression93 supported: 0
|
||||
2026/01/27-22:00:38.559707 1 kCustomCompression94 supported: 0
|
||||
2026/01/27-22:00:38.559709 1 kCustomCompression9E supported: 0
|
||||
2026/01/27-22:00:38.559710 1 kCustomCompressionB4 supported: 0
|
||||
2026/01/27-22:00:38.559710 1 kCustomCompressionFB supported: 0
|
||||
2026/01/27-22:00:38.559711 1 kCustomCompressionB5 supported: 0
|
||||
2026/01/27-22:00:38.559711 1 kCustomCompressionD5 supported: 0
|
||||
2026/01/27-22:00:38.559712 1 kCustomCompressionB8 supported: 0
|
||||
2026/01/27-22:00:38.559712 1 kCustomCompressionD1 supported: 0
|
||||
2026/01/27-22:00:38.559713 1 kCustomCompressionBA supported: 0
|
||||
2026/01/27-22:00:38.559713 1 kCustomCompressionBC supported: 0
|
||||
2026/01/27-22:00:38.559714 1 kCustomCompressionCE supported: 0
|
||||
2026/01/27-22:00:38.559714 1 kCustomCompressionBD supported: 0
|
||||
2026/01/27-22:00:38.559715 1 kCustomCompressionC4 supported: 0
|
||||
2026/01/27-22:00:38.559715 1 kCustomCompression9A supported: 0
|
||||
2026/01/27-22:00:38.559716 1 kCustomCompression99 supported: 0
|
||||
2026/01/27-22:00:38.559716 1 kCustomCompressionBE supported: 0
|
||||
2026/01/27-22:00:38.559717 1 kCustomCompressionE5 supported: 0
|
||||
2026/01/27-22:00:38.559717 1 kCustomCompressionD9 supported: 0
|
||||
2026/01/27-22:00:38.559718 1 kCustomCompressionC1 supported: 0
|
||||
2026/01/27-22:00:38.559718 1 kCustomCompressionC5 supported: 0
|
||||
2026/01/27-22:00:38.559719 1 kCustomCompressionC2 supported: 0
|
||||
2026/01/27-22:00:38.559719 1 kCustomCompressionA5 supported: 0
|
||||
2026/01/27-22:00:38.559720 1 kCustomCompressionC7 supported: 0
|
||||
2026/01/27-22:00:38.559720 1 kCustomCompressionBF supported: 0
|
||||
2026/01/27-22:00:38.559721 1 kCustomCompressionE8 supported: 0
|
||||
2026/01/27-22:00:38.559721 1 kCustomCompressionC8 supported: 0
|
||||
2026/01/27-22:00:38.559722 1 kCustomCompressionAF supported: 0
|
||||
2026/01/27-22:00:38.559722 1 kCustomCompressionCA supported: 0
|
||||
2026/01/27-22:00:38.559723 1 kCustomCompressionCD supported: 0
|
||||
2026/01/27-22:00:38.559723 1 kCustomCompressionC0 supported: 0
|
||||
2026/01/27-22:00:38.559724 1 kCustomCompressionCF supported: 0
|
||||
2026/01/27-22:00:38.559724 1 kCustomCompressionF9 supported: 0
|
||||
2026/01/27-22:00:38.559724 1 kCustomCompressionD0 supported: 0
|
||||
2026/01/27-22:00:38.559725 1 kCustomCompressionD2 supported: 0
|
||||
2026/01/27-22:00:38.559726 1 kCustomCompressionAD supported: 0
|
||||
2026/01/27-22:00:38.559726 1 kCustomCompressionD3 supported: 0
|
||||
2026/01/27-22:00:38.559727 1 kCustomCompressionD4 supported: 0
|
||||
2026/01/27-22:00:38.559727 1 kCustomCompressionD7 supported: 0
|
||||
2026/01/27-22:00:38.559728 1 kCustomCompression82 supported: 0
|
||||
2026/01/27-22:00:38.559728 1 kCustomCompressionDD supported: 0
|
||||
2026/01/27-22:00:38.559729 1 kCustomCompressionC3 supported: 0
|
||||
2026/01/27-22:00:38.559729 1 kCustomCompressionEE supported: 0
|
||||
2026/01/27-22:00:38.559730 1 kCustomCompressionDE supported: 0
|
||||
2026/01/27-22:00:38.559730 1 kCustomCompressionDF supported: 0
|
||||
2026/01/27-22:00:38.559731 1 kCustomCompressionA7 supported: 0
|
||||
2026/01/27-22:00:38.559731 1 kCustomCompressionE0 supported: 0
|
||||
2026/01/27-22:00:38.559732 1 kCustomCompressionF1 supported: 0
|
||||
2026/01/27-22:00:38.559732 1 kCustomCompressionE1 supported: 0
|
||||
2026/01/27-22:00:38.559733 1 kCustomCompressionF5 supported: 0
|
||||
2026/01/27-22:00:38.559733 1 kCustomCompression80 supported: 0
|
||||
2026/01/27-22:00:38.559734 1 kCustomCompressionE3 supported: 0
|
||||
2026/01/27-22:00:38.559734 1 kCustomCompressionE4 supported: 0
|
||||
2026/01/27-22:00:38.559735 1 kCustomCompressionB0 supported: 0
|
||||
2026/01/27-22:00:38.559735 1 kCustomCompressionEA supported: 0
|
||||
2026/01/27-22:00:38.559736 1 kCustomCompressionFA supported: 0
|
||||
2026/01/27-22:00:38.559736 1 kCustomCompressionE7 supported: 0
|
||||
2026/01/27-22:00:38.559737 1 kCustomCompressionAE supported: 0
|
||||
2026/01/27-22:00:38.559737 1 kCustomCompressionEB supported: 0
|
||||
2026/01/27-22:00:38.559738 1 kCustomCompressionED supported: 0
|
||||
2026/01/27-22:00:38.559738 1 kCustomCompressionB6 supported: 0
|
||||
2026/01/27-22:00:38.559739 1 kCustomCompressionEF supported: 0
|
||||
2026/01/27-22:00:38.559739 1 kCustomCompressionF0 supported: 0
|
||||
2026/01/27-22:00:38.559740 1 kCustomCompressionB7 supported: 0
|
||||
2026/01/27-22:00:38.559740 1 kCustomCompressionF2 supported: 0
|
||||
2026/01/27-22:00:38.559741 1 kCustomCompressionA1 supported: 0
|
||||
2026/01/27-22:00:38.559741 1 kCustomCompressionF4 supported: 0
|
||||
2026/01/27-22:00:38.559742 1 kSnappyCompression supported: 1
|
||||
2026/01/27-22:00:38.559742 1 kCustomCompressionF6 supported: 0
|
||||
2026/01/27-22:00:38.559743 1 Fast CRC32 supported: Supported on Arm64
|
||||
2026/01/27-22:00:38.559744 1 DMutex implementation: pthread_mutex_t
|
||||
2026/01/27-22:00:38.559744 1 Jemalloc supported: 1
|
||||
2026/01/27-22:00:38.560596 1 [db/version_set.cc:6190] Recovering from manifest file: /data/db/MANIFEST-000011
|
||||
2026/01/27-22:00:38.560798 1 [db/column_family.cc:693] --------------- Options for column family [default]:
|
||||
2026/01/27-22:00:38.560800 1 Options.comparator: leveldb.BytewiseComparator
|
||||
2026/01/27-22:00:38.560801 1 Options.merge_operator: None
|
||||
2026/01/27-22:00:38.560802 1 Options.compaction_filter: None
|
||||
2026/01/27-22:00:38.560802 1 Options.compaction_filter_factory: None
|
||||
2026/01/27-22:00:38.560803 1 Options.sst_partitioner_factory: None
|
||||
2026/01/27-22:00:38.560803 1 Options.memtable_factory: SkipListFactory
|
||||
2026/01/27-22:00:38.560804 1 Options.table_factory: BlockBasedTable
|
||||
2026/01/27-22:00:38.560817 1 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0xffffa56d15a0)
|
||||
cache_index_and_filter_blocks: 0
|
||||
cache_index_and_filter_blocks_with_high_priority: 1
|
||||
pin_l0_filter_and_index_blocks_in_cache: 0
|
||||
pin_top_level_index_and_filter: 1
|
||||
index_type: 0
|
||||
data_block_index_type: 0
|
||||
index_shortening: 1
|
||||
data_block_hash_table_util_ratio: 0.750000
|
||||
checksum: 4
|
||||
no_block_cache: 0
|
||||
block_cache: 0xffffa566bd90
|
||||
block_cache_name: LRUCache
|
||||
block_cache_options:
|
||||
capacity : 33554432
|
||||
num_shard_bits : 6
|
||||
strict_capacity_limit : 0
|
||||
memory_allocator : None
|
||||
high_pri_pool_ratio: 0.500
|
||||
low_pri_pool_ratio: 0.000
|
||||
persistent_cache: (nil)
|
||||
block_size: 4096
|
||||
block_size_deviation: 10
|
||||
block_restart_interval: 16
|
||||
index_block_restart_interval: 1
|
||||
metadata_block_size: 4096
|
||||
partition_filters: 0
|
||||
use_delta_encoding: 1
|
||||
filter_policy: nullptr
|
||||
user_defined_index_factory: nullptr
|
||||
fail_if_no_udi_on_open: 0
|
||||
whole_key_filtering: 1
|
||||
verify_compression: 0
|
||||
read_amp_bytes_per_bit: 0
|
||||
format_version: 6
|
||||
enable_index_compression: 1
|
||||
block_align: 0
|
||||
max_auto_readahead_size: 262144
|
||||
prepopulate_block_cache: 0
|
||||
initial_auto_readahead_size: 8192
|
||||
num_file_reads_for_auto_readahead: 2
|
||||
2026/01/27-22:00:38.560818 1 Options.write_buffer_size: 67108864
|
||||
2026/01/27-22:00:38.560819 1 Options.max_write_buffer_number: 4
|
||||
2026/01/27-22:00:38.560820 1 Options.compression[0]: NoCompression
|
||||
2026/01/27-22:00:38.560820 1 Options.compression[1]: NoCompression
|
||||
2026/01/27-22:00:38.560821 1 Options.compression[2]: Snappy
|
||||
2026/01/27-22:00:38.560822 1 Options.compression[3]: Snappy
|
||||
2026/01/27-22:00:38.560822 1 Options.compression[4]: Snappy
|
||||
2026/01/27-22:00:38.560823 1 Options.compression[5]: Snappy
|
||||
2026/01/27-22:00:38.560823 1 Options.compression[6]: Snappy
|
||||
2026/01/27-22:00:38.560824 1 Options.bottommost_compression: Disabled
|
||||
2026/01/27-22:00:38.560824 1 Options.prefix_extractor: nullptr
|
||||
2026/01/27-22:00:38.560825 1 Options.memtable_insert_with_hint_prefix_extractor: nullptr
|
||||
2026/01/27-22:00:38.560825 1 Options.num_levels: 7
|
||||
2026/01/27-22:00:38.560826 1 Options.min_write_buffer_number_to_merge: 1
|
||||
2026/01/27-22:00:38.560826 1 Options.max_write_buffer_size_to_maintain: 0
|
||||
2026/01/27-22:00:38.560827 1 Options.bottommost_compression_opts.window_bits: -14
|
||||
2026/01/27-22:00:38.560827 1 Options.bottommost_compression_opts.level: 32767
|
||||
2026/01/27-22:00:38.560828 1 Options.bottommost_compression_opts.strategy: 0
|
||||
2026/01/27-22:00:38.560828 1 Options.bottommost_compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-22:00:38.560829 1 Options.bottommost_compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-22:00:38.560829 1 Options.bottommost_compression_opts.parallel_threads: 1
|
||||
2026/01/27-22:00:38.560830 1 Options.bottommost_compression_opts.enabled: false
|
||||
2026/01/27-22:00:38.560830 1 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-22:00:38.560831 1 Options.bottommost_compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-22:00:38.560831 1 Options.compression_opts.window_bits: -14
|
||||
2026/01/27-22:00:38.560832 1 Options.compression_opts.level: 32767
|
||||
2026/01/27-22:00:38.560832 1 Options.compression_opts.strategy: 0
|
||||
2026/01/27-22:00:38.560833 1 Options.compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-22:00:38.560833 1 Options.compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-22:00:38.560834 1 Options.compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-22:00:38.560834 1 Options.compression_opts.parallel_threads: 1
|
||||
2026/01/27-22:00:38.560835 1 Options.compression_opts.enabled: false
|
||||
2026/01/27-22:00:38.560835 1 Options.compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-22:00:38.560836 1 Options.level0_file_num_compaction_trigger: 4
|
||||
2026/01/27-22:00:38.560836 1 Options.level0_slowdown_writes_trigger: 20
|
||||
2026/01/27-22:00:38.560837 1 Options.level0_stop_writes_trigger: 40
|
||||
2026/01/27-22:00:38.560837 1 Options.target_file_size_base: 134217728
|
||||
2026/01/27-22:00:38.560838 1 Options.target_file_size_multiplier: 1
|
||||
2026/01/27-22:00:38.560838 1 Options.max_bytes_for_level_base: 268435456
|
||||
2026/01/27-22:00:38.560838 1 Options.level_compaction_dynamic_level_bytes: 1
|
||||
2026/01/27-22:00:38.560840 1 Options.max_bytes_for_level_multiplier: 10.000000
|
||||
2026/01/27-22:00:38.560840 1 Options.max_bytes_for_level_multiplier_addtl[0]: 1
|
||||
2026/01/27-22:00:38.560841 1 Options.max_bytes_for_level_multiplier_addtl[1]: 1
|
||||
2026/01/27-22:00:38.560841 1 Options.max_bytes_for_level_multiplier_addtl[2]: 1
|
||||
2026/01/27-22:00:38.560842 1 Options.max_bytes_for_level_multiplier_addtl[3]: 1
|
||||
2026/01/27-22:00:38.560842 1 Options.max_bytes_for_level_multiplier_addtl[4]: 1
|
||||
2026/01/27-22:00:38.560843 1 Options.max_bytes_for_level_multiplier_addtl[5]: 1
|
||||
2026/01/27-22:00:38.560843 1 Options.max_bytes_for_level_multiplier_addtl[6]: 1
|
||||
2026/01/27-22:00:38.560844 1 Options.max_sequential_skip_in_iterations: 8
|
||||
2026/01/27-22:00:38.560844 1 Options.memtable_op_scan_flush_trigger: 0
|
||||
2026/01/27-22:00:38.560844 1 Options.memtable_avg_op_scan_flush_trigger: 0
|
||||
2026/01/27-22:00:38.560845 1 Options.max_compaction_bytes: 3355443200
|
||||
2026/01/27-22:00:38.560845 1 Options.arena_block_size: 1048576
|
||||
2026/01/27-22:00:38.560846 1 Options.soft_pending_compaction_bytes_limit: 68719476736
|
||||
2026/01/27-22:00:38.560846 1 Options.hard_pending_compaction_bytes_limit: 274877906944
|
||||
2026/01/27-22:00:38.560847 1 Options.disable_auto_compactions: 0
|
||||
2026/01/27-22:00:38.560848 1 Options.compaction_style: kCompactionStyleLevel
|
||||
2026/01/27-22:00:38.560849 1 Options.compaction_pri: kMinOverlappingRatio
|
||||
2026/01/27-22:00:38.560849 1 Options.compaction_options_universal.size_ratio: 1
|
||||
2026/01/27-22:00:38.560850 1 Options.compaction_options_universal.min_merge_width: 2
|
||||
2026/01/27-22:00:38.560850 1 Options.compaction_options_universal.max_merge_width: 4294967295
|
||||
2026/01/27-22:00:38.560851 1 Options.compaction_options_universal.max_size_amplification_percent: 200
|
||||
2026/01/27-22:00:38.560851 1 Options.compaction_options_universal.compression_size_percent: -1
|
||||
2026/01/27-22:00:38.560852 1 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize
|
||||
2026/01/27-22:00:38.560852 1 Options.compaction_options_universal.max_read_amp: -1
|
||||
2026/01/27-22:00:38.560853 1 Options.compaction_options_universal.reduce_file_locking: 0
|
||||
2026/01/27-22:00:38.560853 1 Options.compaction_options_fifo.max_table_files_size: 1073741824
|
||||
2026/01/27-22:00:38.560854 1 Options.compaction_options_fifo.allow_compaction: 0
|
||||
2026/01/27-22:00:38.560855 1 Options.table_properties_collectors:
|
||||
2026/01/27-22:00:38.560856 1 Options.inplace_update_support: 0
|
||||
2026/01/27-22:00:38.560857 1 Options.inplace_update_num_locks: 10000
|
||||
2026/01/27-22:00:38.560857 1 Options.memtable_prefix_bloom_size_ratio: 0.000000
|
||||
2026/01/27-22:00:38.560858 1 Options.memtable_whole_key_filtering: 0
|
||||
2026/01/27-22:00:38.560858 1 Options.memtable_huge_page_size: 0
|
||||
2026/01/27-22:00:38.560859 1 Options.bloom_locality: 0
|
||||
2026/01/27-22:00:38.560859 1 Options.max_successive_merges: 0
|
||||
2026/01/27-22:00:38.560860 1 Options.strict_max_successive_merges: 0
|
||||
2026/01/27-22:00:38.560860 1 Options.optimize_filters_for_hits: 0
|
||||
2026/01/27-22:00:38.560861 1 Options.paranoid_file_checks: 0
|
||||
2026/01/27-22:00:38.560861 1 Options.force_consistency_checks: 1
|
||||
2026/01/27-22:00:38.560862 1 Options.report_bg_io_stats: 0
|
||||
2026/01/27-22:00:38.560862 1 Options.disallow_memtable_writes: 0
|
||||
2026/01/27-22:00:38.560863 1 Options.ttl: 2592000
|
||||
2026/01/27-22:00:38.560863 1 Options.periodic_compaction_seconds: 0
|
||||
2026/01/27-22:00:38.560864 1 Options.default_temperature: kUnknown
|
||||
2026/01/27-22:00:38.560864 1 Options.preclude_last_level_data_seconds: 0
|
||||
2026/01/27-22:00:38.560865 1 Options.preserve_internal_time_seconds: 0
|
||||
2026/01/27-22:00:38.560865 1 Options.enable_blob_files: false
|
||||
2026/01/27-22:00:38.560866 1 Options.min_blob_size: 0
|
||||
2026/01/27-22:00:38.560866 1 Options.blob_file_size: 268435456
|
||||
2026/01/27-22:00:38.560867 1 Options.blob_compression_type: NoCompression
|
||||
2026/01/27-22:00:38.560867 1 Options.enable_blob_garbage_collection: false
|
||||
2026/01/27-22:00:38.560879 1 Options.blob_garbage_collection_age_cutoff: 0.250000
|
||||
2026/01/27-22:00:38.560880 1 Options.blob_garbage_collection_force_threshold: 1.000000
|
||||
2026/01/27-22:00:38.560880 1 Options.blob_compaction_readahead_size: 0
|
||||
2026/01/27-22:00:38.560881 1 Options.blob_file_starting_level: 0
|
||||
2026/01/27-22:00:38.560881 1 Options.experimental_mempurge_threshold: 0.000000
|
||||
2026/01/27-22:00:38.560882 1 Options.memtable_max_range_deletions: 0
|
||||
2026/01/27-22:00:38.560882 1 Options.cf_allow_ingest_behind: false
|
||||
2026/01/27-22:00:38.561145 1 [WARN] [db/db_impl/db_impl_open.cc:2688] DB::Open() failed: Invalid argument: Column families not opened: index, search, stream, propagate, pubsub, zset_score, metadata
|
||||
2026/01/27-22:00:38.561190 1 [db/db_impl/db_impl.cc:467] Shutdown: canceling all background work
|
||||
2026/01/27-22:00:38.561240 1 [db/db_impl/db_impl.cc:681] Shutdown complete
|
||||
1449
local-dev/data/kvrocks/db/LOG.old.1769551520856746
Normal file
1449
local-dev/data/kvrocks/db/LOG.old.1769551520856746
Normal file
File diff suppressed because it is too large
Load Diff
391
local-dev/data/kvrocks/db/LOG.old.1769551520859596
Normal file
391
local-dev/data/kvrocks/db/LOG.old.1769551520859596
Normal file
@@ -0,0 +1,391 @@
|
||||
2026/01/27-22:05:20.858109 1 RocksDB version: 10.6.2
|
||||
2026/01/27-22:05:20.858266 1 Git sha 0
|
||||
2026/01/27-22:05:20.858267 1 Compile date 2025-11-08 14:59:16
|
||||
2026/01/27-22:05:20.858268 1 DB SUMMARY
|
||||
2026/01/27-22:05:20.858269 1 Host name (Env): 83baffb0044b
|
||||
2026/01/27-22:05:20.858270 1 DB Session ID: KLAN4OO9759DRR73WKX0
|
||||
2026/01/27-22:05:20.858345 1 CURRENT file: CURRENT
|
||||
2026/01/27-22:05:20.858346 1 IDENTITY file: IDENTITY
|
||||
2026/01/27-22:05:20.858357 1 MANIFEST file: MANIFEST-000016 size: 941 Bytes
|
||||
2026/01/27-22:05:20.858358 1 SST files in /data/db dir, Total Num: 1, files: 000014.sst
|
||||
2026/01/27-22:05:20.858359 1 Write Ahead Log file in /data/db: 000015.log size: 0 ;
|
||||
2026/01/27-22:05:20.858360 1 Options.error_if_exists: 0
|
||||
2026/01/27-22:05:20.858361 1 Options.create_if_missing: 1
|
||||
2026/01/27-22:05:20.858361 1 Options.paranoid_checks: 1
|
||||
2026/01/27-22:05:20.858362 1 Options.flush_verify_memtable_count: 1
|
||||
2026/01/27-22:05:20.858362 1 Options.compaction_verify_record_count: 1
|
||||
2026/01/27-22:05:20.858363 1 Options.track_and_verify_wals_in_manifest: 0
|
||||
2026/01/27-22:05:20.858363 1 Options.track_and_verify_wals: 0
|
||||
2026/01/27-22:05:20.858364 1 Options.verify_sst_unique_id_in_manifest: 1
|
||||
2026/01/27-22:05:20.858364 1 Options.env: 0xffff8221d0c0
|
||||
2026/01/27-22:05:20.858365 1 Options.fs: PosixFileSystem
|
||||
2026/01/27-22:05:20.858366 1 Options.info_log: 0xffff8230f600
|
||||
2026/01/27-22:05:20.858366 1 Options.max_file_opening_threads: 16
|
||||
2026/01/27-22:05:20.858366 1 Options.statistics: 0xffff822135b0
|
||||
2026/01/27-22:05:20.858367 1 Options.statistics stats level: 3
|
||||
2026/01/27-22:05:20.858367 1 Options.use_fsync: 0
|
||||
2026/01/27-22:05:20.858368 1 Options.max_log_file_size: 268435456
|
||||
2026/01/27-22:05:20.858369 1 Options.max_manifest_file_size: 67108864
|
||||
2026/01/27-22:05:20.858369 1 Options.log_file_time_to_roll: 0
|
||||
2026/01/27-22:05:20.858370 1 Options.keep_log_file_num: 12
|
||||
2026/01/27-22:05:20.858370 1 Options.recycle_log_file_num: 0
|
||||
2026/01/27-22:05:20.858371 1 Options.allow_fallocate: 1
|
||||
2026/01/27-22:05:20.858371 1 Options.allow_mmap_reads: 0
|
||||
2026/01/27-22:05:20.858371 1 Options.allow_mmap_writes: 0
|
||||
2026/01/27-22:05:20.858372 1 Options.use_direct_reads: 0
|
||||
2026/01/27-22:05:20.858372 1 Options.use_direct_io_for_flush_and_compaction: 0
|
||||
2026/01/27-22:05:20.858373 1 Options.create_missing_column_families: 1
|
||||
2026/01/27-22:05:20.858373 1 Options.db_log_dir:
|
||||
2026/01/27-22:05:20.858374 1 Options.wal_dir:
|
||||
2026/01/27-22:05:20.858374 1 Options.table_cache_numshardbits: 6
|
||||
2026/01/27-22:05:20.858375 1 Options.WAL_ttl_seconds: 10800
|
||||
2026/01/27-22:05:20.858375 1 Options.WAL_size_limit_MB: 16384
|
||||
2026/01/27-22:05:20.858376 1 Options.max_write_batch_group_size_bytes: 1048576
|
||||
2026/01/27-22:05:20.858376 1 Options.manifest_preallocation_size: 4194304
|
||||
2026/01/27-22:05:20.858377 1 Options.is_fd_close_on_exec: 1
|
||||
2026/01/27-22:05:20.858377 1 Options.advise_random_on_open: 1
|
||||
2026/01/27-22:05:20.858378 1 Options.db_write_buffer_size: 0
|
||||
2026/01/27-22:05:20.858378 1 Options.write_buffer_manager: 0xffff8226bfc0
|
||||
2026/01/27-22:05:20.858379 1 Options.use_adaptive_mutex: 0
|
||||
2026/01/27-22:05:20.858379 1 Options.rate_limiter: 0xffff82236000
|
||||
2026/01/27-22:05:20.858380 1 Options.sst_file_manager.rate_bytes_per_sec: 0
|
||||
2026/01/27-22:05:20.858380 1 Options.wal_recovery_mode: 2
|
||||
2026/01/27-22:05:20.858381 1 Options.enable_thread_tracking: 0
|
||||
2026/01/27-22:05:20.858381 1 Options.enable_pipelined_write: 0
|
||||
2026/01/27-22:05:20.858382 1 Options.unordered_write: 0
|
||||
2026/01/27-22:05:20.858382 1 Options.allow_concurrent_memtable_write: 1
|
||||
2026/01/27-22:05:20.858383 1 Options.enable_write_thread_adaptive_yield: 1
|
||||
2026/01/27-22:05:20.858383 1 Options.write_thread_max_yield_usec: 100
|
||||
2026/01/27-22:05:20.858384 1 Options.write_thread_slow_yield_usec: 3
|
||||
2026/01/27-22:05:20.858384 1 Options.row_cache: None
|
||||
2026/01/27-22:05:20.858384 1 Options.wal_filter: None
|
||||
2026/01/27-22:05:20.858385 1 Options.avoid_flush_during_recovery: 0
|
||||
2026/01/27-22:05:20.858385 1 Options.allow_ingest_behind: 0
|
||||
2026/01/27-22:05:20.858386 1 Options.two_write_queues: 0
|
||||
2026/01/27-22:05:20.858386 1 Options.manual_wal_flush: 0
|
||||
2026/01/27-22:05:20.858387 1 Options.wal_compression: 0
|
||||
2026/01/27-22:05:20.858387 1 Options.background_close_inactive_wals: 0
|
||||
2026/01/27-22:05:20.858388 1 Options.atomic_flush: 0
|
||||
2026/01/27-22:05:20.858388 1 Options.avoid_unnecessary_blocking_io: 1
|
||||
2026/01/27-22:05:20.858389 1 Options.prefix_seek_opt_in_only: 0
|
||||
2026/01/27-22:05:20.858389 1 Options.persist_stats_to_disk: 0
|
||||
2026/01/27-22:05:20.858390 1 Options.write_dbid_to_manifest: 1
|
||||
2026/01/27-22:05:20.858390 1 Options.write_identity_file: 1
|
||||
2026/01/27-22:05:20.858391 1 Options.log_readahead_size: 0
|
||||
2026/01/27-22:05:20.858391 1 Options.file_checksum_gen_factory: Unknown
|
||||
2026/01/27-22:05:20.858392 1 Options.best_efforts_recovery: 0
|
||||
2026/01/27-22:05:20.858392 1 Options.max_bgerror_resume_count: 2147483647
|
||||
2026/01/27-22:05:20.858393 1 Options.bgerror_resume_retry_interval: 1000000
|
||||
2026/01/27-22:05:20.858393 1 Options.allow_data_in_errors: 0
|
||||
2026/01/27-22:05:20.858394 1 Options.db_host_id: __hostname__
|
||||
2026/01/27-22:05:20.858394 1 Options.enforce_single_del_contracts: true
|
||||
2026/01/27-22:05:20.858395 1 Options.metadata_write_temperature: kUnknown
|
||||
2026/01/27-22:05:20.858396 1 Options.wal_write_temperature: kUnknown
|
||||
2026/01/27-22:05:20.858396 1 Options.max_background_jobs: 4
|
||||
2026/01/27-22:05:20.858396 1 Options.max_background_compactions: -1
|
||||
2026/01/27-22:05:20.858397 1 Options.max_subcompactions: 2
|
||||
2026/01/27-22:05:20.858397 1 Options.avoid_flush_during_shutdown: 0
|
||||
2026/01/27-22:05:20.858398 1 Options.writable_file_max_buffer_size: 1048576
|
||||
2026/01/27-22:05:20.858399 1 Options.delayed_write_rate : 536870912000
|
||||
2026/01/27-22:05:20.858399 1 Options.max_total_wal_size: 536870912
|
||||
2026/01/27-22:05:20.858400 1 Options.delete_obsolete_files_period_micros: 21600000000
|
||||
2026/01/27-22:05:20.858400 1 Options.stats_dump_period_sec: 0
|
||||
2026/01/27-22:05:20.858401 1 Options.stats_persist_period_sec: 600
|
||||
2026/01/27-22:05:20.858401 1 Options.stats_history_buffer_size: 1048576
|
||||
2026/01/27-22:05:20.858402 1 Options.max_open_files: 8096
|
||||
2026/01/27-22:05:20.858402 1 Options.bytes_per_sync: 1048576
|
||||
2026/01/27-22:05:20.858403 1 Options.wal_bytes_per_sync: 0
|
||||
2026/01/27-22:05:20.858403 1 Options.strict_bytes_per_sync: 0
|
||||
2026/01/27-22:05:20.858404 1 Options.compaction_readahead_size: 2097152
|
||||
2026/01/27-22:05:20.858405 1 Options.max_background_flushes: -1
|
||||
2026/01/27-22:05:20.858405 1 Options.daily_offpeak_time_utc:
|
||||
2026/01/27-22:05:20.858406 1 Compression algorithms supported:
|
||||
2026/01/27-22:05:20.858406 1 kCustomCompressionFE supported: 0
|
||||
2026/01/27-22:05:20.858407 1 kCustomCompressionFC supported: 0
|
||||
2026/01/27-22:05:20.858407 1 kCustomCompressionF8 supported: 0
|
||||
2026/01/27-22:05:20.858408 1 kCustomCompressionF7 supported: 0
|
||||
2026/01/27-22:05:20.858408 1 kCustomCompressionB2 supported: 0
|
||||
2026/01/27-22:05:20.858409 1 kLZ4Compression supported: 1
|
||||
2026/01/27-22:05:20.858410 1 kCustomCompression88 supported: 0
|
||||
2026/01/27-22:05:20.858410 1 kCustomCompressionD8 supported: 0
|
||||
2026/01/27-22:05:20.858411 1 kCustomCompression9F supported: 0
|
||||
2026/01/27-22:05:20.858411 1 kCustomCompressionD6 supported: 0
|
||||
2026/01/27-22:05:20.858412 1 kCustomCompressionA9 supported: 0
|
||||
2026/01/27-22:05:20.858412 1 kCustomCompressionEC supported: 0
|
||||
2026/01/27-22:05:20.858413 1 kCustomCompressionA3 supported: 0
|
||||
2026/01/27-22:05:20.858413 1 kCustomCompressionCB supported: 0
|
||||
2026/01/27-22:05:20.858414 1 kCustomCompression90 supported: 0
|
||||
2026/01/27-22:05:20.858414 1 kCustomCompressionA0 supported: 0
|
||||
2026/01/27-22:05:20.858415 1 kCustomCompressionC6 supported: 0
|
||||
2026/01/27-22:05:20.858415 1 kCustomCompression9D supported: 0
|
||||
2026/01/27-22:05:20.858415 1 kCustomCompression8B supported: 0
|
||||
2026/01/27-22:05:20.858416 1 kCustomCompressionA8 supported: 0
|
||||
2026/01/27-22:05:20.858416 1 kCustomCompression8D supported: 0
|
||||
2026/01/27-22:05:20.858417 1 kCustomCompression97 supported: 0
|
||||
2026/01/27-22:05:20.858417 1 kCustomCompression98 supported: 0
|
||||
2026/01/27-22:05:20.858418 1 kCustomCompressionAC supported: 0
|
||||
2026/01/27-22:05:20.858418 1 kCustomCompressionE9 supported: 0
|
||||
2026/01/27-22:05:20.858419 1 kCustomCompression96 supported: 0
|
||||
2026/01/27-22:05:20.858419 1 kCustomCompressionB1 supported: 0
|
||||
2026/01/27-22:05:20.858420 1 kCustomCompression95 supported: 0
|
||||
2026/01/27-22:05:20.858420 1 kCustomCompression84 supported: 0
|
||||
2026/01/27-22:05:20.858421 1 kCustomCompression91 supported: 0
|
||||
2026/01/27-22:05:20.858421 1 kCustomCompressionAB supported: 0
|
||||
2026/01/27-22:05:20.858422 1 kCustomCompressionB3 supported: 0
|
||||
2026/01/27-22:05:20.858422 1 kCustomCompression81 supported: 0
|
||||
2026/01/27-22:05:20.858423 1 kCustomCompressionDC supported: 0
|
||||
2026/01/27-22:05:20.858423 1 kBZip2Compression supported: 0
|
||||
2026/01/27-22:05:20.858424 1 kCustomCompressionBB supported: 0
|
||||
2026/01/27-22:05:20.858424 1 kCustomCompression9C supported: 0
|
||||
2026/01/27-22:05:20.858425 1 kCustomCompressionC9 supported: 0
|
||||
2026/01/27-22:05:20.858425 1 kCustomCompressionCC supported: 0
|
||||
2026/01/27-22:05:20.858426 1 kCustomCompression92 supported: 0
|
||||
2026/01/27-22:05:20.858426 1 kCustomCompressionB9 supported: 0
|
||||
2026/01/27-22:05:20.858427 1 kCustomCompression8F supported: 0
|
||||
2026/01/27-22:05:20.858427 1 kCustomCompression8A supported: 0
|
||||
2026/01/27-22:05:20.858427 1 kCustomCompression9B supported: 0
|
||||
2026/01/27-22:05:20.858428 1 kZSTD supported: 1
|
||||
2026/01/27-22:05:20.858428 1 kCustomCompressionAA supported: 0
|
||||
2026/01/27-22:05:20.858429 1 kCustomCompressionA2 supported: 0
|
||||
2026/01/27-22:05:20.858429 1 kZlibCompression supported: 1
|
||||
2026/01/27-22:05:20.858430 1 kXpressCompression supported: 0
|
||||
2026/01/27-22:05:20.858431 1 kCustomCompressionFD supported: 0
|
||||
2026/01/27-22:05:20.858431 1 kCustomCompressionE2 supported: 0
|
||||
2026/01/27-22:05:20.858432 1 kLZ4HCCompression supported: 1
|
||||
2026/01/27-22:05:20.858432 1 kCustomCompressionA6 supported: 0
|
||||
2026/01/27-22:05:20.858433 1 kCustomCompression85 supported: 0
|
||||
2026/01/27-22:05:20.858433 1 kCustomCompressionA4 supported: 0
|
||||
2026/01/27-22:05:20.858434 1 kCustomCompression86 supported: 0
|
||||
2026/01/27-22:05:20.858434 1 kCustomCompression83 supported: 0
|
||||
2026/01/27-22:05:20.858434 1 kCustomCompression87 supported: 0
|
||||
2026/01/27-22:05:20.858435 1 kCustomCompression89 supported: 0
|
||||
2026/01/27-22:05:20.858435 1 kCustomCompression8C supported: 0
|
||||
2026/01/27-22:05:20.858436 1 kCustomCompressionDB supported: 0
|
||||
2026/01/27-22:05:20.858436 1 kCustomCompressionF3 supported: 0
|
||||
2026/01/27-22:05:20.858437 1 kCustomCompressionE6 supported: 0
|
||||
2026/01/27-22:05:20.858437 1 kCustomCompression8E supported: 0
|
||||
2026/01/27-22:05:20.858438 1 kCustomCompressionDA supported: 0
|
||||
2026/01/27-22:05:20.858438 1 kCustomCompression93 supported: 0
|
||||
2026/01/27-22:05:20.858439 1 kCustomCompression94 supported: 0
|
||||
2026/01/27-22:05:20.858440 1 kCustomCompression9E supported: 0
|
||||
2026/01/27-22:05:20.858441 1 kCustomCompressionB4 supported: 0
|
||||
2026/01/27-22:05:20.858441 1 kCustomCompressionFB supported: 0
|
||||
2026/01/27-22:05:20.858442 1 kCustomCompressionB5 supported: 0
|
||||
2026/01/27-22:05:20.858442 1 kCustomCompressionD5 supported: 0
|
||||
2026/01/27-22:05:20.858443 1 kCustomCompressionB8 supported: 0
|
||||
2026/01/27-22:05:20.858443 1 kCustomCompressionD1 supported: 0
|
||||
2026/01/27-22:05:20.858444 1 kCustomCompressionBA supported: 0
|
||||
2026/01/27-22:05:20.858444 1 kCustomCompressionBC supported: 0
|
||||
2026/01/27-22:05:20.858445 1 kCustomCompressionCE supported: 0
|
||||
2026/01/27-22:05:20.858445 1 kCustomCompressionBD supported: 0
|
||||
2026/01/27-22:05:20.858445 1 kCustomCompressionC4 supported: 0
|
||||
2026/01/27-22:05:20.858446 1 kCustomCompression9A supported: 0
|
||||
2026/01/27-22:05:20.858446 1 kCustomCompression99 supported: 0
|
||||
2026/01/27-22:05:20.858447 1 kCustomCompressionBE supported: 0
|
||||
2026/01/27-22:05:20.858447 1 kCustomCompressionE5 supported: 0
|
||||
2026/01/27-22:05:20.858448 1 kCustomCompressionD9 supported: 0
|
||||
2026/01/27-22:05:20.858448 1 kCustomCompressionC1 supported: 0
|
||||
2026/01/27-22:05:20.858449 1 kCustomCompressionC5 supported: 0
|
||||
2026/01/27-22:05:20.858449 1 kCustomCompressionC2 supported: 0
|
||||
2026/01/27-22:05:20.858450 1 kCustomCompressionA5 supported: 0
|
||||
2026/01/27-22:05:20.858450 1 kCustomCompressionC7 supported: 0
|
||||
2026/01/27-22:05:20.858451 1 kCustomCompressionBF supported: 0
|
||||
2026/01/27-22:05:20.858451 1 kCustomCompressionE8 supported: 0
|
||||
2026/01/27-22:05:20.858452 1 kCustomCompressionC8 supported: 0
|
||||
2026/01/27-22:05:20.858452 1 kCustomCompressionAF supported: 0
|
||||
2026/01/27-22:05:20.858453 1 kCustomCompressionCA supported: 0
|
||||
2026/01/27-22:05:20.858453 1 kCustomCompressionCD supported: 0
|
||||
2026/01/27-22:05:20.858454 1 kCustomCompressionC0 supported: 0
|
||||
2026/01/27-22:05:20.858454 1 kCustomCompressionCF supported: 0
|
||||
2026/01/27-22:05:20.858455 1 kCustomCompressionF9 supported: 0
|
||||
2026/01/27-22:05:20.858455 1 kCustomCompressionD0 supported: 0
|
||||
2026/01/27-22:05:20.858456 1 kCustomCompressionD2 supported: 0
|
||||
2026/01/27-22:05:20.858456 1 kCustomCompressionAD supported: 0
|
||||
2026/01/27-22:05:20.858457 1 kCustomCompressionD3 supported: 0
|
||||
2026/01/27-22:05:20.858457 1 kCustomCompressionD4 supported: 0
|
||||
2026/01/27-22:05:20.858458 1 kCustomCompressionD7 supported: 0
|
||||
2026/01/27-22:05:20.858458 1 kCustomCompression82 supported: 0
|
||||
2026/01/27-22:05:20.858459 1 kCustomCompressionDD supported: 0
|
||||
2026/01/27-22:05:20.858459 1 kCustomCompressionC3 supported: 0
|
||||
2026/01/27-22:05:20.858459 1 kCustomCompressionEE supported: 0
|
||||
2026/01/27-22:05:20.858460 1 kCustomCompressionDE supported: 0
|
||||
2026/01/27-22:05:20.858460 1 kCustomCompressionDF supported: 0
|
||||
2026/01/27-22:05:20.858461 1 kCustomCompressionA7 supported: 0
|
||||
2026/01/27-22:05:20.858461 1 kCustomCompressionE0 supported: 0
|
||||
2026/01/27-22:05:20.858462 1 kCustomCompressionF1 supported: 0
|
||||
2026/01/27-22:05:20.858462 1 kCustomCompressionE1 supported: 0
|
||||
2026/01/27-22:05:20.858463 1 kCustomCompressionF5 supported: 0
|
||||
2026/01/27-22:05:20.858463 1 kCustomCompression80 supported: 0
|
||||
2026/01/27-22:05:20.858464 1 kCustomCompressionE3 supported: 0
|
||||
2026/01/27-22:05:20.858464 1 kCustomCompressionE4 supported: 0
|
||||
2026/01/27-22:05:20.858465 1 kCustomCompressionB0 supported: 0
|
||||
2026/01/27-22:05:20.858465 1 kCustomCompressionEA supported: 0
|
||||
2026/01/27-22:05:20.858466 1 kCustomCompressionFA supported: 0
|
||||
2026/01/27-22:05:20.858466 1 kCustomCompressionE7 supported: 0
|
||||
2026/01/27-22:05:20.858467 1 kCustomCompressionAE supported: 0
|
||||
2026/01/27-22:05:20.858467 1 kCustomCompressionEB supported: 0
|
||||
2026/01/27-22:05:20.858468 1 kCustomCompressionED supported: 0
|
||||
2026/01/27-22:05:20.858468 1 kCustomCompressionB6 supported: 0
|
||||
2026/01/27-22:05:20.858469 1 kCustomCompressionEF supported: 0
|
||||
2026/01/27-22:05:20.858469 1 kCustomCompressionF0 supported: 0
|
||||
2026/01/27-22:05:20.858470 1 kCustomCompressionB7 supported: 0
|
||||
2026/01/27-22:05:20.858470 1 kCustomCompressionF2 supported: 0
|
||||
2026/01/27-22:05:20.858470 1 kCustomCompressionA1 supported: 0
|
||||
2026/01/27-22:05:20.858471 1 kCustomCompressionF4 supported: 0
|
||||
2026/01/27-22:05:20.858472 1 kSnappyCompression supported: 1
|
||||
2026/01/27-22:05:20.858472 1 kCustomCompressionF6 supported: 0
|
||||
2026/01/27-22:05:20.858476 1 Fast CRC32 supported: Supported on Arm64
|
||||
2026/01/27-22:05:20.858477 1 DMutex implementation: pthread_mutex_t
|
||||
2026/01/27-22:05:20.858477 1 Jemalloc supported: 1
|
||||
2026/01/27-22:05:20.858838 1 [db/version_set.cc:6190] Recovering from manifest file: /data/db/MANIFEST-000016
|
||||
2026/01/27-22:05:20.858987 1 [db/column_family.cc:693] --------------- Options for column family [default]:
|
||||
2026/01/27-22:05:20.858989 1 Options.comparator: leveldb.BytewiseComparator
|
||||
2026/01/27-22:05:20.858990 1 Options.merge_operator: None
|
||||
2026/01/27-22:05:20.858990 1 Options.compaction_filter: None
|
||||
2026/01/27-22:05:20.858991 1 Options.compaction_filter_factory: None
|
||||
2026/01/27-22:05:20.858991 1 Options.sst_partitioner_factory: None
|
||||
2026/01/27-22:05:20.858992 1 Options.memtable_factory: SkipListFactory
|
||||
2026/01/27-22:05:20.858992 1 Options.table_factory: BlockBasedTable
|
||||
2026/01/27-22:05:20.859006 1 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0xffff822d15a0)
|
||||
cache_index_and_filter_blocks: 0
|
||||
cache_index_and_filter_blocks_with_high_priority: 1
|
||||
pin_l0_filter_and_index_blocks_in_cache: 0
|
||||
pin_top_level_index_and_filter: 1
|
||||
index_type: 0
|
||||
data_block_index_type: 0
|
||||
index_shortening: 1
|
||||
data_block_hash_table_util_ratio: 0.750000
|
||||
checksum: 4
|
||||
no_block_cache: 0
|
||||
block_cache: 0xffff8226bd90
|
||||
block_cache_name: LRUCache
|
||||
block_cache_options:
|
||||
capacity : 33554432
|
||||
num_shard_bits : 6
|
||||
strict_capacity_limit : 0
|
||||
memory_allocator : None
|
||||
high_pri_pool_ratio: 0.500
|
||||
low_pri_pool_ratio: 0.000
|
||||
persistent_cache: (nil)
|
||||
block_size: 4096
|
||||
block_size_deviation: 10
|
||||
block_restart_interval: 16
|
||||
index_block_restart_interval: 1
|
||||
metadata_block_size: 4096
|
||||
partition_filters: 0
|
||||
use_delta_encoding: 1
|
||||
filter_policy: nullptr
|
||||
user_defined_index_factory: nullptr
|
||||
fail_if_no_udi_on_open: 0
|
||||
whole_key_filtering: 1
|
||||
verify_compression: 0
|
||||
read_amp_bytes_per_bit: 0
|
||||
format_version: 6
|
||||
enable_index_compression: 1
|
||||
block_align: 0
|
||||
max_auto_readahead_size: 262144
|
||||
prepopulate_block_cache: 0
|
||||
initial_auto_readahead_size: 8192
|
||||
num_file_reads_for_auto_readahead: 2
|
||||
2026/01/27-22:05:20.859009 1 Options.write_buffer_size: 67108864
|
||||
2026/01/27-22:05:20.859009 1 Options.max_write_buffer_number: 4
|
||||
2026/01/27-22:05:20.859010 1 Options.compression[0]: NoCompression
|
||||
2026/01/27-22:05:20.859011 1 Options.compression[1]: NoCompression
|
||||
2026/01/27-22:05:20.859011 1 Options.compression[2]: Snappy
|
||||
2026/01/27-22:05:20.859012 1 Options.compression[3]: Snappy
|
||||
2026/01/27-22:05:20.859012 1 Options.compression[4]: Snappy
|
||||
2026/01/27-22:05:20.859013 1 Options.compression[5]: Snappy
|
||||
2026/01/27-22:05:20.859013 1 Options.compression[6]: Snappy
|
||||
2026/01/27-22:05:20.859014 1 Options.bottommost_compression: Disabled
|
||||
2026/01/27-22:05:20.859015 1 Options.prefix_extractor: nullptr
|
||||
2026/01/27-22:05:20.859015 1 Options.memtable_insert_with_hint_prefix_extractor: nullptr
|
||||
2026/01/27-22:05:20.859016 1 Options.num_levels: 7
|
||||
2026/01/27-22:05:20.859016 1 Options.min_write_buffer_number_to_merge: 1
|
||||
2026/01/27-22:05:20.859017 1 Options.max_write_buffer_size_to_maintain: 0
|
||||
2026/01/27-22:05:20.859017 1 Options.bottommost_compression_opts.window_bits: -14
|
||||
2026/01/27-22:05:20.859018 1 Options.bottommost_compression_opts.level: 32767
|
||||
2026/01/27-22:05:20.859018 1 Options.bottommost_compression_opts.strategy: 0
|
||||
2026/01/27-22:05:20.859019 1 Options.bottommost_compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-22:05:20.859019 1 Options.bottommost_compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-22:05:20.859020 1 Options.bottommost_compression_opts.parallel_threads: 1
|
||||
2026/01/27-22:05:20.859020 1 Options.bottommost_compression_opts.enabled: false
|
||||
2026/01/27-22:05:20.859021 1 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-22:05:20.859021 1 Options.bottommost_compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-22:05:20.859022 1 Options.compression_opts.window_bits: -14
|
||||
2026/01/27-22:05:20.859022 1 Options.compression_opts.level: 32767
|
||||
2026/01/27-22:05:20.859023 1 Options.compression_opts.strategy: 0
|
||||
2026/01/27-22:05:20.859023 1 Options.compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-22:05:20.859024 1 Options.compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-22:05:20.859024 1 Options.compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-22:05:20.859025 1 Options.compression_opts.parallel_threads: 1
|
||||
2026/01/27-22:05:20.859025 1 Options.compression_opts.enabled: false
|
||||
2026/01/27-22:05:20.859026 1 Options.compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-22:05:20.859026 1 Options.level0_file_num_compaction_trigger: 4
|
||||
2026/01/27-22:05:20.859026 1 Options.level0_slowdown_writes_trigger: 20
|
||||
2026/01/27-22:05:20.859027 1 Options.level0_stop_writes_trigger: 40
|
||||
2026/01/27-22:05:20.859027 1 Options.target_file_size_base: 134217728
|
||||
2026/01/27-22:05:20.859028 1 Options.target_file_size_multiplier: 1
|
||||
2026/01/27-22:05:20.859028 1 Options.max_bytes_for_level_base: 268435456
|
||||
2026/01/27-22:05:20.859029 1 Options.level_compaction_dynamic_level_bytes: 1
|
||||
2026/01/27-22:05:20.859030 1 Options.max_bytes_for_level_multiplier: 10.000000
|
||||
2026/01/27-22:05:20.859030 1 Options.max_bytes_for_level_multiplier_addtl[0]: 1
|
||||
2026/01/27-22:05:20.859031 1 Options.max_bytes_for_level_multiplier_addtl[1]: 1
|
||||
2026/01/27-22:05:20.859031 1 Options.max_bytes_for_level_multiplier_addtl[2]: 1
|
||||
2026/01/27-22:05:20.859032 1 Options.max_bytes_for_level_multiplier_addtl[3]: 1
|
||||
2026/01/27-22:05:20.859032 1 Options.max_bytes_for_level_multiplier_addtl[4]: 1
|
||||
2026/01/27-22:05:20.859033 1 Options.max_bytes_for_level_multiplier_addtl[5]: 1
|
||||
2026/01/27-22:05:20.859033 1 Options.max_bytes_for_level_multiplier_addtl[6]: 1
|
||||
2026/01/27-22:05:20.859034 1 Options.max_sequential_skip_in_iterations: 8
|
||||
2026/01/27-22:05:20.859034 1 Options.memtable_op_scan_flush_trigger: 0
|
||||
2026/01/27-22:05:20.859035 1 Options.memtable_avg_op_scan_flush_trigger: 0
|
||||
2026/01/27-22:05:20.859035 1 Options.max_compaction_bytes: 3355443200
|
||||
2026/01/27-22:05:20.859036 1 Options.arena_block_size: 1048576
|
||||
2026/01/27-22:05:20.859036 1 Options.soft_pending_compaction_bytes_limit: 68719476736
|
||||
2026/01/27-22:05:20.859037 1 Options.hard_pending_compaction_bytes_limit: 274877906944
|
||||
2026/01/27-22:05:20.859037 1 Options.disable_auto_compactions: 0
|
||||
2026/01/27-22:05:20.859038 1 Options.compaction_style: kCompactionStyleLevel
|
||||
2026/01/27-22:05:20.859038 1 Options.compaction_pri: kMinOverlappingRatio
|
||||
2026/01/27-22:05:20.859039 1 Options.compaction_options_universal.size_ratio: 1
|
||||
2026/01/27-22:05:20.859039 1 Options.compaction_options_universal.min_merge_width: 2
|
||||
2026/01/27-22:05:20.859040 1 Options.compaction_options_universal.max_merge_width: 4294967295
|
||||
2026/01/27-22:05:20.859040 1 Options.compaction_options_universal.max_size_amplification_percent: 200
|
||||
2026/01/27-22:05:20.859041 1 Options.compaction_options_universal.compression_size_percent: -1
|
||||
2026/01/27-22:05:20.859042 1 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize
|
||||
2026/01/27-22:05:20.859042 1 Options.compaction_options_universal.max_read_amp: -1
|
||||
2026/01/27-22:05:20.859043 1 Options.compaction_options_universal.reduce_file_locking: 0
|
||||
2026/01/27-22:05:20.859043 1 Options.compaction_options_fifo.max_table_files_size: 1073741824
|
||||
2026/01/27-22:05:20.859043 1 Options.compaction_options_fifo.allow_compaction: 0
|
||||
2026/01/27-22:05:20.859044 1 Options.table_properties_collectors:
|
||||
2026/01/27-22:05:20.859045 1 Options.inplace_update_support: 0
|
||||
2026/01/27-22:05:20.859046 1 Options.inplace_update_num_locks: 10000
|
||||
2026/01/27-22:05:20.859046 1 Options.memtable_prefix_bloom_size_ratio: 0.000000
|
||||
2026/01/27-22:05:20.859049 1 Options.memtable_whole_key_filtering: 0
|
||||
2026/01/27-22:05:20.859049 1 Options.memtable_huge_page_size: 0
|
||||
2026/01/27-22:05:20.859050 1 Options.bloom_locality: 0
|
||||
2026/01/27-22:05:20.859050 1 Options.max_successive_merges: 0
|
||||
2026/01/27-22:05:20.859051 1 Options.strict_max_successive_merges: 0
|
||||
2026/01/27-22:05:20.859051 1 Options.optimize_filters_for_hits: 0
|
||||
2026/01/27-22:05:20.859052 1 Options.paranoid_file_checks: 0
|
||||
2026/01/27-22:05:20.859052 1 Options.force_consistency_checks: 1
|
||||
2026/01/27-22:05:20.859053 1 Options.report_bg_io_stats: 0
|
||||
2026/01/27-22:05:20.859053 1 Options.disallow_memtable_writes: 0
|
||||
2026/01/27-22:05:20.859054 1 Options.ttl: 2592000
|
||||
2026/01/27-22:05:20.859054 1 Options.periodic_compaction_seconds: 0
|
||||
2026/01/27-22:05:20.859055 1 Options.default_temperature: kUnknown
|
||||
2026/01/27-22:05:20.859055 1 Options.preclude_last_level_data_seconds: 0
|
||||
2026/01/27-22:05:20.859056 1 Options.preserve_internal_time_seconds: 0
|
||||
2026/01/27-22:05:20.859056 1 Options.enable_blob_files: false
|
||||
2026/01/27-22:05:20.859057 1 Options.min_blob_size: 0
|
||||
2026/01/27-22:05:20.859058 1 Options.blob_file_size: 268435456
|
||||
2026/01/27-22:05:20.859058 1 Options.blob_compression_type: NoCompression
|
||||
2026/01/27-22:05:20.859059 1 Options.enable_blob_garbage_collection: false
|
||||
2026/01/27-22:05:20.859059 1 Options.blob_garbage_collection_age_cutoff: 0.250000
|
||||
2026/01/27-22:05:20.859060 1 Options.blob_garbage_collection_force_threshold: 1.000000
|
||||
2026/01/27-22:05:20.859060 1 Options.blob_compaction_readahead_size: 0
|
||||
2026/01/27-22:05:20.859061 1 Options.blob_file_starting_level: 0
|
||||
2026/01/27-22:05:20.859061 1 Options.experimental_mempurge_threshold: 0.000000
|
||||
2026/01/27-22:05:20.859062 1 Options.memtable_max_range_deletions: 0
|
||||
2026/01/27-22:05:20.859062 1 Options.cf_allow_ingest_behind: false
|
||||
2026/01/27-22:05:20.859382 1 [WARN] [db/db_impl/db_impl_open.cc:2688] DB::Open() failed: Invalid argument: Column families not opened: index, search, stream, propagate, pubsub, zset_score, metadata
|
||||
2026/01/27-22:05:20.859417 1 [db/db_impl/db_impl.cc:467] Shutdown: canceling all background work
|
||||
2026/01/27-22:05:20.859456 1 [db/db_impl/db_impl.cc:681] Shutdown complete
|
||||
1456
local-dev/data/kvrocks/db/LOG.old.1769552965239568
Normal file
1456
local-dev/data/kvrocks/db/LOG.old.1769552965239568
Normal file
File diff suppressed because it is too large
Load Diff
391
local-dev/data/kvrocks/db/LOG.old.1769552965242971
Normal file
391
local-dev/data/kvrocks/db/LOG.old.1769552965242971
Normal file
@@ -0,0 +1,391 @@
|
||||
2026/01/27-22:29:25.240257 1 RocksDB version: 10.6.2
|
||||
2026/01/27-22:29:25.240319 1 Git sha 0
|
||||
2026/01/27-22:29:25.240320 1 Compile date 2025-11-08 14:59:16
|
||||
2026/01/27-22:29:25.240321 1 DB SUMMARY
|
||||
2026/01/27-22:29:25.240322 1 Host name (Env): 5872b7ec3319
|
||||
2026/01/27-22:29:25.240323 1 DB Session ID: NHD5FTT50V3BVJ0V5F19
|
||||
2026/01/27-22:29:25.240426 1 CURRENT file: CURRENT
|
||||
2026/01/27-22:29:25.240428 1 IDENTITY file: IDENTITY
|
||||
2026/01/27-22:29:25.240430 1 MANIFEST file: MANIFEST-000020 size: 941 Bytes
|
||||
2026/01/27-22:29:25.240432 1 SST files in /data/db dir, Total Num: 1, files: 000014.sst
|
||||
2026/01/27-22:29:25.240433 1 Write Ahead Log file in /data/db: 000019.log size: 101968 ;
|
||||
2026/01/27-22:29:25.240437 1 Options.error_if_exists: 0
|
||||
2026/01/27-22:29:25.240437 1 Options.create_if_missing: 1
|
||||
2026/01/27-22:29:25.240438 1 Options.paranoid_checks: 1
|
||||
2026/01/27-22:29:25.240438 1 Options.flush_verify_memtable_count: 1
|
||||
2026/01/27-22:29:25.240439 1 Options.compaction_verify_record_count: 1
|
||||
2026/01/27-22:29:25.240440 1 Options.track_and_verify_wals_in_manifest: 0
|
||||
2026/01/27-22:29:25.240440 1 Options.track_and_verify_wals: 0
|
||||
2026/01/27-22:29:25.240441 1 Options.verify_sst_unique_id_in_manifest: 1
|
||||
2026/01/27-22:29:25.240441 1 Options.env: 0xffff8981d0c0
|
||||
2026/01/27-22:29:25.240442 1 Options.fs: PosixFileSystem
|
||||
2026/01/27-22:29:25.240442 1 Options.info_log: 0xffff8990f600
|
||||
2026/01/27-22:29:25.240443 1 Options.max_file_opening_threads: 16
|
||||
2026/01/27-22:29:25.240443 1 Options.statistics: 0xffff898135b0
|
||||
2026/01/27-22:29:25.240444 1 Options.statistics stats level: 3
|
||||
2026/01/27-22:29:25.240444 1 Options.use_fsync: 0
|
||||
2026/01/27-22:29:25.240445 1 Options.max_log_file_size: 268435456
|
||||
2026/01/27-22:29:25.240446 1 Options.max_manifest_file_size: 67108864
|
||||
2026/01/27-22:29:25.240448 1 Options.log_file_time_to_roll: 0
|
||||
2026/01/27-22:29:25.240449 1 Options.keep_log_file_num: 12
|
||||
2026/01/27-22:29:25.240450 1 Options.recycle_log_file_num: 0
|
||||
2026/01/27-22:29:25.240450 1 Options.allow_fallocate: 1
|
||||
2026/01/27-22:29:25.240451 1 Options.allow_mmap_reads: 0
|
||||
2026/01/27-22:29:25.240451 1 Options.allow_mmap_writes: 0
|
||||
2026/01/27-22:29:25.240452 1 Options.use_direct_reads: 0
|
||||
2026/01/27-22:29:25.240453 1 Options.use_direct_io_for_flush_and_compaction: 0
|
||||
2026/01/27-22:29:25.240453 1 Options.create_missing_column_families: 1
|
||||
2026/01/27-22:29:25.240454 1 Options.db_log_dir:
|
||||
2026/01/27-22:29:25.240455 1 Options.wal_dir:
|
||||
2026/01/27-22:29:25.240455 1 Options.table_cache_numshardbits: 6
|
||||
2026/01/27-22:29:25.240456 1 Options.WAL_ttl_seconds: 10800
|
||||
2026/01/27-22:29:25.240456 1 Options.WAL_size_limit_MB: 16384
|
||||
2026/01/27-22:29:25.240457 1 Options.max_write_batch_group_size_bytes: 1048576
|
||||
2026/01/27-22:29:25.240458 1 Options.manifest_preallocation_size: 4194304
|
||||
2026/01/27-22:29:25.240458 1 Options.is_fd_close_on_exec: 1
|
||||
2026/01/27-22:29:25.240459 1 Options.advise_random_on_open: 1
|
||||
2026/01/27-22:29:25.240460 1 Options.db_write_buffer_size: 0
|
||||
2026/01/27-22:29:25.240460 1 Options.write_buffer_manager: 0xffff8986bfc0
|
||||
2026/01/27-22:29:25.240461 1 Options.use_adaptive_mutex: 0
|
||||
2026/01/27-22:29:25.240461 1 Options.rate_limiter: 0xffff89836000
|
||||
2026/01/27-22:29:25.240462 1 Options.sst_file_manager.rate_bytes_per_sec: 0
|
||||
2026/01/27-22:29:25.240465 1 Options.wal_recovery_mode: 2
|
||||
2026/01/27-22:29:25.240466 1 Options.enable_thread_tracking: 0
|
||||
2026/01/27-22:29:25.240467 1 Options.enable_pipelined_write: 0
|
||||
2026/01/27-22:29:25.240468 1 Options.unordered_write: 0
|
||||
2026/01/27-22:29:25.240468 1 Options.allow_concurrent_memtable_write: 1
|
||||
2026/01/27-22:29:25.240469 1 Options.enable_write_thread_adaptive_yield: 1
|
||||
2026/01/27-22:29:25.240469 1 Options.write_thread_max_yield_usec: 100
|
||||
2026/01/27-22:29:25.240470 1 Options.write_thread_slow_yield_usec: 3
|
||||
2026/01/27-22:29:25.240471 1 Options.row_cache: None
|
||||
2026/01/27-22:29:25.240471 1 Options.wal_filter: None
|
||||
2026/01/27-22:29:25.240472 1 Options.avoid_flush_during_recovery: 0
|
||||
2026/01/27-22:29:25.240472 1 Options.allow_ingest_behind: 0
|
||||
2026/01/27-22:29:25.240473 1 Options.two_write_queues: 0
|
||||
2026/01/27-22:29:25.240474 1 Options.manual_wal_flush: 0
|
||||
2026/01/27-22:29:25.240474 1 Options.wal_compression: 0
|
||||
2026/01/27-22:29:25.240475 1 Options.background_close_inactive_wals: 0
|
||||
2026/01/27-22:29:25.240475 1 Options.atomic_flush: 0
|
||||
2026/01/27-22:29:25.240476 1 Options.avoid_unnecessary_blocking_io: 1
|
||||
2026/01/27-22:29:25.240476 1 Options.prefix_seek_opt_in_only: 0
|
||||
2026/01/27-22:29:25.240477 1 Options.persist_stats_to_disk: 0
|
||||
2026/01/27-22:29:25.240480 1 Options.write_dbid_to_manifest: 1
|
||||
2026/01/27-22:29:25.240481 1 Options.write_identity_file: 1
|
||||
2026/01/27-22:29:25.240481 1 Options.log_readahead_size: 0
|
||||
2026/01/27-22:29:25.240482 1 Options.file_checksum_gen_factory: Unknown
|
||||
2026/01/27-22:29:25.240483 1 Options.best_efforts_recovery: 0
|
||||
2026/01/27-22:29:25.240483 1 Options.max_bgerror_resume_count: 2147483647
|
||||
2026/01/27-22:29:25.240484 1 Options.bgerror_resume_retry_interval: 1000000
|
||||
2026/01/27-22:29:25.240484 1 Options.allow_data_in_errors: 0
|
||||
2026/01/27-22:29:25.240485 1 Options.db_host_id: __hostname__
|
||||
2026/01/27-22:29:25.240485 1 Options.enforce_single_del_contracts: true
|
||||
2026/01/27-22:29:25.240486 1 Options.metadata_write_temperature: kUnknown
|
||||
2026/01/27-22:29:25.240487 1 Options.wal_write_temperature: kUnknown
|
||||
2026/01/27-22:29:25.240487 1 Options.max_background_jobs: 4
|
||||
2026/01/27-22:29:25.240488 1 Options.max_background_compactions: -1
|
||||
2026/01/27-22:29:25.240488 1 Options.max_subcompactions: 2
|
||||
2026/01/27-22:29:25.240489 1 Options.avoid_flush_during_shutdown: 0
|
||||
2026/01/27-22:29:25.240489 1 Options.writable_file_max_buffer_size: 1048576
|
||||
2026/01/27-22:29:25.240492 1 Options.delayed_write_rate : 536870912000
|
||||
2026/01/27-22:29:25.240493 1 Options.max_total_wal_size: 536870912
|
||||
2026/01/27-22:29:25.240494 1 Options.delete_obsolete_files_period_micros: 21600000000
|
||||
2026/01/27-22:29:25.240494 1 Options.stats_dump_period_sec: 0
|
||||
2026/01/27-22:29:25.240495 1 Options.stats_persist_period_sec: 600
|
||||
2026/01/27-22:29:25.240495 1 Options.stats_history_buffer_size: 1048576
|
||||
2026/01/27-22:29:25.240496 1 Options.max_open_files: 8096
|
||||
2026/01/27-22:29:25.240496 1 Options.bytes_per_sync: 1048576
|
||||
2026/01/27-22:29:25.240497 1 Options.wal_bytes_per_sync: 0
|
||||
2026/01/27-22:29:25.240498 1 Options.strict_bytes_per_sync: 0
|
||||
2026/01/27-22:29:25.240498 1 Options.compaction_readahead_size: 2097152
|
||||
2026/01/27-22:29:25.240499 1 Options.max_background_flushes: -1
|
||||
2026/01/27-22:29:25.240499 1 Options.daily_offpeak_time_utc:
|
||||
2026/01/27-22:29:25.240500 1 Compression algorithms supported:
|
||||
2026/01/27-22:29:25.240500 1 kCustomCompressionFE supported: 0
|
||||
2026/01/27-22:29:25.240501 1 kCustomCompressionFC supported: 0
|
||||
2026/01/27-22:29:25.240502 1 kCustomCompressionF8 supported: 0
|
||||
2026/01/27-22:29:25.240502 1 kCustomCompressionF7 supported: 0
|
||||
2026/01/27-22:29:25.240503 1 kCustomCompressionB2 supported: 0
|
||||
2026/01/27-22:29:25.240503 1 kLZ4Compression supported: 1
|
||||
2026/01/27-22:29:25.240506 1 kCustomCompression88 supported: 0
|
||||
2026/01/27-22:29:25.240506 1 kCustomCompressionD8 supported: 0
|
||||
2026/01/27-22:29:25.240507 1 kCustomCompression9F supported: 0
|
||||
2026/01/27-22:29:25.240507 1 kCustomCompressionD6 supported: 0
|
||||
2026/01/27-22:29:25.240508 1 kCustomCompressionA9 supported: 0
|
||||
2026/01/27-22:29:25.240508 1 kCustomCompressionEC supported: 0
|
||||
2026/01/27-22:29:25.240509 1 kCustomCompressionA3 supported: 0
|
||||
2026/01/27-22:29:25.240509 1 kCustomCompressionCB supported: 0
|
||||
2026/01/27-22:29:25.240510 1 kCustomCompression90 supported: 0
|
||||
2026/01/27-22:29:25.240510 1 kCustomCompressionA0 supported: 0
|
||||
2026/01/27-22:29:25.240511 1 kCustomCompressionC6 supported: 0
|
||||
2026/01/27-22:29:25.240513 1 kCustomCompression9D supported: 0
|
||||
2026/01/27-22:29:25.240514 1 kCustomCompression8B supported: 0
|
||||
2026/01/27-22:29:25.240514 1 kCustomCompressionA8 supported: 0
|
||||
2026/01/27-22:29:25.240515 1 kCustomCompression8D supported: 0
|
||||
2026/01/27-22:29:25.240515 1 kCustomCompression97 supported: 0
|
||||
2026/01/27-22:29:25.240516 1 kCustomCompression98 supported: 0
|
||||
2026/01/27-22:29:25.240516 1 kCustomCompressionAC supported: 0
|
||||
2026/01/27-22:29:25.240517 1 kCustomCompressionE9 supported: 0
|
||||
2026/01/27-22:29:25.240517 1 kCustomCompression96 supported: 0
|
||||
2026/01/27-22:29:25.240518 1 kCustomCompressionB1 supported: 0
|
||||
2026/01/27-22:29:25.240518 1 kCustomCompression95 supported: 0
|
||||
2026/01/27-22:29:25.240519 1 kCustomCompression84 supported: 0
|
||||
2026/01/27-22:29:25.240519 1 kCustomCompression91 supported: 0
|
||||
2026/01/27-22:29:25.240520 1 kCustomCompressionAB supported: 0
|
||||
2026/01/27-22:29:25.240520 1 kCustomCompressionB3 supported: 0
|
||||
2026/01/27-22:29:25.240521 1 kCustomCompression81 supported: 0
|
||||
2026/01/27-22:29:25.240521 1 kCustomCompressionDC supported: 0
|
||||
2026/01/27-22:29:25.240522 1 kBZip2Compression supported: 0
|
||||
2026/01/27-22:29:25.240523 1 kCustomCompressionBB supported: 0
|
||||
2026/01/27-22:29:25.240523 1 kCustomCompression9C supported: 0
|
||||
2026/01/27-22:29:25.240524 1 kCustomCompressionC9 supported: 0
|
||||
2026/01/27-22:29:25.240525 1 kCustomCompressionCC supported: 0
|
||||
2026/01/27-22:29:25.240525 1 kCustomCompression92 supported: 0
|
||||
2026/01/27-22:29:25.240526 1 kCustomCompressionB9 supported: 0
|
||||
2026/01/27-22:29:25.240526 1 kCustomCompression8F supported: 0
|
||||
2026/01/27-22:29:25.240527 1 kCustomCompression8A supported: 0
|
||||
2026/01/27-22:29:25.240528 1 kCustomCompression9B supported: 0
|
||||
2026/01/27-22:29:25.240528 1 kZSTD supported: 1
|
||||
2026/01/27-22:29:25.240529 1 kCustomCompressionAA supported: 0
|
||||
2026/01/27-22:29:25.240529 1 kCustomCompressionA2 supported: 0
|
||||
2026/01/27-22:29:25.240530 1 kZlibCompression supported: 1
|
||||
2026/01/27-22:29:25.240531 1 kXpressCompression supported: 0
|
||||
2026/01/27-22:29:25.240532 1 kCustomCompressionFD supported: 0
|
||||
2026/01/27-22:29:25.240532 1 kCustomCompressionE2 supported: 0
|
||||
2026/01/27-22:29:25.240533 1 kLZ4HCCompression supported: 1
|
||||
2026/01/27-22:29:25.240534 1 kCustomCompressionA6 supported: 0
|
||||
2026/01/27-22:29:25.240534 1 kCustomCompression85 supported: 0
|
||||
2026/01/27-22:29:25.240535 1 kCustomCompressionA4 supported: 0
|
||||
2026/01/27-22:29:25.240536 1 kCustomCompression86 supported: 0
|
||||
2026/01/27-22:29:25.240536 1 kCustomCompression83 supported: 0
|
||||
2026/01/27-22:29:25.240537 1 kCustomCompression87 supported: 0
|
||||
2026/01/27-22:29:25.240537 1 kCustomCompression89 supported: 0
|
||||
2026/01/27-22:29:25.240538 1 kCustomCompression8C supported: 0
|
||||
2026/01/27-22:29:25.240539 1 kCustomCompressionDB supported: 0
|
||||
2026/01/27-22:29:25.240539 1 kCustomCompressionF3 supported: 0
|
||||
2026/01/27-22:29:25.240540 1 kCustomCompressionE6 supported: 0
|
||||
2026/01/27-22:29:25.240541 1 kCustomCompression8E supported: 0
|
||||
2026/01/27-22:29:25.240541 1 kCustomCompressionDA supported: 0
|
||||
2026/01/27-22:29:25.240542 1 kCustomCompression93 supported: 0
|
||||
2026/01/27-22:29:25.240542 1 kCustomCompression94 supported: 0
|
||||
2026/01/27-22:29:25.240545 1 kCustomCompression9E supported: 0
|
||||
2026/01/27-22:29:25.240545 1 kCustomCompressionB4 supported: 0
|
||||
2026/01/27-22:29:25.240546 1 kCustomCompressionFB supported: 0
|
||||
2026/01/27-22:29:25.240547 1 kCustomCompressionB5 supported: 0
|
||||
2026/01/27-22:29:25.240547 1 kCustomCompressionD5 supported: 0
|
||||
2026/01/27-22:29:25.240548 1 kCustomCompressionB8 supported: 0
|
||||
2026/01/27-22:29:25.240548 1 kCustomCompressionD1 supported: 0
|
||||
2026/01/27-22:29:25.240549 1 kCustomCompressionBA supported: 0
|
||||
2026/01/27-22:29:25.240550 1 kCustomCompressionBC supported: 0
|
||||
2026/01/27-22:29:25.240550 1 kCustomCompressionCE supported: 0
|
||||
2026/01/27-22:29:25.240551 1 kCustomCompressionBD supported: 0
|
||||
2026/01/27-22:29:25.240551 1 kCustomCompressionC4 supported: 0
|
||||
2026/01/27-22:29:25.240552 1 kCustomCompression9A supported: 0
|
||||
2026/01/27-22:29:25.240553 1 kCustomCompression99 supported: 0
|
||||
2026/01/27-22:29:25.240553 1 kCustomCompressionBE supported: 0
|
||||
2026/01/27-22:29:25.240554 1 kCustomCompressionE5 supported: 0
|
||||
2026/01/27-22:29:25.240554 1 kCustomCompressionD9 supported: 0
|
||||
2026/01/27-22:29:25.240555 1 kCustomCompressionC1 supported: 0
|
||||
2026/01/27-22:29:25.240555 1 kCustomCompressionC5 supported: 0
|
||||
2026/01/27-22:29:25.240556 1 kCustomCompressionC2 supported: 0
|
||||
2026/01/27-22:29:25.240557 1 kCustomCompressionA5 supported: 0
|
||||
2026/01/27-22:29:25.240557 1 kCustomCompressionC7 supported: 0
|
||||
2026/01/27-22:29:25.240558 1 kCustomCompressionBF supported: 0
|
||||
2026/01/27-22:29:25.240558 1 kCustomCompressionE8 supported: 0
|
||||
2026/01/27-22:29:25.240559 1 kCustomCompressionC8 supported: 0
|
||||
2026/01/27-22:29:25.240559 1 kCustomCompressionAF supported: 0
|
||||
2026/01/27-22:29:25.240560 1 kCustomCompressionCA supported: 0
|
||||
2026/01/27-22:29:25.240561 1 kCustomCompressionCD supported: 0
|
||||
2026/01/27-22:29:25.240561 1 kCustomCompressionC0 supported: 0
|
||||
2026/01/27-22:29:25.240562 1 kCustomCompressionCF supported: 0
|
||||
2026/01/27-22:29:25.240562 1 kCustomCompressionF9 supported: 0
|
||||
2026/01/27-22:29:25.240563 1 kCustomCompressionD0 supported: 0
|
||||
2026/01/27-22:29:25.240564 1 kCustomCompressionD2 supported: 0
|
||||
2026/01/27-22:29:25.240564 1 kCustomCompressionAD supported: 0
|
||||
2026/01/27-22:29:25.240565 1 kCustomCompressionD3 supported: 0
|
||||
2026/01/27-22:29:25.240566 1 kCustomCompressionD4 supported: 0
|
||||
2026/01/27-22:29:25.240566 1 kCustomCompressionD7 supported: 0
|
||||
2026/01/27-22:29:25.240567 1 kCustomCompression82 supported: 0
|
||||
2026/01/27-22:29:25.240567 1 kCustomCompressionDD supported: 0
|
||||
2026/01/27-22:29:25.240568 1 kCustomCompressionC3 supported: 0
|
||||
2026/01/27-22:29:25.240568 1 kCustomCompressionEE supported: 0
|
||||
2026/01/27-22:29:25.240569 1 kCustomCompressionDE supported: 0
|
||||
2026/01/27-22:29:25.240569 1 kCustomCompressionDF supported: 0
|
||||
2026/01/27-22:29:25.240570 1 kCustomCompressionA7 supported: 0
|
||||
2026/01/27-22:29:25.240570 1 kCustomCompressionE0 supported: 0
|
||||
2026/01/27-22:29:25.240570 1 kCustomCompressionF1 supported: 0
|
||||
2026/01/27-22:29:25.240571 1 kCustomCompressionE1 supported: 0
|
||||
2026/01/27-22:29:25.240571 1 kCustomCompressionF5 supported: 0
|
||||
2026/01/27-22:29:25.240572 1 kCustomCompression80 supported: 0
|
||||
2026/01/27-22:29:25.240572 1 kCustomCompressionE3 supported: 0
|
||||
2026/01/27-22:29:25.240573 1 kCustomCompressionE4 supported: 0
|
||||
2026/01/27-22:29:25.240573 1 kCustomCompressionB0 supported: 0
|
||||
2026/01/27-22:29:25.240574 1 kCustomCompressionEA supported: 0
|
||||
2026/01/27-22:29:25.240574 1 kCustomCompressionFA supported: 0
|
||||
2026/01/27-22:29:25.240575 1 kCustomCompressionE7 supported: 0
|
||||
2026/01/27-22:29:25.240575 1 kCustomCompressionAE supported: 0
|
||||
2026/01/27-22:29:25.240576 1 kCustomCompressionEB supported: 0
|
||||
2026/01/27-22:29:25.240576 1 kCustomCompressionED supported: 0
|
||||
2026/01/27-22:29:25.240577 1 kCustomCompressionB6 supported: 0
|
||||
2026/01/27-22:29:25.240579 1 kCustomCompressionEF supported: 0
|
||||
2026/01/27-22:29:25.240580 1 kCustomCompressionF0 supported: 0
|
||||
2026/01/27-22:29:25.240580 1 kCustomCompressionB7 supported: 0
|
||||
2026/01/27-22:29:25.240581 1 kCustomCompressionF2 supported: 0
|
||||
2026/01/27-22:29:25.240581 1 kCustomCompressionA1 supported: 0
|
||||
2026/01/27-22:29:25.240582 1 kCustomCompressionF4 supported: 0
|
||||
2026/01/27-22:29:25.240582 1 kSnappyCompression supported: 1
|
||||
2026/01/27-22:29:25.240583 1 kCustomCompressionF6 supported: 0
|
||||
2026/01/27-22:29:25.240584 1 Fast CRC32 supported: Supported on Arm64
|
||||
2026/01/27-22:29:25.240584 1 DMutex implementation: pthread_mutex_t
|
||||
2026/01/27-22:29:25.240585 1 Jemalloc supported: 1
|
||||
2026/01/27-22:29:25.241055 1 [db/version_set.cc:6190] Recovering from manifest file: /data/db/MANIFEST-000020
|
||||
2026/01/27-22:29:25.241240 1 [db/column_family.cc:693] --------------- Options for column family [default]:
|
||||
2026/01/27-22:29:25.241243 1 Options.comparator: leveldb.BytewiseComparator
|
||||
2026/01/27-22:29:25.241244 1 Options.merge_operator: None
|
||||
2026/01/27-22:29:25.241245 1 Options.compaction_filter: None
|
||||
2026/01/27-22:29:25.241246 1 Options.compaction_filter_factory: None
|
||||
2026/01/27-22:29:25.241246 1 Options.sst_partitioner_factory: None
|
||||
2026/01/27-22:29:25.241247 1 Options.memtable_factory: SkipListFactory
|
||||
2026/01/27-22:29:25.241248 1 Options.table_factory: BlockBasedTable
|
||||
2026/01/27-22:29:25.241278 1 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0xffff898d15a0)
|
||||
cache_index_and_filter_blocks: 0
|
||||
cache_index_and_filter_blocks_with_high_priority: 1
|
||||
pin_l0_filter_and_index_blocks_in_cache: 0
|
||||
pin_top_level_index_and_filter: 1
|
||||
index_type: 0
|
||||
data_block_index_type: 0
|
||||
index_shortening: 1
|
||||
data_block_hash_table_util_ratio: 0.750000
|
||||
checksum: 4
|
||||
no_block_cache: 0
|
||||
block_cache: 0xffff8986bd90
|
||||
block_cache_name: LRUCache
|
||||
block_cache_options:
|
||||
capacity : 33554432
|
||||
num_shard_bits : 6
|
||||
strict_capacity_limit : 0
|
||||
memory_allocator : None
|
||||
high_pri_pool_ratio: 0.500
|
||||
low_pri_pool_ratio: 0.000
|
||||
persistent_cache: (nil)
|
||||
block_size: 4096
|
||||
block_size_deviation: 10
|
||||
block_restart_interval: 16
|
||||
index_block_restart_interval: 1
|
||||
metadata_block_size: 4096
|
||||
partition_filters: 0
|
||||
use_delta_encoding: 1
|
||||
filter_policy: nullptr
|
||||
user_defined_index_factory: nullptr
|
||||
fail_if_no_udi_on_open: 0
|
||||
whole_key_filtering: 1
|
||||
verify_compression: 0
|
||||
read_amp_bytes_per_bit: 0
|
||||
format_version: 6
|
||||
enable_index_compression: 1
|
||||
block_align: 0
|
||||
max_auto_readahead_size: 262144
|
||||
prepopulate_block_cache: 0
|
||||
initial_auto_readahead_size: 8192
|
||||
num_file_reads_for_auto_readahead: 2
|
||||
2026/01/27-22:29:25.241289 1 Options.write_buffer_size: 67108864
|
||||
2026/01/27-22:29:25.241289 1 Options.max_write_buffer_number: 4
|
||||
2026/01/27-22:29:25.241291 1 Options.compression[0]: NoCompression
|
||||
2026/01/27-22:29:25.241291 1 Options.compression[1]: NoCompression
|
||||
2026/01/27-22:29:25.241292 1 Options.compression[2]: Snappy
|
||||
2026/01/27-22:29:25.241292 1 Options.compression[3]: Snappy
|
||||
2026/01/27-22:29:25.241293 1 Options.compression[4]: Snappy
|
||||
2026/01/27-22:29:25.241293 1 Options.compression[5]: Snappy
|
||||
2026/01/27-22:29:25.241294 1 Options.compression[6]: Snappy
|
||||
2026/01/27-22:29:25.241294 1 Options.bottommost_compression: Disabled
|
||||
2026/01/27-22:29:25.241295 1 Options.prefix_extractor: nullptr
|
||||
2026/01/27-22:29:25.241295 1 Options.memtable_insert_with_hint_prefix_extractor: nullptr
|
||||
2026/01/27-22:29:25.241296 1 Options.num_levels: 7
|
||||
2026/01/27-22:29:25.241296 1 Options.min_write_buffer_number_to_merge: 1
|
||||
2026/01/27-22:29:25.241297 1 Options.max_write_buffer_size_to_maintain: 0
|
||||
2026/01/27-22:29:25.241297 1 Options.bottommost_compression_opts.window_bits: -14
|
||||
2026/01/27-22:29:25.241298 1 Options.bottommost_compression_opts.level: 32767
|
||||
2026/01/27-22:29:25.241298 1 Options.bottommost_compression_opts.strategy: 0
|
||||
2026/01/27-22:29:25.241299 1 Options.bottommost_compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-22:29:25.241299 1 Options.bottommost_compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-22:29:25.241300 1 Options.bottommost_compression_opts.parallel_threads: 1
|
||||
2026/01/27-22:29:25.241301 1 Options.bottommost_compression_opts.enabled: false
|
||||
2026/01/27-22:29:25.241301 1 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-22:29:25.241302 1 Options.bottommost_compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-22:29:25.241302 1 Options.compression_opts.window_bits: -14
|
||||
2026/01/27-22:29:25.241303 1 Options.compression_opts.level: 32767
|
||||
2026/01/27-22:29:25.241303 1 Options.compression_opts.strategy: 0
|
||||
2026/01/27-22:29:25.241304 1 Options.compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-22:29:25.241304 1 Options.compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-22:29:25.241304 1 Options.compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-22:29:25.241305 1 Options.compression_opts.parallel_threads: 1
|
||||
2026/01/27-22:29:25.241305 1 Options.compression_opts.enabled: false
|
||||
2026/01/27-22:29:25.241306 1 Options.compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-22:29:25.241306 1 Options.level0_file_num_compaction_trigger: 4
|
||||
2026/01/27-22:29:25.241307 1 Options.level0_slowdown_writes_trigger: 20
|
||||
2026/01/27-22:29:25.241307 1 Options.level0_stop_writes_trigger: 40
|
||||
2026/01/27-22:29:25.241308 1 Options.target_file_size_base: 134217728
|
||||
2026/01/27-22:29:25.241308 1 Options.target_file_size_multiplier: 1
|
||||
2026/01/27-22:29:25.241309 1 Options.max_bytes_for_level_base: 268435456
|
||||
2026/01/27-22:29:25.241309 1 Options.level_compaction_dynamic_level_bytes: 1
|
||||
2026/01/27-22:29:25.241310 1 Options.max_bytes_for_level_multiplier: 10.000000
|
||||
2026/01/27-22:29:25.241311 1 Options.max_bytes_for_level_multiplier_addtl[0]: 1
|
||||
2026/01/27-22:29:25.241311 1 Options.max_bytes_for_level_multiplier_addtl[1]: 1
|
||||
2026/01/27-22:29:25.241312 1 Options.max_bytes_for_level_multiplier_addtl[2]: 1
|
||||
2026/01/27-22:29:25.241312 1 Options.max_bytes_for_level_multiplier_addtl[3]: 1
|
||||
2026/01/27-22:29:25.241313 1 Options.max_bytes_for_level_multiplier_addtl[4]: 1
|
||||
2026/01/27-22:29:25.241313 1 Options.max_bytes_for_level_multiplier_addtl[5]: 1
|
||||
2026/01/27-22:29:25.241314 1 Options.max_bytes_for_level_multiplier_addtl[6]: 1
|
||||
2026/01/27-22:29:25.241314 1 Options.max_sequential_skip_in_iterations: 8
|
||||
2026/01/27-22:29:25.241315 1 Options.memtable_op_scan_flush_trigger: 0
|
||||
2026/01/27-22:29:25.241315 1 Options.memtable_avg_op_scan_flush_trigger: 0
|
||||
2026/01/27-22:29:25.241316 1 Options.max_compaction_bytes: 3355443200
|
||||
2026/01/27-22:29:25.241316 1 Options.arena_block_size: 1048576
|
||||
2026/01/27-22:29:25.241317 1 Options.soft_pending_compaction_bytes_limit: 68719476736
|
||||
2026/01/27-22:29:25.241317 1 Options.hard_pending_compaction_bytes_limit: 274877906944
|
||||
2026/01/27-22:29:25.241318 1 Options.disable_auto_compactions: 0
|
||||
2026/01/27-22:29:25.241318 1 Options.compaction_style: kCompactionStyleLevel
|
||||
2026/01/27-22:29:25.241319 1 Options.compaction_pri: kMinOverlappingRatio
|
||||
2026/01/27-22:29:25.241319 1 Options.compaction_options_universal.size_ratio: 1
|
||||
2026/01/27-22:29:25.241320 1 Options.compaction_options_universal.min_merge_width: 2
|
||||
2026/01/27-22:29:25.241320 1 Options.compaction_options_universal.max_merge_width: 4294967295
|
||||
2026/01/27-22:29:25.241321 1 Options.compaction_options_universal.max_size_amplification_percent: 200
|
||||
2026/01/27-22:29:25.241322 1 Options.compaction_options_universal.compression_size_percent: -1
|
||||
2026/01/27-22:29:25.241322 1 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize
|
||||
2026/01/27-22:29:25.241323 1 Options.compaction_options_universal.max_read_amp: -1
|
||||
2026/01/27-22:29:25.241323 1 Options.compaction_options_universal.reduce_file_locking: 0
|
||||
2026/01/27-22:29:25.241324 1 Options.compaction_options_fifo.max_table_files_size: 1073741824
|
||||
2026/01/27-22:29:25.241324 1 Options.compaction_options_fifo.allow_compaction: 0
|
||||
2026/01/27-22:29:25.241326 1 Options.table_properties_collectors:
|
||||
2026/01/27-22:29:25.241327 1 Options.inplace_update_support: 0
|
||||
2026/01/27-22:29:25.241327 1 Options.inplace_update_num_locks: 10000
|
||||
2026/01/27-22:29:25.241328 1 Options.memtable_prefix_bloom_size_ratio: 0.000000
|
||||
2026/01/27-22:29:25.241329 1 Options.memtable_whole_key_filtering: 0
|
||||
2026/01/27-22:29:25.241329 1 Options.memtable_huge_page_size: 0
|
||||
2026/01/27-22:29:25.241329 1 Options.bloom_locality: 0
|
||||
2026/01/27-22:29:25.241330 1 Options.max_successive_merges: 0
|
||||
2026/01/27-22:29:25.241330 1 Options.strict_max_successive_merges: 0
|
||||
2026/01/27-22:29:25.241331 1 Options.optimize_filters_for_hits: 0
|
||||
2026/01/27-22:29:25.241331 1 Options.paranoid_file_checks: 0
|
||||
2026/01/27-22:29:25.241332 1 Options.force_consistency_checks: 1
|
||||
2026/01/27-22:29:25.241332 1 Options.report_bg_io_stats: 0
|
||||
2026/01/27-22:29:25.241333 1 Options.disallow_memtable_writes: 0
|
||||
2026/01/27-22:29:25.241333 1 Options.ttl: 2592000
|
||||
2026/01/27-22:29:25.241334 1 Options.periodic_compaction_seconds: 0
|
||||
2026/01/27-22:29:25.241334 1 Options.default_temperature: kUnknown
|
||||
2026/01/27-22:29:25.241335 1 Options.preclude_last_level_data_seconds: 0
|
||||
2026/01/27-22:29:25.241335 1 Options.preserve_internal_time_seconds: 0
|
||||
2026/01/27-22:29:25.241336 1 Options.enable_blob_files: false
|
||||
2026/01/27-22:29:25.241336 1 Options.min_blob_size: 0
|
||||
2026/01/27-22:29:25.241337 1 Options.blob_file_size: 268435456
|
||||
2026/01/27-22:29:25.241337 1 Options.blob_compression_type: NoCompression
|
||||
2026/01/27-22:29:25.241338 1 Options.enable_blob_garbage_collection: false
|
||||
2026/01/27-22:29:25.241338 1 Options.blob_garbage_collection_age_cutoff: 0.250000
|
||||
2026/01/27-22:29:25.241339 1 Options.blob_garbage_collection_force_threshold: 1.000000
|
||||
2026/01/27-22:29:25.241340 1 Options.blob_compaction_readahead_size: 0
|
||||
2026/01/27-22:29:25.241340 1 Options.blob_file_starting_level: 0
|
||||
2026/01/27-22:29:25.241341 1 Options.experimental_mempurge_threshold: 0.000000
|
||||
2026/01/27-22:29:25.241341 1 Options.memtable_max_range_deletions: 0
|
||||
2026/01/27-22:29:25.241342 1 Options.cf_allow_ingest_behind: false
|
||||
2026/01/27-22:29:25.242680 1 [WARN] [db/db_impl/db_impl_open.cc:2688] DB::Open() failed: Invalid argument: Column families not opened: index, search, stream, propagate, pubsub, zset_score, metadata
|
||||
2026/01/27-22:29:25.242737 1 [db/db_impl/db_impl.cc:467] Shutdown: canceling all background work
|
||||
2026/01/27-22:29:25.242782 1 [db/db_impl/db_impl.cc:681] Shutdown complete
|
||||
1449
local-dev/data/kvrocks/db/LOG.old.1769552979167350
Normal file
1449
local-dev/data/kvrocks/db/LOG.old.1769552979167350
Normal file
File diff suppressed because it is too large
Load Diff
391
local-dev/data/kvrocks/db/LOG.old.1769552979169205
Normal file
391
local-dev/data/kvrocks/db/LOG.old.1769552979169205
Normal file
@@ -0,0 +1,391 @@
|
||||
2026/01/27-22:29:39.168333 1 RocksDB version: 10.6.2
|
||||
2026/01/27-22:29:39.168394 1 Git sha 0
|
||||
2026/01/27-22:29:39.168395 1 Compile date 2025-11-08 14:59:16
|
||||
2026/01/27-22:29:39.168396 1 DB SUMMARY
|
||||
2026/01/27-22:29:39.168397 1 Host name (Env): db98b7caa3d9
|
||||
2026/01/27-22:29:39.168398 1 DB Session ID: M5A8FG11TXBDE6GONB6Y
|
||||
2026/01/27-22:29:39.168483 1 CURRENT file: CURRENT
|
||||
2026/01/27-22:29:39.168484 1 IDENTITY file: IDENTITY
|
||||
2026/01/27-22:29:39.168485 1 MANIFEST file: MANIFEST-000025 size: 1092 Bytes
|
||||
2026/01/27-22:29:39.168487 1 SST files in /data/db dir, Total Num: 2, files: 000014.sst 000023.sst
|
||||
2026/01/27-22:29:39.168487 1 Write Ahead Log file in /data/db: 000024.log size: 0 ;
|
||||
2026/01/27-22:29:39.168488 1 Options.error_if_exists: 0
|
||||
2026/01/27-22:29:39.168489 1 Options.create_if_missing: 1
|
||||
2026/01/27-22:29:39.168489 1 Options.paranoid_checks: 1
|
||||
2026/01/27-22:29:39.168490 1 Options.flush_verify_memtable_count: 1
|
||||
2026/01/27-22:29:39.168490 1 Options.compaction_verify_record_count: 1
|
||||
2026/01/27-22:29:39.168491 1 Options.track_and_verify_wals_in_manifest: 0
|
||||
2026/01/27-22:29:39.168491 1 Options.track_and_verify_wals: 0
|
||||
2026/01/27-22:29:39.168492 1 Options.verify_sst_unique_id_in_manifest: 1
|
||||
2026/01/27-22:29:39.168492 1 Options.env: 0xffff9a21d0c0
|
||||
2026/01/27-22:29:39.168493 1 Options.fs: PosixFileSystem
|
||||
2026/01/27-22:29:39.168493 1 Options.info_log: 0xffff9a30f600
|
||||
2026/01/27-22:29:39.168494 1 Options.max_file_opening_threads: 16
|
||||
2026/01/27-22:29:39.168494 1 Options.statistics: 0xffff9a2135b0
|
||||
2026/01/27-22:29:39.168495 1 Options.statistics stats level: 3
|
||||
2026/01/27-22:29:39.168495 1 Options.use_fsync: 0
|
||||
2026/01/27-22:29:39.168496 1 Options.max_log_file_size: 268435456
|
||||
2026/01/27-22:29:39.168496 1 Options.max_manifest_file_size: 67108864
|
||||
2026/01/27-22:29:39.168497 1 Options.log_file_time_to_roll: 0
|
||||
2026/01/27-22:29:39.168497 1 Options.keep_log_file_num: 12
|
||||
2026/01/27-22:29:39.168498 1 Options.recycle_log_file_num: 0
|
||||
2026/01/27-22:29:39.168498 1 Options.allow_fallocate: 1
|
||||
2026/01/27-22:29:39.168499 1 Options.allow_mmap_reads: 0
|
||||
2026/01/27-22:29:39.168499 1 Options.allow_mmap_writes: 0
|
||||
2026/01/27-22:29:39.168500 1 Options.use_direct_reads: 0
|
||||
2026/01/27-22:29:39.168500 1 Options.use_direct_io_for_flush_and_compaction: 0
|
||||
2026/01/27-22:29:39.168501 1 Options.create_missing_column_families: 1
|
||||
2026/01/27-22:29:39.168501 1 Options.db_log_dir:
|
||||
2026/01/27-22:29:39.168502 1 Options.wal_dir:
|
||||
2026/01/27-22:29:39.168502 1 Options.table_cache_numshardbits: 6
|
||||
2026/01/27-22:29:39.168503 1 Options.WAL_ttl_seconds: 10800
|
||||
2026/01/27-22:29:39.168503 1 Options.WAL_size_limit_MB: 16384
|
||||
2026/01/27-22:29:39.168504 1 Options.max_write_batch_group_size_bytes: 1048576
|
||||
2026/01/27-22:29:39.168504 1 Options.manifest_preallocation_size: 4194304
|
||||
2026/01/27-22:29:39.168505 1 Options.is_fd_close_on_exec: 1
|
||||
2026/01/27-22:29:39.168505 1 Options.advise_random_on_open: 1
|
||||
2026/01/27-22:29:39.168506 1 Options.db_write_buffer_size: 0
|
||||
2026/01/27-22:29:39.168506 1 Options.write_buffer_manager: 0xffff9a26bfc0
|
||||
2026/01/27-22:29:39.168507 1 Options.use_adaptive_mutex: 0
|
||||
2026/01/27-22:29:39.168507 1 Options.rate_limiter: 0xffff9a236000
|
||||
2026/01/27-22:29:39.168508 1 Options.sst_file_manager.rate_bytes_per_sec: 0
|
||||
2026/01/27-22:29:39.168508 1 Options.wal_recovery_mode: 2
|
||||
2026/01/27-22:29:39.168509 1 Options.enable_thread_tracking: 0
|
||||
2026/01/27-22:29:39.168509 1 Options.enable_pipelined_write: 0
|
||||
2026/01/27-22:29:39.168509 1 Options.unordered_write: 0
|
||||
2026/01/27-22:29:39.168510 1 Options.allow_concurrent_memtable_write: 1
|
||||
2026/01/27-22:29:39.168510 1 Options.enable_write_thread_adaptive_yield: 1
|
||||
2026/01/27-22:29:39.168511 1 Options.write_thread_max_yield_usec: 100
|
||||
2026/01/27-22:29:39.168511 1 Options.write_thread_slow_yield_usec: 3
|
||||
2026/01/27-22:29:39.168512 1 Options.row_cache: None
|
||||
2026/01/27-22:29:39.168512 1 Options.wal_filter: None
|
||||
2026/01/27-22:29:39.168513 1 Options.avoid_flush_during_recovery: 0
|
||||
2026/01/27-22:29:39.168513 1 Options.allow_ingest_behind: 0
|
||||
2026/01/27-22:29:39.168514 1 Options.two_write_queues: 0
|
||||
2026/01/27-22:29:39.168514 1 Options.manual_wal_flush: 0
|
||||
2026/01/27-22:29:39.168515 1 Options.wal_compression: 0
|
||||
2026/01/27-22:29:39.168515 1 Options.background_close_inactive_wals: 0
|
||||
2026/01/27-22:29:39.168516 1 Options.atomic_flush: 0
|
||||
2026/01/27-22:29:39.168516 1 Options.avoid_unnecessary_blocking_io: 1
|
||||
2026/01/27-22:29:39.168516 1 Options.prefix_seek_opt_in_only: 0
|
||||
2026/01/27-22:29:39.168517 1 Options.persist_stats_to_disk: 0
|
||||
2026/01/27-22:29:39.168517 1 Options.write_dbid_to_manifest: 1
|
||||
2026/01/27-22:29:39.168518 1 Options.write_identity_file: 1
|
||||
2026/01/27-22:29:39.168518 1 Options.log_readahead_size: 0
|
||||
2026/01/27-22:29:39.168519 1 Options.file_checksum_gen_factory: Unknown
|
||||
2026/01/27-22:29:39.168519 1 Options.best_efforts_recovery: 0
|
||||
2026/01/27-22:29:39.168520 1 Options.max_bgerror_resume_count: 2147483647
|
||||
2026/01/27-22:29:39.168520 1 Options.bgerror_resume_retry_interval: 1000000
|
||||
2026/01/27-22:29:39.168521 1 Options.allow_data_in_errors: 0
|
||||
2026/01/27-22:29:39.168521 1 Options.db_host_id: __hostname__
|
||||
2026/01/27-22:29:39.168522 1 Options.enforce_single_del_contracts: true
|
||||
2026/01/27-22:29:39.168523 1 Options.metadata_write_temperature: kUnknown
|
||||
2026/01/27-22:29:39.168523 1 Options.wal_write_temperature: kUnknown
|
||||
2026/01/27-22:29:39.168523 1 Options.max_background_jobs: 4
|
||||
2026/01/27-22:29:39.168524 1 Options.max_background_compactions: -1
|
||||
2026/01/27-22:29:39.168524 1 Options.max_subcompactions: 2
|
||||
2026/01/27-22:29:39.168525 1 Options.avoid_flush_during_shutdown: 0
|
||||
2026/01/27-22:29:39.168525 1 Options.writable_file_max_buffer_size: 1048576
|
||||
2026/01/27-22:29:39.168526 1 Options.delayed_write_rate : 536870912000
|
||||
2026/01/27-22:29:39.168527 1 Options.max_total_wal_size: 536870912
|
||||
2026/01/27-22:29:39.168528 1 Options.delete_obsolete_files_period_micros: 21600000000
|
||||
2026/01/27-22:29:39.168528 1 Options.stats_dump_period_sec: 0
|
||||
2026/01/27-22:29:39.168528 1 Options.stats_persist_period_sec: 600
|
||||
2026/01/27-22:29:39.168529 1 Options.stats_history_buffer_size: 1048576
|
||||
2026/01/27-22:29:39.168530 1 Options.max_open_files: 8096
|
||||
2026/01/27-22:29:39.168530 1 Options.bytes_per_sync: 1048576
|
||||
2026/01/27-22:29:39.168531 1 Options.wal_bytes_per_sync: 0
|
||||
2026/01/27-22:29:39.168531 1 Options.strict_bytes_per_sync: 0
|
||||
2026/01/27-22:29:39.168532 1 Options.compaction_readahead_size: 2097152
|
||||
2026/01/27-22:29:39.168532 1 Options.max_background_flushes: -1
|
||||
2026/01/27-22:29:39.168533 1 Options.daily_offpeak_time_utc:
|
||||
2026/01/27-22:29:39.168533 1 Compression algorithms supported:
|
||||
2026/01/27-22:29:39.168534 1 kCustomCompressionFE supported: 0
|
||||
2026/01/27-22:29:39.168534 1 kCustomCompressionFC supported: 0
|
||||
2026/01/27-22:29:39.168535 1 kCustomCompressionF8 supported: 0
|
||||
2026/01/27-22:29:39.168535 1 kCustomCompressionF7 supported: 0
|
||||
2026/01/27-22:29:39.168536 1 kCustomCompressionB2 supported: 0
|
||||
2026/01/27-22:29:39.168537 1 kLZ4Compression supported: 1
|
||||
2026/01/27-22:29:39.168537 1 kCustomCompression88 supported: 0
|
||||
2026/01/27-22:29:39.168538 1 kCustomCompressionD8 supported: 0
|
||||
2026/01/27-22:29:39.168538 1 kCustomCompression9F supported: 0
|
||||
2026/01/27-22:29:39.168539 1 kCustomCompressionD6 supported: 0
|
||||
2026/01/27-22:29:39.168539 1 kCustomCompressionA9 supported: 0
|
||||
2026/01/27-22:29:39.168540 1 kCustomCompressionEC supported: 0
|
||||
2026/01/27-22:29:39.168540 1 kCustomCompressionA3 supported: 0
|
||||
2026/01/27-22:29:39.168541 1 kCustomCompressionCB supported: 0
|
||||
2026/01/27-22:29:39.168541 1 kCustomCompression90 supported: 0
|
||||
2026/01/27-22:29:39.168542 1 kCustomCompressionA0 supported: 0
|
||||
2026/01/27-22:29:39.168542 1 kCustomCompressionC6 supported: 0
|
||||
2026/01/27-22:29:39.168543 1 kCustomCompression9D supported: 0
|
||||
2026/01/27-22:29:39.168543 1 kCustomCompression8B supported: 0
|
||||
2026/01/27-22:29:39.168544 1 kCustomCompressionA8 supported: 0
|
||||
2026/01/27-22:29:39.168544 1 kCustomCompression8D supported: 0
|
||||
2026/01/27-22:29:39.168545 1 kCustomCompression97 supported: 0
|
||||
2026/01/27-22:29:39.168545 1 kCustomCompression98 supported: 0
|
||||
2026/01/27-22:29:39.168546 1 kCustomCompressionAC supported: 0
|
||||
2026/01/27-22:29:39.168546 1 kCustomCompressionE9 supported: 0
|
||||
2026/01/27-22:29:39.168547 1 kCustomCompression96 supported: 0
|
||||
2026/01/27-22:29:39.168547 1 kCustomCompressionB1 supported: 0
|
||||
2026/01/27-22:29:39.168548 1 kCustomCompression95 supported: 0
|
||||
2026/01/27-22:29:39.168548 1 kCustomCompression84 supported: 0
|
||||
2026/01/27-22:29:39.168549 1 kCustomCompression91 supported: 0
|
||||
2026/01/27-22:29:39.168549 1 kCustomCompressionAB supported: 0
|
||||
2026/01/27-22:29:39.168550 1 kCustomCompressionB3 supported: 0
|
||||
2026/01/27-22:29:39.168550 1 kCustomCompression81 supported: 0
|
||||
2026/01/27-22:29:39.168551 1 kCustomCompressionDC supported: 0
|
||||
2026/01/27-22:29:39.168551 1 kBZip2Compression supported: 0
|
||||
2026/01/27-22:29:39.168552 1 kCustomCompressionBB supported: 0
|
||||
2026/01/27-22:29:39.168552 1 kCustomCompression9C supported: 0
|
||||
2026/01/27-22:29:39.168553 1 kCustomCompressionC9 supported: 0
|
||||
2026/01/27-22:29:39.168553 1 kCustomCompressionCC supported: 0
|
||||
2026/01/27-22:29:39.168554 1 kCustomCompression92 supported: 0
|
||||
2026/01/27-22:29:39.168554 1 kCustomCompressionB9 supported: 0
|
||||
2026/01/27-22:29:39.168554 1 kCustomCompression8F supported: 0
|
||||
2026/01/27-22:29:39.168555 1 kCustomCompression8A supported: 0
|
||||
2026/01/27-22:29:39.168555 1 kCustomCompression9B supported: 0
|
||||
2026/01/27-22:29:39.168556 1 kZSTD supported: 1
|
||||
2026/01/27-22:29:39.168556 1 kCustomCompressionAA supported: 0
|
||||
2026/01/27-22:29:39.168557 1 kCustomCompressionA2 supported: 0
|
||||
2026/01/27-22:29:39.168558 1 kZlibCompression supported: 1
|
||||
2026/01/27-22:29:39.168558 1 kXpressCompression supported: 0
|
||||
2026/01/27-22:29:39.168559 1 kCustomCompressionFD supported: 0
|
||||
2026/01/27-22:29:39.168559 1 kCustomCompressionE2 supported: 0
|
||||
2026/01/27-22:29:39.168560 1 kLZ4HCCompression supported: 1
|
||||
2026/01/27-22:29:39.168560 1 kCustomCompressionA6 supported: 0
|
||||
2026/01/27-22:29:39.168561 1 kCustomCompression85 supported: 0
|
||||
2026/01/27-22:29:39.168561 1 kCustomCompressionA4 supported: 0
|
||||
2026/01/27-22:29:39.168562 1 kCustomCompression86 supported: 0
|
||||
2026/01/27-22:29:39.168562 1 kCustomCompression83 supported: 0
|
||||
2026/01/27-22:29:39.168562 1 kCustomCompression87 supported: 0
|
||||
2026/01/27-22:29:39.168563 1 kCustomCompression89 supported: 0
|
||||
2026/01/27-22:29:39.168563 1 kCustomCompression8C supported: 0
|
||||
2026/01/27-22:29:39.168564 1 kCustomCompressionDB supported: 0
|
||||
2026/01/27-22:29:39.168564 1 kCustomCompressionF3 supported: 0
|
||||
2026/01/27-22:29:39.168565 1 kCustomCompressionE6 supported: 0
|
||||
2026/01/27-22:29:39.168565 1 kCustomCompression8E supported: 0
|
||||
2026/01/27-22:29:39.168566 1 kCustomCompressionDA supported: 0
|
||||
2026/01/27-22:29:39.168566 1 kCustomCompression93 supported: 0
|
||||
2026/01/27-22:29:39.168567 1 kCustomCompression94 supported: 0
|
||||
2026/01/27-22:29:39.168570 1 kCustomCompression9E supported: 0
|
||||
2026/01/27-22:29:39.168570 1 kCustomCompressionB4 supported: 0
|
||||
2026/01/27-22:29:39.168571 1 kCustomCompressionFB supported: 0
|
||||
2026/01/27-22:29:39.168571 1 kCustomCompressionB5 supported: 0
|
||||
2026/01/27-22:29:39.168572 1 kCustomCompressionD5 supported: 0
|
||||
2026/01/27-22:29:39.168572 1 kCustomCompressionB8 supported: 0
|
||||
2026/01/27-22:29:39.168572 1 kCustomCompressionD1 supported: 0
|
||||
2026/01/27-22:29:39.168573 1 kCustomCompressionBA supported: 0
|
||||
2026/01/27-22:29:39.168573 1 kCustomCompressionBC supported: 0
|
||||
2026/01/27-22:29:39.168574 1 kCustomCompressionCE supported: 0
|
||||
2026/01/27-22:29:39.168574 1 kCustomCompressionBD supported: 0
|
||||
2026/01/27-22:29:39.168575 1 kCustomCompressionC4 supported: 0
|
||||
2026/01/27-22:29:39.168575 1 kCustomCompression9A supported: 0
|
||||
2026/01/27-22:29:39.168577 1 kCustomCompression99 supported: 0
|
||||
2026/01/27-22:29:39.168578 1 kCustomCompressionBE supported: 0
|
||||
2026/01/27-22:29:39.168578 1 kCustomCompressionE5 supported: 0
|
||||
2026/01/27-22:29:39.168579 1 kCustomCompressionD9 supported: 0
|
||||
2026/01/27-22:29:39.168579 1 kCustomCompressionC1 supported: 0
|
||||
2026/01/27-22:29:39.168580 1 kCustomCompressionC5 supported: 0
|
||||
2026/01/27-22:29:39.168580 1 kCustomCompressionC2 supported: 0
|
||||
2026/01/27-22:29:39.168581 1 kCustomCompressionA5 supported: 0
|
||||
2026/01/27-22:29:39.168581 1 kCustomCompressionC7 supported: 0
|
||||
2026/01/27-22:29:39.168581 1 kCustomCompressionBF supported: 0
|
||||
2026/01/27-22:29:39.168582 1 kCustomCompressionE8 supported: 0
|
||||
2026/01/27-22:29:39.168582 1 kCustomCompressionC8 supported: 0
|
||||
2026/01/27-22:29:39.168583 1 kCustomCompressionAF supported: 0
|
||||
2026/01/27-22:29:39.168583 1 kCustomCompressionCA supported: 0
|
||||
2026/01/27-22:29:39.168584 1 kCustomCompressionCD supported: 0
|
||||
2026/01/27-22:29:39.168584 1 kCustomCompressionC0 supported: 0
|
||||
2026/01/27-22:29:39.168585 1 kCustomCompressionCF supported: 0
|
||||
2026/01/27-22:29:39.168585 1 kCustomCompressionF9 supported: 0
|
||||
2026/01/27-22:29:39.168586 1 kCustomCompressionD0 supported: 0
|
||||
2026/01/27-22:29:39.168586 1 kCustomCompressionD2 supported: 0
|
||||
2026/01/27-22:29:39.168587 1 kCustomCompressionAD supported: 0
|
||||
2026/01/27-22:29:39.168587 1 kCustomCompressionD3 supported: 0
|
||||
2026/01/27-22:29:39.168588 1 kCustomCompressionD4 supported: 0
|
||||
2026/01/27-22:29:39.168588 1 kCustomCompressionD7 supported: 0
|
||||
2026/01/27-22:29:39.168589 1 kCustomCompression82 supported: 0
|
||||
2026/01/27-22:29:39.168589 1 kCustomCompressionDD supported: 0
|
||||
2026/01/27-22:29:39.168590 1 kCustomCompressionC3 supported: 0
|
||||
2026/01/27-22:29:39.168590 1 kCustomCompressionEE supported: 0
|
||||
2026/01/27-22:29:39.168591 1 kCustomCompressionDE supported: 0
|
||||
2026/01/27-22:29:39.168591 1 kCustomCompressionDF supported: 0
|
||||
2026/01/27-22:29:39.168592 1 kCustomCompressionA7 supported: 0
|
||||
2026/01/27-22:29:39.168592 1 kCustomCompressionE0 supported: 0
|
||||
2026/01/27-22:29:39.168593 1 kCustomCompressionF1 supported: 0
|
||||
2026/01/27-22:29:39.168593 1 kCustomCompressionE1 supported: 0
|
||||
2026/01/27-22:29:39.168594 1 kCustomCompressionF5 supported: 0
|
||||
2026/01/27-22:29:39.168594 1 kCustomCompression80 supported: 0
|
||||
2026/01/27-22:29:39.168595 1 kCustomCompressionE3 supported: 0
|
||||
2026/01/27-22:29:39.168595 1 kCustomCompressionE4 supported: 0
|
||||
2026/01/27-22:29:39.168596 1 kCustomCompressionB0 supported: 0
|
||||
2026/01/27-22:29:39.168596 1 kCustomCompressionEA supported: 0
|
||||
2026/01/27-22:29:39.168597 1 kCustomCompressionFA supported: 0
|
||||
2026/01/27-22:29:39.168597 1 kCustomCompressionE7 supported: 0
|
||||
2026/01/27-22:29:39.168598 1 kCustomCompressionAE supported: 0
|
||||
2026/01/27-22:29:39.168598 1 kCustomCompressionEB supported: 0
|
||||
2026/01/27-22:29:39.168598 1 kCustomCompressionED supported: 0
|
||||
2026/01/27-22:29:39.168599 1 kCustomCompressionB6 supported: 0
|
||||
2026/01/27-22:29:39.168599 1 kCustomCompressionEF supported: 0
|
||||
2026/01/27-22:29:39.168600 1 kCustomCompressionF0 supported: 0
|
||||
2026/01/27-22:29:39.168600 1 kCustomCompressionB7 supported: 0
|
||||
2026/01/27-22:29:39.168601 1 kCustomCompressionF2 supported: 0
|
||||
2026/01/27-22:29:39.168601 1 kCustomCompressionA1 supported: 0
|
||||
2026/01/27-22:29:39.168602 1 kCustomCompressionF4 supported: 0
|
||||
2026/01/27-22:29:39.168602 1 kSnappyCompression supported: 1
|
||||
2026/01/27-22:29:39.168603 1 kCustomCompressionF6 supported: 0
|
||||
2026/01/27-22:29:39.168604 1 Fast CRC32 supported: Supported on Arm64
|
||||
2026/01/27-22:29:39.168605 1 DMutex implementation: pthread_mutex_t
|
||||
2026/01/27-22:29:39.168605 1 Jemalloc supported: 1
|
||||
2026/01/27-22:29:39.168746 1 [db/version_set.cc:6190] Recovering from manifest file: /data/db/MANIFEST-000025
|
||||
2026/01/27-22:29:39.168844 1 [db/column_family.cc:693] --------------- Options for column family [default]:
|
||||
2026/01/27-22:29:39.168846 1 Options.comparator: leveldb.BytewiseComparator
|
||||
2026/01/27-22:29:39.168846 1 Options.merge_operator: None
|
||||
2026/01/27-22:29:39.168847 1 Options.compaction_filter: None
|
||||
2026/01/27-22:29:39.168847 1 Options.compaction_filter_factory: None
|
||||
2026/01/27-22:29:39.168848 1 Options.sst_partitioner_factory: None
|
||||
2026/01/27-22:29:39.168848 1 Options.memtable_factory: SkipListFactory
|
||||
2026/01/27-22:29:39.168849 1 Options.table_factory: BlockBasedTable
|
||||
2026/01/27-22:29:39.168863 1 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0xffff9a2d15a0)
|
||||
cache_index_and_filter_blocks: 0
|
||||
cache_index_and_filter_blocks_with_high_priority: 1
|
||||
pin_l0_filter_and_index_blocks_in_cache: 0
|
||||
pin_top_level_index_and_filter: 1
|
||||
index_type: 0
|
||||
data_block_index_type: 0
|
||||
index_shortening: 1
|
||||
data_block_hash_table_util_ratio: 0.750000
|
||||
checksum: 4
|
||||
no_block_cache: 0
|
||||
block_cache: 0xffff9a26bd90
|
||||
block_cache_name: LRUCache
|
||||
block_cache_options:
|
||||
capacity : 33554432
|
||||
num_shard_bits : 6
|
||||
strict_capacity_limit : 0
|
||||
memory_allocator : None
|
||||
high_pri_pool_ratio: 0.500
|
||||
low_pri_pool_ratio: 0.000
|
||||
persistent_cache: (nil)
|
||||
block_size: 4096
|
||||
block_size_deviation: 10
|
||||
block_restart_interval: 16
|
||||
index_block_restart_interval: 1
|
||||
metadata_block_size: 4096
|
||||
partition_filters: 0
|
||||
use_delta_encoding: 1
|
||||
filter_policy: nullptr
|
||||
user_defined_index_factory: nullptr
|
||||
fail_if_no_udi_on_open: 0
|
||||
whole_key_filtering: 1
|
||||
verify_compression: 0
|
||||
read_amp_bytes_per_bit: 0
|
||||
format_version: 6
|
||||
enable_index_compression: 1
|
||||
block_align: 0
|
||||
max_auto_readahead_size: 262144
|
||||
prepopulate_block_cache: 0
|
||||
initial_auto_readahead_size: 8192
|
||||
num_file_reads_for_auto_readahead: 2
|
||||
2026/01/27-22:29:39.168867 1 Options.write_buffer_size: 67108864
|
||||
2026/01/27-22:29:39.168873 1 Options.max_write_buffer_number: 4
|
||||
2026/01/27-22:29:39.168874 1 Options.compression[0]: NoCompression
|
||||
2026/01/27-22:29:39.168875 1 Options.compression[1]: NoCompression
|
||||
2026/01/27-22:29:39.168875 1 Options.compression[2]: Snappy
|
||||
2026/01/27-22:29:39.168876 1 Options.compression[3]: Snappy
|
||||
2026/01/27-22:29:39.168876 1 Options.compression[4]: Snappy
|
||||
2026/01/27-22:29:39.168877 1 Options.compression[5]: Snappy
|
||||
2026/01/27-22:29:39.168877 1 Options.compression[6]: Snappy
|
||||
2026/01/27-22:29:39.168878 1 Options.bottommost_compression: Disabled
|
||||
2026/01/27-22:29:39.168878 1 Options.prefix_extractor: nullptr
|
||||
2026/01/27-22:29:39.168879 1 Options.memtable_insert_with_hint_prefix_extractor: nullptr
|
||||
2026/01/27-22:29:39.168879 1 Options.num_levels: 7
|
||||
2026/01/27-22:29:39.168880 1 Options.min_write_buffer_number_to_merge: 1
|
||||
2026/01/27-22:29:39.168880 1 Options.max_write_buffer_size_to_maintain: 0
|
||||
2026/01/27-22:29:39.168881 1 Options.bottommost_compression_opts.window_bits: -14
|
||||
2026/01/27-22:29:39.168881 1 Options.bottommost_compression_opts.level: 32767
|
||||
2026/01/27-22:29:39.168882 1 Options.bottommost_compression_opts.strategy: 0
|
||||
2026/01/27-22:29:39.168882 1 Options.bottommost_compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-22:29:39.168883 1 Options.bottommost_compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-22:29:39.168883 1 Options.bottommost_compression_opts.parallel_threads: 1
|
||||
2026/01/27-22:29:39.168884 1 Options.bottommost_compression_opts.enabled: false
|
||||
2026/01/27-22:29:39.168884 1 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-22:29:39.168885 1 Options.bottommost_compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-22:29:39.168885 1 Options.compression_opts.window_bits: -14
|
||||
2026/01/27-22:29:39.168886 1 Options.compression_opts.level: 32767
|
||||
2026/01/27-22:29:39.168886 1 Options.compression_opts.strategy: 0
|
||||
2026/01/27-22:29:39.168887 1 Options.compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-22:29:39.168887 1 Options.compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-22:29:39.168887 1 Options.compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-22:29:39.168888 1 Options.compression_opts.parallel_threads: 1
|
||||
2026/01/27-22:29:39.168888 1 Options.compression_opts.enabled: false
|
||||
2026/01/27-22:29:39.168889 1 Options.compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-22:29:39.168889 1 Options.level0_file_num_compaction_trigger: 4
|
||||
2026/01/27-22:29:39.168890 1 Options.level0_slowdown_writes_trigger: 20
|
||||
2026/01/27-22:29:39.168890 1 Options.level0_stop_writes_trigger: 40
|
||||
2026/01/27-22:29:39.168891 1 Options.target_file_size_base: 134217728
|
||||
2026/01/27-22:29:39.168891 1 Options.target_file_size_multiplier: 1
|
||||
2026/01/27-22:29:39.168892 1 Options.max_bytes_for_level_base: 268435456
|
||||
2026/01/27-22:29:39.168892 1 Options.level_compaction_dynamic_level_bytes: 1
|
||||
2026/01/27-22:29:39.168893 1 Options.max_bytes_for_level_multiplier: 10.000000
|
||||
2026/01/27-22:29:39.168894 1 Options.max_bytes_for_level_multiplier_addtl[0]: 1
|
||||
2026/01/27-22:29:39.168895 1 Options.max_bytes_for_level_multiplier_addtl[1]: 1
|
||||
2026/01/27-22:29:39.168895 1 Options.max_bytes_for_level_multiplier_addtl[2]: 1
|
||||
2026/01/27-22:29:39.168895 1 Options.max_bytes_for_level_multiplier_addtl[3]: 1
|
||||
2026/01/27-22:29:39.168896 1 Options.max_bytes_for_level_multiplier_addtl[4]: 1
|
||||
2026/01/27-22:29:39.168896 1 Options.max_bytes_for_level_multiplier_addtl[5]: 1
|
||||
2026/01/27-22:29:39.168897 1 Options.max_bytes_for_level_multiplier_addtl[6]: 1
|
||||
2026/01/27-22:29:39.168897 1 Options.max_sequential_skip_in_iterations: 8
|
||||
2026/01/27-22:29:39.168898 1 Options.memtable_op_scan_flush_trigger: 0
|
||||
2026/01/27-22:29:39.168898 1 Options.memtable_avg_op_scan_flush_trigger: 0
|
||||
2026/01/27-22:29:39.168899 1 Options.max_compaction_bytes: 3355443200
|
||||
2026/01/27-22:29:39.168899 1 Options.arena_block_size: 1048576
|
||||
2026/01/27-22:29:39.168900 1 Options.soft_pending_compaction_bytes_limit: 68719476736
|
||||
2026/01/27-22:29:39.168900 1 Options.hard_pending_compaction_bytes_limit: 274877906944
|
||||
2026/01/27-22:29:39.168901 1 Options.disable_auto_compactions: 0
|
||||
2026/01/27-22:29:39.168901 1 Options.compaction_style: kCompactionStyleLevel
|
||||
2026/01/27-22:29:39.168902 1 Options.compaction_pri: kMinOverlappingRatio
|
||||
2026/01/27-22:29:39.168902 1 Options.compaction_options_universal.size_ratio: 1
|
||||
2026/01/27-22:29:39.168903 1 Options.compaction_options_universal.min_merge_width: 2
|
||||
2026/01/27-22:29:39.168903 1 Options.compaction_options_universal.max_merge_width: 4294967295
|
||||
2026/01/27-22:29:39.168904 1 Options.compaction_options_universal.max_size_amplification_percent: 200
|
||||
2026/01/27-22:29:39.168904 1 Options.compaction_options_universal.compression_size_percent: -1
|
||||
2026/01/27-22:29:39.168905 1 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize
|
||||
2026/01/27-22:29:39.168905 1 Options.compaction_options_universal.max_read_amp: -1
|
||||
2026/01/27-22:29:39.168906 1 Options.compaction_options_universal.reduce_file_locking: 0
|
||||
2026/01/27-22:29:39.168906 1 Options.compaction_options_fifo.max_table_files_size: 1073741824
|
||||
2026/01/27-22:29:39.168907 1 Options.compaction_options_fifo.allow_compaction: 0
|
||||
2026/01/27-22:29:39.168908 1 Options.table_properties_collectors:
|
||||
2026/01/27-22:29:39.168908 1 Options.inplace_update_support: 0
|
||||
2026/01/27-22:29:39.168909 1 Options.inplace_update_num_locks: 10000
|
||||
2026/01/27-22:29:39.168910 1 Options.memtable_prefix_bloom_size_ratio: 0.000000
|
||||
2026/01/27-22:29:39.168911 1 Options.memtable_whole_key_filtering: 0
|
||||
2026/01/27-22:29:39.168911 1 Options.memtable_huge_page_size: 0
|
||||
2026/01/27-22:29:39.168911 1 Options.bloom_locality: 0
|
||||
2026/01/27-22:29:39.168912 1 Options.max_successive_merges: 0
|
||||
2026/01/27-22:29:39.168912 1 Options.strict_max_successive_merges: 0
|
||||
2026/01/27-22:29:39.168913 1 Options.optimize_filters_for_hits: 0
|
||||
2026/01/27-22:29:39.168913 1 Options.paranoid_file_checks: 0
|
||||
2026/01/27-22:29:39.168914 1 Options.force_consistency_checks: 1
|
||||
2026/01/27-22:29:39.168914 1 Options.report_bg_io_stats: 0
|
||||
2026/01/27-22:29:39.168915 1 Options.disallow_memtable_writes: 0
|
||||
2026/01/27-22:29:39.168915 1 Options.ttl: 2592000
|
||||
2026/01/27-22:29:39.168916 1 Options.periodic_compaction_seconds: 0
|
||||
2026/01/27-22:29:39.168916 1 Options.default_temperature: kUnknown
|
||||
2026/01/27-22:29:39.168917 1 Options.preclude_last_level_data_seconds: 0
|
||||
2026/01/27-22:29:39.168917 1 Options.preserve_internal_time_seconds: 0
|
||||
2026/01/27-22:29:39.168918 1 Options.enable_blob_files: false
|
||||
2026/01/27-22:29:39.168918 1 Options.min_blob_size: 0
|
||||
2026/01/27-22:29:39.168919 1 Options.blob_file_size: 268435456
|
||||
2026/01/27-22:29:39.168919 1 Options.blob_compression_type: NoCompression
|
||||
2026/01/27-22:29:39.168920 1 Options.enable_blob_garbage_collection: false
|
||||
2026/01/27-22:29:39.168920 1 Options.blob_garbage_collection_age_cutoff: 0.250000
|
||||
2026/01/27-22:29:39.168921 1 Options.blob_garbage_collection_force_threshold: 1.000000
|
||||
2026/01/27-22:29:39.168921 1 Options.blob_compaction_readahead_size: 0
|
||||
2026/01/27-22:29:39.168922 1 Options.blob_file_starting_level: 0
|
||||
2026/01/27-22:29:39.168922 1 Options.experimental_mempurge_threshold: 0.000000
|
||||
2026/01/27-22:29:39.168923 1 Options.memtable_max_range_deletions: 0
|
||||
2026/01/27-22:29:39.168923 1 Options.cf_allow_ingest_behind: false
|
||||
2026/01/27-22:29:39.168977 1 [WARN] [db/db_impl/db_impl_open.cc:2688] DB::Open() failed: Invalid argument: Column families not opened: index, search, stream, propagate, pubsub, zset_score, metadata
|
||||
2026/01/27-22:29:39.169006 1 [db/db_impl/db_impl.cc:467] Shutdown: canceling all background work
|
||||
2026/01/27-22:29:39.169031 1 [db/db_impl/db_impl.cc:681] Shutdown complete
|
||||
1468
local-dev/data/kvrocks/db/LOG.old.1769556178501276
Normal file
1468
local-dev/data/kvrocks/db/LOG.old.1769556178501276
Normal file
File diff suppressed because it is too large
Load Diff
391
local-dev/data/kvrocks/db/LOG.old.1769556178503672
Normal file
391
local-dev/data/kvrocks/db/LOG.old.1769556178503672
Normal file
@@ -0,0 +1,391 @@
|
||||
2026/01/27-23:22:58.502039 1 RocksDB version: 10.6.2
|
||||
2026/01/27-23:22:58.502469 1 Git sha 0
|
||||
2026/01/27-23:22:58.502470 1 Compile date 2025-11-08 14:59:16
|
||||
2026/01/27-23:22:58.502472 1 DB SUMMARY
|
||||
2026/01/27-23:22:58.502473 1 Host name (Env): aae070ac4a88
|
||||
2026/01/27-23:22:58.502474 1 DB Session ID: SI1XGUJ3JZXKO7QMB6X0
|
||||
2026/01/27-23:22:58.502586 1 CURRENT file: CURRENT
|
||||
2026/01/27-23:22:58.502587 1 IDENTITY file: IDENTITY
|
||||
2026/01/27-23:22:58.502589 1 MANIFEST file: MANIFEST-000029 size: 1104 Bytes
|
||||
2026/01/27-23:22:58.502590 1 SST files in /data/db dir, Total Num: 2, files: 000014.sst 000023.sst
|
||||
2026/01/27-23:22:58.502591 1 Write Ahead Log file in /data/db: 000028.log size: 86377 ;
|
||||
2026/01/27-23:22:58.502592 1 Options.error_if_exists: 0
|
||||
2026/01/27-23:22:58.502593 1 Options.create_if_missing: 1
|
||||
2026/01/27-23:22:58.502593 1 Options.paranoid_checks: 1
|
||||
2026/01/27-23:22:58.502594 1 Options.flush_verify_memtable_count: 1
|
||||
2026/01/27-23:22:58.502595 1 Options.compaction_verify_record_count: 1
|
||||
2026/01/27-23:22:58.502595 1 Options.track_and_verify_wals_in_manifest: 0
|
||||
2026/01/27-23:22:58.502596 1 Options.track_and_verify_wals: 0
|
||||
2026/01/27-23:22:58.502596 1 Options.verify_sst_unique_id_in_manifest: 1
|
||||
2026/01/27-23:22:58.502597 1 Options.env: 0xffff9341d0c0
|
||||
2026/01/27-23:22:58.502598 1 Options.fs: PosixFileSystem
|
||||
2026/01/27-23:22:58.502599 1 Options.info_log: 0xffff9350f600
|
||||
2026/01/27-23:22:58.502599 1 Options.max_file_opening_threads: 16
|
||||
2026/01/27-23:22:58.502600 1 Options.statistics: 0xffff934135b0
|
||||
2026/01/27-23:22:58.502600 1 Options.statistics stats level: 3
|
||||
2026/01/27-23:22:58.502601 1 Options.use_fsync: 0
|
||||
2026/01/27-23:22:58.502602 1 Options.max_log_file_size: 268435456
|
||||
2026/01/27-23:22:58.502602 1 Options.max_manifest_file_size: 67108864
|
||||
2026/01/27-23:22:58.502603 1 Options.log_file_time_to_roll: 0
|
||||
2026/01/27-23:22:58.502603 1 Options.keep_log_file_num: 12
|
||||
2026/01/27-23:22:58.502604 1 Options.recycle_log_file_num: 0
|
||||
2026/01/27-23:22:58.502605 1 Options.allow_fallocate: 1
|
||||
2026/01/27-23:22:58.502605 1 Options.allow_mmap_reads: 0
|
||||
2026/01/27-23:22:58.502606 1 Options.allow_mmap_writes: 0
|
||||
2026/01/27-23:22:58.502606 1 Options.use_direct_reads: 0
|
||||
2026/01/27-23:22:58.502607 1 Options.use_direct_io_for_flush_and_compaction: 0
|
||||
2026/01/27-23:22:58.502607 1 Options.create_missing_column_families: 1
|
||||
2026/01/27-23:22:58.502608 1 Options.db_log_dir:
|
||||
2026/01/27-23:22:58.502609 1 Options.wal_dir:
|
||||
2026/01/27-23:22:58.502609 1 Options.table_cache_numshardbits: 6
|
||||
2026/01/27-23:22:58.502610 1 Options.WAL_ttl_seconds: 10800
|
||||
2026/01/27-23:22:58.502611 1 Options.WAL_size_limit_MB: 16384
|
||||
2026/01/27-23:22:58.502611 1 Options.max_write_batch_group_size_bytes: 1048576
|
||||
2026/01/27-23:22:58.502612 1 Options.manifest_preallocation_size: 4194304
|
||||
2026/01/27-23:22:58.502612 1 Options.is_fd_close_on_exec: 1
|
||||
2026/01/27-23:22:58.502613 1 Options.advise_random_on_open: 1
|
||||
2026/01/27-23:22:58.502614 1 Options.db_write_buffer_size: 0
|
||||
2026/01/27-23:22:58.502614 1 Options.write_buffer_manager: 0xffff9346bfc0
|
||||
2026/01/27-23:22:58.502615 1 Options.use_adaptive_mutex: 0
|
||||
2026/01/27-23:22:58.502615 1 Options.rate_limiter: 0xffff93436000
|
||||
2026/01/27-23:22:58.502616 1 Options.sst_file_manager.rate_bytes_per_sec: 0
|
||||
2026/01/27-23:22:58.502617 1 Options.wal_recovery_mode: 2
|
||||
2026/01/27-23:22:58.502617 1 Options.enable_thread_tracking: 0
|
||||
2026/01/27-23:22:58.502618 1 Options.enable_pipelined_write: 0
|
||||
2026/01/27-23:22:58.502618 1 Options.unordered_write: 0
|
||||
2026/01/27-23:22:58.502619 1 Options.allow_concurrent_memtable_write: 1
|
||||
2026/01/27-23:22:58.502619 1 Options.enable_write_thread_adaptive_yield: 1
|
||||
2026/01/27-23:22:58.502620 1 Options.write_thread_max_yield_usec: 100
|
||||
2026/01/27-23:22:58.502621 1 Options.write_thread_slow_yield_usec: 3
|
||||
2026/01/27-23:22:58.502621 1 Options.row_cache: None
|
||||
2026/01/27-23:22:58.502622 1 Options.wal_filter: None
|
||||
2026/01/27-23:22:58.502622 1 Options.avoid_flush_during_recovery: 0
|
||||
2026/01/27-23:22:58.502623 1 Options.allow_ingest_behind: 0
|
||||
2026/01/27-23:22:58.502623 1 Options.two_write_queues: 0
|
||||
2026/01/27-23:22:58.502624 1 Options.manual_wal_flush: 0
|
||||
2026/01/27-23:22:58.502625 1 Options.wal_compression: 0
|
||||
2026/01/27-23:22:58.502625 1 Options.background_close_inactive_wals: 0
|
||||
2026/01/27-23:22:58.502626 1 Options.atomic_flush: 0
|
||||
2026/01/27-23:22:58.502626 1 Options.avoid_unnecessary_blocking_io: 1
|
||||
2026/01/27-23:22:58.502627 1 Options.prefix_seek_opt_in_only: 0
|
||||
2026/01/27-23:22:58.502628 1 Options.persist_stats_to_disk: 0
|
||||
2026/01/27-23:22:58.502628 1 Options.write_dbid_to_manifest: 1
|
||||
2026/01/27-23:22:58.502629 1 Options.write_identity_file: 1
|
||||
2026/01/27-23:22:58.502629 1 Options.log_readahead_size: 0
|
||||
2026/01/27-23:22:58.502630 1 Options.file_checksum_gen_factory: Unknown
|
||||
2026/01/27-23:22:58.502630 1 Options.best_efforts_recovery: 0
|
||||
2026/01/27-23:22:58.502631 1 Options.max_bgerror_resume_count: 2147483647
|
||||
2026/01/27-23:22:58.502632 1 Options.bgerror_resume_retry_interval: 1000000
|
||||
2026/01/27-23:22:58.502632 1 Options.allow_data_in_errors: 0
|
||||
2026/01/27-23:22:58.502633 1 Options.db_host_id: __hostname__
|
||||
2026/01/27-23:22:58.502633 1 Options.enforce_single_del_contracts: true
|
||||
2026/01/27-23:22:58.502634 1 Options.metadata_write_temperature: kUnknown
|
||||
2026/01/27-23:22:58.502635 1 Options.wal_write_temperature: kUnknown
|
||||
2026/01/27-23:22:58.502635 1 Options.max_background_jobs: 4
|
||||
2026/01/27-23:22:58.502636 1 Options.max_background_compactions: -1
|
||||
2026/01/27-23:22:58.502637 1 Options.max_subcompactions: 2
|
||||
2026/01/27-23:22:58.502637 1 Options.avoid_flush_during_shutdown: 0
|
||||
2026/01/27-23:22:58.502638 1 Options.writable_file_max_buffer_size: 1048576
|
||||
2026/01/27-23:22:58.502638 1 Options.delayed_write_rate : 536870912000
|
||||
2026/01/27-23:22:58.502640 1 Options.max_total_wal_size: 536870912
|
||||
2026/01/27-23:22:58.502640 1 Options.delete_obsolete_files_period_micros: 21600000000
|
||||
2026/01/27-23:22:58.502641 1 Options.stats_dump_period_sec: 0
|
||||
2026/01/27-23:22:58.502642 1 Options.stats_persist_period_sec: 600
|
||||
2026/01/27-23:22:58.502643 1 Options.stats_history_buffer_size: 1048576
|
||||
2026/01/27-23:22:58.502644 1 Options.max_open_files: 8096
|
||||
2026/01/27-23:22:58.502644 1 Options.bytes_per_sync: 1048576
|
||||
2026/01/27-23:22:58.502645 1 Options.wal_bytes_per_sync: 0
|
||||
2026/01/27-23:22:58.502645 1 Options.strict_bytes_per_sync: 0
|
||||
2026/01/27-23:22:58.502646 1 Options.compaction_readahead_size: 2097152
|
||||
2026/01/27-23:22:58.502646 1 Options.max_background_flushes: -1
|
||||
2026/01/27-23:22:58.502647 1 Options.daily_offpeak_time_utc:
|
||||
2026/01/27-23:22:58.502647 1 Compression algorithms supported:
|
||||
2026/01/27-23:22:58.502648 1 kCustomCompressionFE supported: 0
|
||||
2026/01/27-23:22:58.502649 1 kCustomCompressionFC supported: 0
|
||||
2026/01/27-23:22:58.502649 1 kCustomCompressionF8 supported: 0
|
||||
2026/01/27-23:22:58.502650 1 kCustomCompressionF7 supported: 0
|
||||
2026/01/27-23:22:58.502651 1 kCustomCompressionB2 supported: 0
|
||||
2026/01/27-23:22:58.502651 1 kLZ4Compression supported: 1
|
||||
2026/01/27-23:22:58.502652 1 kCustomCompression88 supported: 0
|
||||
2026/01/27-23:22:58.502653 1 kCustomCompressionD8 supported: 0
|
||||
2026/01/27-23:22:58.502653 1 kCustomCompression9F supported: 0
|
||||
2026/01/27-23:22:58.502654 1 kCustomCompressionD6 supported: 0
|
||||
2026/01/27-23:22:58.502654 1 kCustomCompressionA9 supported: 0
|
||||
2026/01/27-23:22:58.502655 1 kCustomCompressionEC supported: 0
|
||||
2026/01/27-23:22:58.502655 1 kCustomCompressionA3 supported: 0
|
||||
2026/01/27-23:22:58.502656 1 kCustomCompressionCB supported: 0
|
||||
2026/01/27-23:22:58.502657 1 kCustomCompression90 supported: 0
|
||||
2026/01/27-23:22:58.502657 1 kCustomCompressionA0 supported: 0
|
||||
2026/01/27-23:22:58.502658 1 kCustomCompressionC6 supported: 0
|
||||
2026/01/27-23:22:58.502659 1 kCustomCompression9D supported: 0
|
||||
2026/01/27-23:22:58.502659 1 kCustomCompression8B supported: 0
|
||||
2026/01/27-23:22:58.502660 1 kCustomCompressionA8 supported: 0
|
||||
2026/01/27-23:22:58.502660 1 kCustomCompression8D supported: 0
|
||||
2026/01/27-23:22:58.502661 1 kCustomCompression97 supported: 0
|
||||
2026/01/27-23:22:58.502661 1 kCustomCompression98 supported: 0
|
||||
2026/01/27-23:22:58.502662 1 kCustomCompressionAC supported: 0
|
||||
2026/01/27-23:22:58.502662 1 kCustomCompressionE9 supported: 0
|
||||
2026/01/27-23:22:58.502663 1 kCustomCompression96 supported: 0
|
||||
2026/01/27-23:22:58.502663 1 kCustomCompressionB1 supported: 0
|
||||
2026/01/27-23:22:58.502664 1 kCustomCompression95 supported: 0
|
||||
2026/01/27-23:22:58.502664 1 kCustomCompression84 supported: 0
|
||||
2026/01/27-23:22:58.502665 1 kCustomCompression91 supported: 0
|
||||
2026/01/27-23:22:58.502665 1 kCustomCompressionAB supported: 0
|
||||
2026/01/27-23:22:58.502666 1 kCustomCompressionB3 supported: 0
|
||||
2026/01/27-23:22:58.502666 1 kCustomCompression81 supported: 0
|
||||
2026/01/27-23:22:58.502667 1 kCustomCompressionDC supported: 0
|
||||
2026/01/27-23:22:58.502667 1 kBZip2Compression supported: 0
|
||||
2026/01/27-23:22:58.502668 1 kCustomCompressionBB supported: 0
|
||||
2026/01/27-23:22:58.502668 1 kCustomCompression9C supported: 0
|
||||
2026/01/27-23:22:58.502668 1 kCustomCompressionC9 supported: 0
|
||||
2026/01/27-23:22:58.502669 1 kCustomCompressionCC supported: 0
|
||||
2026/01/27-23:22:58.502669 1 kCustomCompression92 supported: 0
|
||||
2026/01/27-23:22:58.502670 1 kCustomCompressionB9 supported: 0
|
||||
2026/01/27-23:22:58.502670 1 kCustomCompression8F supported: 0
|
||||
2026/01/27-23:22:58.502671 1 kCustomCompression8A supported: 0
|
||||
2026/01/27-23:22:58.502671 1 kCustomCompression9B supported: 0
|
||||
2026/01/27-23:22:58.502671 1 kZSTD supported: 1
|
||||
2026/01/27-23:22:58.502672 1 kCustomCompressionAA supported: 0
|
||||
2026/01/27-23:22:58.502672 1 kCustomCompressionA2 supported: 0
|
||||
2026/01/27-23:22:58.502673 1 kZlibCompression supported: 1
|
||||
2026/01/27-23:22:58.502673 1 kXpressCompression supported: 0
|
||||
2026/01/27-23:22:58.502674 1 kCustomCompressionFD supported: 0
|
||||
2026/01/27-23:22:58.502674 1 kCustomCompressionE2 supported: 0
|
||||
2026/01/27-23:22:58.502675 1 kLZ4HCCompression supported: 1
|
||||
2026/01/27-23:22:58.502675 1 kCustomCompressionA6 supported: 0
|
||||
2026/01/27-23:22:58.502676 1 kCustomCompression85 supported: 0
|
||||
2026/01/27-23:22:58.502676 1 kCustomCompressionA4 supported: 0
|
||||
2026/01/27-23:22:58.502676 1 kCustomCompression86 supported: 0
|
||||
2026/01/27-23:22:58.502677 1 kCustomCompression83 supported: 0
|
||||
2026/01/27-23:22:58.502677 1 kCustomCompression87 supported: 0
|
||||
2026/01/27-23:22:58.502678 1 kCustomCompression89 supported: 0
|
||||
2026/01/27-23:22:58.502678 1 kCustomCompression8C supported: 0
|
||||
2026/01/27-23:22:58.502679 1 kCustomCompressionDB supported: 0
|
||||
2026/01/27-23:22:58.502679 1 kCustomCompressionF3 supported: 0
|
||||
2026/01/27-23:22:58.502679 1 kCustomCompressionE6 supported: 0
|
||||
2026/01/27-23:22:58.502680 1 kCustomCompression8E supported: 0
|
||||
2026/01/27-23:22:58.502680 1 kCustomCompressionDA supported: 0
|
||||
2026/01/27-23:22:58.502681 1 kCustomCompression93 supported: 0
|
||||
2026/01/27-23:22:58.502681 1 kCustomCompression94 supported: 0
|
||||
2026/01/27-23:22:58.502685 1 kCustomCompression9E supported: 0
|
||||
2026/01/27-23:22:58.502685 1 kCustomCompressionB4 supported: 0
|
||||
2026/01/27-23:22:58.502686 1 kCustomCompressionFB supported: 0
|
||||
2026/01/27-23:22:58.502686 1 kCustomCompressionB5 supported: 0
|
||||
2026/01/27-23:22:58.502686 1 kCustomCompressionD5 supported: 0
|
||||
2026/01/27-23:22:58.502687 1 kCustomCompressionB8 supported: 0
|
||||
2026/01/27-23:22:58.502687 1 kCustomCompressionD1 supported: 0
|
||||
2026/01/27-23:22:58.502688 1 kCustomCompressionBA supported: 0
|
||||
2026/01/27-23:22:58.502688 1 kCustomCompressionBC supported: 0
|
||||
2026/01/27-23:22:58.502689 1 kCustomCompressionCE supported: 0
|
||||
2026/01/27-23:22:58.502689 1 kCustomCompressionBD supported: 0
|
||||
2026/01/27-23:22:58.502689 1 kCustomCompressionC4 supported: 0
|
||||
2026/01/27-23:22:58.502690 1 kCustomCompression9A supported: 0
|
||||
2026/01/27-23:22:58.502690 1 kCustomCompression99 supported: 0
|
||||
2026/01/27-23:22:58.502691 1 kCustomCompressionBE supported: 0
|
||||
2026/01/27-23:22:58.502691 1 kCustomCompressionE5 supported: 0
|
||||
2026/01/27-23:22:58.502692 1 kCustomCompressionD9 supported: 0
|
||||
2026/01/27-23:22:58.502692 1 kCustomCompressionC1 supported: 0
|
||||
2026/01/27-23:22:58.502693 1 kCustomCompressionC5 supported: 0
|
||||
2026/01/27-23:22:58.502694 1 kCustomCompressionC2 supported: 0
|
||||
2026/01/27-23:22:58.502694 1 kCustomCompressionA5 supported: 0
|
||||
2026/01/27-23:22:58.502695 1 kCustomCompressionC7 supported: 0
|
||||
2026/01/27-23:22:58.502695 1 kCustomCompressionBF supported: 0
|
||||
2026/01/27-23:22:58.502696 1 kCustomCompressionE8 supported: 0
|
||||
2026/01/27-23:22:58.502696 1 kCustomCompressionC8 supported: 0
|
||||
2026/01/27-23:22:58.502697 1 kCustomCompressionAF supported: 0
|
||||
2026/01/27-23:22:58.502698 1 kCustomCompressionCA supported: 0
|
||||
2026/01/27-23:22:58.502698 1 kCustomCompressionCD supported: 0
|
||||
2026/01/27-23:22:58.502699 1 kCustomCompressionC0 supported: 0
|
||||
2026/01/27-23:22:58.502699 1 kCustomCompressionCF supported: 0
|
||||
2026/01/27-23:22:58.502700 1 kCustomCompressionF9 supported: 0
|
||||
2026/01/27-23:22:58.502700 1 kCustomCompressionD0 supported: 0
|
||||
2026/01/27-23:22:58.502701 1 kCustomCompressionD2 supported: 0
|
||||
2026/01/27-23:22:58.502701 1 kCustomCompressionAD supported: 0
|
||||
2026/01/27-23:22:58.502702 1 kCustomCompressionD3 supported: 0
|
||||
2026/01/27-23:22:58.502703 1 kCustomCompressionD4 supported: 0
|
||||
2026/01/27-23:22:58.502703 1 kCustomCompressionD7 supported: 0
|
||||
2026/01/27-23:22:58.502704 1 kCustomCompression82 supported: 0
|
||||
2026/01/27-23:22:58.502704 1 kCustomCompressionDD supported: 0
|
||||
2026/01/27-23:22:58.502705 1 kCustomCompressionC3 supported: 0
|
||||
2026/01/27-23:22:58.502705 1 kCustomCompressionEE supported: 0
|
||||
2026/01/27-23:22:58.502706 1 kCustomCompressionDE supported: 0
|
||||
2026/01/27-23:22:58.502706 1 kCustomCompressionDF supported: 0
|
||||
2026/01/27-23:22:58.502707 1 kCustomCompressionA7 supported: 0
|
||||
2026/01/27-23:22:58.502707 1 kCustomCompressionE0 supported: 0
|
||||
2026/01/27-23:22:58.502708 1 kCustomCompressionF1 supported: 0
|
||||
2026/01/27-23:22:58.502708 1 kCustomCompressionE1 supported: 0
|
||||
2026/01/27-23:22:58.502709 1 kCustomCompressionF5 supported: 0
|
||||
2026/01/27-23:22:58.502709 1 kCustomCompression80 supported: 0
|
||||
2026/01/27-23:22:58.502710 1 kCustomCompressionE3 supported: 0
|
||||
2026/01/27-23:22:58.502711 1 kCustomCompressionE4 supported: 0
|
||||
2026/01/27-23:22:58.502711 1 kCustomCompressionB0 supported: 0
|
||||
2026/01/27-23:22:58.502712 1 kCustomCompressionEA supported: 0
|
||||
2026/01/27-23:22:58.502712 1 kCustomCompressionFA supported: 0
|
||||
2026/01/27-23:22:58.502713 1 kCustomCompressionE7 supported: 0
|
||||
2026/01/27-23:22:58.502713 1 kCustomCompressionAE supported: 0
|
||||
2026/01/27-23:22:58.502714 1 kCustomCompressionEB supported: 0
|
||||
2026/01/27-23:22:58.502714 1 kCustomCompressionED supported: 0
|
||||
2026/01/27-23:22:58.502715 1 kCustomCompressionB6 supported: 0
|
||||
2026/01/27-23:22:58.502715 1 kCustomCompressionEF supported: 0
|
||||
2026/01/27-23:22:58.502716 1 kCustomCompressionF0 supported: 0
|
||||
2026/01/27-23:22:58.502716 1 kCustomCompressionB7 supported: 0
|
||||
2026/01/27-23:22:58.502717 1 kCustomCompressionF2 supported: 0
|
||||
2026/01/27-23:22:58.502717 1 kCustomCompressionA1 supported: 0
|
||||
2026/01/27-23:22:58.502718 1 kCustomCompressionF4 supported: 0
|
||||
2026/01/27-23:22:58.502719 1 kSnappyCompression supported: 1
|
||||
2026/01/27-23:22:58.502719 1 kCustomCompressionF6 supported: 0
|
||||
2026/01/27-23:22:58.502720 1 Fast CRC32 supported: Supported on Arm64
|
||||
2026/01/27-23:22:58.502721 1 DMutex implementation: pthread_mutex_t
|
||||
2026/01/27-23:22:58.502722 1 Jemalloc supported: 1
|
||||
2026/01/27-23:22:58.503054 1 [db/version_set.cc:6190] Recovering from manifest file: /data/db/MANIFEST-000029
|
||||
2026/01/27-23:22:58.503246 1 [db/column_family.cc:693] --------------- Options for column family [default]:
|
||||
2026/01/27-23:22:58.503249 1 Options.comparator: leveldb.BytewiseComparator
|
||||
2026/01/27-23:22:58.503250 1 Options.merge_operator: None
|
||||
2026/01/27-23:22:58.503250 1 Options.compaction_filter: None
|
||||
2026/01/27-23:22:58.503251 1 Options.compaction_filter_factory: None
|
||||
2026/01/27-23:22:58.503251 1 Options.sst_partitioner_factory: None
|
||||
2026/01/27-23:22:58.503252 1 Options.memtable_factory: SkipListFactory
|
||||
2026/01/27-23:22:58.503253 1 Options.table_factory: BlockBasedTable
|
||||
2026/01/27-23:22:58.503273 1 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0xffff934d15a0)
|
||||
cache_index_and_filter_blocks: 0
|
||||
cache_index_and_filter_blocks_with_high_priority: 1
|
||||
pin_l0_filter_and_index_blocks_in_cache: 0
|
||||
pin_top_level_index_and_filter: 1
|
||||
index_type: 0
|
||||
data_block_index_type: 0
|
||||
index_shortening: 1
|
||||
data_block_hash_table_util_ratio: 0.750000
|
||||
checksum: 4
|
||||
no_block_cache: 0
|
||||
block_cache: 0xffff9346bd90
|
||||
block_cache_name: LRUCache
|
||||
block_cache_options:
|
||||
capacity : 33554432
|
||||
num_shard_bits : 6
|
||||
strict_capacity_limit : 0
|
||||
memory_allocator : None
|
||||
high_pri_pool_ratio: 0.500
|
||||
low_pri_pool_ratio: 0.000
|
||||
persistent_cache: (nil)
|
||||
block_size: 4096
|
||||
block_size_deviation: 10
|
||||
block_restart_interval: 16
|
||||
index_block_restart_interval: 1
|
||||
metadata_block_size: 4096
|
||||
partition_filters: 0
|
||||
use_delta_encoding: 1
|
||||
filter_policy: nullptr
|
||||
user_defined_index_factory: nullptr
|
||||
fail_if_no_udi_on_open: 0
|
||||
whole_key_filtering: 1
|
||||
verify_compression: 0
|
||||
read_amp_bytes_per_bit: 0
|
||||
format_version: 6
|
||||
enable_index_compression: 1
|
||||
block_align: 0
|
||||
max_auto_readahead_size: 262144
|
||||
prepopulate_block_cache: 0
|
||||
initial_auto_readahead_size: 8192
|
||||
num_file_reads_for_auto_readahead: 2
|
||||
2026/01/27-23:22:58.503278 1 Options.write_buffer_size: 67108864
|
||||
2026/01/27-23:22:58.503279 1 Options.max_write_buffer_number: 4
|
||||
2026/01/27-23:22:58.503280 1 Options.compression[0]: NoCompression
|
||||
2026/01/27-23:22:58.503281 1 Options.compression[1]: NoCompression
|
||||
2026/01/27-23:22:58.503281 1 Options.compression[2]: Snappy
|
||||
2026/01/27-23:22:58.503282 1 Options.compression[3]: Snappy
|
||||
2026/01/27-23:22:58.503282 1 Options.compression[4]: Snappy
|
||||
2026/01/27-23:22:58.503283 1 Options.compression[5]: Snappy
|
||||
2026/01/27-23:22:58.503284 1 Options.compression[6]: Snappy
|
||||
2026/01/27-23:22:58.503284 1 Options.bottommost_compression: Disabled
|
||||
2026/01/27-23:22:58.503285 1 Options.prefix_extractor: nullptr
|
||||
2026/01/27-23:22:58.503286 1 Options.memtable_insert_with_hint_prefix_extractor: nullptr
|
||||
2026/01/27-23:22:58.503286 1 Options.num_levels: 7
|
||||
2026/01/27-23:22:58.503287 1 Options.min_write_buffer_number_to_merge: 1
|
||||
2026/01/27-23:22:58.503287 1 Options.max_write_buffer_size_to_maintain: 0
|
||||
2026/01/27-23:22:58.503288 1 Options.bottommost_compression_opts.window_bits: -14
|
||||
2026/01/27-23:22:58.503289 1 Options.bottommost_compression_opts.level: 32767
|
||||
2026/01/27-23:22:58.503290 1 Options.bottommost_compression_opts.strategy: 0
|
||||
2026/01/27-23:22:58.503290 1 Options.bottommost_compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-23:22:58.503291 1 Options.bottommost_compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-23:22:58.503292 1 Options.bottommost_compression_opts.parallel_threads: 1
|
||||
2026/01/27-23:22:58.503292 1 Options.bottommost_compression_opts.enabled: false
|
||||
2026/01/27-23:22:58.503293 1 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-23:22:58.503294 1 Options.bottommost_compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-23:22:58.503294 1 Options.compression_opts.window_bits: -14
|
||||
2026/01/27-23:22:58.503295 1 Options.compression_opts.level: 32767
|
||||
2026/01/27-23:22:58.503296 1 Options.compression_opts.strategy: 0
|
||||
2026/01/27-23:22:58.503296 1 Options.compression_opts.max_dict_bytes: 0
|
||||
2026/01/27-23:22:58.503297 1 Options.compression_opts.zstd_max_train_bytes: 0
|
||||
2026/01/27-23:22:58.503297 1 Options.compression_opts.use_zstd_dict_trainer: true
|
||||
2026/01/27-23:22:58.503298 1 Options.compression_opts.parallel_threads: 1
|
||||
2026/01/27-23:22:58.503299 1 Options.compression_opts.enabled: false
|
||||
2026/01/27-23:22:58.503299 1 Options.compression_opts.max_dict_buffer_bytes: 0
|
||||
2026/01/27-23:22:58.503300 1 Options.level0_file_num_compaction_trigger: 4
|
||||
2026/01/27-23:22:58.503300 1 Options.level0_slowdown_writes_trigger: 20
|
||||
2026/01/27-23:22:58.503301 1 Options.level0_stop_writes_trigger: 40
|
||||
2026/01/27-23:22:58.503301 1 Options.target_file_size_base: 134217728
|
||||
2026/01/27-23:22:58.503302 1 Options.target_file_size_multiplier: 1
|
||||
2026/01/27-23:22:58.503303 1 Options.max_bytes_for_level_base: 268435456
|
||||
2026/01/27-23:22:58.503303 1 Options.level_compaction_dynamic_level_bytes: 1
|
||||
2026/01/27-23:22:58.503304 1 Options.max_bytes_for_level_multiplier: 10.000000
|
||||
2026/01/27-23:22:58.503305 1 Options.max_bytes_for_level_multiplier_addtl[0]: 1
|
||||
2026/01/27-23:22:58.503306 1 Options.max_bytes_for_level_multiplier_addtl[1]: 1
|
||||
2026/01/27-23:22:58.503307 1 Options.max_bytes_for_level_multiplier_addtl[2]: 1
|
||||
2026/01/27-23:22:58.503307 1 Options.max_bytes_for_level_multiplier_addtl[3]: 1
|
||||
2026/01/27-23:22:58.503308 1 Options.max_bytes_for_level_multiplier_addtl[4]: 1
|
||||
2026/01/27-23:22:58.503308 1 Options.max_bytes_for_level_multiplier_addtl[5]: 1
|
||||
2026/01/27-23:22:58.503309 1 Options.max_bytes_for_level_multiplier_addtl[6]: 1
|
||||
2026/01/27-23:22:58.503309 1 Options.max_sequential_skip_in_iterations: 8
|
||||
2026/01/27-23:22:58.503310 1 Options.memtable_op_scan_flush_trigger: 0
|
||||
2026/01/27-23:22:58.503310 1 Options.memtable_avg_op_scan_flush_trigger: 0
|
||||
2026/01/27-23:22:58.503311 1 Options.max_compaction_bytes: 3355443200
|
||||
2026/01/27-23:22:58.503311 1 Options.arena_block_size: 1048576
|
||||
2026/01/27-23:22:58.503312 1 Options.soft_pending_compaction_bytes_limit: 68719476736
|
||||
2026/01/27-23:22:58.503312 1 Options.hard_pending_compaction_bytes_limit: 274877906944
|
||||
2026/01/27-23:22:58.503313 1 Options.disable_auto_compactions: 0
|
||||
2026/01/27-23:22:58.503314 1 Options.compaction_style: kCompactionStyleLevel
|
||||
2026/01/27-23:22:58.503314 1 Options.compaction_pri: kMinOverlappingRatio
|
||||
2026/01/27-23:22:58.503315 1 Options.compaction_options_universal.size_ratio: 1
|
||||
2026/01/27-23:22:58.503315 1 Options.compaction_options_universal.min_merge_width: 2
|
||||
2026/01/27-23:22:58.503316 1 Options.compaction_options_universal.max_merge_width: 4294967295
|
||||
2026/01/27-23:22:58.503316 1 Options.compaction_options_universal.max_size_amplification_percent: 200
|
||||
2026/01/27-23:22:58.503317 1 Options.compaction_options_universal.compression_size_percent: -1
|
||||
2026/01/27-23:22:58.503318 1 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize
|
||||
2026/01/27-23:22:58.503318 1 Options.compaction_options_universal.max_read_amp: -1
|
||||
2026/01/27-23:22:58.503319 1 Options.compaction_options_universal.reduce_file_locking: 0
|
||||
2026/01/27-23:22:58.503319 1 Options.compaction_options_fifo.max_table_files_size: 1073741824
|
||||
2026/01/27-23:22:58.503320 1 Options.compaction_options_fifo.allow_compaction: 0
|
||||
2026/01/27-23:22:58.503321 1 Options.table_properties_collectors:
|
||||
2026/01/27-23:22:58.503321 1 Options.inplace_update_support: 0
|
||||
2026/01/27-23:22:58.503322 1 Options.inplace_update_num_locks: 10000
|
||||
2026/01/27-23:22:58.503323 1 Options.memtable_prefix_bloom_size_ratio: 0.000000
|
||||
2026/01/27-23:22:58.503323 1 Options.memtable_whole_key_filtering: 0
|
||||
2026/01/27-23:22:58.503324 1 Options.memtable_huge_page_size: 0
|
||||
2026/01/27-23:22:58.503324 1 Options.bloom_locality: 0
|
||||
2026/01/27-23:22:58.503325 1 Options.max_successive_merges: 0
|
||||
2026/01/27-23:22:58.503325 1 Options.strict_max_successive_merges: 0
|
||||
2026/01/27-23:22:58.503326 1 Options.optimize_filters_for_hits: 0
|
||||
2026/01/27-23:22:58.503326 1 Options.paranoid_file_checks: 0
|
||||
2026/01/27-23:22:58.503327 1 Options.force_consistency_checks: 1
|
||||
2026/01/27-23:22:58.503327 1 Options.report_bg_io_stats: 0
|
||||
2026/01/27-23:22:58.503328 1 Options.disallow_memtable_writes: 0
|
||||
2026/01/27-23:22:58.503328 1 Options.ttl: 2592000
|
||||
2026/01/27-23:22:58.503329 1 Options.periodic_compaction_seconds: 0
|
||||
2026/01/27-23:22:58.503329 1 Options.default_temperature: kUnknown
|
||||
2026/01/27-23:22:58.503330 1 Options.preclude_last_level_data_seconds: 0
|
||||
2026/01/27-23:22:58.503330 1 Options.preserve_internal_time_seconds: 0
|
||||
2026/01/27-23:22:58.503331 1 Options.enable_blob_files: false
|
||||
2026/01/27-23:22:58.503331 1 Options.min_blob_size: 0
|
||||
2026/01/27-23:22:58.503332 1 Options.blob_file_size: 268435456
|
||||
2026/01/27-23:22:58.503332 1 Options.blob_compression_type: NoCompression
|
||||
2026/01/27-23:22:58.503333 1 Options.enable_blob_garbage_collection: false
|
||||
2026/01/27-23:22:58.503333 1 Options.blob_garbage_collection_age_cutoff: 0.250000
|
||||
2026/01/27-23:22:58.503334 1 Options.blob_garbage_collection_force_threshold: 1.000000
|
||||
2026/01/27-23:22:58.503335 1 Options.blob_compaction_readahead_size: 0
|
||||
2026/01/27-23:22:58.503335 1 Options.blob_file_starting_level: 0
|
||||
2026/01/27-23:22:58.503336 1 Options.experimental_mempurge_threshold: 0.000000
|
||||
2026/01/27-23:22:58.503336 1 Options.memtable_max_range_deletions: 0
|
||||
2026/01/27-23:22:58.503337 1 Options.cf_allow_ingest_behind: false
|
||||
2026/01/27-23:22:58.503427 1 [WARN] [db/db_impl/db_impl_open.cc:2688] DB::Open() failed: Invalid argument: Column families not opened: index, search, stream, propagate, pubsub, zset_score, metadata
|
||||
2026/01/27-23:22:58.503463 1 [db/db_impl/db_impl.cc:467] Shutdown: canceling all background work
|
||||
2026/01/27-23:22:58.503492 1 [db/db_impl/db_impl.cc:681] Shutdown complete
|
||||
BIN
local-dev/data/kvrocks/db/MANIFEST-000035
Normal file
BIN
local-dev/data/kvrocks/db/MANIFEST-000035
Normal file
Binary file not shown.
1071
local-dev/data/kvrocks/db/OPTIONS-000031
Normal file
1071
local-dev/data/kvrocks/db/OPTIONS-000031
Normal file
File diff suppressed because it is too large
Load Diff
1071
local-dev/data/kvrocks/db/OPTIONS-000037
Normal file
1071
local-dev/data/kvrocks/db/OPTIONS-000037
Normal file
File diff suppressed because it is too large
Load Diff
143
local-dev/data/kvrocks/kvrocks_2026-01-27.log
Normal file
143
local-dev/data/kvrocks/kvrocks_2026-01-27.log
Normal file
@@ -0,0 +1,143 @@
|
||||
[2026-01-27T21:55:32.811461+00:00][I][main.cc:168] kvrocks version 2.14.0 (commit a71eb42f)
|
||||
[2026-01-27T21:55:32.841671+00:00][I][storage.cc:407] [storage] Success to load the data from disk: 12 ms
|
||||
[2026-01-27T21:55:32.843628+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T21:55:32.843938+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T21:55:32.844064+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T21:55:32.844141+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T21:55:32.844201+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T21:55:32.844268+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T21:55:32.844338+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T21:55:32.844401+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T21:55:32.844625+00:00][I][worker.cc:596] [worker] Thread #281473273094752 started
|
||||
[2026-01-27T21:55:32.844653+00:00][I][worker.cc:596] [worker] Thread #281473281548896 started
|
||||
[2026-01-27T21:55:32.844671+00:00][I][worker.cc:596] [worker] Thread #281473290003040 started
|
||||
[2026-01-27T21:55:32.844708+00:00][I][worker.cc:596] [worker] Thread #281473366090336 started
|
||||
[2026-01-27T21:55:32.844742+00:00][I][worker.cc:596] [worker] Thread #281473357636192 started
|
||||
[2026-01-27T21:55:32.844787+00:00][I][worker.cc:596] [worker] Thread #281473349182048 started
|
||||
[2026-01-27T21:55:32.844825+00:00][I][worker.cc:596] [worker] Thread #281473340727904 started
|
||||
[2026-01-27T21:55:32.844861+00:00][I][worker.cc:596] [worker] Thread #281473332273760 started
|
||||
[2026-01-27T21:55:32.844960+00:00][I][server.cc:245] [server] Ready to accept connections
|
||||
[2026-01-27T21:56:34.369240+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: pubsub
|
||||
[2026-01-27T21:56:34.377799+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: pubsub finished, result: OK
|
||||
[2026-01-27T21:56:34.378156+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: propagate
|
||||
[2026-01-27T21:56:34.378349+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: propagate finished, result: OK
|
||||
[2026-01-27T21:59:36.368485+00:00][I][main.cc:53] Signal Terminated (15) received, stopping the server
|
||||
[2026-01-27T22:00:38.556809+00:00][I][main.cc:168] kvrocks version 2.14.0 (commit a71eb42f)
|
||||
[2026-01-27T22:00:38.571773+00:00][I][event_listener.cc:187] [event_listener/table_file_created] column family: metadata, file path: /data/db/000014.sst, file size: 2427, job_id: 1, reason: recovery, status: OK
|
||||
[2026-01-27T22:00:38.579255+00:00][I][storage.cc:407] [storage] Success to load the data from disk: 17 ms
|
||||
[2026-01-27T22:00:38.581394+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:00:38.581635+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:00:38.581731+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:00:38.581905+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:00:38.582059+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:00:38.582166+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:00:38.582287+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:00:38.582369+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:00:38.582500+00:00][I][worker.cc:596] [worker] Thread #281473264837216 started
|
||||
[2026-01-27T22:00:38.582607+00:00][I][worker.cc:596] [worker] Thread #281473273291360 started
|
||||
[2026-01-27T22:00:38.582648+00:00][I][worker.cc:596] [worker] Thread #281473281745504 started
|
||||
[2026-01-27T22:00:38.582698+00:00][I][worker.cc:596] [worker] Thread #281473336730208 started
|
||||
[2026-01-27T22:00:38.582731+00:00][I][worker.cc:596] [worker] Thread #281473328276064 started
|
||||
[2026-01-27T22:00:38.582767+00:00][I][worker.cc:596] [worker] Thread #281473319821920 started
|
||||
[2026-01-27T22:00:38.582805+00:00][I][worker.cc:596] [worker] Thread #281473311367776 started
|
||||
[2026-01-27T22:00:38.582859+00:00][I][worker.cc:596] [worker] Thread #281473302913632 started
|
||||
[2026-01-27T22:00:38.582953+00:00][I][server.cc:245] [server] Ready to accept connections
|
||||
[2026-01-27T22:01:40.207616+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: pubsub
|
||||
[2026-01-27T22:01:40.208939+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: pubsub finished, result: OK
|
||||
[2026-01-27T22:01:40.209035+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: propagate
|
||||
[2026-01-27T22:01:40.209124+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: propagate finished, result: OK
|
||||
[2026-01-27T22:04:28.316771+00:00][I][main.cc:53] Signal Terminated (15) received, stopping the server
|
||||
[2026-01-27T22:05:20.855690+00:00][I][main.cc:168] kvrocks version 2.14.0 (commit a71eb42f)
|
||||
[2026-01-27T22:05:20.871111+00:00][I][storage.cc:407] [storage] Success to load the data from disk: 11 ms
|
||||
[2026-01-27T22:05:20.872974+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:05:20.873484+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:05:20.873699+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:05:20.874015+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:05:20.874074+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:05:20.874129+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:05:20.874184+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:05:20.874242+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:05:20.874356+00:00][I][worker.cc:596] [worker] Thread #281472658825824 started
|
||||
[2026-01-27T22:05:20.874371+00:00][I][worker.cc:596] [worker] Thread #281472667279968 started
|
||||
[2026-01-27T22:05:20.874386+00:00][I][worker.cc:596] [worker] Thread #281472675734112 started
|
||||
[2026-01-27T22:05:20.874415+00:00][I][worker.cc:596] [worker] Thread #281472739041888 started
|
||||
[2026-01-27T22:05:20.874436+00:00][I][worker.cc:596] [worker] Thread #281472726458976 started
|
||||
[2026-01-27T22:05:20.874450+00:00][I][worker.cc:596] [worker] Thread #281472718004832 started
|
||||
[2026-01-27T22:05:20.874464+00:00][I][worker.cc:596] [worker] Thread #281472709550688 started
|
||||
[2026-01-27T22:05:20.874485+00:00][I][worker.cc:596] [worker] Thread #281472701096544 started
|
||||
[2026-01-27T22:05:20.874543+00:00][I][server.cc:245] [server] Ready to accept connections
|
||||
[2026-01-27T22:06:22.367592+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: pubsub
|
||||
[2026-01-27T22:06:22.370793+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: pubsub finished, result: OK
|
||||
[2026-01-27T22:06:22.371106+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: propagate
|
||||
[2026-01-27T22:06:22.371313+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: propagate finished, result: OK
|
||||
[2026-01-27T22:29:24.678219+00:00][I][main.cc:53] Signal Terminated (15) received, stopping the server
|
||||
[2026-01-27T22:29:25.238671+00:00][I][main.cc:168] kvrocks version 2.14.0 (commit a71eb42f)
|
||||
[2026-01-27T22:29:25.251917+00:00][I][event_listener.cc:187] [event_listener/table_file_created] column family: metadata, file path: /data/db/000023.sst, file size: 2476, job_id: 1, reason: recovery, status: OK
|
||||
[2026-01-27T22:29:25.259557+00:00][I][storage.cc:407] [storage] Success to load the data from disk: 16 ms
|
||||
[2026-01-27T22:29:25.262110+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:25.262274+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:25.262372+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:25.262449+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:25.262518+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:25.262618+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:25.262684+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:25.262746+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:25.262900+00:00][I][worker.cc:596] [worker] Thread #281472793043552 started
|
||||
[2026-01-27T22:29:25.262932+00:00][I][worker.cc:596] [worker] Thread #281472801497696 started
|
||||
[2026-01-27T22:29:25.262956+00:00][I][worker.cc:596] [worker] Thread #281472809951840 started
|
||||
[2026-01-27T22:29:25.262989+00:00][I][worker.cc:596] [worker] Thread #281472873259616 started
|
||||
[2026-01-27T22:29:25.263017+00:00][I][worker.cc:596] [worker] Thread #281472860676704 started
|
||||
[2026-01-27T22:29:25.263051+00:00][I][worker.cc:596] [worker] Thread #281472852222560 started
|
||||
[2026-01-27T22:29:25.263086+00:00][I][worker.cc:596] [worker] Thread #281472843768416 started
|
||||
[2026-01-27T22:29:25.263115+00:00][I][worker.cc:596] [worker] Thread #281472835314272 started
|
||||
[2026-01-27T22:29:25.263201+00:00][I][server.cc:245] [server] Ready to accept connections
|
||||
[2026-01-27T22:29:38.668808+00:00][I][main.cc:53] Signal Terminated (15) received, stopping the server
|
||||
[2026-01-27T22:29:39.166069+00:00][I][main.cc:168] kvrocks version 2.14.0 (commit a71eb42f)
|
||||
[2026-01-27T22:29:39.182475+00:00][I][storage.cc:407] [storage] Success to load the data from disk: 13 ms
|
||||
[2026-01-27T22:29:39.183886+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:39.184001+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:39.184110+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:39.184542+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:39.184647+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:39.184722+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:39.184822+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:39.184900+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T22:29:39.185008+00:00][I][worker.cc:596] [worker] Thread #281473065673312 started
|
||||
[2026-01-27T22:29:39.185045+00:00][I][worker.cc:596] [worker] Thread #281473074127456 started
|
||||
[2026-01-27T22:29:39.185068+00:00][I][worker.cc:596] [worker] Thread #281473082581600 started
|
||||
[2026-01-27T22:29:39.185101+00:00][I][worker.cc:596] [worker] Thread #281473147986528 started
|
||||
[2026-01-27T22:29:39.185127+00:00][I][worker.cc:596] [worker] Thread #281473133306464 started
|
||||
[2026-01-27T22:29:39.185153+00:00][I][worker.cc:596] [worker] Thread #281473124852320 started
|
||||
[2026-01-27T22:29:39.185193+00:00][I][worker.cc:596] [worker] Thread #281473116398176 started
|
||||
[2026-01-27T22:29:39.185221+00:00][I][worker.cc:596] [worker] Thread #281473107944032 started
|
||||
[2026-01-27T22:29:39.185302+00:00][I][server.cc:245] [server] Ready to accept connections
|
||||
[2026-01-27T22:30:40.725701+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: pubsub
|
||||
[2026-01-27T22:30:40.726451+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: pubsub finished, result: OK
|
||||
[2026-01-27T22:30:40.726546+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: propagate
|
||||
[2026-01-27T22:30:40.726624+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: propagate finished, result: OK
|
||||
[2026-01-27T23:22:53.541798+00:00][I][main.cc:53] Signal Terminated (15) received, stopping the server
|
||||
[2026-01-27T23:22:58.500203+00:00][I][main.cc:168] kvrocks version 2.14.0 (commit a71eb42f)
|
||||
[2026-01-27T23:22:58.511777+00:00][I][event_listener.cc:187] [event_listener/table_file_created] column family: default, file path: /data/db/000032.sst, file size: 2104, job_id: 1, reason: recovery, status: OK
|
||||
[2026-01-27T23:22:58.513628+00:00][I][event_listener.cc:187] [event_listener/table_file_created] column family: metadata, file path: /data/db/000033.sst, file size: 6606, job_id: 1, reason: recovery, status: OK
|
||||
[2026-01-27T23:22:58.525506+00:00][I][storage.cc:407] [storage] Success to load the data from disk: 21 ms
|
||||
[2026-01-27T23:22:58.527352+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T23:22:58.527574+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T23:22:58.527688+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T23:22:58.527767+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T23:22:58.527848+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T23:22:58.527949+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T23:22:58.528244+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T23:22:58.528341+00:00][I][worker.cc:76] [worker] Listening on: 0.0.0.0:6666
|
||||
[2026-01-27T23:22:58.528525+00:00][I][worker.cc:596] [worker] Thread #281472916578912 started
|
||||
[2026-01-27T23:22:58.528585+00:00][I][worker.cc:596] [worker] Thread #281472958849632 started
|
||||
[2026-01-27T23:22:58.528618+00:00][I][worker.cc:596] [worker] Thread #281472967303776 started
|
||||
[2026-01-27T23:22:58.528648+00:00][I][worker.cc:596] [worker] Thread #281473036837472 started
|
||||
[2026-01-27T23:22:58.528952+00:00][I][worker.cc:596] [worker] Thread #281473022157408 started
|
||||
[2026-01-27T23:22:58.528990+00:00][I][worker.cc:596] [worker] Thread #281473009574496 started
|
||||
[2026-01-27T23:22:58.529021+00:00][I][worker.cc:596] [worker] Thread #281473001120352 started
|
||||
[2026-01-27T23:22:58.529044+00:00][I][worker.cc:596] [worker] Thread #281472992666208 started
|
||||
[2026-01-27T23:22:58.529123+00:00][I][server.cc:245] [server] Ready to accept connections
|
||||
[2026-01-27T23:24:00.045680+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: pubsub
|
||||
[2026-01-27T23:24:00.046224+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: pubsub finished, result: OK
|
||||
[2026-01-27T23:24:00.046312+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: propagate
|
||||
[2026-01-27T23:24:00.046352+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: propagate finished, result: OK
|
||||
4
local-dev/data/kvrocks/kvrocks_2026-01-28.log
Normal file
4
local-dev/data/kvrocks/kvrocks_2026-01-28.log
Normal file
@@ -0,0 +1,4 @@
|
||||
[2026-01-28T00:00:56.433217+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: pubsub
|
||||
[2026-01-28T00:00:56.435397+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: pubsub finished, result: OK
|
||||
[2026-01-28T00:00:56.435483+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: propagate
|
||||
[2026-01-28T00:00:56.435537+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: propagate finished, result: OK
|
||||
4
local-dev/data/kvrocks/kvrocks_2026-01-29.log
Normal file
4
local-dev/data/kvrocks/kvrocks_2026-01-29.log
Normal file
@@ -0,0 +1,4 @@
|
||||
[2026-01-29T00:00:32.264105+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: pubsub
|
||||
[2026-01-29T00:00:32.270121+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: pubsub finished, result: OK
|
||||
[2026-01-29T00:00:32.270189+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: propagate
|
||||
[2026-01-29T00:00:32.270251+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: propagate finished, result: OK
|
||||
5
local-dev/data/kvrocks/kvrocks_2026-01-30.log
Normal file
5
local-dev/data/kvrocks/kvrocks_2026-01-30.log
Normal file
@@ -0,0 +1,5 @@
|
||||
[2026-01-30T00:00:53.731511+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: pubsub
|
||||
[2026-01-30T00:00:53.732305+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: pubsub finished, result: OK
|
||||
[2026-01-30T00:00:53.732942+00:00][I][compaction_checker.cc:35] [compaction checker] Start to compact the column family: propagate
|
||||
[2026-01-30T00:00:53.733051+00:00][I][compaction_checker.cc:38] [compaction checker] Compact the column family: propagate finished, result: OK
|
||||
[2026-01-30T00:17:42.821542+00:00][I][main.cc:53] Signal Terminated (15) received, stopping the server
|
||||
169
local-dev/debug-middleware.js
Normal file
169
local-dev/debug-middleware.js
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* Debug Middleware for Local Development
|
||||
* Captures and logs all requests for research
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const DATA_DIR = process.env.DATA_DIR || '/app/data';
|
||||
const LOG_FILE = path.join(DATA_DIR, 'debug-requests.jsonl');
|
||||
const capturedRequests = [];
|
||||
const MAX_CAPTURED = 500;
|
||||
|
||||
function extractSubdomain(host) {
|
||||
if (!host) return null;
|
||||
const hostname = host.split(':')[0];
|
||||
const match = hostname.match(/^(sessions|account-data|telemetry|tools|oauth\.accounts|accounts)\./);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug middleware - logs requests (skip internal paths)
|
||||
*/
|
||||
function debugMiddleware(req, res, next) {
|
||||
// Skip debug/health/admin endpoints
|
||||
if (req.path.startsWith('/debug') || req.path === '/health' || req.path === '/favicon.ico') {
|
||||
return next();
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
const subdomain = extractSubdomain(req.headers.host);
|
||||
|
||||
// Color logging
|
||||
const colors = { reset: '\x1b[0m', green: '\x1b[32m', blue: '\x1b[34m', yellow: '\x1b[33m', magenta: '\x1b[35m', cyan: '\x1b[36m', red: '\x1b[31m' };
|
||||
const subColor = { 'sessions': colors.green, 'account-data': colors.blue, 'telemetry': colors.yellow, 'tools': colors.magenta }[subdomain] || colors.cyan;
|
||||
|
||||
console.log(`${subColor}[${subdomain || 'main'}]${colors.reset} ${req.method} ${req.url}`);
|
||||
|
||||
// Capture response
|
||||
const origSend = res.send.bind(res);
|
||||
const origJson = res.json.bind(res);
|
||||
let responseBody = null;
|
||||
|
||||
res.send = function(body) {
|
||||
responseBody = typeof body === 'string' && body.length > 2000 ? body.substring(0, 2000) + '...' : body;
|
||||
return origSend(body);
|
||||
};
|
||||
|
||||
res.json = function(body) {
|
||||
try {
|
||||
const str = JSON.stringify(body);
|
||||
responseBody = str.length > 2000 ? { _truncated: true, _len: str.length } : body;
|
||||
} catch (e) {
|
||||
responseBody = { _error: 'serialize failed' };
|
||||
}
|
||||
return origJson(body);
|
||||
};
|
||||
|
||||
res.on('finish', () => {
|
||||
const duration = Date.now() - startTime;
|
||||
const statusColor = res.statusCode >= 400 ? colors.red : colors.green;
|
||||
console.log(` ${statusColor}-> ${res.statusCode}${colors.reset} (${duration}ms)`);
|
||||
|
||||
const entry = {
|
||||
timestamp: new Date().toISOString(),
|
||||
method: req.method,
|
||||
path: req.path,
|
||||
host: req.headers.host,
|
||||
subdomain,
|
||||
query: req.query,
|
||||
body: req.body,
|
||||
response: { statusCode: res.statusCode, duration, body: responseBody }
|
||||
};
|
||||
|
||||
capturedRequests.unshift(entry);
|
||||
if (capturedRequests.length > MAX_CAPTURED) capturedRequests.pop();
|
||||
|
||||
try { fs.appendFileSync(LOG_FILE, JSON.stringify(entry) + '\n'); } catch (e) {}
|
||||
});
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup debug routes
|
||||
*/
|
||||
function setupDebugRoutes(app) {
|
||||
// Debug dashboard
|
||||
app.get('/debug', (req, res) => {
|
||||
res.send(`<!DOCTYPE html>
|
||||
<html><head><title>Debug</title>
|
||||
<style>
|
||||
body{font-family:monospace;background:#1a1a2e;color:#eee;padding:20px}
|
||||
h1{color:#00d4ff}.section{background:#16213e;padding:15px;margin:10px 0;border-radius:8px}
|
||||
.req{background:#0f3460;padding:8px;margin:4px 0;border-radius:4px;font-size:12px}
|
||||
.GET{color:#4ade80}.POST{color:#60a5fa}.DELETE{color:#f87171}
|
||||
.sub{color:#c084fc;font-weight:bold}pre{background:#0a0a1a;padding:8px;overflow-x:auto;font-size:11px}
|
||||
button{background:#00d4ff;color:#000;border:none;padding:6px 12px;cursor:pointer;margin:4px;border-radius:4px}
|
||||
select{padding:6px;background:#0f3460;border:1px solid #00d4ff;color:#fff;border-radius:4px}
|
||||
details summary{cursor:pointer;color:#60a5fa}
|
||||
</style></head><body>
|
||||
<h1>Debug Dashboard</h1>
|
||||
<div class="section"><button onclick="refresh()">Refresh</button><button onclick="clearReqs()">Clear</button>
|
||||
<select id="filter" onchange="refresh()"><option value="">All</option><option value="sessions">sessions</option>
|
||||
<option value="account-data">account-data</option><option value="telemetry">telemetry</option><option value="tools">tools</option></select>
|
||||
<span id="count"></span></div>
|
||||
<div class="section"><h2>Subdomains</h2><div id="subs"></div></div>
|
||||
<div class="section"><h2>Requests</h2><div id="reqs" style="max-height:500px;overflow-y:auto"></div></div>
|
||||
<script>
|
||||
async function refresh(){
|
||||
const f=document.getElementById('filter').value;
|
||||
const r=await fetch('/debug/requests'+(f?'?subdomain='+f:''));
|
||||
const d=await r.json();
|
||||
document.getElementById('count').textContent=d.total+' total';
|
||||
document.getElementById('reqs').innerHTML=d.requests.map(r=>\`<div class="req">
|
||||
<span class="\${r.method}">\${r.method}</span> <span class="sub">\${r.subdomain||'main'}</span> \${r.path}
|
||||
<span style="color:\${r.response?.statusCode<400?'#4ade80':'#f87171'}">\${r.response?.statusCode}</span>
|
||||
<details><summary>Details</summary><pre>\${JSON.stringify(r,null,2)}</pre></details></div>\`).join('');
|
||||
const s=await fetch('/debug/subdomains');
|
||||
const sd=await s.json();
|
||||
document.getElementById('subs').innerHTML=Object.entries(sd).map(([k,v])=>'<span class="sub">'+k+'</span>: '+v.count).join(' | ');
|
||||
}
|
||||
async function clearReqs(){await fetch('/debug/requests',{method:'DELETE'});refresh();}
|
||||
setInterval(refresh,5000);refresh();
|
||||
</script></body></html>`);
|
||||
});
|
||||
|
||||
app.get('/debug/requests', (req, res) => {
|
||||
let filtered = capturedRequests;
|
||||
if (req.query.subdomain) filtered = filtered.filter(r => r.subdomain === req.query.subdomain);
|
||||
res.json({ total: capturedRequests.length, filtered: filtered.length, requests: filtered.slice(0, 100) });
|
||||
});
|
||||
|
||||
app.get('/debug/subdomains', (req, res) => {
|
||||
const summary = {};
|
||||
for (const r of capturedRequests) {
|
||||
const sub = r.subdomain || 'main';
|
||||
if (!summary[sub]) summary[sub] = { count: 0 };
|
||||
summary[sub].count++;
|
||||
}
|
||||
res.json(summary);
|
||||
});
|
||||
|
||||
app.delete('/debug/requests', (req, res) => {
|
||||
capturedRequests.length = 0;
|
||||
res.json({ cleared: true });
|
||||
});
|
||||
|
||||
// Catch-all for unknown endpoints (must be registered last)
|
||||
app.all('*', (req, res, next) => {
|
||||
// Only catch truly unknown paths
|
||||
if (res.headersSent) return;
|
||||
|
||||
const subdomain = extractSubdomain(req.headers.host);
|
||||
console.log(`\x1b[31m[UNKNOWN]\x1b[0m ${req.method} ${req.path} (subdomain: ${subdomain})`);
|
||||
|
||||
res.status(200).json({
|
||||
debug: true,
|
||||
message: 'Unknown endpoint captured',
|
||||
method: req.method,
|
||||
path: req.path,
|
||||
subdomain,
|
||||
headers: req.headers,
|
||||
body: req.body
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { debugMiddleware, setupDebugRoutes, capturedRequests };
|
||||
800
local-dev/debug-wrapper.js
Normal file
800
local-dev/debug-wrapper.js
Normal file
@@ -0,0 +1,800 @@
|
||||
/**
|
||||
* Debug Wrapper Entry Point
|
||||
* Wraps the existing hytale-auth-server with debug capabilities
|
||||
*/
|
||||
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// Only load debug functionality if DEBUG_MODE is enabled
|
||||
const DEBUG_MODE = process.env.DEBUG_MODE === 'true';
|
||||
|
||||
const DATA_DIR = process.env.DATA_DIR || '/app/data';
|
||||
const LOG_FILE = path.join(DATA_DIR, 'debug-requests.jsonl');
|
||||
const SSL_DIR = path.join(DATA_DIR, 'ssl');
|
||||
const SSL_KEY = path.join(SSL_DIR, 'server.key');
|
||||
const SSL_CERT = path.join(SSL_DIR, 'server.crt');
|
||||
const HTTPS_PORT = parseInt(process.env.HTTPS_PORT || '3443');
|
||||
|
||||
/**
|
||||
* Generate self-signed SSL certificates if they don't exist
|
||||
*/
|
||||
function ensureSSLCertificates() {
|
||||
if (!fs.existsSync(SSL_DIR)) {
|
||||
fs.mkdirSync(SSL_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(SSL_KEY) || !fs.existsSync(SSL_CERT)) {
|
||||
console.log('Generating self-signed SSL certificates...');
|
||||
try {
|
||||
execSync(`openssl req -x509 -newkey rsa:2048 -keyout "${SSL_KEY}" -out "${SSL_CERT}" -days 365 -nodes -subj "/CN=localhost" 2>/dev/null`);
|
||||
console.log('SSL certificates generated successfully');
|
||||
} catch (err) {
|
||||
console.error('Failed to generate SSL certificates:', err.message);
|
||||
console.error('HTTPS will not be available. Install openssl or provide certificates manually.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return {
|
||||
key: fs.readFileSync(SSL_KEY),
|
||||
cert: fs.readFileSync(SSL_CERT)
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('Failed to read SSL certificates:', err.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// In-memory request storage for debug dashboard
|
||||
const capturedRequests = [];
|
||||
const MAX_CAPTURED = 500;
|
||||
|
||||
// Color codes for terminal output
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
green: '\x1b[32m',
|
||||
blue: '\x1b[34m',
|
||||
yellow: '\x1b[33m',
|
||||
magenta: '\x1b[35m',
|
||||
cyan: '\x1b[36m',
|
||||
red: '\x1b[31m'
|
||||
};
|
||||
|
||||
function extractSubdomain(host) {
|
||||
if (!host) return null;
|
||||
const hostname = host.split(':')[0];
|
||||
const match = hostname.match(/^(sessions|account-data|telemetry|tools|oauth\.accounts|accounts)\./);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function getSubdomainColor(subdomain) {
|
||||
const subColors = {
|
||||
'sessions': colors.green,
|
||||
'account-data': colors.blue,
|
||||
'telemetry': colors.yellow,
|
||||
'tools': colors.magenta
|
||||
};
|
||||
return subColors[subdomain] || colors.cyan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve the debug dashboard HTML
|
||||
*/
|
||||
function serveDebugDashboard(res) {
|
||||
const html = `<!DOCTYPE html>
|
||||
<html><head><title>Debug Dashboard - Local Dev</title>
|
||||
<style>
|
||||
body{font-family:monospace;background:#1a1a2e;color:#eee;padding:20px;margin:0}
|
||||
h1{color:#00d4ff;margin-bottom:10px}
|
||||
.info{color:#888;font-size:12px;margin-bottom:20px}
|
||||
.section{background:#16213e;padding:15px;margin:10px 0;border-radius:8px}
|
||||
.req{background:#0f3460;padding:10px;margin:5px 0;border-radius:4px;font-size:12px;border-left:3px solid #00d4ff}
|
||||
.req.error{border-left-color:#f87171}
|
||||
.GET{color:#4ade80}.POST{color:#60a5fa}.DELETE{color:#f87171}.PUT{color:#fbbf24}
|
||||
.sub{color:#c084fc;font-weight:bold}
|
||||
.status{padding:2px 6px;border-radius:3px;font-size:11px}
|
||||
.status.ok{background:#166534;color:#4ade80}
|
||||
.status.error{background:#7f1d1d;color:#f87171}
|
||||
pre{background:#0a0a1a;padding:10px;overflow-x:auto;font-size:11px;border-radius:4px;max-height:300px;overflow-y:auto}
|
||||
button{background:#00d4ff;color:#000;border:none;padding:8px 16px;cursor:pointer;margin:4px;border-radius:4px;font-weight:bold}
|
||||
button:hover{background:#00b8e6}
|
||||
button.danger{background:#dc2626}
|
||||
button.danger:hover{background:#b91c1c}
|
||||
button.paused{background:#fbbf24;color:#000}
|
||||
select{padding:8px;background:#0f3460;border:1px solid #00d4ff;color:#fff;border-radius:4px}
|
||||
details summary{cursor:pointer;color:#60a5fa;padding:5px 0}
|
||||
.timestamp{color:#666;font-size:10px}
|
||||
.duration{color:#fbbf24}
|
||||
.path{color:#fff}
|
||||
#count{margin-left:10px;color:#4ade80}
|
||||
#pauseStatus{margin-left:10px;color:#fbbf24;font-weight:bold}
|
||||
.controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
||||
</style></head><body>
|
||||
<h1>Debug Dashboard - Local Development</h1>
|
||||
<div class="info">Capturing all requests to auth server for research purposes</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="controls">
|
||||
<button onclick="refresh()">Refresh</button>
|
||||
<button id="pauseBtn" onclick="togglePause()">Pause</button>
|
||||
<button class="danger" onclick="clearReqs()">Clear All</button>
|
||||
<select id="filter" onchange="refresh()">
|
||||
<option value="">All Subdomains</option>
|
||||
<option value="sessions">sessions</option>
|
||||
<option value="account-data">account-data</option>
|
||||
<option value="telemetry">telemetry</option>
|
||||
<option value="tools">tools</option>
|
||||
<option value="main">main (no subdomain)</option>
|
||||
</select>
|
||||
<span id="count"></span>
|
||||
<span id="pauseStatus"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2>Subdomain Summary</h2>
|
||||
<div id="subs"></div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2>Recent Requests</h2>
|
||||
<div id="reqs" style="max-height:600px;overflow-y:auto"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let isPaused = false;
|
||||
let refreshInterval = null;
|
||||
|
||||
async function refresh() {
|
||||
const f = document.getElementById('filter').value;
|
||||
const r = await fetch('/debug/requests' + (f ? '?subdomain=' + f : ''));
|
||||
const d = await r.json();
|
||||
document.getElementById('count').textContent = d.total + ' total, ' + d.filtered + ' shown';
|
||||
|
||||
document.getElementById('reqs').innerHTML = d.requests.map((r, i) => {
|
||||
const statusClass = r.response?.statusCode >= 400 ? 'error' : 'ok';
|
||||
const reqClass = r.response?.statusCode >= 400 ? 'error' : '';
|
||||
return \`<div class="req \${reqClass}">
|
||||
<div>
|
||||
<span class="timestamp">\${r.timestamp}</span>
|
||||
<span class="\${r.method}">\${r.method}</span>
|
||||
<span class="sub">\${r.subdomain || 'main'}</span>
|
||||
<span class="path">\${r.path}</span>
|
||||
<span class="status \${statusClass}">\${r.response?.statusCode || '?'}</span>
|
||||
<span class="duration">\${r.response?.duration || 0}ms</span>
|
||||
</div>
|
||||
<details ontoggle="handleDetailsToggle(this)">
|
||||
<summary>Request Details</summary>
|
||||
<pre>\${JSON.stringify({
|
||||
host: r.host,
|
||||
query: r.query,
|
||||
body: r.body,
|
||||
headers: r.headers
|
||||
}, null, 2)}</pre>
|
||||
</details>
|
||||
<details ontoggle="handleDetailsToggle(this)">
|
||||
<summary>Response</summary>
|
||||
<pre>\${JSON.stringify(r.response, null, 2)}</pre>
|
||||
</details>
|
||||
</div>\`;
|
||||
}).join('');
|
||||
|
||||
const s = await fetch('/debug/subdomains');
|
||||
const sd = await s.json();
|
||||
document.getElementById('subs').innerHTML = Object.entries(sd)
|
||||
.map(([k, v]) => '<span class="sub">' + k + '</span>: ' + v.count)
|
||||
.join(' | ') || '<em>No requests yet</em>';
|
||||
}
|
||||
|
||||
function handleDetailsToggle(el) {
|
||||
// Auto-pause when any details block is opened
|
||||
if (el.open && !isPaused) {
|
||||
togglePause();
|
||||
}
|
||||
}
|
||||
|
||||
function togglePause() {
|
||||
isPaused = !isPaused;
|
||||
const btn = document.getElementById('pauseBtn');
|
||||
const status = document.getElementById('pauseStatus');
|
||||
if (isPaused) {
|
||||
btn.textContent = 'Resume';
|
||||
btn.classList.add('paused');
|
||||
status.textContent = '(auto-refresh paused)';
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
refreshInterval = null;
|
||||
}
|
||||
} else {
|
||||
btn.textContent = 'Pause';
|
||||
btn.classList.remove('paused');
|
||||
status.textContent = '';
|
||||
refreshInterval = setInterval(autoRefresh, 3000);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
function autoRefresh() {
|
||||
if (!isPaused) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
async function clearReqs() {
|
||||
if (confirm('Clear all captured requests?')) {
|
||||
await fetch('/debug/requests', { method: 'DELETE' });
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
refreshInterval = setInterval(autoRefresh, 3000);
|
||||
refresh();
|
||||
</script>
|
||||
</body></html>`;
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end(html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle debug API endpoints
|
||||
*/
|
||||
function handleDebugApi(req, res, urlPath, url) {
|
||||
if (urlPath === '/debug' || urlPath === '/debug/') {
|
||||
serveDebugDashboard(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (urlPath === '/debug/requests') {
|
||||
if (req.method === 'DELETE') {
|
||||
capturedRequests.length = 0;
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ cleared: true }));
|
||||
return true;
|
||||
}
|
||||
|
||||
let filtered = capturedRequests;
|
||||
const subdomain = url.searchParams.get('subdomain');
|
||||
if (subdomain) {
|
||||
if (subdomain === 'main') {
|
||||
filtered = filtered.filter(r => !r.subdomain);
|
||||
} else {
|
||||
filtered = filtered.filter(r => r.subdomain === subdomain);
|
||||
}
|
||||
}
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({
|
||||
total: capturedRequests.length,
|
||||
filtered: filtered.length,
|
||||
requests: filtered.slice(0, 100)
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (urlPath === '/debug/subdomains') {
|
||||
const summary = {};
|
||||
for (const r of capturedRequests) {
|
||||
const sub = r.subdomain || 'main';
|
||||
if (!summary[sub]) summary[sub] = { count: 0 };
|
||||
summary[sub].count++;
|
||||
}
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(summary));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture request/response for debugging
|
||||
*/
|
||||
function captureRequest(req, res, url, startTime) {
|
||||
const subdomain = extractSubdomain(req.headers.host);
|
||||
const originalEnd = res.end.bind(res);
|
||||
let responseBody = null;
|
||||
|
||||
// Intercept res.end to capture response
|
||||
res.end = function(chunk, encoding, callback) {
|
||||
if (chunk) {
|
||||
try {
|
||||
const str = chunk.toString();
|
||||
if (str.length > 2000) {
|
||||
responseBody = { _truncated: true, _length: str.length, _preview: str.substring(0, 500) };
|
||||
} else {
|
||||
try {
|
||||
responseBody = JSON.parse(str);
|
||||
} catch {
|
||||
responseBody = str;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
responseBody = { _error: 'Could not capture response' };
|
||||
}
|
||||
}
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
// Log with colors
|
||||
const subColor = getSubdomainColor(subdomain);
|
||||
const statusColor = res.statusCode >= 400 ? colors.red : colors.green;
|
||||
console.log(`${subColor}[${subdomain || 'main'}]${colors.reset} ${req.method} ${url.pathname} ${statusColor}${res.statusCode}${colors.reset} (${duration}ms)`);
|
||||
|
||||
// Store in memory
|
||||
const entry = {
|
||||
timestamp: new Date().toISOString(),
|
||||
method: req.method,
|
||||
path: url.pathname,
|
||||
host: req.headers.host,
|
||||
subdomain,
|
||||
query: Object.fromEntries(url.searchParams),
|
||||
headers: sanitizeHeaders(req.headers),
|
||||
response: {
|
||||
statusCode: res.statusCode,
|
||||
duration,
|
||||
body: responseBody
|
||||
}
|
||||
};
|
||||
|
||||
capturedRequests.unshift(entry);
|
||||
if (capturedRequests.length > MAX_CAPTURED) {
|
||||
capturedRequests.pop();
|
||||
}
|
||||
|
||||
// Also write to file
|
||||
try {
|
||||
fs.appendFileSync(LOG_FILE, JSON.stringify(entry) + '\n');
|
||||
} catch (e) {
|
||||
// Ignore file write errors
|
||||
}
|
||||
|
||||
return originalEnd(chunk, encoding, callback);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize headers for logging (remove sensitive data)
|
||||
*/
|
||||
function sanitizeHeaders(headers) {
|
||||
const sanitized = { ...headers };
|
||||
// Keep Authorization header but might want to truncate in future
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point - wraps the existing server
|
||||
*/
|
||||
async function main() {
|
||||
if (DEBUG_MODE) {
|
||||
console.log('\x1b[33m=== DEBUG MODE ENABLED ===\x1b[0m');
|
||||
console.log(`Debug dashboard: http://localhost:${process.env.PORT || 3000}/debug`);
|
||||
console.log(`Request log: ${LOG_FILE}`);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Load the original app module
|
||||
const app = require('./app');
|
||||
|
||||
// If not debug mode, just run normally
|
||||
if (!DEBUG_MODE) {
|
||||
app.run();
|
||||
return;
|
||||
}
|
||||
|
||||
// In debug mode, we need to wrap the request handler
|
||||
// Re-implement the server startup with debug wrapper
|
||||
const config = require('./config');
|
||||
const { connect: connectRedis, isConnected } = require('./services/redis');
|
||||
const assets = require('./services/assets');
|
||||
|
||||
console.log('=== Hytale Auth Server (Debug Mode) ===');
|
||||
console.log(`Domain: ${config.domain}`);
|
||||
console.log(`Data directory: ${config.dataDir}`);
|
||||
|
||||
// Pre-load cosmetics
|
||||
assets.preloadCosmetics();
|
||||
|
||||
// Connect to Redis
|
||||
await connectRedis();
|
||||
|
||||
// Create the original request handler reference
|
||||
const originalHandler = require('./app').handleRequest || null;
|
||||
|
||||
// Since handleRequest is not exported, we need a different approach
|
||||
// Load all the modules manually
|
||||
const middleware = require('./middleware');
|
||||
const routes = require('./routes');
|
||||
const { sendJson } = require('./utils/response');
|
||||
const auth = require('./services/auth');
|
||||
const crypto = require('crypto');
|
||||
|
||||
// Recreate the request handler with debug wrapping
|
||||
async function debugWrappedHandler(req, res) {
|
||||
const startTime = Date.now();
|
||||
const url = new URL(req.url, `http://${req.headers.host}`);
|
||||
const urlPath = url.pathname;
|
||||
|
||||
// Skip debug endpoints from capture (but still handle them)
|
||||
if (urlPath.startsWith('/debug')) {
|
||||
if (handleDebugApi(req, res, urlPath, url)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip favicon and health from debug capture
|
||||
const skipCapture = urlPath === '/favicon.ico' || urlPath === '/health';
|
||||
|
||||
// Capture the request/response if not skipped
|
||||
if (!skipCapture) {
|
||||
captureRequest(req, res, url, startTime);
|
||||
}
|
||||
|
||||
// Now delegate to original handling logic
|
||||
// CORS headers
|
||||
middleware.corsHeaders(res);
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
middleware.handleOptions(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle binary uploads
|
||||
const headCacheMatch = urlPath.match(/^\/avatar\/([^/]+)\/head-cache$/);
|
||||
if (headCacheMatch && req.method === 'POST') {
|
||||
routes.avatar.handleAvatarRoutes(req, res, urlPath, {});
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse JSON body
|
||||
const body = await middleware.parseBody(req);
|
||||
|
||||
// Store body for debug capture
|
||||
if (!skipCapture && capturedRequests.length > 0 && !capturedRequests[0].body) {
|
||||
capturedRequests[0].body = body;
|
||||
}
|
||||
|
||||
// Extract user context
|
||||
const { uuid, name, tokenScope } = middleware.extractUserContext(body, req.headers);
|
||||
|
||||
// Route the request (copied from app.js to avoid modification)
|
||||
await routeRequestDebug(req, res, url, urlPath, body, uuid, name, tokenScope, middleware, routes, sendJson, auth, crypto);
|
||||
}
|
||||
|
||||
// Create HTTP server with debug wrapper
|
||||
const server = http.createServer(debugWrappedHandler);
|
||||
server.listen(config.port, '0.0.0.0', () => {
|
||||
console.log(`HTTP Server running on port ${config.port}`);
|
||||
console.log(`Redis: ${isConnected() ? 'connected' : 'NOT CONNECTED'}`);
|
||||
console.log(`\n\x1b[36mDebug Dashboard: http://localhost:${config.port}/debug\x1b[0m`);
|
||||
});
|
||||
|
||||
// Create HTTPS server if certificates are available
|
||||
const sslOptions = ensureSSLCertificates();
|
||||
if (sslOptions) {
|
||||
const httpsServer = https.createServer(sslOptions, debugWrappedHandler);
|
||||
httpsServer.listen(HTTPS_PORT, '0.0.0.0', () => {
|
||||
console.log(`\x1b[32mHTTPS Server running on port ${HTTPS_PORT}\x1b[0m`);
|
||||
console.log(`\x1b[33mNote: Self-signed cert - add to keychain or use NODE_TLS_REJECT_UNAUTHORIZED=0\x1b[0m\n`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route request - copied from app.js to avoid modifying original
|
||||
*/
|
||||
async function routeRequestDebug(req, res, url, urlPath, body, uuid, name, tokenScope, middleware, routes, sendJson, auth, crypto) {
|
||||
const headers = req.headers;
|
||||
|
||||
// Avatar viewer routes
|
||||
if (urlPath.startsWith('/avatar/')) {
|
||||
await routes.avatar.handleAvatarRoutes(req, res, urlPath, body);
|
||||
return;
|
||||
}
|
||||
|
||||
// Customizer route
|
||||
if (urlPath.startsWith('/customizer')) {
|
||||
routes.avatar.handleCustomizerRoute(req, res, urlPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cosmetics list API
|
||||
if (urlPath === '/cosmetics/list') {
|
||||
routes.assets.handleCosmeticsList(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Single cosmetic item data API
|
||||
if (urlPath.startsWith('/cosmetics/item/')) {
|
||||
routes.assets.handleCosmeticItem(req, res, urlPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Static assets route
|
||||
if (urlPath.startsWith('/assets/')) {
|
||||
routes.assets.handleStaticAssets(req, res, urlPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Asset extraction route
|
||||
if (urlPath.startsWith('/asset/')) {
|
||||
routes.assets.handleAssetRoute(req, res, urlPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Download route
|
||||
if (urlPath.startsWith('/download/')) {
|
||||
routes.assets.handleDownload(req, res, urlPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Health check
|
||||
if (urlPath === '/health' || urlPath === '/') {
|
||||
routes.health.handleHealth(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Favicon
|
||||
if (urlPath === '/favicon.ico') {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// JWKS endpoint
|
||||
if (urlPath === '/.well-known/jwks.json' || urlPath === '/jwks.json') {
|
||||
routes.health.handleJwks(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Server auto-auth
|
||||
if (urlPath === '/server/auto-auth') {
|
||||
routes.server.handleServerAutoAuth(req, res, body);
|
||||
return;
|
||||
}
|
||||
|
||||
// Server game profiles
|
||||
if (urlPath === '/server/game-profiles' || urlPath === '/game-profiles') {
|
||||
routes.server.handleServerGameProfiles(req, res, headers);
|
||||
return;
|
||||
}
|
||||
|
||||
// OAuth device authorization
|
||||
if (urlPath === '/oauth2/device/auth') {
|
||||
routes.server.handleOAuthDeviceAuth(req, res, body);
|
||||
return;
|
||||
}
|
||||
|
||||
// OAuth device verification
|
||||
if (urlPath === '/oauth2/device/verify') {
|
||||
const query = Object.fromEntries(url.searchParams);
|
||||
routes.server.handleOAuthDeviceVerify(req, res, query);
|
||||
return;
|
||||
}
|
||||
|
||||
// OAuth token endpoint
|
||||
if (urlPath === '/oauth2/token') {
|
||||
routes.server.handleOAuthToken(req, res, body);
|
||||
return;
|
||||
}
|
||||
|
||||
// Game session endpoints
|
||||
if (urlPath === '/game-session/new') {
|
||||
routes.session.handleGameSessionNew(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/game-session/refresh') {
|
||||
await routes.session.handleGameSessionRefresh(req, res, body, uuid, name, headers);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/game-session/child' || urlPath.includes('/game-session/child')) {
|
||||
routes.session.handleGameSessionChild(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Authorization grant
|
||||
if (urlPath === '/game-session/authorize' || urlPath.includes('/authorize') || urlPath.includes('/auth-grant')) {
|
||||
routes.session.handleAuthorizationGrant(req, res, body, uuid, name, headers);
|
||||
return;
|
||||
}
|
||||
|
||||
// Token exchange
|
||||
if (urlPath === '/server-join/auth-token' || urlPath === '/game-session/exchange' || urlPath.includes('/auth-token')) {
|
||||
routes.session.handleTokenExchange(req, res, body, uuid, name, headers);
|
||||
return;
|
||||
}
|
||||
|
||||
// Session/Auth endpoints
|
||||
if ((urlPath.includes('/session') || urlPath.includes('/child')) && !urlPath.startsWith('/admin')) {
|
||||
routes.session.handleSession(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath.includes('/auth')) {
|
||||
routes.session.handleAuth(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath.includes('/token')) {
|
||||
routes.session.handleToken(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath.includes('/validate') || urlPath.includes('/verify')) {
|
||||
routes.session.handleValidate(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath.includes('/refresh')) {
|
||||
routes.session.handleRefresh(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Account data endpoints
|
||||
if (urlPath === '/my-account/game-profile' || urlPath.includes('/game-profile')) {
|
||||
await routes.account.handleGameProfile(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/my-account/skin') {
|
||||
await routes.account.handleSkin(req, res, body, uuid, name, routes.avatar.invalidateHeadCache);
|
||||
return;
|
||||
}
|
||||
|
||||
// Account-data skin endpoint
|
||||
if (urlPath.startsWith('/account-data/skin/')) {
|
||||
const skinUuid = urlPath.replace('/account-data/skin/', '');
|
||||
await routes.account.handleSkin(req, res, body, skinUuid, name, routes.avatar.invalidateHeadCache);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/my-account/cosmetics' || urlPath.includes('/my-account/cosmetics')) {
|
||||
routes.account.handleCosmetics(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/my-account/get-launcher-data') {
|
||||
routes.account.handleLauncherData(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/my-account/get-profiles') {
|
||||
routes.account.handleGetProfiles(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Bug reports and feedback
|
||||
if (urlPath === '/bugs/create' || urlPath === '/feedback/create') {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Game session delete
|
||||
if (urlPath === '/game-session' && req.method === 'DELETE') {
|
||||
await routes.session.handleGameSessionDelete(req, res, headers);
|
||||
return;
|
||||
}
|
||||
|
||||
// Admin login
|
||||
if (urlPath === '/admin/login' && req.method === 'POST') {
|
||||
await routes.admin.handleAdminLogin(req, res, body);
|
||||
return;
|
||||
}
|
||||
|
||||
// Admin verify
|
||||
if (urlPath === '/admin/verify') {
|
||||
const token = headers['x-admin-token'] || url.searchParams.get('token');
|
||||
await routes.admin.handleAdminVerify(req, res, token);
|
||||
return;
|
||||
}
|
||||
|
||||
// Admin dashboard
|
||||
if (urlPath === '/admin' || urlPath === '/admin/') {
|
||||
routes.admin.handleAdminDashboard(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Test page for head embed
|
||||
if (urlPath === '/test/head') {
|
||||
routes.avatar.handleTestHeadPage(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Protected admin routes
|
||||
if (urlPath.startsWith('/admin/')) {
|
||||
const validToken = await middleware.verifyAdminAuth(headers);
|
||||
if (!validToken) {
|
||||
sendJson(res, 401, { error: 'Unauthorized. Please login at /admin' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Admin API endpoints
|
||||
if (urlPath === '/admin/sessions' || urlPath === '/sessions/active') {
|
||||
await routes.admin.handleActiveSessions(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/admin/stats') {
|
||||
await routes.admin.handleAdminStats(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath.startsWith('/admin/servers')) {
|
||||
await routes.admin.handleAdminServers(req, res, url);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/admin/search') {
|
||||
await routes.admin.handlePlayerSearch(req, res, url);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/admin/prerender-queue') {
|
||||
await routes.admin.handlePrerenderQueue(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Profile lookup by UUID
|
||||
if (urlPath.startsWith('/profile/uuid/')) {
|
||||
const lookupUuid = urlPath.replace('/profile/uuid/', '');
|
||||
await routes.account.handleProfileLookupByUuid(req, res, lookupUuid, headers);
|
||||
return;
|
||||
}
|
||||
|
||||
// Profile lookup by username
|
||||
if (urlPath.startsWith('/profile/username/')) {
|
||||
const lookupUsername = decodeURIComponent(urlPath.replace('/profile/username/', ''));
|
||||
await routes.account.handleProfileLookupByUsername(req, res, lookupUsername, headers);
|
||||
return;
|
||||
}
|
||||
|
||||
// Profile endpoint
|
||||
if (urlPath.includes('/profile') || urlPath.includes('/user') || urlPath.includes('/me')) {
|
||||
routes.account.handleProfile(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cosmetics endpoint
|
||||
if (urlPath.includes('/cosmetic') || urlPath.includes('/unlocked') || urlPath.includes('/inventory')) {
|
||||
routes.account.handleCosmetics(req, res, body, uuid, name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Telemetry endpoint
|
||||
if (urlPath.includes('/telemetry') || urlPath.includes('/analytics') || urlPath.includes('/event')) {
|
||||
sendJson(res, 200, { success: true, received: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// Catch-all for unknown endpoints - important for research!
|
||||
console.log(`\x1b[31m[UNKNOWN ENDPOINT]\x1b[0m ${req.method} ${urlPath}`);
|
||||
const requestHost = req.headers.host;
|
||||
const authGrant = auth.generateAuthorizationGrant(uuid, name, crypto.randomUUID(), null, requestHost);
|
||||
const accessToken = auth.generateIdentityToken(uuid, name, null, ['game.base'], requestHost);
|
||||
sendJson(res, 200, {
|
||||
debug: true,
|
||||
message: 'Unknown endpoint captured for research',
|
||||
endpoint: urlPath,
|
||||
method: req.method,
|
||||
identityToken: accessToken,
|
||||
sessionToken: auth.generateSessionToken(uuid, requestHost),
|
||||
authorizationGrant: authGrant,
|
||||
accessToken: accessToken,
|
||||
tokenType: 'Bearer',
|
||||
user: { uuid, name, premium: true }
|
||||
});
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Failed to start debug server:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
43
local-dev/docker-compose.yml
Normal file
43
local-dev/docker-compose.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
services:
|
||||
# Redis/Kvrocks for session storage
|
||||
kvrocks:
|
||||
image: apache/kvrocks:latest
|
||||
command: --bind 0.0.0.0 --dir /data
|
||||
ports:
|
||||
- "6666:6666"
|
||||
volumes:
|
||||
- ./data/kvrocks:/data
|
||||
|
||||
# Use the real hytale-auth-server with debug wrapper
|
||||
auth-server:
|
||||
build:
|
||||
context: ../../hytale-auth-server
|
||||
dockerfile: ../Hytale-F2P/local-dev/Dockerfile.debug
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "3443:3443"
|
||||
environment:
|
||||
- PORT=3000
|
||||
- HTTPS_PORT=3443
|
||||
- DOMAIN=localhost:3443
|
||||
- USE_TLS=false
|
||||
- DATA_DIR=/app/data
|
||||
- REDIS_URL=redis://kvrocks:6666
|
||||
- ADMIN_PASSWORD=localdev
|
||||
- DEBUG_MODE=true
|
||||
- KEY_ID=2025-10-01-sanasol
|
||||
volumes:
|
||||
- ./data/auth:/app/data
|
||||
# Mount debug wrapper as entry point
|
||||
- ./debug-wrapper.js:/app/src/debug-wrapper.js:ro
|
||||
depends_on:
|
||||
- kvrocks
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: hytale-local-dev
|
||||
88
local-dev/start.sh
Executable file
88
local-dev/start.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Hytale F2P Local Development - Start Script
|
||||
# Usage: ./start.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
CYAN='\033[0;36m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}========================================"
|
||||
echo "Hytale F2P Local Dev Environment"
|
||||
echo -e "========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Create data directories
|
||||
mkdir -p "$SCRIPT_DIR/data/auth" "$SCRIPT_DIR/data/kvrocks"
|
||||
|
||||
# Start Docker services
|
||||
echo -e "${GREEN}Building and starting services...${NC}"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Stop existing
|
||||
docker compose down 2>/dev/null || true
|
||||
|
||||
# Build and start
|
||||
docker compose up -d --build
|
||||
|
||||
# Wait for ready
|
||||
echo -n "Waiting for server"
|
||||
for i in {1..30}; do
|
||||
if curl -s http://localhost:3000/health > /dev/null 2>&1; then
|
||||
echo ""
|
||||
echo -e "${GREEN}Server ready!${NC}"
|
||||
break
|
||||
fi
|
||||
if [ $i -eq 30 ]; then
|
||||
echo ""
|
||||
echo -e "${RED}Server failed to start! Check logs:${NC}"
|
||||
docker compose logs auth-server
|
||||
exit 1
|
||||
fi
|
||||
echo -n "."
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}========================================"
|
||||
echo "Services Running"
|
||||
echo -e "========================================${NC}"
|
||||
echo ""
|
||||
docker compose ps
|
||||
echo ""
|
||||
echo -e "${CYAN}Endpoints:${NC}"
|
||||
echo -e " Auth Server HTTP: ${GREEN}http://localhost:3000${NC}"
|
||||
echo -e " Auth Server HTTPS: ${GREEN}https://localhost:3443${NC}"
|
||||
echo -e " Debug Dashboard: ${GREEN}http://localhost:3000/debug${NC}"
|
||||
echo -e " Admin Dashboard: ${GREEN}http://localhost:3000/admin${NC} (password: localdev)"
|
||||
echo -e " JWKS: ${GREEN}http://localhost:3000/.well-known/jwks.json${NC}"
|
||||
echo -e " Kvrocks: localhost:6666"
|
||||
echo ""
|
||||
echo -e "${CYAN}========================================"
|
||||
echo "Using with Launcher"
|
||||
echo -e "========================================${NC}"
|
||||
echo ""
|
||||
echo -e "Start launcher with HTTPS (recommended):"
|
||||
echo -e " ${GREEN}cd $PROJECT_ROOT${NC}"
|
||||
echo -e " ${GREEN}HYTALE_AUTH_DOMAIN=localhost:3443 NODE_TLS_REJECT_UNAUTHORIZED=0 npm start${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}First time SSL setup (macOS):${NC}"
|
||||
echo -e " Add cert to keychain to avoid NODE_TLS_REJECT_UNAUTHORIZED:"
|
||||
echo -e " ${GREEN}sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain data/auth/ssl/server.crt${NC}"
|
||||
echo ""
|
||||
echo -e "${CYAN}========================================"
|
||||
echo "Useful Commands"
|
||||
echo -e "========================================${NC}"
|
||||
echo ""
|
||||
echo -e "View logs: ${GREEN}docker compose logs -f auth-server${NC}"
|
||||
echo -e "Stop services: ${GREEN}docker compose down${NC}"
|
||||
echo -e "Restart: ${GREEN}docker compose restart auth-server${NC}"
|
||||
echo -e "Check request log: ${GREEN}cat data/auth/debug-requests.jsonl | jq${NC}"
|
||||
echo ""
|
||||
391
main.js
391
main.js
@@ -3,9 +3,10 @@ require('dotenv').config({ path: path.join(__dirname, '.env') });
|
||||
const { app, BrowserWindow, ipcMain, dialog, shell } = require('electron');
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
const fs = require('fs');
|
||||
const { launchGame, launchGameWithVersionCheck, installGame, saveUsername, loadUsername, saveJavaPath, loadJavaPath, saveInstallPath, loadInstallPath, saveDiscordRPC, loadDiscordRPC, saveLanguage, loadLanguage, saveCloseLauncherOnStart, loadCloseLauncherOnStart, saveLauncherHardwareAcceleration, loadLauncherHardwareAcceleration, isGameInstalled, uninstallGame, repairGame, getHytaleNews, handleFirstLaunchCheck, proposeGameUpdate, markAsLaunched, loadConfig, saveConfig, checkLaunchReady } = require('./backend/launcher');
|
||||
const { launchGame, launchGameWithVersionCheck, installGame, saveUsername, loadUsername, saveJavaPath, loadJavaPath, saveInstallPath, loadInstallPath, saveDiscordRPC, loadDiscordRPC, saveLanguage, loadLanguage, saveCloseLauncherOnStart, loadCloseLauncherOnStart, saveLauncherHardwareAcceleration, loadLauncherHardwareAcceleration, saveAllowMultiInstance, loadAllowMultiInstance, isGameInstalled, uninstallGame, repairGame, getHytaleNews, handleFirstLaunchCheck, proposeGameUpdate, markAsLaunched, loadConfig, saveConfig, checkLaunchReady } = require('./backend/launcher');
|
||||
const { retryPWRDownload } = require('./backend/managers/gameManager');
|
||||
const { migrateUserDataToCentralized } = require('./backend/utils/userDataMigration');
|
||||
const matchaService = require('./backend/services/matchaService');
|
||||
|
||||
// Handle Hardware Acceleration
|
||||
try {
|
||||
@@ -23,8 +24,9 @@ const profileManager = require('./backend/managers/profileManager');
|
||||
|
||||
logger.interceptConsole();
|
||||
|
||||
// Single instance lock
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
// Single instance lock (skip if multi-instance mode is enabled)
|
||||
const multiInstanceEnabled = loadAllowMultiInstance();
|
||||
const gotTheLock = multiInstanceEnabled || app.requestSingleInstanceLock();
|
||||
|
||||
if (!gotTheLock) {
|
||||
console.log('Another instance is already running. Quitting...');
|
||||
@@ -88,8 +90,9 @@ function setDiscordActivity() {
|
||||
url: 'https://git.sanhost.net/sanasol/hytale-f2p/releases'
|
||||
},
|
||||
{
|
||||
label: 'Discord',
|
||||
url: 'https://discord.gg/Fhbb9Yk5WW'
|
||||
label: 'Community',
|
||||
// url: 'https://discord.gg/Fhbb9Yk5WW'
|
||||
url: 'https://chat.sanhost.net/invite/Tfz4jCK4'
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -191,7 +194,7 @@ function createWindow() {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
devTools: false,
|
||||
devTools: process.argv.includes('--dev'),
|
||||
webSecurity: true
|
||||
}
|
||||
});
|
||||
@@ -211,6 +214,9 @@ function createWindow() {
|
||||
// Initialize Discord Rich Presence
|
||||
initDiscordRPC();
|
||||
|
||||
// Initialize Matcha Social service
|
||||
matchaService.init(mainWindow);
|
||||
|
||||
// Configure and initialize electron-updater
|
||||
// Enable auto-download so updates start immediately when available
|
||||
autoUpdater.autoDownload = true;
|
||||
@@ -249,7 +255,7 @@ function createWindow() {
|
||||
mainWindow.webContents.send('update-error', {
|
||||
message: err.message,
|
||||
isMacSigningError: isMacSigningError,
|
||||
requiresManualDownload: isMacSigningError || process.platform === 'darwin'
|
||||
requiresManualDownload: isMacSigningError
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -270,9 +276,7 @@ function createWindow() {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('update-downloaded', {
|
||||
version: info.version,
|
||||
platform: process.platform,
|
||||
// macOS auto-install often fails on unsigned apps
|
||||
autoInstallSupported: process.platform !== 'darwin'
|
||||
platform: process.platform
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -284,9 +288,12 @@ function createWindow() {
|
||||
});
|
||||
}, 3000);
|
||||
|
||||
mainWindow.webContents.on('devtools-opened', () => {
|
||||
mainWindow.webContents.closeDevTools();
|
||||
});
|
||||
const isDev = process.argv.includes('--dev');
|
||||
if (!isDev) {
|
||||
mainWindow.webContents.on('devtools-opened', () => {
|
||||
mainWindow.webContents.closeDevTools();
|
||||
});
|
||||
}
|
||||
|
||||
mainWindow.webContents.on('before-input-event', (event, input) => {
|
||||
// Allow standard copy/paste/cut/select-all shortcuts
|
||||
@@ -299,18 +306,20 @@ function createWindow() {
|
||||
return; // Don't block these
|
||||
}
|
||||
|
||||
// Block devtools shortcuts
|
||||
if (input.control && input.shift && input.key.toLowerCase() === 'i') {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (input.control && input.shift && input.key.toLowerCase() === 'j') {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (input.control && input.shift && input.key.toLowerCase() === 'c') {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (input.key === 'F12') {
|
||||
event.preventDefault();
|
||||
// Block devtools shortcuts (except in dev mode)
|
||||
if (!isDev) {
|
||||
if (input.control && input.shift && input.key.toLowerCase() === 'i') {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (input.control && input.shift && input.key.toLowerCase() === 'j') {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (input.control && input.shift && input.key.toLowerCase() === 'c') {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (input.key === 'F12') {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
if (input.key === 'F5') {
|
||||
event.preventDefault();
|
||||
@@ -504,6 +513,7 @@ async function cleanupDiscordRPC() {
|
||||
app.on('before-quit', () => {
|
||||
console.log('=== LAUNCHER BEFORE QUIT ===');
|
||||
cleanupDiscordRPC();
|
||||
matchaService.destroy();
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
@@ -528,9 +538,34 @@ ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, g
|
||||
}
|
||||
};
|
||||
|
||||
const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath, gpuPreference);
|
||||
// Check if UUID has password before launching
|
||||
let launchOptions = {};
|
||||
const { getAuthServerUrl, getUuidForUser } = require('./backend/core/config');
|
||||
const launchUuid = getUuidForUser(playerName);
|
||||
try {
|
||||
const uuid = launchUuid;
|
||||
const authServerUrl = getAuthServerUrl();
|
||||
const statusResp = await fetch(`${authServerUrl}/player/password/status/${uuid}`);
|
||||
if (statusResp.ok) {
|
||||
const status = await statusResp.json();
|
||||
if (status.hasPassword) {
|
||||
// Return to renderer to prompt for password
|
||||
return { success: false, passwordRequired: true, uuid };
|
||||
}
|
||||
}
|
||||
} catch (pwErr) {
|
||||
console.log('[Launch] Password check skipped:', pwErr.message);
|
||||
}
|
||||
|
||||
const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath, gpuPreference, null, launchOptions);
|
||||
|
||||
if (result.success && result.launched) {
|
||||
// Save last played timestamp
|
||||
try { saveConfig({ last_played: Date.now() }); } catch (e) { /* ignore */ }
|
||||
|
||||
// Notify Matcha that game is running (heartbeat will send 'in_game')
|
||||
matchaService.setGameRunning(true);
|
||||
|
||||
const closeOnStart = loadCloseLauncherOnStart();
|
||||
if (closeOnStart) {
|
||||
console.log('Close Launcher on start enabled, quitting application...');
|
||||
@@ -552,10 +587,72 @@ ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, g
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
if (error.passwordRequired) {
|
||||
return { success: false, passwordRequired: true, uuid: launchUuid, error: 'Password required' };
|
||||
}
|
||||
if (error.lockedOut) {
|
||||
return { success: false, error: 'Too many failed attempts. Try again in ' + Math.ceil((error.lockoutSeconds || 900) / 60) + ' minutes.' };
|
||||
}
|
||||
if (error.usernameTaken) {
|
||||
return { success: false, usernameTaken: true, error: errorMessage };
|
||||
}
|
||||
if (error.nameLocked) {
|
||||
return { success: false, nameLocked: true, registeredName: error.registeredName, error: error.message };
|
||||
}
|
||||
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('launch-game-with-password', async (event, playerName, javaPath, installPath, gpuPreference, password) => {
|
||||
try {
|
||||
const progressCallback = (message, percent, speed, downloaded, total, retryState) => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('progress-update', {
|
||||
message: message || null,
|
||||
percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null,
|
||||
speed: speed !== null && speed !== undefined ? speed : null,
|
||||
downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null,
|
||||
total: total !== null && total !== undefined ? total : null,
|
||||
retryState: retryState || null
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath, gpuPreference, null, { password });
|
||||
|
||||
if (result.success && result.launched) {
|
||||
try { saveConfig({ last_played: Date.now() }); } catch (e) { /* ignore */ }
|
||||
|
||||
matchaService.setGameRunning(true);
|
||||
|
||||
const closeOnStart = loadCloseLauncherOnStart();
|
||||
if (closeOnStart) {
|
||||
setTimeout(() => { app.quit(); }, 1000);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Launch with password error:', error);
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
setTimeout(() => { mainWindow.webContents.send('progress-complete'); }, 2000);
|
||||
}
|
||||
if (error.passwordRequired) {
|
||||
return { success: false, passwordRequired: true, error: 'Incorrect password. ' + (error.attemptsRemaining != null ? error.attemptsRemaining + ' attempts remaining.' : '') };
|
||||
}
|
||||
if (error.lockedOut) {
|
||||
return { success: false, error: 'Too many failed attempts. Try again in ' + Math.ceil((error.lockoutSeconds || 900) / 60) + ' minutes.' };
|
||||
}
|
||||
if (error.usernameTaken) {
|
||||
return { success: false, usernameTaken: true, error: error.message || error.toString() };
|
||||
}
|
||||
if (error.nameLocked) {
|
||||
return { success: false, nameLocked: true, registeredName: error.registeredName, error: error.message };
|
||||
}
|
||||
return { success: false, error: error.message || error.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('install-game', async (event, playerName, javaPath, installPath, branch) => {
|
||||
try {
|
||||
console.log(`[IPC] install-game called with parameters:`);
|
||||
@@ -739,6 +836,15 @@ ipcMain.handle('load-close-launcher', () => {
|
||||
return loadCloseLauncherOnStart();
|
||||
});
|
||||
|
||||
ipcMain.handle('save-allow-multi-instance', (event, enabled) => {
|
||||
saveAllowMultiInstance(enabled);
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
ipcMain.handle('load-allow-multi-instance', () => {
|
||||
return loadAllowMultiInstance();
|
||||
});
|
||||
|
||||
ipcMain.handle('save-launcher-hw-accel', (event, enabled) => {
|
||||
saveLauncherHardwareAcceleration(enabled);
|
||||
return { success: true };
|
||||
@@ -1278,6 +1384,11 @@ ipcMain.handle('get-detected-gpu', () => {
|
||||
ipcMain.handle('save-version-branch', (event, branch) => {
|
||||
const { saveVersionBranch } = require('./backend/launcher');
|
||||
saveVersionBranch(branch);
|
||||
// Sync to active profile
|
||||
const activeProfile = profileManager.getActiveProfile();
|
||||
if (activeProfile) {
|
||||
profileManager.updateProfile(activeProfile.id, { versionBranch: branch });
|
||||
}
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
@@ -1291,6 +1402,43 @@ ipcMain.handle('load-version-client', () => {
|
||||
return loadVersionClient();
|
||||
});
|
||||
|
||||
ipcMain.handle('get-game-info', async () => {
|
||||
const { loadVersionClient, loadVersionBranch } = require('./backend/launcher');
|
||||
const { fetchMirrorManifest } = require('./backend/services/versionManager');
|
||||
const config = loadConfig();
|
||||
const branch = loadVersionBranch();
|
||||
|
||||
let version = null;
|
||||
let readableVersion = null;
|
||||
try {
|
||||
const manifest = await fetchMirrorManifest();
|
||||
if (manifest?.versions?.[branch]) {
|
||||
const branchVersions = manifest.versions[branch];
|
||||
// Get the highest version number for the current branch
|
||||
const nums = Object.keys(branchVersions).map(Number).filter(n => !isNaN(n));
|
||||
if (nums.length > 0) {
|
||||
const latest = Math.max(...nums).toString();
|
||||
version = `v${latest}`;
|
||||
readableVersion = branchVersions[latest]?.version || null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Manifest fetch failed, fall back to stored version
|
||||
version = loadVersionClient();
|
||||
}
|
||||
|
||||
if (!version) {
|
||||
version = loadVersionClient();
|
||||
}
|
||||
|
||||
return {
|
||||
version,
|
||||
readableVersion,
|
||||
branch,
|
||||
lastPlayed: config.last_played || null
|
||||
};
|
||||
});
|
||||
|
||||
ipcMain.handle('window-close', () => {
|
||||
app.quit();
|
||||
});
|
||||
@@ -1341,9 +1489,12 @@ ipcMain.handle('get-all-uuid-mappings', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('set-uuid-for-user', async (event, username, uuid) => {
|
||||
ipcMain.handle('set-uuid-for-user', async (event, username, uuid, force) => {
|
||||
try {
|
||||
await setUuidForUser(username, uuid);
|
||||
const result = setUuidForUser(username, uuid, { force: !!force });
|
||||
if (result && result.success === false) {
|
||||
return result; // { success: false, error: 'duplicate', existingUuid }
|
||||
}
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error setting UUID for user:', error);
|
||||
@@ -1380,6 +1531,88 @@ ipcMain.handle('reset-current-user-uuid', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Password Management IPC handlers
|
||||
ipcMain.handle('check-password-status', async (event, uuid) => {
|
||||
try {
|
||||
const { getAuthServerUrl } = require('./backend/core/config');
|
||||
const authServerUrl = getAuthServerUrl();
|
||||
const response = await fetch(`${authServerUrl}/player/password/status/${uuid}`);
|
||||
if (!response.ok) return { hasPassword: false };
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error checking password status:', error);
|
||||
return { hasPassword: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('set-player-password', async (event, uuid, password, currentPassword) => {
|
||||
try {
|
||||
const { getAuthServerUrl } = require('./backend/core/config');
|
||||
const { getUuidForUser, loadUsername } = require('./backend/core/config');
|
||||
const authServerUrl = getAuthServerUrl();
|
||||
// First get a bearer token for auth
|
||||
const name = loadUsername() || 'Player';
|
||||
const tokenResp = await fetch(`${authServerUrl}/game-session/child`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ uuid, name, password: currentPassword || undefined })
|
||||
});
|
||||
if (!tokenResp.ok) {
|
||||
const err = await tokenResp.json().catch(() => ({}));
|
||||
return { success: false, error: err.error || 'Failed to authenticate' };
|
||||
}
|
||||
const tokenData = await tokenResp.json();
|
||||
const bearerToken = tokenData.identityToken || tokenData.IdentityToken;
|
||||
|
||||
const response = await fetch(`${authServerUrl}/player/password/set`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${bearerToken}`
|
||||
},
|
||||
body: JSON.stringify({ uuid, password, currentPassword: currentPassword || undefined })
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error setting password:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('remove-player-password', async (event, uuid, currentPassword) => {
|
||||
try {
|
||||
const { getAuthServerUrl } = require('./backend/core/config');
|
||||
const { loadUsername } = require('./backend/core/config');
|
||||
const authServerUrl = getAuthServerUrl();
|
||||
const name = loadUsername() || 'Player';
|
||||
// Get bearer token with current password
|
||||
const tokenResp = await fetch(`${authServerUrl}/game-session/child`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ uuid, name, password: currentPassword })
|
||||
});
|
||||
if (!tokenResp.ok) {
|
||||
const err = await tokenResp.json().catch(() => ({}));
|
||||
return { success: false, error: err.error || 'Failed to authenticate' };
|
||||
}
|
||||
const tokenData = await tokenResp.json();
|
||||
const bearerToken = tokenData.identityToken || tokenData.IdentityToken;
|
||||
|
||||
const response = await fetch(`${authServerUrl}/player/password/remove`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${bearerToken}`
|
||||
},
|
||||
body: JSON.stringify({ uuid, currentPassword })
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error removing password:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-recent-logs', async (event, maxLines = 100) => {
|
||||
try {
|
||||
const logDir = logger.getLogDirectory();
|
||||
@@ -1586,6 +1819,108 @@ ipcMain.handle('preview-wrapper-script', (event, config, platform) => {
|
||||
return generateWrapperScript(config || require('./backend/launcher').loadWrapperConfig(), platform || process.platform, '/path/to/java');
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// MATCHA SOCIAL IPC HANDLERS
|
||||
// =============================================================================
|
||||
|
||||
ipcMain.handle('matcha:log', (event, level, ...args) => {
|
||||
const prefix = '[Matcha/Renderer]';
|
||||
if (level === 'error') console.error(prefix, ...args);
|
||||
else if (level === 'warn') console.warn(prefix, ...args);
|
||||
else console.log(prefix, ...args);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:register', async (event, username, password, password2) => {
|
||||
return matchaService.register(username, password, password2);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:confirm-register', async (event, pendingId, proofId) => {
|
||||
return matchaService.confirmRegistration(pendingId, proofId);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:login', async (event, handle, password) => {
|
||||
return matchaService.login(handle, password);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:logout', async () => {
|
||||
return matchaService.logout();
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:get-auth-state', () => {
|
||||
return matchaService.getAuthState();
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:get-me', async () => {
|
||||
return matchaService.getMe();
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:get-user', async (event, userId) => {
|
||||
return matchaService.getUser(userId);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:get-friends', async () => {
|
||||
return matchaService.getFriends();
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:friend-request', async (event, handle) => {
|
||||
return matchaService.sendFriendRequest(handle);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:friend-accept', async (event, requestId) => {
|
||||
return matchaService.acceptFriend(requestId);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:friend-reject', async (event, requestId) => {
|
||||
return matchaService.rejectFriend(requestId);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:friend-cancel', async (event, requestId) => {
|
||||
return matchaService.cancelFriendRequest(requestId);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:friend-remove', async (event, friendId) => {
|
||||
return matchaService.removeFriend(friendId);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:get-messages', async (event, withTarget, cursor, after) => {
|
||||
return matchaService.getMessages(withTarget, cursor, after);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:send-message', async (event, to, body, replyTo) => {
|
||||
return matchaService.sendMessage(to, body, replyTo);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:delete-message', async (event, messageId) => {
|
||||
return matchaService.deleteMessage(messageId);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:get-unread', async () => {
|
||||
return matchaService.getUnread();
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:clear-unread', async (event, withTarget) => {
|
||||
return matchaService.clearUnread(withTarget);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:upload-avatar', async (event, mode) => {
|
||||
const result = await dialog.showOpenDialog(mainWindow, {
|
||||
title: 'Select Avatar Image',
|
||||
filters: [{ name: 'PNG Images', extensions: ['png'] }],
|
||||
properties: ['openFile']
|
||||
});
|
||||
if (result.canceled || !result.filePaths[0]) return { ok: false, error: 'Cancelled' };
|
||||
return matchaService.uploadAvatar(result.filePaths[0], mode);
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:delete-avatar', async () => {
|
||||
return matchaService.deleteAvatar();
|
||||
});
|
||||
|
||||
ipcMain.handle('matcha:reconnect', () => {
|
||||
matchaService.manualReconnect();
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
ipcMain.handle('get-current-platform', () => {
|
||||
return process.platform;
|
||||
});
|
||||
|
||||
54
package-lock.json
generated
54
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "hytale-f2p-launcher",
|
||||
"version": "2.4.2",
|
||||
"version": "2.4.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hytale-f2p-launcher",
|
||||
"version": "2.4.2",
|
||||
"version": "2.4.8",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.10",
|
||||
@@ -17,7 +17,8 @@
|
||||
"encoding": "^0.1.13",
|
||||
"fs-extra": "^11.3.3",
|
||||
"tar": "^7.5.7",
|
||||
"uuid": "^9.0.1"
|
||||
"uuid": "^9.0.1",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^40.0.0",
|
||||
@@ -335,6 +336,7 @@
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cross-dirname": "^0.1.0",
|
||||
"debug": "^4.3.4",
|
||||
@@ -789,7 +791,6 @@
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -1592,7 +1593,8 @@
|
||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
@@ -1803,6 +1805,27 @@
|
||||
"register-scheme": "github:devsnek/node-register-scheme"
|
||||
}
|
||||
},
|
||||
"node_modules/discord-rpc/node_modules/ws": {
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/dmg-builder": {
|
||||
"version": "26.6.0",
|
||||
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.6.0.tgz",
|
||||
@@ -2094,6 +2117,7 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@electron/asar": "^3.2.1",
|
||||
"debug": "^4.1.1",
|
||||
@@ -2114,6 +2138,7 @@
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
@@ -2129,6 +2154,7 @@
|
||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
@@ -2139,6 +2165,7 @@
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
@@ -2155,7 +2182,6 @@
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"iconv-lite": "^0.6.2"
|
||||
}
|
||||
@@ -3399,6 +3425,7 @@
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
@@ -3751,7 +3778,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -3781,6 +3807,7 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"commander": "^9.4.0"
|
||||
},
|
||||
@@ -3798,6 +3825,7 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
@@ -4006,6 +4034,7 @@
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
@@ -4403,6 +4432,7 @@
|
||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "~2.6.2"
|
||||
@@ -4712,16 +4742,16 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "hytale-f2p-launcher",
|
||||
"version": "2.4.2",
|
||||
"version": "2.4.8",
|
||||
"description": "A modern, cross-platform launcher for Hytale with automatic updates and multi-client support",
|
||||
"homepage": "https://git.sanhost.net/sanasol/hytale-f2p",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"dev": "electron . --dev",
|
||||
"test": "node backend/utils/__tests__/clientPatcher.test.js",
|
||||
"build": "electron-builder",
|
||||
"build:win": "electron-builder --win",
|
||||
"build:linux": "electron-builder --linux",
|
||||
@@ -56,7 +57,8 @@
|
||||
"electron-updater": "^6.7.3",
|
||||
"fs-extra": "^11.3.3",
|
||||
"tar": "^7.5.7",
|
||||
"uuid": "^9.0.1"
|
||||
"uuid": "^9.0.1",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.hytalef2p.launcher",
|
||||
|
||||
49
preload.js
49
preload.js
@@ -2,6 +2,7 @@ const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
launchGame: (playerName, javaPath, installPath, gpuPreference) => ipcRenderer.invoke('launch-game', playerName, javaPath, installPath, gpuPreference),
|
||||
launchGameWithPassword: (playerName, javaPath, installPath, gpuPreference, password) => ipcRenderer.invoke('launch-game-with-password', playerName, javaPath, installPath, gpuPreference, password),
|
||||
installGame: (playerName, javaPath, installPath, branch) => ipcRenderer.invoke('install-game', playerName, javaPath, installPath, branch),
|
||||
closeWindow: () => ipcRenderer.invoke('window-close'),
|
||||
minimizeWindow: () => ipcRenderer.invoke('window-minimize'),
|
||||
@@ -20,6 +21,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
loadLanguage: () => ipcRenderer.invoke('load-language'),
|
||||
saveCloseLauncher: (enabled) => ipcRenderer.invoke('save-close-launcher', enabled),
|
||||
loadCloseLauncher: () => ipcRenderer.invoke('load-close-launcher'),
|
||||
saveAllowMultiInstance: (enabled) => ipcRenderer.invoke('save-allow-multi-instance', enabled),
|
||||
loadAllowMultiInstance: () => ipcRenderer.invoke('load-allow-multi-instance'),
|
||||
loadConfig: () => ipcRenderer.invoke('load-config'),
|
||||
saveConfig: (configUpdate) => ipcRenderer.invoke('save-config', configUpdate),
|
||||
|
||||
@@ -76,6 +79,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
saveVersionBranch: (branch) => ipcRenderer.invoke('save-version-branch', branch),
|
||||
loadVersionBranch: () => ipcRenderer.invoke('load-version-branch'),
|
||||
loadVersionClient: () => ipcRenderer.invoke('load-version-client'),
|
||||
getGameInfo: () => ipcRenderer.invoke('get-game-info'),
|
||||
|
||||
acceptFirstLaunchUpdate: (existingGame) => ipcRenderer.invoke('accept-first-launch-update', existingGame),
|
||||
markAsLaunched: () => ipcRenderer.invoke('mark-as-launched'),
|
||||
@@ -100,11 +104,20 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
// UUID Management methods
|
||||
getCurrentUuid: () => ipcRenderer.invoke('get-current-uuid'),
|
||||
getAllUuidMappings: () => ipcRenderer.invoke('get-all-uuid-mappings'),
|
||||
setUuidForUser: (username, uuid) => ipcRenderer.invoke('set-uuid-for-user', username, uuid),
|
||||
setUuidForUser: (username, uuid, force) => ipcRenderer.invoke('set-uuid-for-user', username, uuid, force),
|
||||
generateNewUuid: () => ipcRenderer.invoke('generate-new-uuid'),
|
||||
deleteUuidForUser: (username) => ipcRenderer.invoke('delete-uuid-for-user', username),
|
||||
resetCurrentUserUuid: () => ipcRenderer.invoke('reset-current-user-uuid'),
|
||||
|
||||
// Password Management methods
|
||||
checkPasswordStatus: (uuid) => ipcRenderer.invoke('check-password-status', uuid),
|
||||
setPlayerPassword: (uuid, password, currentPassword) => ipcRenderer.invoke('set-player-password', uuid, password, currentPassword),
|
||||
removePlayerPassword: (uuid, currentPassword) => ipcRenderer.invoke('remove-player-password', uuid, currentPassword),
|
||||
promptPassword: () => ipcRenderer.invoke('prompt-password'),
|
||||
onPasswordPrompt: (callback) => {
|
||||
ipcRenderer.on('show-password-prompt', (event, data) => callback(data));
|
||||
},
|
||||
|
||||
// Java Wrapper Config API
|
||||
loadWrapperConfig: () => ipcRenderer.invoke('load-wrapper-config'),
|
||||
saveWrapperConfig: (config) => ipcRenderer.invoke('save-wrapper-config', config),
|
||||
@@ -140,5 +153,39 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
},
|
||||
onUpdateError: (callback) => {
|
||||
ipcRenderer.on('update-error', (event, data) => callback(data));
|
||||
},
|
||||
|
||||
// Matcha Social API
|
||||
matcha: {
|
||||
log: (level, ...args) => ipcRenderer.invoke('matcha:log', level, ...args),
|
||||
register: (username, password, password2) => ipcRenderer.invoke('matcha:register', username, password, password2),
|
||||
confirmRegister: (pendingId, proofId) => ipcRenderer.invoke('matcha:confirm-register', pendingId, proofId),
|
||||
login: (handle, password) => ipcRenderer.invoke('matcha:login', handle, password),
|
||||
logout: () => ipcRenderer.invoke('matcha:logout'),
|
||||
getAuthState: () => ipcRenderer.invoke('matcha:get-auth-state'),
|
||||
getMe: () => ipcRenderer.invoke('matcha:get-me'),
|
||||
getUser: (userId) => ipcRenderer.invoke('matcha:get-user', userId),
|
||||
getFriends: () => ipcRenderer.invoke('matcha:get-friends'),
|
||||
friendRequest: (handle) => ipcRenderer.invoke('matcha:friend-request', handle),
|
||||
friendAccept: (requestId) => ipcRenderer.invoke('matcha:friend-accept', requestId),
|
||||
friendReject: (requestId) => ipcRenderer.invoke('matcha:friend-reject', requestId),
|
||||
friendCancel: (requestId) => ipcRenderer.invoke('matcha:friend-cancel', requestId),
|
||||
friendRemove: (friendId) => ipcRenderer.invoke('matcha:friend-remove', friendId),
|
||||
getMessages: (withTarget, cursor, after) => ipcRenderer.invoke('matcha:get-messages', withTarget, cursor, after),
|
||||
sendMessage: (to, body, replyTo) => ipcRenderer.invoke('matcha:send-message', to, body, replyTo),
|
||||
deleteMessage: (messageId) => ipcRenderer.invoke('matcha:delete-message', messageId),
|
||||
getUnread: () => ipcRenderer.invoke('matcha:get-unread'),
|
||||
clearUnread: (withTarget) => ipcRenderer.invoke('matcha:clear-unread', withTarget),
|
||||
uploadAvatar: (mode) => ipcRenderer.invoke('matcha:upload-avatar', mode),
|
||||
deleteAvatar: () => ipcRenderer.invoke('matcha:delete-avatar'),
|
||||
reconnect: () => ipcRenderer.invoke('matcha:reconnect'),
|
||||
onWsMessage: (callback) => ipcRenderer.on('matcha:ws:message', (event, data) => callback(data)),
|
||||
onWsConnected: (callback) => ipcRenderer.on('matcha:ws:connected', (event, data) => callback(data)),
|
||||
onWsDisconnected: (callback) => ipcRenderer.on('matcha:ws:disconnected', () => callback()),
|
||||
onMessageDeleted: (callback) => ipcRenderer.on('matcha:ws:message-deleted', (event, data) => callback(data)),
|
||||
onAvatarUpdated: (callback) => ipcRenderer.on('matcha:ws:avatar-updated', (event, data) => callback(data)),
|
||||
onBanned: (callback) => ipcRenderer.on('matcha:ws:banned', (event, data) => callback(data)),
|
||||
onAnnouncement: (callback) => ipcRenderer.on('matcha:ws:announcement', (event, data) => callback(data)),
|
||||
onMaxRetries: (callback) => ipcRenderer.on('matcha:ws:max-retries', () => callback())
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
> **Deprecated:** For the new F2P Evo launcher, see [F2P Evo Server Scripts](https://git.sanhost.net/sanasol/f2p-evo/src/branch/main/server/).
|
||||
|
||||
# Hytale F2P - Dedicated Server
|
||||
|
||||
Host your own Hytale server. The scripts handle everything automatically.
|
||||
@@ -128,6 +130,6 @@ your-folder/
|
||||
| Out of memory | Set more RAM: `JVM_XMX=4G ./start.sh` |
|
||||
| Friends can't connect | Forward port 5520 (TCP+UDP) on your router, or use [playit.gg](https://playit.gg) if you can't port forward |
|
||||
|
||||
## Discord
|
||||
## Community
|
||||
|
||||
Need help? Join the community: https://discord.gg/Fhbb9Yk5WW
|
||||
Need help? Join the community: [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4) | [TG Group](https://t.me/sanhostnet) | [TG Channel](https://t.me/hf2p_og)
|
||||
|
||||
19
test-wrapper/java-wrapper.bat
Normal file
19
test-wrapper/java-wrapper.bat
Normal file
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
REM Java wrapper for Windows - strips UseCompactObjectHeaders, adds --disable-sentry
|
||||
REM Uses %* string replacement instead of for-loop to preserve = signs in JVM flags
|
||||
set "REAL_JAVA=%~dp0java-original.exe"
|
||||
set "ARGS=%*"
|
||||
|
||||
REM Strip -XX:+UseCompactObjectHeaders
|
||||
set "ARGS=!ARGS:-XX:+UseCompactObjectHeaders=!"
|
||||
|
||||
REM Check if running HytaleServer.jar and add --disable-sentry
|
||||
echo !ARGS! | findstr /i "HytaleServer.jar" >nul 2>&1
|
||||
if !ERRORLEVEL!==0 (
|
||||
"%REAL_JAVA%" !ARGS! --disable-sentry
|
||||
) else (
|
||||
"%REAL_JAVA%" !ARGS!
|
||||
)
|
||||
exit /b !ERRORLEVEL!
|
||||
53
test.md
Normal file
53
test.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Steam Deck Debug Commands
|
||||
|
||||
## GDB Stack Trace
|
||||
|
||||
Run patched game under gdb to get crash location:
|
||||
|
||||
```bash
|
||||
cd ~/.hytalef2p/release/package/game/latest
|
||||
|
||||
gdb -ex "run --app-dir /home/deck/.hytalef2p/release/package/game/latest --java-exec /home/deck/.hytalef2p/release/package/jre/latest/bin/java --auth-mode authenticated --uuid c500dc3e-2791-4df6-be1e-5574600339c0 --name Player --identity-token eyJhbGciOiJFZERTQSIsImtpZCI6IjIwMjUtMTAtMDEtc2FuYXNvbCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjNTAwZGMzZS0yNzkxLTRkZjYtYmUxZS01NTc0NjAwMzM5YzAiLCJuYW1lIjoiUGxheWVyIiwidXNlcm5hbWUiOiJQbGF5ZXIiLCJwcm9maWxlIjp7InVzZXJuYW1lIjoiUGxheWVyIn0sImVudGl0bGVtZW50cyI6WyJnYW1lLmJhc2UiXSwic2NvcGUiOiJoeXRhbGU6c2VydmVyIGh5dGFsZTpjbGllbnQiLCJpYXQiOjE3Njk0NzkzMjgsImV4cCI6MTc2OTUxNTMyOCwiaXNzIjoiaHR0cHM6Ly9zZXNzaW9ucy5zYW5hc29sLndzIiwianRpIjoiYTAxYjI1MDItZWE1Mi00NTM2LWI3ODQtN2RjNzljMjlkZjM0In0.BtV846L9_rWRINFvDqrtg1ZJhIVQRNsrN550UULN9j5yhMxP1TJx4bDxS2bkpRtxVmkdcA_xhQWapRSlFywZAA --session-token eyJhbGciOiJFZERTQSIsImtpZCI6IjIwMjUtMTAtMDEtc2FuYXNvbCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjNTAwZGMzZS0yNzkxLTRkZjYtYmUxZS01NTc0NjAwMzM5YzAiLCJzY29wZSI6Imh5dGFsZTpzZXJ2ZXIiLCJpYXQiOjE3Njk0NzkzMjgsImV4cCI6MTc2OTUxNTMyOCwiaXNzIjoiaHR0cHM6Ly9zZXNzaW9ucy5zYW5hc29sLndzIiwianRpIjoiMzI0M2EwZTAtMjU2Mi00MzQ1LTljNGMtZWUyZDA5NDQ1NWI0In0.38ae2bCyAtjI0egmuy0v7u6caIi9hJGJFVeoeSvckQmaZMf-KaeHu4k5cF17d9eKXUxfEK_Ahjb1PTvIQhKtAA --user-dir /home/deck/.hytalesaves" ./Client/HytaleClient
|
||||
```
|
||||
|
||||
After crash, in gdb prompt:
|
||||
```
|
||||
bt
|
||||
bt full
|
||||
info registers
|
||||
quit
|
||||
```
|
||||
|
||||
## Hex Dump Commands
|
||||
|
||||
Search for full hytale.com UTF-16LE:
|
||||
```bash
|
||||
xxd ~/.hytalef2p/release/package/game/latest/Client/HytaleClient.original | grep "6800 7900 7400 6100 6c00 6500 2e00 6300 6f00 6d00"
|
||||
```
|
||||
|
||||
Search for partial matches:
|
||||
```bash
|
||||
xxd ~/.hytalef2p/release/package/game/latest/Client/HytaleClient.original | grep -i "h.y.t" | head -10
|
||||
```
|
||||
|
||||
## Test Different Patch Modes
|
||||
|
||||
Restore and test:
|
||||
```bash
|
||||
# Restore original
|
||||
cp ~/.hytalef2p/release/package/game/latest/Client/HytaleClient.original ~/.hytalef2p/release/package/game/latest/Client/HytaleClient
|
||||
rm ~/.hytalef2p/release/package/game/latest/Client/HytaleClient.patched_custom
|
||||
|
||||
# Test UTF-16LE mode with same-length domain
|
||||
HYTALE_PATCH_MODE=utf16le HYTALE_AUTH_DOMAIN=sanasol.ws HYTALE_SKIP_SENTRY_PATCH=1 HYTALE_SKIP_SUBDOMAIN_PATCH=1 npm start
|
||||
|
||||
# Test length-prefixed mode (default)
|
||||
HYTALE_AUTH_DOMAIN=sanasol.ws HYTALE_SKIP_SENTRY_PATCH=1 HYTALE_SKIP_SUBDOMAIN_PATCH=1 npm start
|
||||
```
|
||||
|
||||
## Binary Validation
|
||||
|
||||
```bash
|
||||
file ~/.hytalef2p/release/package/game/latest/Client/HytaleClient
|
||||
ldd ~/.hytalef2p/release/package/game/latest/Client/HytaleClient
|
||||
```
|
||||
Reference in New Issue
Block a user