mirror of
https://git.sanhost.net/sanasol/hytale-f2p
synced 2026-02-26 18:41:48 -03:00
Compare commits
26 Commits
9b20c454d3
...
v2.4.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7042188696 | ||
|
|
c067efbcea | ||
|
|
b83b728d21 | ||
|
|
66493d35ca | ||
|
|
89b9135523 | ||
|
|
0b1716c168 | ||
|
|
9628363455 | ||
|
|
4932a7a51c | ||
|
|
5170f453ea | ||
|
|
db3b2fc966 | ||
|
|
2f5820e850 | ||
|
|
4abb455e0f | ||
|
|
d5828463f9 | ||
|
|
0d15659dc0 | ||
|
|
19c8991a44 | ||
|
|
320ca54758 | ||
|
|
e14d56ef48 | ||
|
|
a649bf1fcc | ||
|
|
66faa1bb1e | ||
|
|
a63e026700 | ||
|
|
552ec42d6c | ||
|
|
fb90277be9 | ||
|
|
27c220a757 | ||
|
|
30929ee0da | ||
|
|
44834e7d12 | ||
|
|
cb7f7e51bf |
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).
|
||||
<!-- Discord: https://discord.gg/Fhbb9Yk5WW --> 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.
|
||||
|
||||
|
||||
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
|
||||
|
||||
236
GUI/index.html
236
GUI/index.html
@@ -8,9 +8,10 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Noto+Sans+Arabic:wght@300;400;500;600;700&display=swap"
|
||||
rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="stylesheet" href="style-RTL.css">
|
||||
</head>
|
||||
|
||||
<body class="bg-black text-white overflow-hidden font-sans select-none" tabindex="-1">
|
||||
@@ -56,8 +57,8 @@
|
||||
<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>
|
||||
<i class="fas fa-comments"></i>
|
||||
<span class="nav-tooltip">Community Chat</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -72,22 +73,40 @@
|
||||
<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 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">
|
||||
@@ -198,6 +217,21 @@
|
||||
<i class="fas fa-play"></i>
|
||||
<span data-i18n="play.playButton">PLAY HYTALE</span>
|
||||
</button>
|
||||
|
||||
<div style="display: flex; justify-content: center; align-items: center; gap: 8px; margin-top: 12px; font-size: 12px;">
|
||||
<span style="color: #93a3b8;">Telegram:</span>
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://t.me/sanhostnet'); return false;" style="color: #93a3b8; text-decoration: none; display: flex; align-items: center; gap: 4px; transition: color 0.2s;" onmouseover="this.style.color='#60a5fa'" onmouseout="this.style.color='#93a3b8'">
|
||||
<i class="fas fa-users"></i> Group
|
||||
</a>
|
||||
<span style="color: #4b5563;">|</span>
|
||||
<a href="#" onclick="window.electronAPI?.openExternal('https://t.me/hf2p_og'); return false;" style="color: #93a3b8; text-decoration: none; display: flex; align-items: center; gap: 4px; transition: color 0.2s;" onmouseover="this.style.color='#60a5fa'" onmouseout="this.style.color='#93a3b8'">
|
||||
<i class="fab fa-telegram"></i> Channel
|
||||
</a>
|
||||
<span style="color: #4b5563;">|</span>
|
||||
<a href="#" onclick="openDiscordExternal(); return false;" style="color: #93a3b8; text-decoration: none; display: flex; align-items: center; gap: 4px; transition: color 0.2s;" onmouseover="this.style.color='#60a5fa'" onmouseout="this.style.color='#93a3b8'">
|
||||
<i class="fas fa-comments"></i> Community Chat
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -429,8 +463,70 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3 class="settings-section-title">
|
||||
<i class="fas fa-scroll"></i>
|
||||
<span data-i18n="settings.wrapperConfig">Java Wrapper Configuration</span>
|
||||
</h3>
|
||||
<p class="settings-hint" style="margin-bottom: 12px;">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<span data-i18n="settings.wrapperConfigHint">Configure how the Java wrapper handles JVM flags and arguments at launch time.</span>
|
||||
</p>
|
||||
|
||||
<!-- Strip Flags -->
|
||||
<label class="settings-label" style="margin-bottom: 6px;">
|
||||
<span data-i18n="settings.wrapperStripFlags">JVM Flags to Remove</span>
|
||||
</label>
|
||||
<div id="wrapperStripFlagsList" class="wrapper-items-list"></div>
|
||||
<div style="display: flex; gap: 6px; margin-top: 6px;">
|
||||
<input type="text" id="wrapperAddFlagInput" class="settings-input" style="flex:1;"
|
||||
data-i18n-placeholder="settings.wrapperAddFlagPlaceholder" placeholder="e.g. -XX:+SomeFlag" spellcheck="false">
|
||||
<button id="wrapperAddFlagBtn" class="settings-browse-btn">
|
||||
<i class="fas fa-plus"></i>
|
||||
<span data-i18n="settings.wrapperAdd">Add</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Inject Args -->
|
||||
<label class="settings-label" style="margin-top: 16px; margin-bottom: 6px;">
|
||||
<span data-i18n="settings.wrapperInjectArgs">Arguments to Inject</span>
|
||||
</label>
|
||||
<div id="wrapperInjectArgsList" class="wrapper-items-list"></div>
|
||||
<div style="display: flex; gap: 6px; margin-top: 6px;">
|
||||
<input type="text" id="wrapperAddArgInput" class="settings-input" style="flex:1;"
|
||||
data-i18n-placeholder="settings.wrapperAddArgPlaceholder" placeholder="e.g. --some-flag" spellcheck="false">
|
||||
<select id="wrapperAddArgCondition" class="wrapper-condition-select">
|
||||
<option value="server" data-i18n="settings.wrapperConditionServer">Server Only</option>
|
||||
<option value="always" data-i18n="settings.wrapperConditionAlways">Always</option>
|
||||
</select>
|
||||
<button id="wrapperAddArgBtn" class="settings-browse-btn">
|
||||
<i class="fas fa-plus"></i>
|
||||
<span data-i18n="settings.wrapperAdd">Add</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Restore Defaults -->
|
||||
<div style="margin-top: 12px;">
|
||||
<button id="wrapperRestoreDefaultsBtn" class="settings-browse-btn" style="background: rgba(239, 68, 68, 0.2); border-color: rgba(239, 68, 68, 0.3);">
|
||||
<i class="fas fa-undo"></i>
|
||||
<span data-i18n="settings.wrapperRestoreDefaults">Restore Defaults</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Script Preview (collapsible) -->
|
||||
<div style="margin-top: 12px;">
|
||||
<button id="wrapperPreviewToggle" class="wrapper-preview-toggle">
|
||||
<i class="fas fa-chevron-right" id="wrapperPreviewChevron"></i>
|
||||
<span data-i18n="settings.wrapperAdvancedPreview">Advanced: Script Preview</span>
|
||||
</button>
|
||||
<div id="wrapperPreviewContainer" style="display: none; margin-top: 8px;">
|
||||
<pre id="wrapperPreviewContent" class="wrapper-preview-content"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="settings-column">
|
||||
<div class="settings-section">
|
||||
<h3 class="settings-section-title">
|
||||
@@ -574,6 +670,9 @@
|
||||
<i class="fas fa-folder-open"></i> <span data-i18n="settings.logsFolder">Open
|
||||
Folder</span>
|
||||
</button>
|
||||
<button class="logs-action-btn logs-send-btn" id="sendLogsBtn" onclick="sendLogs()">
|
||||
<i class="fas fa-paper-plane"></i> <span data-i18n="settings.logsSend">Send Logs</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="logsTerminal" class="logs-terminal">
|
||||
@@ -597,7 +696,11 @@
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mods-modal-body">
|
||||
<div class="mods-modal-body" style="padding-top: 0;">
|
||||
<div class="mods-search-container" style="margin: 1.5rem; margin-bottom: 1rem;">
|
||||
<i class="fas fa-search"></i>
|
||||
<input type="text" id="myModsSearch" placeholder="Search installed mods..." class="mods-search" />
|
||||
</div>
|
||||
<div id="installedModsList" class="installed-mods-list">
|
||||
</div>
|
||||
</div>
|
||||
@@ -651,28 +754,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>
|
||||
@@ -681,21 +796,27 @@
|
||||
</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>
|
||||
<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>
|
||||
@@ -706,8 +827,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>
|
||||
@@ -718,10 +839,10 @@
|
||||
<!-- 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>
|
||||
@@ -767,14 +888,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.
|
||||
@@ -782,11 +903,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
|
||||
@@ -801,6 +922,25 @@
|
||||
<script src="js/featured.js"></script>
|
||||
<script type="module" src="js/settings.js"></script>
|
||||
<script type="module" src="js/update.js"></script>
|
||||
|
||||
<!-- Version Selection Modal (Isolated Container) -->
|
||||
<div id="versionSelectModal" class="modal-overlay" style="display: none; position: fixed; inset: 0; z-index: 9999; background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(5px); align-items: center; justify-content: center;">
|
||||
<div class="glass-panel" style="width: 100%; max-width: 600px; max-height: 80vh; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; margin: 20px;">
|
||||
<div class="modal-header" style="display: flex; justify-content: space-between; align-items: center; padding: 1.5rem; border-bottom: 1px solid rgba(255, 255, 255, 0.1);">
|
||||
<h3 style="margin: 0; font-size: 1.25rem;">Select Version</h3>
|
||||
<button id="closeVersionModal" class="modal-close" style="background: none; border: none; color: #a0a0a0; font-size: 1.25rem; cursor: pointer;"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<div class="modal-body" style="padding: 1.5rem; overflow-y: auto;">
|
||||
<div id="versionList" class="version-list-container">
|
||||
<div class="loading-versions" style="display: flex; flex-direction: column; align-items: center; gap: 1rem; color: #a0a0a0;">
|
||||
<i class="fas fa-spinner fa-spin fa-2x"></i>
|
||||
<span>Loading versions...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- updater.js disabled - using update.js instead which has skip button and macOS handling -->
|
||||
</body>
|
||||
|
||||
|
||||
@@ -12,9 +12,18 @@ const i18n = (() => {
|
||||
{ code: 'ru-RU', name: 'Russian (Russia)' },
|
||||
{ code: 'sv-SE', name: 'Swedish (Sweden)' },
|
||||
{ code: 'tr-TR', name: 'Turkish (Turkey)' },
|
||||
{ code: 'id-ID', name: 'Indonesian (Indonesia)' }
|
||||
{ code: 'id-ID', name: 'Indonesian (Indonesia)' },
|
||||
{ code: 'ar-SA', name: 'Arabic (Saudi Arabia)' }
|
||||
];
|
||||
|
||||
// RTL languages
|
||||
const rtlLanguages = ['ar-SA'];
|
||||
|
||||
// Check if current language is RTL
|
||||
function isRTL() {
|
||||
return rtlLanguages.includes(currentLang);
|
||||
}
|
||||
|
||||
// Load single language file
|
||||
async function loadLanguage(lang) {
|
||||
if (translations[lang]) return true;
|
||||
@@ -73,6 +82,24 @@ const i18n = (() => {
|
||||
const key = el.getAttribute('data-i18n-title');
|
||||
el.title = t(key);
|
||||
});
|
||||
// Update RTL layout
|
||||
updateRTL();
|
||||
}
|
||||
|
||||
// Update RTL layout
|
||||
function updateRTL() {
|
||||
const html = document.documentElement;
|
||||
const body = document.body;
|
||||
|
||||
if (isRTL()) {
|
||||
html.setAttribute('dir', 'rtl');
|
||||
html.setAttribute('lang', currentLang);
|
||||
body.classList.add('rtl');
|
||||
} else {
|
||||
html.removeAttribute('dir');
|
||||
html.setAttribute('lang', currentLang);
|
||||
body.classList.remove('rtl');
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize - load saved language only
|
||||
@@ -88,7 +115,8 @@ const i18n = (() => {
|
||||
t,
|
||||
setLanguage,
|
||||
getAvailableLanguages: () => availableLanguages,
|
||||
getCurrentLanguage: () => currentLang
|
||||
getCurrentLanguage: () => currentLang,
|
||||
isRTL
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -179,7 +180,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);
|
||||
}
|
||||
@@ -676,6 +677,121 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
list.innerHTML = mappings.map(m => {
|
||||
const safe = escapeHtml(m.username);
|
||||
return `
|
||||
<div class="identity-item ${m.username === currentUsername ? 'active' : ''}"
|
||||
onclick="switchIdentity('${safe.replace(/'/g, "'")}')">
|
||||
<span>${safe}</span>
|
||||
${m.username === currentUsername ? '<i class="fas fa-check ml-auto"></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();
|
||||
|
||||
} 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;
|
||||
|
||||
109
GUI/js/logs.js
109
GUI/js/logs.js
@@ -66,6 +66,113 @@ async function openLogsFolder() {
|
||||
await window.electronAPI.openLogsFolder();
|
||||
}
|
||||
|
||||
async function sendLogs() {
|
||||
const btn = document.getElementById('sendLogsBtn');
|
||||
if (!btn || btn.disabled) return;
|
||||
|
||||
// Get i18n strings with fallbacks
|
||||
const i18n = window.i18n || {};
|
||||
const sendingText = (i18n.settings && i18n.settings.logsSending) || 'Sending...';
|
||||
const sentText = (i18n.settings && i18n.settings.logsSent) || 'Sent!';
|
||||
const failedText = (i18n.settings && i18n.settings.logsSendFailed) || 'Failed';
|
||||
const sendText = (i18n.settings && i18n.settings.logsSend) || 'Send Logs';
|
||||
|
||||
const originalHTML = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${sendingText}`;
|
||||
|
||||
try {
|
||||
const result = await window.electronAPI.sendLogs();
|
||||
|
||||
if (result.success) {
|
||||
btn.innerHTML = `<i class="fas fa-check"></i> ${sentText}`;
|
||||
showLogSubmissionResult(result.id);
|
||||
} else {
|
||||
btn.innerHTML = `<i class="fas fa-times"></i> ${failedText}`;
|
||||
console.error('Send logs failed:', result.error);
|
||||
|
||||
// Show error notification if available
|
||||
if (window.LauncherUI && window.LauncherUI.showNotification) {
|
||||
window.LauncherUI.showNotification(result.error || 'Failed to send logs', 'error');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Send logs error:', err);
|
||||
btn.innerHTML = `<i class="fas fa-times"></i> ${failedText}`;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalHTML;
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function showLogSubmissionResult(id) {
|
||||
// Remove existing popup if any
|
||||
const existing = document.getElementById('logSubmissionPopup');
|
||||
if (existing) existing.remove();
|
||||
|
||||
const i18n = window.i18n || {};
|
||||
const idLabel = (i18n.settings && i18n.settings.logsSubmissionId) || 'Submission ID';
|
||||
const copyText = (i18n.common && i18n.common.copy) || 'Copy';
|
||||
const closeText = (i18n.common && i18n.common.close) || 'Close';
|
||||
const shareText = (i18n.settings && i18n.settings.logsShareId) || 'Share this ID with support when reporting issues';
|
||||
|
||||
const popup = document.createElement('div');
|
||||
popup.id = 'logSubmissionPopup';
|
||||
popup.style.cssText = `
|
||||
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
||||
background: rgba(20, 20, 35, 0.98); border: 1px solid rgba(0, 212, 255, 0.3);
|
||||
border-radius: 12px; padding: 24px 32px; z-index: 10000;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.5); text-align: center;
|
||||
min-width: 320px; backdrop-filter: blur(10px);
|
||||
`;
|
||||
|
||||
popup.innerHTML = `
|
||||
<div style="margin-bottom: 16px;">
|
||||
<i class="fas fa-check-circle" style="font-size: 2em; color: #00d4ff;"></i>
|
||||
</div>
|
||||
<div style="color: #888; font-size: 0.85em; margin-bottom: 8px;">${idLabel}</div>
|
||||
<div id="logSubId" style="font-family: monospace; font-size: 1.5em; color: #00d4ff; letter-spacing: 2px; margin-bottom: 12px; user-select: all;">${id}</div>
|
||||
<div style="color: #666; font-size: 0.8em; margin-bottom: 20px;">${shareText}</div>
|
||||
<div style="display: flex; gap: 10px; justify-content: center;">
|
||||
<button onclick="copyLogSubmissionId('${id}')" style="
|
||||
background: rgba(0,212,255,0.2); border: 1px solid rgba(0,212,255,0.3);
|
||||
color: #00d4ff; padding: 8px 20px; border-radius: 6px; cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
"><i class="fas fa-copy"></i> ${copyText}</button>
|
||||
<button onclick="document.getElementById('logSubmissionPopup').remove()" style="
|
||||
background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2);
|
||||
color: #ccc; padding: 8px 20px; border-radius: 6px; cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
">${closeText}</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(popup);
|
||||
|
||||
// Auto-close after 30s
|
||||
setTimeout(() => {
|
||||
if (document.getElementById('logSubmissionPopup')) {
|
||||
popup.remove();
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
async function copyLogSubmissionId(id) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(id);
|
||||
const btn = event.target.closest('button');
|
||||
if (btn) {
|
||||
const orig = btn.innerHTML;
|
||||
btn.innerHTML = '<i class="fas fa-check"></i> Copied!';
|
||||
setTimeout(() => { btn.innerHTML = orig; }, 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to copy submission ID:', err);
|
||||
}
|
||||
}
|
||||
|
||||
function openLogs() {
|
||||
// Navigation is handled by sidebar logic, but we can trigger a refresh
|
||||
window.LauncherUI.showPage('logs-page');
|
||||
@@ -77,6 +184,8 @@ function openLogs() {
|
||||
window.refreshLogs = refreshLogs;
|
||||
window.copyLogs = copyLogs;
|
||||
window.openLogsFolder = openLogsFolder;
|
||||
window.sendLogs = sendLogs;
|
||||
window.copyLogSubmissionId = copyLogSubmissionId;
|
||||
window.openLogs = openLogs;
|
||||
|
||||
// Auto-load logs when the page becomes active
|
||||
|
||||
184
GUI/js/mods.js
184
GUI/js/mods.js
@@ -49,6 +49,18 @@ function setupModsEventListeners() {
|
||||
closeModalBtn.addEventListener('click', closeMyModsModal);
|
||||
}
|
||||
|
||||
const myModsSearchInput = document.getElementById('myModsSearch');
|
||||
if (myModsSearchInput) {
|
||||
let myModsSearchTimeout;
|
||||
myModsSearchInput.addEventListener('input', (e) => {
|
||||
const query = e.target.value.toLowerCase().trim();
|
||||
clearTimeout(myModsSearchTimeout);
|
||||
myModsSearchTimeout = setTimeout(() => {
|
||||
filterInstalledMods(query);
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
|
||||
const modal = document.getElementById('myModsModal');
|
||||
if (modal) {
|
||||
modal.addEventListener('click', (e) => {
|
||||
@@ -78,12 +90,30 @@ function setupModsEventListeners() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const browseContainer = document.getElementById('browseModsList');
|
||||
if (browseContainer) {
|
||||
browseContainer.addEventListener('click', (e) => {
|
||||
const installBtn = e.target.closest('[data-install-mod-id]');
|
||||
if (installBtn) {
|
||||
const modId = installBtn.getAttribute('data-install-mod-id');
|
||||
const mod = browseMods.find(m => m.id == modId);
|
||||
if (mod) {
|
||||
openVersionSelectModal(mod);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function openMyModsModal() {
|
||||
const modal = document.getElementById('myModsModal');
|
||||
if (modal) {
|
||||
modal.classList.add('active');
|
||||
const searchInput = document.getElementById('myModsSearch');
|
||||
if (searchInput) {
|
||||
searchInput.value = '';
|
||||
}
|
||||
loadInstalledMods();
|
||||
}
|
||||
}
|
||||
@@ -92,6 +122,10 @@ function closeMyModsModal() {
|
||||
const modal = document.getElementById('myModsModal');
|
||||
if (modal) {
|
||||
modal.classList.remove('active');
|
||||
const searchInput = document.getElementById('myModsSearch');
|
||||
if (searchInput) {
|
||||
searchInput.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,19 +147,39 @@ async function loadInstalledMods() {
|
||||
}
|
||||
}
|
||||
|
||||
function filterInstalledMods(query) {
|
||||
if (!query || query === '') {
|
||||
displayInstalledMods(installedMods);
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = installedMods.filter(mod => {
|
||||
const nameMatch = mod.name?.toLowerCase().includes(query);
|
||||
const fileNameMatch = mod.fileName?.toLowerCase().includes(query);
|
||||
const descriptionMatch = mod.description?.toLowerCase().includes(query);
|
||||
const authorMatch = mod.author?.toLowerCase().includes(query);
|
||||
return nameMatch || fileNameMatch || descriptionMatch || authorMatch;
|
||||
});
|
||||
|
||||
displayInstalledMods(filtered);
|
||||
}
|
||||
|
||||
function displayInstalledMods(mods) {
|
||||
const modsContainer = document.getElementById('installedModsList');
|
||||
if (!modsContainer) return;
|
||||
|
||||
if (mods.length === 0) {
|
||||
const searchInput = document.getElementById('myModsSearch');
|
||||
const isSearching = searchInput && searchInput.value.trim() !== '';
|
||||
|
||||
modsContainer.innerHTML = `
|
||||
<div class=\"empty-installed-mods\">
|
||||
<i class=\"fas fa-box-open\"></i>
|
||||
<h4 data-i18n="mods.noModsInstalled">No Mods Installed</h4>
|
||||
<p data-i18n="mods.noModsInstalledDesc">Add mods from CurseForge or import local files</p>
|
||||
<i class=\"fas fa-${isSearching ? 'search' : 'box-open'}\"></i>
|
||||
<h4 data-i18n="${isSearching ? 'mods.noModsFound' : 'mods.noModsInstalled'}">${isSearching ? 'No Mods Found' : 'No Mods Installed'}</h4>
|
||||
<p data-i18n="${isSearching ? 'mods.noModsFoundDesc' : 'mods.noModsInstalledDesc'}">${isSearching ? 'Try a different search term' : 'Add mods from CurseForge or import local files'}</p>
|
||||
</div>
|
||||
`;
|
||||
if (window.i18n) {
|
||||
if (window.i18n && !isSearching) {
|
||||
const container = modsContainer.querySelector('.empty-installed-mods');
|
||||
container.querySelector('h4').textContent = window.i18n.t('mods.noModsInstalled');
|
||||
container.querySelector('p').textContent = window.i18n.t('mods.noModsInstalledDesc');
|
||||
@@ -165,7 +219,7 @@ function createInstalledModCard(mod) {
|
||||
<div class="installed-mod-info">
|
||||
<div class="installed-mod-header">
|
||||
<h4 class="installed-mod-name">${mod.name}</h4>
|
||||
<span class="installed-mod-version">v${mod.version}</span>
|
||||
<span class="installed-mod-version">${mod.fileName || 'v' + mod.version}</span>
|
||||
</div>
|
||||
<p class="installed-mod-description">${mod.description || (window.i18n ? window.i18n.t('mods.noDescription') : 'No description available')}</p>
|
||||
</div>
|
||||
@@ -295,13 +349,6 @@ function displayBrowseMods(mods) {
|
||||
}
|
||||
|
||||
browseContainer.innerHTML = mods.map(mod => createBrowseModCard(mod)).join('');
|
||||
|
||||
mods.forEach(mod => {
|
||||
const installBtn = document.getElementById(`install-${mod.id}`);
|
||||
if (installBtn) {
|
||||
installBtn.addEventListener('click', () => downloadAndInstallMod(mod));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createBrowseModCard(mod) {
|
||||
@@ -350,12 +397,12 @@ function createBrowseModCard(mod) {
|
||||
${window.i18n ? window.i18n.t('mods.view') : 'VIEW'}
|
||||
</button>
|
||||
${!isInstalled ?
|
||||
`<button id="install-${mod.id}" class="mod-btn-toggle bg-primary text-black hover:bg-primary/80">
|
||||
<i class="fas fa-download"></i>
|
||||
`<button data-install-mod-id=\"${mod.id}\" class=\"mod-btn-toggle bg-primary text-black hover:bg-primary/80\">
|
||||
<i class=\"fas fa-download\"></i>
|
||||
${window.i18n ? window.i18n.t('mods.install') : 'INSTALL'}
|
||||
</button>` :
|
||||
`<button class="mod-btn-toggle bg-white/10 text-white" disabled>
|
||||
<i class="fas fa-check"></i>
|
||||
`<button class=\"mod-btn-toggle bg-white/10 text-white\" disabled>
|
||||
<i class=\"fas fa-check\"></i>
|
||||
${window.i18n ? window.i18n.t('mods.installed') : 'INSTALLED'}
|
||||
</button>`
|
||||
}
|
||||
@@ -364,6 +411,104 @@ function createBrowseModCard(mod) {
|
||||
`;
|
||||
}
|
||||
|
||||
let currentSelectedMod = null;
|
||||
|
||||
function openVersionSelectModal(mod) {
|
||||
currentSelectedMod = mod;
|
||||
const modal = document.getElementById('versionSelectModal');
|
||||
const closeBtn = document.getElementById('closeVersionModal');
|
||||
const versionList = document.getElementById('versionList');
|
||||
|
||||
if (modal) {
|
||||
modal.style.display = 'flex';
|
||||
modal.classList.add('active');
|
||||
|
||||
const closeHandler = () => {
|
||||
modal.classList.remove('active');
|
||||
setTimeout(() => {
|
||||
modal.style.display = 'none';
|
||||
}, 300);
|
||||
currentSelectedMod = null;
|
||||
};
|
||||
|
||||
if (closeBtn) {
|
||||
closeBtn.onclick = closeHandler;
|
||||
}
|
||||
modal.onclick = (e) => {
|
||||
if (e.target === modal) closeHandler();
|
||||
};
|
||||
|
||||
loadModVersions(mod.id, versionList);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadModVersions(modId, container) {
|
||||
container.innerHTML = `
|
||||
<div class="loading-versions">
|
||||
<i class="fas fa-spinner fa-spin fa-2x" style="margin-bottom: 10px; display: block;"></i>
|
||||
<span>Loading versions...</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
try {
|
||||
const versions = await window.electronAPI.getModFiles(modId);
|
||||
|
||||
if (!versions || versions.length === 0) {
|
||||
container.innerHTML = `<div class="p-4 text-center text-gray-400" style="padding: 2rem;">No versions found for this mod.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort versions by date desc (API returns desc but ensure)
|
||||
versions.sort((a, b) => new Date(b.fileDate) - new Date(a.fileDate));
|
||||
|
||||
container.innerHTML = versions.map(file => `
|
||||
<div class="version-item">
|
||||
<div class="version-info">
|
||||
<div class="version-name">${file.displayName}</div>
|
||||
<div class="version-meta">
|
||||
<span><i class="fas fa-calendar"></i> ${new Date(file.fileDate).toLocaleDateString()}</span>
|
||||
<span><i class="fas fa-download"></i> ${formatNumber(file.downloadCount)}</span>
|
||||
<span><i class="fas fa-file-archive"></i> ${(file.fileLength / 1024 / 1024).toFixed(2)} MB</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="version-actions">
|
||||
<button class="btn-install" data-file-id="${file.id}">
|
||||
Install
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Add event listeners securely
|
||||
container.querySelectorAll('.btn-install').forEach((btn, index) => {
|
||||
const file = versions[index]; // Map index to file data
|
||||
btn.onclick = () => installVersion(file);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading versions:', error);
|
||||
container.innerHTML = `<div class="p-4 text-center text-red-400" style="padding: 2rem;">Error loading versions.<br><small>${error.message}</small></div>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function installVersion(file) {
|
||||
if (!currentSelectedMod) return;
|
||||
|
||||
const modal = document.getElementById('versionSelectModal');
|
||||
modal.style.display = 'none';
|
||||
|
||||
const modInfo = {
|
||||
...currentSelectedMod,
|
||||
fileId: file.id,
|
||||
downloadUrl: file.downloadUrl,
|
||||
fileName: file.fileName,
|
||||
fileSize: file.fileLength
|
||||
};
|
||||
|
||||
await downloadAndInstallMod(modInfo);
|
||||
currentSelectedMod = null;
|
||||
}
|
||||
|
||||
async function downloadAndInstallMod(modInfo) {
|
||||
try {
|
||||
const downloadMsg = window.i18n ? window.i18n.t('notifications.modsDownloading').replace('{name}', modInfo.name) : `Downloading ${modInfo.name}...`;
|
||||
@@ -762,7 +907,10 @@ window.modsManager = {
|
||||
closeMyModsModal,
|
||||
viewModPage,
|
||||
loadInstalledMods,
|
||||
loadBrowseMods
|
||||
loadBrowseMods,
|
||||
openVersionSelectModal
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initModsManager);
|
||||
// Remove auto-init since we are now calling it from script.js explicitly
|
||||
// which guarantees order and environment readiness
|
||||
// document.addEventListener('DOMContentLoaded', initModsManager);
|
||||
|
||||
@@ -2,7 +2,7 @@ import './ui.js';
|
||||
import './install.js';
|
||||
import './launcher.js';
|
||||
import './news.js';
|
||||
import './mods.js';
|
||||
import { initModsManager } from './mods.js';
|
||||
import './players.js';
|
||||
import './settings.js';
|
||||
import './logs.js';
|
||||
@@ -15,6 +15,12 @@ let i18nInitialized = false;
|
||||
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
updateLanguageSelector();
|
||||
initModsManager();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
updateLanguageSelector();
|
||||
initModsManager();
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -53,7 +59,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 });
|
||||
|
||||
@@ -18,10 +18,15 @@ let regenerateUuidBtn;
|
||||
let manageUuidsBtn;
|
||||
let uuidModal;
|
||||
let uuidModalClose;
|
||||
let modalCurrentUuid;
|
||||
let modalCopyUuidBtn;
|
||||
let modalRegenerateUuidBtn;
|
||||
let generateNewUuidBtn;
|
||||
let addIdentityBtn;
|
||||
let uuidAddForm;
|
||||
let addIdentityUsername;
|
||||
let addIdentityUuid;
|
||||
let addIdentityRegenerateBtn;
|
||||
let addIdentityConfirmBtn;
|
||||
let addIdentityCancelBtn;
|
||||
let uuidAdvancedToggle;
|
||||
let uuidAdvancedContent;
|
||||
let uuidList;
|
||||
let customUuidInput;
|
||||
let setCustomUuidBtn;
|
||||
@@ -180,10 +185,15 @@ function setupSettingsElements() {
|
||||
manageUuidsBtn = document.getElementById('manageUuidsBtn');
|
||||
uuidModal = document.getElementById('uuidModal');
|
||||
uuidModalClose = document.getElementById('uuidModalClose');
|
||||
modalCurrentUuid = document.getElementById('modalCurrentUuid');
|
||||
modalCopyUuidBtn = document.getElementById('modalCopyUuidBtn');
|
||||
modalRegenerateUuidBtn = document.getElementById('modalRegenerateUuidBtn');
|
||||
generateNewUuidBtn = document.getElementById('generateNewUuidBtn');
|
||||
addIdentityBtn = document.getElementById('addIdentityBtn');
|
||||
uuidAddForm = document.getElementById('uuidAddForm');
|
||||
addIdentityUsername = document.getElementById('addIdentityUsername');
|
||||
addIdentityUuid = document.getElementById('addIdentityUuid');
|
||||
addIdentityRegenerateBtn = document.getElementById('addIdentityRegenerateBtn');
|
||||
addIdentityConfirmBtn = document.getElementById('addIdentityConfirmBtn');
|
||||
addIdentityCancelBtn = document.getElementById('addIdentityCancelBtn');
|
||||
uuidAdvancedToggle = document.getElementById('uuidAdvancedToggle');
|
||||
uuidAdvancedContent = document.getElementById('uuidAdvancedContent');
|
||||
uuidList = document.getElementById('uuidList');
|
||||
customUuidInput = document.getElementById('customUuidInput');
|
||||
setCustomUuidBtn = document.getElementById('setCustomUuidBtn');
|
||||
@@ -230,16 +240,24 @@ function setupSettingsElements() {
|
||||
uuidModalClose.addEventListener('click', closeUuidModal);
|
||||
}
|
||||
|
||||
if (modalCopyUuidBtn) {
|
||||
modalCopyUuidBtn.addEventListener('click', copyCurrentUuid);
|
||||
if (addIdentityBtn) {
|
||||
addIdentityBtn.addEventListener('click', showAddIdentityForm);
|
||||
}
|
||||
|
||||
if (modalRegenerateUuidBtn) {
|
||||
modalRegenerateUuidBtn.addEventListener('click', regenerateCurrentUuid);
|
||||
if (addIdentityRegenerateBtn) {
|
||||
addIdentityRegenerateBtn.addEventListener('click', regenerateAddIdentityUuid);
|
||||
}
|
||||
|
||||
if (generateNewUuidBtn) {
|
||||
generateNewUuidBtn.addEventListener('click', generateNewUuid);
|
||||
if (addIdentityConfirmBtn) {
|
||||
addIdentityConfirmBtn.addEventListener('click', confirmAddIdentity);
|
||||
}
|
||||
|
||||
if (addIdentityCancelBtn) {
|
||||
addIdentityCancelBtn.addEventListener('click', hideAddIdentityForm);
|
||||
}
|
||||
|
||||
if (uuidAdvancedToggle) {
|
||||
uuidAdvancedToggle.addEventListener('click', toggleAdvancedSection);
|
||||
}
|
||||
|
||||
if (setCustomUuidBtn) {
|
||||
@@ -467,6 +485,9 @@ async function savePlayerName() {
|
||||
// Also refresh the UUID list to update which entry is marked as current
|
||||
await loadAllUuids();
|
||||
|
||||
// Refresh header identity dropdown
|
||||
if (window.loadIdentities) window.loadIdentities();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error saving player name:', error);
|
||||
const errorMsg = window.i18n ? window.i18n.t('notifications.playerNameSaveFailed') : 'Failed to save player name';
|
||||
@@ -569,6 +590,7 @@ async function loadAllSettings() {
|
||||
await loadLauncherHwAccel();
|
||||
await loadGpuPreference();
|
||||
await loadVersionBranch();
|
||||
await loadWrapperConfigUI();
|
||||
}
|
||||
|
||||
|
||||
@@ -629,7 +651,6 @@ async function loadCurrentUuid() {
|
||||
const uuid = await window.electronAPI.getCurrentUuid();
|
||||
if (uuid) {
|
||||
if (currentUuidDisplay) currentUuidDisplay.value = uuid;
|
||||
if (modalCurrentUuid) modalCurrentUuid.value = uuid;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -639,7 +660,7 @@ async function loadCurrentUuid() {
|
||||
|
||||
async function copyCurrentUuid() {
|
||||
try {
|
||||
const uuid = currentUuidDisplay ? currentUuidDisplay.value : modalCurrentUuid?.value;
|
||||
const uuid = currentUuidDisplay ? currentUuidDisplay.value : null;
|
||||
if (uuid && navigator.clipboard) {
|
||||
await navigator.clipboard.writeText(uuid);
|
||||
const msg = window.i18n ? window.i18n.t('notifications.uuidCopied') : 'UUID copied to clipboard!';
|
||||
@@ -687,13 +708,13 @@ async function performRegenerateUuid() {
|
||||
const result = await window.electronAPI.resetCurrentUserUuid();
|
||||
if (result.success && result.uuid) {
|
||||
if (currentUuidDisplay) currentUuidDisplay.value = result.uuid;
|
||||
if (modalCurrentUuid) modalCurrentUuid.value = result.uuid;
|
||||
const msg = window.i18n ? window.i18n.t('notifications.uuidGenerated') : 'New UUID generated successfully!';
|
||||
showNotification(msg, 'success');
|
||||
|
||||
if (uuidModal && uuidModal.style.display !== 'none') {
|
||||
await loadAllUuids();
|
||||
}
|
||||
if (window.loadIdentities) window.loadIdentities();
|
||||
} else {
|
||||
throw new Error(result.error || 'Failed to generate new UUID');
|
||||
}
|
||||
@@ -716,6 +737,10 @@ async function openUuidModal() {
|
||||
}
|
||||
}
|
||||
|
||||
// Expose globally so identity dropdown and Escape handler can use them
|
||||
window.openUuidModal = openUuidModal;
|
||||
window.loadCurrentUuid = loadCurrentUuid;
|
||||
|
||||
function closeUuidModal() {
|
||||
if (uuidModal) {
|
||||
uuidModal.classList.remove('active');
|
||||
@@ -724,6 +749,7 @@ function closeUuidModal() {
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
window.closeUuidModal = closeUuidModal;
|
||||
|
||||
async function loadAllUuids() {
|
||||
try {
|
||||
@@ -768,6 +794,9 @@ async function loadAllUuids() {
|
||||
<button class="uuid-item-btn copy" onclick="copyUuid('${mapping.uuid}')" title="Copy UUID">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
${mapping.isCurrent ? `<button class="uuid-item-btn regenerate" onclick="regenerateUuidForUser('${escapeHtml(mapping.username)}')" title="Regenerate UUID">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
</button>` : ''}
|
||||
${!mapping.isCurrent ? `<button class="uuid-item-btn delete" onclick="deleteUuid('${escapeHtml(mapping.username)}')" title="Delete UUID">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>` : ''}
|
||||
@@ -790,23 +819,119 @@ async function loadAllUuids() {
|
||||
}
|
||||
}
|
||||
|
||||
async function generateNewUuid() {
|
||||
async function showAddIdentityForm() {
|
||||
if (!uuidAddForm) return;
|
||||
uuidAddForm.style.display = 'block';
|
||||
if (addIdentityUsername) {
|
||||
addIdentityUsername.value = '';
|
||||
addIdentityUsername.focus();
|
||||
}
|
||||
if (addIdentityUuid) {
|
||||
try {
|
||||
if (window.electronAPI && window.electronAPI.generateNewUuid) {
|
||||
const newUuid = await window.electronAPI.generateNewUuid();
|
||||
if (newUuid) addIdentityUuid.value = newUuid;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error pre-generating UUID:', e);
|
||||
}
|
||||
}
|
||||
if (addIdentityBtn) addIdentityBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
function hideAddIdentityForm() {
|
||||
if (uuidAddForm) uuidAddForm.style.display = 'none';
|
||||
if (addIdentityBtn) addIdentityBtn.style.display = '';
|
||||
}
|
||||
|
||||
async function regenerateAddIdentityUuid() {
|
||||
try {
|
||||
if (window.electronAPI && window.electronAPI.generateNewUuid) {
|
||||
const newUuid = await window.electronAPI.generateNewUuid();
|
||||
if (newUuid) {
|
||||
if (customUuidInput) customUuidInput.value = newUuid;
|
||||
const msg = window.i18n ? window.i18n.t('notifications.uuidGeneratedShort') : 'New UUID generated!';
|
||||
showNotification(msg, 'success');
|
||||
if (newUuid && addIdentityUuid) {
|
||||
addIdentityUuid.value = newUuid;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error generating new UUID:', error);
|
||||
const msg = window.i18n ? window.i18n.t('notifications.uuidGenerateFailed') : 'Failed to generate new UUID';
|
||||
console.error('Error generating UUID:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmAddIdentity() {
|
||||
try {
|
||||
const username = addIdentityUsername ? addIdentityUsername.value.trim() : '';
|
||||
const uuid = addIdentityUuid ? addIdentityUuid.value.trim() : '';
|
||||
|
||||
if (!username) {
|
||||
const msg = window.i18n ? window.i18n.t('notifications.playerNameRequired') : 'Please enter a username';
|
||||
showNotification(msg, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.length > 16) {
|
||||
const msg = window.i18n ? window.i18n.t('notifications.playerNameTooLong') : 'Username must be 16 characters or less';
|
||||
showNotification(msg, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
if (!uuid || !uuidRegex.test(uuid)) {
|
||||
const msg = window.i18n ? window.i18n.t('notifications.uuidInvalidFormat') : 'Invalid UUID format';
|
||||
showNotification(msg, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.electronAPI && window.electronAPI.setUuidForUser) {
|
||||
const result = await window.electronAPI.setUuidForUser(username, uuid);
|
||||
if (result.success) {
|
||||
const msg = window.i18n ? window.i18n.t('notifications.identityAdded') : 'Identity added successfully!';
|
||||
showNotification(msg, 'success');
|
||||
hideAddIdentityForm();
|
||||
await loadAllUuids();
|
||||
if (window.loadIdentities) window.loadIdentities();
|
||||
} else {
|
||||
throw new Error(result.error || 'Failed to add identity');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error adding identity:', error);
|
||||
const msg = window.i18n ? window.i18n.t('notifications.identityAddFailed') : 'Failed to add identity';
|
||||
showNotification(msg, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAdvancedSection() {
|
||||
if (!uuidAdvancedContent || !uuidAdvancedToggle) return;
|
||||
const isOpen = uuidAdvancedContent.style.display !== 'none';
|
||||
uuidAdvancedContent.style.display = isOpen ? 'none' : 'block';
|
||||
const chevron = uuidAdvancedToggle.querySelector('.uuid-advanced-chevron');
|
||||
if (chevron) {
|
||||
chevron.classList.toggle('open', !isOpen);
|
||||
}
|
||||
}
|
||||
|
||||
window.regenerateUuidForUser = async function (username) {
|
||||
try {
|
||||
const message = window.i18n ? window.i18n.t('confirm.regenerateUuidMessage') : 'Are you sure you want to generate a new UUID? This will change your player identity.';
|
||||
const title = window.i18n ? window.i18n.t('confirm.regenerateUuidTitle') : 'Generate New UUID';
|
||||
const confirmBtn = window.i18n ? window.i18n.t('confirm.regenerateUuidButton') : 'Generate';
|
||||
const cancelBtn = window.i18n ? window.i18n.t('common.cancel') : 'Cancel';
|
||||
|
||||
showCustomConfirm(
|
||||
message,
|
||||
title,
|
||||
async () => {
|
||||
await performRegenerateUuid();
|
||||
},
|
||||
null,
|
||||
confirmBtn,
|
||||
cancelBtn
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error regenerating UUID:', error);
|
||||
}
|
||||
};
|
||||
|
||||
async function setCustomUuid() {
|
||||
try {
|
||||
if (!customUuidInput || !customUuidInput.value.trim()) {
|
||||
@@ -864,13 +989,13 @@ async function performSetCustomUuid(uuid) {
|
||||
|
||||
if (result.success) {
|
||||
if (currentUuidDisplay) currentUuidDisplay.value = uuid;
|
||||
if (modalCurrentUuid) modalCurrentUuid.value = uuid;
|
||||
if (customUuidInput) customUuidInput.value = '';
|
||||
|
||||
const msg = window.i18n ? window.i18n.t('notifications.uuidSetSuccess') : 'Custom UUID set successfully!';
|
||||
showNotification(msg, 'success');
|
||||
|
||||
await loadAllUuids();
|
||||
if (window.loadIdentities) window.loadIdentities();
|
||||
} else {
|
||||
throw new Error(result.error || 'Failed to set custom UUID');
|
||||
}
|
||||
@@ -949,6 +1074,9 @@ async function performSwitchToUsername(username) {
|
||||
// Refresh the UUID list to show new "Current" badge
|
||||
await loadAllUuids();
|
||||
|
||||
// Refresh header identity dropdown
|
||||
if (window.loadIdentities) window.loadIdentities();
|
||||
|
||||
const msg = window.i18n
|
||||
? window.i18n.t('notifications.switchUsernameSuccess').replace('{username}', username)
|
||||
: `Switched to "${username}" successfully!`;
|
||||
@@ -996,6 +1124,7 @@ async function performDeleteUuid(username) {
|
||||
const msg = window.i18n ? window.i18n.t('notifications.uuidDeleteSuccess') : 'UUID deleted successfully!';
|
||||
showNotification(msg, 'success');
|
||||
await loadAllUuids();
|
||||
if (window.loadIdentities) window.loadIdentities();
|
||||
} else {
|
||||
throw new Error(result.error || 'Failed to delete UUID');
|
||||
}
|
||||
@@ -1254,3 +1383,235 @@ async function loadVersionBranch() {
|
||||
return 'release';
|
||||
}
|
||||
}
|
||||
|
||||
// === Java Wrapper Configuration UI ===
|
||||
|
||||
let _wrapperConfig = null;
|
||||
let _wrapperPreviewOpen = false;
|
||||
|
||||
async function loadWrapperConfigUI() {
|
||||
try {
|
||||
if (!window.electronAPI || !window.electronAPI.loadWrapperConfig) return;
|
||||
|
||||
_wrapperConfig = await window.electronAPI.loadWrapperConfig();
|
||||
renderStripFlagsList();
|
||||
renderInjectArgsList();
|
||||
setupWrapperEventListeners();
|
||||
} catch (error) {
|
||||
console.error('Error loading wrapper config UI:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderStripFlagsList() {
|
||||
const container = document.getElementById('wrapperStripFlagsList');
|
||||
if (!container || !_wrapperConfig) return;
|
||||
|
||||
if (_wrapperConfig.stripFlags.length === 0) {
|
||||
container.innerHTML = '<div class="wrapper-items-empty">No flags configured</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = '';
|
||||
_wrapperConfig.stripFlags.forEach((flag, index) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'wrapper-item';
|
||||
item.innerHTML = `
|
||||
<span class="wrapper-item-text">${escapeHtml(flag)}</span>
|
||||
<button class="wrapper-item-delete" data-index="${index}" title="Remove">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
`;
|
||||
item.querySelector('.wrapper-item-delete').addEventListener('click', () => removeStripFlag(index));
|
||||
container.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
function renderInjectArgsList() {
|
||||
const container = document.getElementById('wrapperInjectArgsList');
|
||||
if (!container || !_wrapperConfig) return;
|
||||
|
||||
if (_wrapperConfig.injectArgs.length === 0) {
|
||||
container.innerHTML = '<div class="wrapper-items-empty">No arguments configured</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = '';
|
||||
_wrapperConfig.injectArgs.forEach((entry, index) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'wrapper-item';
|
||||
|
||||
const serverLabel = window.i18n ? window.i18n.t('settings.wrapperConditionServer') : 'Server Only';
|
||||
const alwaysLabel = window.i18n ? window.i18n.t('settings.wrapperConditionAlways') : 'Always';
|
||||
|
||||
item.innerHTML = `
|
||||
<span class="wrapper-item-text">${escapeHtml(entry.arg)}</span>
|
||||
<div class="wrapper-item-condition">
|
||||
<select data-index="${index}">
|
||||
<option value="server"${entry.condition === 'server' ? ' selected' : ''}>${serverLabel}</option>
|
||||
<option value="always"${entry.condition === 'always' ? ' selected' : ''}>${alwaysLabel}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="wrapper-item-delete" data-index="${index}" title="Remove">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
`;
|
||||
item.querySelector('select').addEventListener('change', (e) => updateArgCondition(index, e.target.value));
|
||||
item.querySelector('.wrapper-item-delete').addEventListener('click', () => removeInjectArg(index));
|
||||
container.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
async function addStripFlag() {
|
||||
const input = document.getElementById('wrapperAddFlagInput');
|
||||
if (!input || !_wrapperConfig) return;
|
||||
|
||||
const flag = input.value.trim();
|
||||
if (!flag) return;
|
||||
|
||||
if (_wrapperConfig.stripFlags.includes(flag)) {
|
||||
const msg = window.i18n ? window.i18n.t('notifications.wrapperFlagExists') : 'This flag is already in the list';
|
||||
showNotification(msg, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
_wrapperConfig.stripFlags.push(flag);
|
||||
input.value = '';
|
||||
renderStripFlagsList();
|
||||
await saveWrapperConfigToBackend();
|
||||
await updateWrapperPreview();
|
||||
}
|
||||
|
||||
async function removeStripFlag(index) {
|
||||
if (!_wrapperConfig) return;
|
||||
_wrapperConfig.stripFlags.splice(index, 1);
|
||||
renderStripFlagsList();
|
||||
await saveWrapperConfigToBackend();
|
||||
await updateWrapperPreview();
|
||||
}
|
||||
|
||||
async function addInjectArg() {
|
||||
const input = document.getElementById('wrapperAddArgInput');
|
||||
const condSelect = document.getElementById('wrapperAddArgCondition');
|
||||
if (!input || !condSelect || !_wrapperConfig) return;
|
||||
|
||||
const arg = input.value.trim();
|
||||
if (!arg) return;
|
||||
|
||||
const exists = _wrapperConfig.injectArgs.some(e => e.arg === arg);
|
||||
if (exists) {
|
||||
const msg = window.i18n ? window.i18n.t('notifications.wrapperArgExists') : 'This argument is already in the list';
|
||||
showNotification(msg, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
_wrapperConfig.injectArgs.push({ arg, condition: condSelect.value });
|
||||
input.value = '';
|
||||
renderInjectArgsList();
|
||||
await saveWrapperConfigToBackend();
|
||||
await updateWrapperPreview();
|
||||
}
|
||||
|
||||
async function removeInjectArg(index) {
|
||||
if (!_wrapperConfig) return;
|
||||
_wrapperConfig.injectArgs.splice(index, 1);
|
||||
renderInjectArgsList();
|
||||
await saveWrapperConfigToBackend();
|
||||
await updateWrapperPreview();
|
||||
}
|
||||
|
||||
async function updateArgCondition(index, condition) {
|
||||
if (!_wrapperConfig || !_wrapperConfig.injectArgs[index]) return;
|
||||
_wrapperConfig.injectArgs[index].condition = condition;
|
||||
await saveWrapperConfigToBackend();
|
||||
await updateWrapperPreview();
|
||||
}
|
||||
|
||||
async function saveWrapperConfigToBackend() {
|
||||
try {
|
||||
const result = await window.electronAPI.saveWrapperConfig(_wrapperConfig);
|
||||
if (!result || !result.success) {
|
||||
throw new Error(result?.error || 'Save failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error saving wrapper config:', error);
|
||||
const msg = window.i18n ? window.i18n.t('notifications.wrapperConfigSaveFailed') : 'Failed to save wrapper configuration';
|
||||
showNotification(msg, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function setupWrapperEventListeners() {
|
||||
const addFlagBtn = document.getElementById('wrapperAddFlagBtn');
|
||||
const addFlagInput = document.getElementById('wrapperAddFlagInput');
|
||||
const addArgBtn = document.getElementById('wrapperAddArgBtn');
|
||||
const addArgInput = document.getElementById('wrapperAddArgInput');
|
||||
const restoreBtn = document.getElementById('wrapperRestoreDefaultsBtn');
|
||||
const previewToggle = document.getElementById('wrapperPreviewToggle');
|
||||
|
||||
if (addFlagBtn) addFlagBtn.addEventListener('click', addStripFlag);
|
||||
if (addFlagInput) addFlagInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') addStripFlag(); });
|
||||
if (addArgBtn) addArgBtn.addEventListener('click', addInjectArg);
|
||||
if (addArgInput) addArgInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') addInjectArg(); });
|
||||
|
||||
if (restoreBtn) {
|
||||
restoreBtn.addEventListener('click', () => {
|
||||
const message = window.i18n ? window.i18n.t('confirm.resetWrapperMessage') : 'Are you sure you want to restore defaults? Your custom changes will be lost.';
|
||||
const title = window.i18n ? window.i18n.t('confirm.resetWrapperTitle') : 'Restore Defaults';
|
||||
|
||||
showCustomConfirm(message, title, async () => {
|
||||
try {
|
||||
const result = await window.electronAPI.resetWrapperConfig();
|
||||
if (result && result.success) {
|
||||
_wrapperConfig = result.config;
|
||||
renderStripFlagsList();
|
||||
renderInjectArgsList();
|
||||
await updateWrapperPreview();
|
||||
const msg = window.i18n ? window.i18n.t('notifications.wrapperConfigReset') : 'Wrapper configuration restored to defaults';
|
||||
showNotification(msg, 'success');
|
||||
} else {
|
||||
throw new Error(result?.error || 'Reset failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error resetting wrapper config:', error);
|
||||
const msg = window.i18n ? window.i18n.t('notifications.wrapperConfigResetFailed') : 'Failed to restore wrapper configuration';
|
||||
showNotification(msg, 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (previewToggle) {
|
||||
previewToggle.addEventListener('click', toggleWrapperPreview);
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleWrapperPreview() {
|
||||
const container = document.getElementById('wrapperPreviewContainer');
|
||||
const chevron = document.getElementById('wrapperPreviewChevron');
|
||||
if (!container) return;
|
||||
|
||||
_wrapperPreviewOpen = !_wrapperPreviewOpen;
|
||||
|
||||
if (_wrapperPreviewOpen) {
|
||||
container.style.display = 'block';
|
||||
if (chevron) chevron.classList.add('expanded');
|
||||
await updateWrapperPreview();
|
||||
} else {
|
||||
container.style.display = 'none';
|
||||
if (chevron) chevron.classList.remove('expanded');
|
||||
}
|
||||
}
|
||||
|
||||
async function updateWrapperPreview() {
|
||||
if (!_wrapperPreviewOpen || !_wrapperConfig) return;
|
||||
|
||||
const previewEl = document.getElementById('wrapperPreviewContent');
|
||||
if (!previewEl) return;
|
||||
|
||||
try {
|
||||
const platform = await window.electronAPI.getCurrentPlatform();
|
||||
const script = await window.electronAPI.previewWrapperScript(_wrapperConfig, platform);
|
||||
previewEl.textContent = script;
|
||||
} catch (error) {
|
||||
previewEl.textContent = 'Error generating preview: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
58
GUI/js/ui.js
58
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) {
|
||||
@@ -1103,9 +1112,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);
|
||||
|
||||
271
GUI/locales/ar-SA.json
Normal file
271
GUI/locales/ar-SA.json
Normal file
@@ -0,0 +1,271 @@
|
||||
{
|
||||
"nav": {
|
||||
"play": "لعب",
|
||||
"mods": "المودات",
|
||||
"news": "الأخبار",
|
||||
"chat": "دردشة اللاعبين",
|
||||
"settings": "الإعدادات"
|
||||
},
|
||||
"header": {
|
||||
"playersLabel": "اللاعبون:",
|
||||
"manageProfiles": "إدارة",
|
||||
"manageIdentities": "إدارة",
|
||||
"identityTooltip": "اسم اللاعب ومعرّف UUID المستخدمان في اللعبة",
|
||||
"configTooltip": "إعدادات اللعبة: المودات، Java والذاكرة",
|
||||
"defaultProfile": "الافتراضي"
|
||||
},
|
||||
"install": {
|
||||
"title": "مشغل اللعب المجاني",
|
||||
"playerName": "اسم اللاعب",
|
||||
"playerNamePlaceholder": "أدخل اسمك",
|
||||
"gameBranch": "إصدار اللعبة",
|
||||
"releaseVersion": "إصدار نهائي (مستقر)",
|
||||
"preReleaseVersion": "إصدار تجريبي (تجريبي)",
|
||||
"customInstallation": "تثبيت مخصص",
|
||||
"installationFolder": "مجلد التثبيت",
|
||||
"pathPlaceholder": "الموقع الافتراضي",
|
||||
"browse": "تصفح",
|
||||
"installButton": "تثبيت HYTALE",
|
||||
"installing": "جاري التثبيت..."
|
||||
},
|
||||
"play": {
|
||||
"ready": "جاهز للعب",
|
||||
"subtitle": "شغل Hytale وابدأ المغامرة",
|
||||
"playButton": "لعب HYTALE",
|
||||
"latestNews": "آخر الأخبار",
|
||||
"viewAll": "عرض الكل",
|
||||
"checking": "جاري التحقق...",
|
||||
"play": "بدء"
|
||||
},
|
||||
"mods": {
|
||||
"searchPlaceholder": "البحث عن مودات...",
|
||||
"myMods": "موداتي",
|
||||
"previous": "السابق",
|
||||
"next": "التالي",
|
||||
"page": "صفحة",
|
||||
"of": "من",
|
||||
"modalTitle": "موداتي",
|
||||
"noModsFound": "لم يتم العثور على مودات",
|
||||
"noModsFoundDesc": "حاول تعديل معايير البحث",
|
||||
"noModsInstalled": "لا توجد مودات مثبتة",
|
||||
"noModsInstalledDesc": "أضف مودات من CurseForge أو استورد ملفات محلية",
|
||||
"view": "عرض",
|
||||
"install": "تثبيت",
|
||||
"installed": "مثبت",
|
||||
"enable": "تفعيل",
|
||||
"disable": "تعطيل",
|
||||
"active": "نشط",
|
||||
"disabled": "معطل",
|
||||
"delete": "حذف المود",
|
||||
"noDescription": "لا يوجد وصف متاح",
|
||||
"confirmDelete": "هل أنت متأكد أنك تريد حذف \"{name}\"؟",
|
||||
"confirmDeleteDesc": "لا يمكن التراجع عن هذا الإجراء.",
|
||||
"confirmDeletion": "تأكيد الحذف",
|
||||
"apiKeyRequired": "مطلوب مفتاح API",
|
||||
"apiKeyRequiredDesc": "مطلوب مفتاح CurseForge API لتصفح المودات"
|
||||
},
|
||||
"news": {
|
||||
"title": "كل الأخبار",
|
||||
"readMore": "اقرأ المزيد"
|
||||
},
|
||||
"chat": {
|
||||
"title": "دردشة اللاعبين",
|
||||
"pickColor": "اللون",
|
||||
"inputPlaceholder": "اكتب رسالتك...",
|
||||
"send": "إرسال",
|
||||
"online": "متصل",
|
||||
"charCounter": "{current}/{max}",
|
||||
"secureChat": "دردشة آمنة - الروابط محجوبة",
|
||||
"joinChat": "انضمام للدردشة",
|
||||
"chooseUsername": "اختر اسم مستخدم للانضمام إلى دردشة اللاعبين",
|
||||
"username": "اسم المستخدم",
|
||||
"usernamePlaceholder": "أدخل اسم المستخدم...",
|
||||
"usernameHint": "3-20 حرفاً، حروف، أرقام، و - و _ فقط",
|
||||
"joinButton": "انضمام",
|
||||
"colorModal": {
|
||||
"title": "تخصيص لون اسم المستخدم",
|
||||
"chooseSolid": "اختر لوناً ثابتاً:",
|
||||
"customColor": "لون مخصص:",
|
||||
"preview": "معاينة:",
|
||||
"previewUsername": "اسم المستخدم",
|
||||
"apply": "تطبيق اللون"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "الإعدادات",
|
||||
"java": "بيئة تشغيل جافا",
|
||||
"useCustomJava": "استخدام مسار جافا مخصص",
|
||||
"javaDescription": "تجاوز بيئة جافا المرفقة واستخدام تثبيت خاص بك",
|
||||
"javaPath": "مسار ملف جافا التنفيذي",
|
||||
"javaPathPlaceholder": "اختر مسار جافا...",
|
||||
"javaBrowse": "تصفح",
|
||||
"javaHint": "اختر مجلد تثبيت جافا (يدعم ويندوز، ماك، ولينكس)",
|
||||
"discord": "تكامل ديسكورد",
|
||||
"enableRPC": "تفعيل نشاط ديسكورد (Rich Presence)",
|
||||
"discordDescription": "إظهار نشاط المشغل الخاص بك على ديسكورد",
|
||||
"game": "خيارات اللعبة",
|
||||
"playerName": "اسم اللاعب",
|
||||
"playerNamePlaceholder": "أدخل اسم اللاعب",
|
||||
"playerNameHint": "سيتم استخدام هذا الاسم داخل اللعبة (1-16 حرفاً)",
|
||||
"openGameLocation": "فتح موقع اللعبة",
|
||||
"openGameLocationDesc": "فتح مجلد تثبيت اللعبة",
|
||||
"account": "إدارة UUID اللاعب",
|
||||
"currentUUID": "الـ UUID الحالي",
|
||||
"uuidPlaceholder": "جاري تحميل UUID...",
|
||||
"copyUUID": "نسخ UUID",
|
||||
"regenerateUUID": "إعادة إنشاء UUID",
|
||||
"uuidHint": "معرف اللاعب الفريد الخاص بك لهذا الاسم",
|
||||
"manageUUIDs": "إدارة جميع الـ UUIDs",
|
||||
"manageUUIDsDesc": "عرض وإدارة جميع معرفات اللاعبين",
|
||||
"language": "اللغة",
|
||||
"selectLanguage": "اختر اللغة",
|
||||
"repairGame": "إصلاح اللعبة",
|
||||
"reinstallGame": "إعادة تثبيت ملفات اللعبة (يحفظ البيانات)",
|
||||
"gpuPreference": "تفضيل معالج الرسوميات (GPU)",
|
||||
"gpuHint": "ميزة للمحمول فقط؛ اضبطها على Integrated إذا كنت تستخدم كمبيوتر مكتبي",
|
||||
"gpuAuto": "تلقائي",
|
||||
"gpuIntegrated": "مدمج",
|
||||
"gpuDedicated": "منفصل",
|
||||
"logs": "سجلات النظام",
|
||||
"logsCopy": "نسخ",
|
||||
"logsRefresh": "تحديث",
|
||||
"logsFolder": "فتح المجلد",
|
||||
"logsSend": "إرسال السجلات",
|
||||
"logsSending": "جارٍ الإرسال...",
|
||||
"logsSent": "تم الإرسال!",
|
||||
"logsSendFailed": "فشل",
|
||||
"logsSubmissionId": "معرف الإرسال",
|
||||
"logsShareId": "شارك هذا المعرف مع الدعم عند الإبلاغ عن المشاكل",
|
||||
"logsLoading": "جاري تحميل السجلات...",
|
||||
"closeLauncher": "سلوك المشغل",
|
||||
"closeOnStart": "إغلاق المشغل عند بدء اللعبة",
|
||||
"closeOnStartDescription": "إغلاق المشغل تلقائياً بعد تشغيل Hytale",
|
||||
"hwAccel": "تسريع الأجهزة (Hardware Acceleration)",
|
||||
"hwAccelDescription": "تفعيل تسريع الأجهزة للمشغل",
|
||||
"gameBranch": "فرع اللعبة",
|
||||
"branchRelease": "إصدار نهائي",
|
||||
"branchPreRelease": "إصدار تجريبي",
|
||||
"branchHint": "التبديل بين الإصدار المستقر والإصدار التجريبي",
|
||||
"branchWarning": "تغيير الفرع سيؤدي إلى تحميل وتثبيت نسخة مختلفة من اللعبة",
|
||||
"branchSwitching": "جاري التبديل إلى {branch}...",
|
||||
"branchSwitched": "تم التبديل إلى {branch} بنجاح!",
|
||||
"installRequired": "التثبيت مطلوب",
|
||||
"branchInstallConfirm": "سيتم تثبيت اللعبة لفرع {branch}. هل تريد الاستمرار؟"
|
||||
},
|
||||
"uuid": {
|
||||
"modalTitle": "إدارة UUID",
|
||||
"allPlayerUUIDs": "جميع معرفات UUID للاعبين",
|
||||
"addIdentity": "إضافة هوية",
|
||||
"usernamePlaceholder": "اسم المستخدم",
|
||||
"add": "إضافة",
|
||||
"cancel": "إلغاء",
|
||||
"advanced": "متقدم",
|
||||
"loadingUUIDs": "جاري تحميل الـ UUIDs...",
|
||||
"setCustomUUID": "تعيين UUID مخصص",
|
||||
"customPlaceholder": "أدخل UUID مخصص (الصيغة: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)",
|
||||
"setUUID": "تعيين UUID",
|
||||
"warning": "تحذير: تعيين UUID مخصص سيغير هوية اللاعب الحالية",
|
||||
"copyTooltip": "نسخ UUID",
|
||||
"regenerateTooltip": "إنشاء UUID جديد"
|
||||
},
|
||||
"configurations": {
|
||||
"modalTitle": "إدارة التكوينات",
|
||||
"newProfilePlaceholder": "اسم التكوين الجديد",
|
||||
"createProfile": "إنشاء تكوين"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "انضم إلى مجتمعنا على ديسكورد!",
|
||||
"joinButton": "انضم إلى ديسكورد"
|
||||
},
|
||||
"common": {
|
||||
"confirm": "تأكيد",
|
||||
"cancel": "إلغاء",
|
||||
"save": "حفظ",
|
||||
"close": "إغلاق",
|
||||
"delete": "حذف",
|
||||
"edit": "تعديل",
|
||||
"loading": "جاري التحميل...",
|
||||
"apply": "تطبيق",
|
||||
"install": "تثبيت"
|
||||
},
|
||||
"notifications": {
|
||||
"gameDataNotFound": "خطأ: لم يتم العثور على بيانات اللعبة",
|
||||
"gameUpdatedSuccess": "تم تحديث اللعبة بنجاح! 🎉",
|
||||
"updateFailed": "فشل التحديث: {error}",
|
||||
"updateError": "خطأ في التحديث: {error}",
|
||||
"discordEnabled": "تم تفعيل نشاط ديسكورد",
|
||||
"discordDisabled": "تم تعطيل نشاط ديسكورد",
|
||||
"discordSaveFailed": "فشل حفظ إعدادات ديسكورد",
|
||||
"playerNameRequired": "يرجى إدخال اسم لاعب صالح",
|
||||
"playerNameSaved": "تم حفظ اسم اللاعب بنجاح",
|
||||
"playerNameSaveFailed": "فشل حفظ اسم اللاعب",
|
||||
"uuidCopied": "تم نسخ الـ UUID إلى الحافظة!",
|
||||
"uuidCopyFailed": "فشل نسخ الـ UUID",
|
||||
"uuidRegenNotAvailable": "إعادة إنشاء UUID غير متاحة",
|
||||
"uuidRegenFailed": "فشل إعادة إنشاء الـ UUID",
|
||||
"uuidGenerated": "تم إنشاء UUID جديد بنجاح!",
|
||||
"uuidGeneratedShort": "تم إنشاء UUID جديد!",
|
||||
"uuidGenerateFailed": "فشل إنشاء UUID جديد",
|
||||
"uuidRequired": "يرجى إدخال UUID",
|
||||
"uuidInvalidFormat": "صيغة UUID غير صالحة",
|
||||
"uuidSetFailed": "فشل تعيين الـ UUID المخصص",
|
||||
"uuidSetSuccess": "تم تعيين الـ UUID المخصص بنجاح!",
|
||||
"uuidDeleteFailed": "فشل حذف الـ UUID",
|
||||
"uuidDeleteSuccess": "تم حذف الـ UUID بنجاح!",
|
||||
"modsDownloading": "جاري تحميل {name}...",
|
||||
"modsTogglingMod": "جاري تبديل حالة المود...",
|
||||
"modsDeletingMod": "جاري حذف المود...",
|
||||
"modsLoadingMods": "جاري تحميل المودات من CurseForge...",
|
||||
"modsInstalledSuccess": "تم تثبيت {name} بنجاح! 🎉",
|
||||
"modsDeletedSuccess": "تم حذف {name} بنجاح",
|
||||
"modsDownloadFailed": "فشل تحميل المود: {error}",
|
||||
"modsToggleFailed": "فشل تبديل المود: {error}",
|
||||
"modsDeleteFailed": "فشل حذف المود: {error}",
|
||||
"modsModNotFound": "لم يتم العثور على معلومات المود",
|
||||
"hwAccelSaved": "تم حفظ إعداد تسريع الأجهزة",
|
||||
"hwAccelSaveFailed": "فشل حفظ إعداد تسريع الأجهزة",
|
||||
"noUsername": "لم يتم تهيئة اسم مستخدم. يرجى حفظ اسم المستخدم أولاً.",
|
||||
"identityAdded": "تمت إضافة الهوية بنجاح!",
|
||||
"identityAddFailed": "فشل في إضافة الهوية",
|
||||
"switchUsernameSuccess": "تم التبديل إلى المستخدم \"{username}\" بنجاح!",
|
||||
"switchUsernameFailed": "فشل تبديل اسم المستخدم",
|
||||
"playerNameTooLong": "يجب أن يكون اسم اللاعب 16 حرفاً أو أقل"
|
||||
},
|
||||
"confirm": {
|
||||
"defaultTitle": "تأكيد الإجراء",
|
||||
"regenerateUuidTitle": "إنشاء UUID جديد",
|
||||
"regenerateUuidMessage": "هل أنت متأكد أنك تريد إنشاء UUID جديد؟ سيؤدي ذلك إلى تغيير هوية اللاعب الخاصة بك.",
|
||||
"regenerateUuidButton": "إنشاء",
|
||||
"setCustomUuidTitle": "تعيين UUID مخصص",
|
||||
"setCustomUuidMessage": "هل أنت متأكد أنك تريد تعيين هذا الـ UUID المخصص؟ سيؤدي ذلك إلى تغيير هوية اللاعب الخاصة بك.",
|
||||
"setCustomUuidButton": "تعيين UUID",
|
||||
"deleteUuidTitle": "حذف UUID",
|
||||
"deleteUuidMessage": "هل أنت متأكد أنك تريد حذف الـ UUID الخاص بـ \"{username}\"؟ لا يمكن التراجع عن هذا الإجراء.",
|
||||
"deleteUuidButton": "حذف",
|
||||
"uninstallGameTitle": "إلغاء تثبيت اللعبة",
|
||||
"uninstallGameMessage": "هل أنت متأكد أنك تريد إلغاء تثبيت Hytale؟ سيتم حذف جميع ملفات اللعبة.",
|
||||
"uninstallGameButton": "إلغاء التثبيت",
|
||||
"switchUsernameTitle": "تبديل الهوية",
|
||||
"switchUsernameMessage": "التبديل إلى اسم المستخدم \"{username}\"؟ سيؤدي هذا إلى تغيير هوية اللاعب الحالية.",
|
||||
"switchUsernameButton": "تبديل"
|
||||
},
|
||||
"progress": {
|
||||
"initializing": "جاري التهيئة...",
|
||||
"downloading": "جاري التحميل...",
|
||||
"installing": "جاري التثبيت...",
|
||||
"extracting": "جاري الاستخراج...",
|
||||
"verifying": "جاري التحقق...",
|
||||
"switchingProfile": "جاري تبديل التكوين...",
|
||||
"profileSwitched": "تم تبديل التكوين!",
|
||||
"startingGame": "جاري بدء اللعبة...",
|
||||
"launching": "جاري التشغيل...",
|
||||
"uninstallingGame": "جاري إلغاء تثبيت اللعبة...",
|
||||
"gameUninstalled": "تم إلغاء تثبيت اللعبة بنجاح!",
|
||||
"uninstallFailed": "فشل إلغاء التثبيت: {error}",
|
||||
"startingUpdate": "جاري بدء تحديث اللعبة الإجباري...",
|
||||
"installationComplete": "تم اكتمال التثبيت بنجاح!",
|
||||
"installationFailed": "فشل التثبيت: {error}",
|
||||
"installingGameFiles": "جاري تثبيت ملفات اللعبة...",
|
||||
"installComplete": "اكتمل التثبيت!"
|
||||
}
|
||||
}
|
||||
@@ -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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Kopieren",
|
||||
"logsRefresh": "Aktualisieren",
|
||||
"logsFolder": "Ordner öffnen",
|
||||
"logsSend": "Logs senden",
|
||||
"logsSending": "Senden...",
|
||||
"logsSent": "Gesendet!",
|
||||
"logsSendFailed": "Fehlgeschlagen",
|
||||
"logsSubmissionId": "Einreichungs-ID",
|
||||
"logsShareId": "Teilen Sie diese ID dem Support mit, wenn Sie Probleme melden",
|
||||
"logsLoading": "Protokolle werden geladen...",
|
||||
"closeLauncher": "Launcher-Verhalten",
|
||||
"closeOnStart": "Launcher beim Spielstart schließen",
|
||||
@@ -145,9 +154,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)",
|
||||
@@ -156,10 +168,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!",
|
||||
@@ -213,6 +225,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"
|
||||
@@ -241,8 +255,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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Copy",
|
||||
"logsRefresh": "Refresh",
|
||||
"logsFolder": "Open Folder",
|
||||
"logsSend": "Send Logs",
|
||||
"logsSending": "Sending...",
|
||||
"logsSent": "Sent!",
|
||||
"logsSendFailed": "Failed",
|
||||
"logsSubmissionId": "Submission ID",
|
||||
"logsShareId": "Share this ID with support when reporting issues",
|
||||
"logsLoading": "Loading logs...",
|
||||
"closeLauncher": "Launcher Behavior",
|
||||
"closeOnStart": "Close Launcher on game start",
|
||||
@@ -141,25 +150,39 @@
|
||||
"branchSwitching": "Switching to {branch}...",
|
||||
"branchSwitched": "Switched to {branch} successfully!",
|
||||
"installRequired": "Installation Required",
|
||||
"branchInstallConfirm": "The game will be installed for the {branch} branch. Continue?"
|
||||
"branchInstallConfirm": "The game will be installed for the {branch} branch. Continue?",
|
||||
"wrapperConfig": "Java Wrapper Configuration",
|
||||
"wrapperConfigHint": "Configure how the Java wrapper handles JVM flags and arguments at launch time.",
|
||||
"wrapperStripFlags": "JVM Flags to Remove",
|
||||
"wrapperInjectArgs": "Arguments to Inject",
|
||||
"wrapperAddFlagPlaceholder": "e.g. -XX:+SomeFlag",
|
||||
"wrapperAddArgPlaceholder": "e.g. --some-flag",
|
||||
"wrapperAdd": "Add",
|
||||
"wrapperConditionServer": "Server Only",
|
||||
"wrapperConditionAlways": "Always",
|
||||
"wrapperRestoreDefaults": "Restore Defaults",
|
||||
"wrapperAdvancedPreview": "Advanced: Script Preview"
|
||||
},
|
||||
"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!",
|
||||
@@ -213,9 +236,17 @@
|
||||
"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"
|
||||
"playerNameTooLong": "Player name must be 16 characters or less",
|
||||
"wrapperConfigSaved": "Wrapper configuration saved",
|
||||
"wrapperConfigSaveFailed": "Failed to save wrapper configuration",
|
||||
"wrapperConfigReset": "Wrapper configuration restored to defaults",
|
||||
"wrapperConfigResetFailed": "Failed to restore wrapper configuration",
|
||||
"wrapperFlagExists": "This flag is already in the list",
|
||||
"wrapperArgExists": "This argument is already in the list"
|
||||
},
|
||||
"confirm": {
|
||||
"defaultTitle": "Confirm action",
|
||||
@@ -233,7 +264,9 @@
|
||||
"uninstallGameButton": "Uninstall",
|
||||
"switchUsernameTitle": "Switch Identity",
|
||||
"switchUsernameMessage": "Switch to username \"{username}\"? This will change your current player identity.",
|
||||
"switchUsernameButton": "Switch"
|
||||
"switchUsernameButton": "Switch",
|
||||
"resetWrapperTitle": "Restore Defaults",
|
||||
"resetWrapperMessage": "Are you sure you want to restore the default wrapper configuration? Your custom changes will be lost."
|
||||
},
|
||||
"progress": {
|
||||
"initializing": "Initializing...",
|
||||
@@ -241,8 +274,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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Copiar",
|
||||
"logsRefresh": "Actualizar",
|
||||
"logsFolder": "Abrir Carpeta",
|
||||
"logsSend": "Enviar logs",
|
||||
"logsSending": "Enviando...",
|
||||
"logsSent": "Enviado!",
|
||||
"logsSendFailed": "Fallido",
|
||||
"logsSubmissionId": "ID de envío",
|
||||
"logsShareId": "Comparte este ID con soporte al reportar problemas",
|
||||
"logsLoading": "Cargando registros...",
|
||||
"closeLauncher": "Comportamiento del Launcher",
|
||||
"closeOnStart": "Cerrar Launcher al iniciar el juego",
|
||||
@@ -145,9 +154,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)",
|
||||
@@ -156,10 +168,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!",
|
||||
@@ -213,6 +225,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"
|
||||
@@ -241,8 +255,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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Copier",
|
||||
"logsRefresh": "Actualiser",
|
||||
"logsFolder": "Ouvrir le Dossier",
|
||||
"logsSend": "Envoyer les logs",
|
||||
"logsSending": "Envoi...",
|
||||
"logsSent": "Envoyé !",
|
||||
"logsSendFailed": "Échoué",
|
||||
"logsSubmissionId": "ID de soumission",
|
||||
"logsShareId": "Partagez cet ID avec le support pour signaler des problèmes",
|
||||
"logsLoading": "Chargement des journaux...",
|
||||
"closeLauncher": "Comportement du Launcher",
|
||||
"closeOnStart": "Fermer le Launcher au démarrage du jeu",
|
||||
@@ -145,9 +154,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)",
|
||||
@@ -156,10 +168,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!",
|
||||
@@ -213,6 +225,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"
|
||||
@@ -241,8 +255,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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Salin",
|
||||
"logsRefresh": "Segarkan",
|
||||
"logsFolder": "Buka Folder",
|
||||
"logsSend": "Kirim Log",
|
||||
"logsSending": "Mengirim...",
|
||||
"logsSent": "Terkirim!",
|
||||
"logsSendFailed": "Gagal",
|
||||
"logsSubmissionId": "ID Pengiriman",
|
||||
"logsShareId": "Bagikan ID ini ke dukungan saat melaporkan masalah",
|
||||
"logsLoading": "Memuat log...",
|
||||
"closeLauncher": "Perilaku Launcher",
|
||||
"closeOnStart": "Tutup launcher saat game dimulai",
|
||||
@@ -145,9 +154,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)",
|
||||
@@ -156,10 +168,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!",
|
||||
@@ -213,6 +225,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"
|
||||
@@ -241,8 +255,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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Kopiuj",
|
||||
"logsRefresh": "Odśwież",
|
||||
"logsFolder": "Otwórz Folder",
|
||||
"logsSend": "Wyślij logi",
|
||||
"logsSending": "Wysyłanie...",
|
||||
"logsSent": "Wysłano!",
|
||||
"logsSendFailed": "Błąd",
|
||||
"logsSubmissionId": "ID zgłoszenia",
|
||||
"logsShareId": "Udostępnij ten ID wsparciu technicznemu przy zgłaszaniu problemów",
|
||||
"logsLoading": "Ładowanie logów...",
|
||||
"closeLauncher": "Zachowanie Launchera",
|
||||
"closeOnStart": "Zamknij Launcher przy starcie gry",
|
||||
@@ -145,9 +154,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)",
|
||||
@@ -156,10 +168,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!",
|
||||
@@ -213,6 +225,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"
|
||||
@@ -241,8 +255,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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Copiar",
|
||||
"logsRefresh": "Atualizar",
|
||||
"logsFolder": "Abrir Pasta",
|
||||
"logsSend": "Enviar logs",
|
||||
"logsSending": "Enviando...",
|
||||
"logsSent": "Enviado!",
|
||||
"logsSendFailed": "Falhou",
|
||||
"logsSubmissionId": "ID de envio",
|
||||
"logsShareId": "Compartilhe este ID com o suporte ao relatar problemas",
|
||||
"logsLoading": "Carregando registros...",
|
||||
"closeLauncher": "Comportamento do Lançador",
|
||||
"closeOnStart": "Fechar Lançador ao iniciar o jogo",
|
||||
@@ -145,9 +154,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)",
|
||||
@@ -156,10 +168,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!",
|
||||
@@ -213,6 +225,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"
|
||||
@@ -241,8 +255,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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Копировать",
|
||||
"logsRefresh": "Обновить",
|
||||
"logsFolder": "Открыть папку",
|
||||
"logsSend": "Отправить логи",
|
||||
"logsSending": "Отправка...",
|
||||
"logsSent": "Отправлено!",
|
||||
"logsSendFailed": "Ошибка",
|
||||
"logsSubmissionId": "ID отправки",
|
||||
"logsShareId": "Поделитесь этим ID с поддержкой при обращении",
|
||||
"logsLoading": "Загрузка логов...",
|
||||
"closeLauncher": "Поведение лаунчера",
|
||||
"closeOnStart": "Закрыть лаунчер при старте игры",
|
||||
@@ -145,9 +154,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)",
|
||||
@@ -156,10 +168,10 @@
|
||||
"copyTooltip": "Скопировать UUID",
|
||||
"regenerateTooltip": "Сгенерировать новый UUID"
|
||||
},
|
||||
"profiles": {
|
||||
"modalTitle": "Управление профилями",
|
||||
"newProfilePlaceholder": "Новое имя профиля",
|
||||
"createProfile": "Создать профиль"
|
||||
"configurations": {
|
||||
"modalTitle": "Управление конфигурациями",
|
||||
"newProfilePlaceholder": "Название новой конфигурации",
|
||||
"createProfile": "Создать конфигурацию"
|
||||
},
|
||||
"discord": {
|
||||
"notificationText": "Присоединитесь к нашему сообществу в Discord!",
|
||||
@@ -213,6 +225,8 @@
|
||||
"hwAccelSaved": "Настройка аппаратного ускорения сохранена!",
|
||||
"hwAccelSaveFailed": "Не удалось сохранить настройку аппаратного ускорения",
|
||||
"noUsername": "Имя пользователя не настроено. Пожалуйста, сначала сохраните имя пользователя.",
|
||||
"identityAdded": "Личность успешно добавлена!",
|
||||
"identityAddFailed": "Не удалось добавить личность",
|
||||
"switchUsernameSuccess": "Успешно переключено на \"{username}\"!",
|
||||
"switchUsernameFailed": "Не удалось переключить имя пользователя",
|
||||
"playerNameTooLong": "Имя игрока должно быть не более 16 символов"
|
||||
@@ -241,8 +255,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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Kopiera",
|
||||
"logsRefresh": "Uppdatera",
|
||||
"logsFolder": "Öppna mapp",
|
||||
"logsSend": "Skicka loggar",
|
||||
"logsSending": "Skickar...",
|
||||
"logsSent": "Skickat!",
|
||||
"logsSendFailed": "Misslyckades",
|
||||
"logsSubmissionId": "Inlämnings-ID",
|
||||
"logsShareId": "Dela detta ID med support vid felanmälan",
|
||||
"logsLoading": "Laddar loggar...",
|
||||
"closeLauncher": "Launcher-beteende",
|
||||
"closeOnStart": "Stäng launcher vid spelstart",
|
||||
@@ -145,9 +154,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)",
|
||||
@@ -156,10 +168,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!",
|
||||
@@ -213,6 +225,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"
|
||||
@@ -241,8 +255,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": {
|
||||
@@ -127,6 +130,12 @@
|
||||
"logsCopy": "Kopyala",
|
||||
"logsRefresh": "Yenile",
|
||||
"logsFolder": "Klasörü Aç",
|
||||
"logsSend": "Logları Gönder",
|
||||
"logsSending": "Gönderiliyor...",
|
||||
"logsSent": "Gönderildi!",
|
||||
"logsSendFailed": "Başarısız",
|
||||
"logsSubmissionId": "Gönderim ID",
|
||||
"logsShareId": "Sorun bildirirken bu ID'yi destekle paylaşın",
|
||||
"logsLoading": "Loglar yükleniyor...",
|
||||
"closeLauncher": "Başlatıcı Davranışı",
|
||||
"closeOnStart": "Oyun başlatıldığında Başlatıcıyı Kapat",
|
||||
@@ -145,9 +154,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)",
|
||||
@@ -156,10 +168,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!",
|
||||
@@ -213,6 +225,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"
|
||||
@@ -241,8 +255,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...",
|
||||
|
||||
209
GUI/style-RTL.css
Normal file
209
GUI/style-RTL.css
Normal file
@@ -0,0 +1,209 @@
|
||||
body.rtl {
|
||||
direction: rtl;
|
||||
font-family: 'Noto Sans Arabic', 'Space Grotesk', sans-serif;
|
||||
}
|
||||
|
||||
body.rtl .sidebar {
|
||||
right: 0;
|
||||
left: auto;
|
||||
border-right: none;
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body.rtl .nav-item.active::before {
|
||||
right: -8px;
|
||||
left: auto;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
body.rtl .nav-tooltip {
|
||||
right: 100%;
|
||||
left: auto;
|
||||
margin-right: 0.5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
body.rtl .nav-item:hover .nav-tooltip {
|
||||
transform: translateX(-8px);
|
||||
}
|
||||
|
||||
|
||||
body.rtl .main-content {
|
||||
margin-right: 80px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* Header Layout*/
|
||||
|
||||
body.rtl .players-counter {
|
||||
order: 2;
|
||||
margin-left: 1.5rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
body.rtl .profile-selector {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
body.rtl .window-controls {
|
||||
order: 3;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
body.rtl .profile-dropdown {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
body.rtl .form-group {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
body.rtl .radio-label,
|
||||
body.rtl .checkbox-group {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
body.rtl .form-input {
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
|
||||
body.rtl .mods-pagination {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
body.rtl .pagination-btn:first-child i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
body.rtl .pagination-btn:last-child i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* UUID Display */
|
||||
|
||||
body.rtl .uuid-display-container {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
body.rtl .uuid-btn {
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
body.rtl .uuid-input {
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
|
||||
body.rtl .segmented-control {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
/* Mod Grid Layout */
|
||||
body.rtl .mods-search {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
body.rtl .mods-search-container {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
body.rtl .mods-actions {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
body.rtl .mod-card {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
body.rtl .installed-mod-card {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
body.rtl .installed-mod-card .mod-info {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
body.rtl .mods-header {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
body.rtl .news-section .news-header {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
/* Settings Layout */
|
||||
|
||||
body.rtl .settings-option {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
body.rtl .settings-input-group {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
body.rtl .settings-input {
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
body.rtl .settings-section-title i {
|
||||
margin-right: 0;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
body.rtl .settings-hint i {
|
||||
margin-right: 0;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
body.rtl .custom-options,
|
||||
body.rtl .custom-java-options {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
body.rtl .checkbox-content {
|
||||
margin-right: 2rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
body.rtl .btn-content {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Icons & Transformations */
|
||||
|
||||
body.rtl .news-title i {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
body.rtl .uuid-modal-title i {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
body.rtl .mods-modal-title i {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
body.rtl .view-all-btn i,
|
||||
body.rtl .sidebar-nav div i,
|
||||
body.rtl .logs-header i,
|
||||
body.rtl .home-play-button i {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
body.rtl .play-title i {
|
||||
transform: scaleX(-1);
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
body.rtl .logs-terminal {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
body.rtl .version-display-bottom {
|
||||
right: auto;
|
||||
left: 1rem;
|
||||
}
|
||||
478
GUI/style.css
478
GUI/style.css
@@ -873,6 +873,22 @@ body {
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.logs-action-btn.logs-send-btn {
|
||||
background: rgba(0, 212, 255, 0.15);
|
||||
border-color: rgba(0, 212, 255, 0.3);
|
||||
color: #00d4ff;
|
||||
}
|
||||
|
||||
.logs-action-btn.logs-send-btn:hover {
|
||||
background: rgba(0, 212, 255, 0.25);
|
||||
border-color: rgba(0, 212, 255, 0.5);
|
||||
}
|
||||
|
||||
.logs-action-btn.logs-send-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.logs-terminal {
|
||||
flex: 1;
|
||||
background: #0d1117;
|
||||
@@ -4813,6 +4829,140 @@ select.settings-input option {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.wrapper-items-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.wrapper-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.wrapper-item:hover {
|
||||
border-color: rgba(147, 51, 234, 0.3);
|
||||
background: rgba(147, 51, 234, 0.05);
|
||||
}
|
||||
|
||||
.wrapper-item-text {
|
||||
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
||||
font-size: 0.82rem;
|
||||
color: #e5e7eb;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wrapper-item-condition select {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 6px;
|
||||
color: #e5e7eb;
|
||||
font-size: 0.75rem;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.wrapper-item-condition select:focus {
|
||||
outline: none;
|
||||
border-color: rgba(147, 51, 234, 0.5);
|
||||
}
|
||||
|
||||
.wrapper-item-delete {
|
||||
padding: 4px 8px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.8rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.wrapper-item-delete:hover {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
border-color: rgba(239, 68, 68, 0.4);
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.wrapper-items-empty {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
padding: 12px;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.wrapper-condition-select {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 6px;
|
||||
color: #e5e7eb;
|
||||
font-size: 0.82rem;
|
||||
padding: 6px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wrapper-condition-select:focus {
|
||||
outline: none;
|
||||
border-color: rgba(147, 51, 234, 0.5);
|
||||
}
|
||||
|
||||
.wrapper-preview-toggle {
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
cursor: pointer;
|
||||
font-size: 0.82rem;
|
||||
padding: 4px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.wrapper-preview-toggle:hover {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.wrapper-preview-toggle i {
|
||||
transition: transform 0.2s;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.wrapper-preview-toggle i.expanded {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.wrapper-preview-content {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
||||
font-size: 0.72rem;
|
||||
line-height: 1.5;
|
||||
padding: 12px;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
white-space: pre;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
.settings-hint {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
@@ -5425,9 +5575,8 @@ input[type="text"].uuid-input,
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
|
||||
.uuid-current-section,
|
||||
.uuid-list-section,
|
||||
.uuid-custom-section {
|
||||
.uuid-advanced-section {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
@@ -5674,6 +5823,96 @@ input[type="text"].uuid-input,
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.uuid-item-btn.regenerate:hover {
|
||||
background: rgba(249, 115, 22, 0.2);
|
||||
border-color: rgba(249, 115, 22, 0.4);
|
||||
color: #f97316;
|
||||
}
|
||||
|
||||
/* Add Identity Form */
|
||||
.uuid-add-form {
|
||||
background: rgba(147, 51, 234, 0.05);
|
||||
border: 1px dashed rgba(147, 51, 234, 0.3);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.uuid-add-form-row {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.uuid-add-form-row .uuid-input {
|
||||
flex: 1;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.uuid-add-form-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.uuid-cancel-btn {
|
||||
padding: 0.75rem 1.25rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.uuid-cancel-btn:hover {
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
border-color: rgba(239, 68, 68, 0.3);
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
/* Advanced Section */
|
||||
.uuid-advanced-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 0;
|
||||
transition: color 0.3s ease;
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
|
||||
.uuid-advanced-toggle:hover {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.uuid-advanced-chevron {
|
||||
font-size: 0.75rem;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.uuid-advanced-chevron.open {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.uuid-advanced-content {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.uuid-modal-content {
|
||||
width: 95vw;
|
||||
@@ -5685,8 +5924,8 @@ input[type="text"].uuid-input,
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.uuid-current-display,
|
||||
.uuid-custom-form {
|
||||
.uuid-custom-form,
|
||||
.uuid-add-form-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -6063,6 +6302,155 @@ input[type="text"].uuid-input,
|
||||
box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* Header Tooltip Styles */
|
||||
.header-tooltip {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(4px);
|
||||
font-size: 0.65rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.03em;
|
||||
color: #9ca3af;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
padding: 0.3rem 0.6rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.identity-selector:hover .header-tooltip,
|
||||
.profile-selector:hover .header-tooltip {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.identity-dropdown.show ~ .header-tooltip,
|
||||
.profile-dropdown.show ~ .header-tooltip {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
/* Identity Selector Styles */
|
||||
.identity-selector {
|
||||
position: relative;
|
||||
pointer-events: auto;
|
||||
margin-left: 1rem;
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
.identity-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
z-index: 100000 !important;
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
.identity-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(34, 197, 94, 0.4);
|
||||
}
|
||||
|
||||
.identity-btn i {
|
||||
color: #22c55e;
|
||||
}
|
||||
|
||||
.identity-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
margin-top: 0.5rem;
|
||||
width: 200px;
|
||||
background: rgba(20, 20, 20, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
padding: 0.5rem;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
z-index: 2000;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
|
||||
animation: fadeIn 0.1s ease;
|
||||
}
|
||||
|
||||
.identity-dropdown.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.identity-list {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.identity-item {
|
||||
padding: 0.6rem 0.8rem;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: #ccc;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.identity-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.identity-item.active {
|
||||
background: rgba(34, 197, 94, 0.2);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.identity-item.active::before {
|
||||
content: '\2022';
|
||||
color: #22c55e;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.identity-divider {
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.identity-action {
|
||||
padding: 0.6rem 0.8rem;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: #22c55e;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.identity-action:hover {
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
}
|
||||
|
||||
.identity-empty {
|
||||
padding: 0.6rem 0.8rem;
|
||||
color: #666;
|
||||
font-size: 0.85rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Profile Selector Styles */
|
||||
.profile-selector {
|
||||
position: relative;
|
||||
@@ -6460,3 +6848,85 @@ input[type="text"].uuid-input,
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Version Selection Styles */
|
||||
.version-list-container::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.version-list-container::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.version-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 0.5rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.version-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.version-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.version-name {
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.version-meta {
|
||||
font-size: 0.8rem;
|
||||
color: #a0a0a0;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.version-meta span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.version-meta i {
|
||||
font-size: 0.8em;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.version-actions .btn-install {
|
||||
padding: 0.5rem 1.25rem;
|
||||
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.version-actions .btn-install:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.3);
|
||||
background: linear-gradient(135deg, #4f93f6, #3b82f6);
|
||||
}
|
||||
|
||||
.loading-versions {
|
||||
padding: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -18,7 +18,8 @@
|
||||
|
||||
### ⚠️ **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`** 🛑
|
||||
#### 🛑 **Found a problem? [TG Group](https://t.me/sanhostnet) | [TG Channel](https://t.me/hf2p_og) | [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4)** 🛑
|
||||
<!-- #### 🛑 **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>
|
||||
@@ -455,7 +456,8 @@ See [BUILD.md](docs/BUILD.md) for comprehensive build instructions.
|
||||
<div align="center">
|
||||
|
||||
**Questions? Ads? Collaboration? Endorsement? Other business-related?**
|
||||
Message the founders at https://discord.gg/Fhbb9Yk5WW
|
||||
Message the founders at [TG Group](https://t.me/sanhostnet) | [TG Channel](https://t.me/hf2p_og) | [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4)
|
||||
<!-- Message the founders at https://discord.gg/Fhbb9Yk5WW -->
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
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**
|
||||
### **DOWNLOAD SERVER FILES (JAR/RAR/SCRIPTS) HERE: https://chat.sanhost.net/invite/Tfz4jCK4**
|
||||
<!-- ### **DOWNLOAD SERVER FILES (JAR/RAR/SCRIPTS) HERE: https://discord.gg/Fhbb9Yk5WW** -->
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# 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://github.com/amiayweb/Hytale-F2P/issues) or join our [TG Group](https://t.me/sanhostnet) | [TG Channel](https://t.me/hf2p_og) | [Community Chat](https://chat.sanhost.net/invite/Tfz4jCK4).
|
||||
<!-- Discord: https://discord.gg/Fhbb9Yk5WW -->
|
||||
|
||||
---
|
||||
|
||||
@@ -437,7 +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)
|
||||
2. **Join Community:** [TG Group](https://t.me/sanhostnet) | [TG Channel](https://t.me/hf2p_og) | [Chat](https://chat.sanhost.net/invite/Tfz4jCK4)
|
||||
<!-- Discord: https://discord.gg/Fhbb9Yk5WW -->
|
||||
3. **Open a new issue** with:
|
||||
- Your operating system and version
|
||||
- Launcher version
|
||||
|
||||
@@ -858,6 +858,212 @@ function checkLaunchReady() {
|
||||
};
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// JAVA WRAPPER CONFIGURATION (Structured)
|
||||
// =============================================================================
|
||||
|
||||
const DEFAULT_WRAPPER_CONFIG = {
|
||||
stripFlags: [],
|
||||
injectArgs: [
|
||||
{ arg: '--disable-sentry', condition: 'server' }
|
||||
]
|
||||
};
|
||||
|
||||
function getDefaultWrapperConfig() {
|
||||
return JSON.parse(JSON.stringify(DEFAULT_WRAPPER_CONFIG));
|
||||
}
|
||||
|
||||
function loadWrapperConfig() {
|
||||
const config = loadConfig();
|
||||
if (config.javaWrapperConfig && typeof config.javaWrapperConfig === 'object') {
|
||||
const wc = config.javaWrapperConfig;
|
||||
if (Array.isArray(wc.stripFlags) && Array.isArray(wc.injectArgs)) {
|
||||
const loaded = JSON.parse(JSON.stringify(wc));
|
||||
// Normalize entries: ensure every injectArg has a valid condition
|
||||
for (const entry of loaded.injectArgs) {
|
||||
if (!['server', 'always'].includes(entry.condition)) {
|
||||
entry.condition = 'always';
|
||||
}
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
return getDefaultWrapperConfig();
|
||||
}
|
||||
|
||||
function saveWrapperConfig(wrapperConfig) {
|
||||
if (!wrapperConfig || typeof wrapperConfig !== 'object') {
|
||||
throw new Error('Invalid wrapper config');
|
||||
}
|
||||
if (!Array.isArray(wrapperConfig.stripFlags) || !Array.isArray(wrapperConfig.injectArgs)) {
|
||||
throw new Error('Invalid wrapper config structure');
|
||||
}
|
||||
// Validate injectArgs entries
|
||||
for (const entry of wrapperConfig.injectArgs) {
|
||||
if (!entry.arg || typeof entry.arg !== 'string') {
|
||||
throw new Error('Each inject arg must have a string "arg" property');
|
||||
}
|
||||
if (!['server', 'always'].includes(entry.condition)) {
|
||||
throw new Error('Inject arg condition must be "server" or "always"');
|
||||
}
|
||||
}
|
||||
saveConfig({ javaWrapperConfig: wrapperConfig });
|
||||
console.log('[Config] Wrapper config saved');
|
||||
}
|
||||
|
||||
function resetWrapperConfig() {
|
||||
const config = loadConfig();
|
||||
delete config.javaWrapperConfig;
|
||||
delete config.javaWrapperScripts; // Clean up legacy key if present
|
||||
|
||||
// Write the cleaned config using the same atomic pattern as saveConfig.
|
||||
// We cannot use saveConfig() here because it merges (spread) which cannot remove keys.
|
||||
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);
|
||||
console.log('[Config] Wrapper config reset to default');
|
||||
return getDefaultWrapperConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a platform-specific wrapper script from structured config
|
||||
* @param {Object} config - { stripFlags: string[], injectArgs: {arg, condition}[] }
|
||||
* @param {string} platform - 'darwin', 'win32', or 'linux'
|
||||
* @param {string|null} javaBin - Path to real java binary (required for darwin/linux)
|
||||
* @returns {string} Generated script content
|
||||
*/
|
||||
function generateWrapperScript(config, platform, javaBin) {
|
||||
const { stripFlags, injectArgs } = config;
|
||||
const alwaysArgs = injectArgs.filter(a => a.condition === 'always');
|
||||
const serverArgs = injectArgs.filter(a => a.condition === 'server');
|
||||
|
||||
if (platform === 'win32') {
|
||||
return _generateWindowsWrapper(stripFlags, alwaysArgs, serverArgs);
|
||||
} else {
|
||||
return _generateUnixWrapper(stripFlags, alwaysArgs, serverArgs, javaBin);
|
||||
}
|
||||
}
|
||||
|
||||
function _generateUnixWrapper(stripFlags, alwaysArgs, serverArgs, javaBin) {
|
||||
const lines = [
|
||||
'#!/bin/bash',
|
||||
'# Java wrapper - generated by HytaleF2P launcher',
|
||||
`REAL_JAVA="${javaBin || '${JAVA_BIN}'}"`,
|
||||
'ARGS=("$@")',
|
||||
''
|
||||
];
|
||||
|
||||
// Strip flags
|
||||
if (stripFlags.length > 0) {
|
||||
lines.push('# Strip JVM flags');
|
||||
lines.push('FILTERED_ARGS=()');
|
||||
lines.push('for arg in "${ARGS[@]}"; do');
|
||||
lines.push(' case "$arg" in');
|
||||
for (const flag of stripFlags) {
|
||||
lines.push(` "${flag}") echo "[Wrapper] Stripped: $arg" ;;`);
|
||||
}
|
||||
lines.push(' *) FILTERED_ARGS+=("$arg") ;;');
|
||||
lines.push(' esac');
|
||||
lines.push('done');
|
||||
} else {
|
||||
lines.push('FILTERED_ARGS=("${ARGS[@]}")');
|
||||
}
|
||||
lines.push('');
|
||||
|
||||
// Always-inject args
|
||||
if (alwaysArgs.length > 0) {
|
||||
lines.push('# Inject args (always)');
|
||||
for (const a of alwaysArgs) {
|
||||
lines.push(`FILTERED_ARGS+=("${a.arg}")`);
|
||||
lines.push(`echo "[Wrapper] Injected ${a.arg}"`);
|
||||
}
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
// Server-conditional args (appended after HytaleServer.jar if present)
|
||||
if (serverArgs.length > 0) {
|
||||
lines.push('# Inject args (server only)');
|
||||
lines.push('IS_SERVER=false');
|
||||
lines.push('for arg in "${FILTERED_ARGS[@]}"; do');
|
||||
lines.push(' if [[ "$arg" == *"HytaleServer.jar"* ]]; then');
|
||||
lines.push(' IS_SERVER=true');
|
||||
lines.push(' break');
|
||||
lines.push(' fi');
|
||||
lines.push('done');
|
||||
lines.push('if [ "$IS_SERVER" = true ]; then');
|
||||
for (const a of serverArgs) {
|
||||
lines.push(` FILTERED_ARGS+=("${a.arg}")`);
|
||||
lines.push(` echo "[Wrapper] Injected ${a.arg}"`);
|
||||
}
|
||||
lines.push('fi');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
lines.push('echo "[Wrapper] Executing: $REAL_JAVA ${FILTERED_ARGS[*]}"');
|
||||
lines.push('exec "$REAL_JAVA" "${FILTERED_ARGS[@]}"');
|
||||
lines.push('');
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function _generateWindowsWrapper(stripFlags, alwaysArgs, serverArgs) {
|
||||
const lines = [
|
||||
'@echo off',
|
||||
'setlocal EnableDelayedExpansion',
|
||||
'',
|
||||
'REM Java wrapper - generated by HytaleF2P launcher',
|
||||
'set "REAL_JAVA=%~dp0java-original.exe"',
|
||||
'set "ARGS=%*"',
|
||||
''
|
||||
];
|
||||
|
||||
// Strip flags using string replacement
|
||||
if (stripFlags.length > 0) {
|
||||
lines.push('REM Strip JVM flags');
|
||||
for (const flag of stripFlags) {
|
||||
lines.push(`set "ARGS=!ARGS:${flag}=!"`);
|
||||
}
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
// Always-inject args
|
||||
const alwaysExtra = alwaysArgs.map(a => a.arg).join(' ');
|
||||
|
||||
// Server-conditional args
|
||||
if (serverArgs.length > 0) {
|
||||
const serverExtra = serverArgs.map(a => a.arg).join(' ');
|
||||
lines.push('REM Check if running HytaleServer.jar and inject server args');
|
||||
lines.push('echo !ARGS! | findstr /i "HytaleServer.jar" >nul 2>&1');
|
||||
lines.push('if "!ERRORLEVEL!"=="0" (');
|
||||
if (alwaysExtra) {
|
||||
lines.push(` echo [Wrapper] Injected ${alwaysExtra} ${serverExtra}`);
|
||||
lines.push(` "%REAL_JAVA%" !ARGS! ${alwaysExtra} ${serverExtra}`);
|
||||
} else {
|
||||
lines.push(` echo [Wrapper] Injected ${serverExtra}`);
|
||||
lines.push(` "%REAL_JAVA%" !ARGS! ${serverExtra}`);
|
||||
}
|
||||
lines.push(') else (');
|
||||
if (alwaysExtra) {
|
||||
lines.push(` "%REAL_JAVA%" !ARGS! ${alwaysExtra}`);
|
||||
} else {
|
||||
lines.push(' "%REAL_JAVA%" !ARGS!');
|
||||
}
|
||||
lines.push(')');
|
||||
} else if (alwaysExtra) {
|
||||
lines.push(`"%REAL_JAVA%" !ARGS! ${alwaysExtra}`);
|
||||
} else {
|
||||
lines.push('"%REAL_JAVA%" !ARGS!');
|
||||
}
|
||||
|
||||
lines.push('exit /b !ERRORLEVEL!');
|
||||
lines.push('');
|
||||
|
||||
return lines.join('\r\n');
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// EXPORTS
|
||||
// =============================================================================
|
||||
@@ -924,6 +1130,13 @@ module.exports = {
|
||||
saveVersionBranch,
|
||||
loadVersionBranch,
|
||||
|
||||
// Java Wrapper Config
|
||||
getDefaultWrapperConfig,
|
||||
loadWrapperConfig,
|
||||
saveWrapperConfig,
|
||||
resetWrapperConfig,
|
||||
generateWrapperScript,
|
||||
|
||||
// Constants
|
||||
CONFIG_FILE,
|
||||
UUID_STORE_FILE
|
||||
|
||||
@@ -45,7 +45,13 @@ const {
|
||||
saveVersionClient,
|
||||
loadVersionClient,
|
||||
saveVersionBranch,
|
||||
loadVersionBranch
|
||||
loadVersionBranch,
|
||||
// Java Wrapper Config
|
||||
getDefaultWrapperConfig,
|
||||
loadWrapperConfig,
|
||||
saveWrapperConfig,
|
||||
resetWrapperConfig,
|
||||
generateWrapperScript
|
||||
} = require('./core/config');
|
||||
|
||||
const { getResolvedAppDir, getModsPath } = require('./core/paths');
|
||||
@@ -78,7 +84,8 @@ const {
|
||||
loadInstalledMods,
|
||||
downloadMod,
|
||||
uninstallMod,
|
||||
toggleMod
|
||||
toggleMod,
|
||||
getModFiles
|
||||
} = require('./managers/modManager');
|
||||
|
||||
// Services
|
||||
@@ -181,6 +188,7 @@ module.exports = {
|
||||
downloadMod,
|
||||
uninstallMod,
|
||||
toggleMod,
|
||||
getModFiles,
|
||||
saveModsToConfig,
|
||||
loadModsFromConfig,
|
||||
|
||||
@@ -197,6 +205,13 @@ module.exports = {
|
||||
proposeGameUpdate,
|
||||
handleFirstLaunchCheck,
|
||||
|
||||
// Java Wrapper Config functions
|
||||
getDefaultWrapperConfig,
|
||||
loadWrapperConfig,
|
||||
saveWrapperConfig,
|
||||
resetWrapperConfig,
|
||||
generateWrapperScript,
|
||||
|
||||
// Path functions
|
||||
getResolvedAppDir
|
||||
};
|
||||
|
||||
@@ -18,7 +18,9 @@ const {
|
||||
saveVersionClient,
|
||||
loadUsername,
|
||||
hasUsername,
|
||||
checkLaunchReady
|
||||
checkLaunchReady,
|
||||
loadWrapperConfig,
|
||||
generateWrapperScript
|
||||
} = require('../core/config');
|
||||
const { resolveJavaPath, getJavaExec, getBundledJavaPath, detectSystemJava, JAVA_EXECUTABLE } = require('./javaManager');
|
||||
const { getLatestClientVersion } = require('../services/versionManager');
|
||||
@@ -27,6 +29,7 @@ const { ensureGameInstalled } = require('./differentialUpdateManager');
|
||||
const { syncModsForCurrentProfile } = require('./modManager');
|
||||
const { getUserDataPath } = require('../utils/userDataMigration');
|
||||
const { syncServerList } = require('../utils/serverListSync');
|
||||
const { killGameProcesses } = require('./gameManager');
|
||||
|
||||
// Client patcher for custom auth server (sanasol.ws)
|
||||
let clientPatcher = null;
|
||||
@@ -327,23 +330,13 @@ async function launchGame(playerNameOverride = null, progressCallback, javaPathO
|
||||
console.log('Signed server binaries (after patching)');
|
||||
}
|
||||
|
||||
// Create java wrapper (must be signed on macOS)
|
||||
if (javaBin && fs.existsSync(javaBin)) {
|
||||
const javaWrapperPath = path.join(path.dirname(javaBin), 'java-wrapper');
|
||||
const wrapperScript = `#!/bin/bash
|
||||
# Java wrapper for macOS - adds --disable-sentry to fix Sentry hang issue
|
||||
REAL_JAVA="${javaBin}"
|
||||
ARGS=("$@")
|
||||
for i in "\${!ARGS[@]}"; do
|
||||
if [[ "\${ARGS[$i]}" == *"HytaleServer.jar"* ]]; then
|
||||
ARGS=("\${ARGS[@]:0:$((i+1))}" "--disable-sentry" "\${ARGS[@]:$((i+1))}")
|
||||
break
|
||||
fi
|
||||
done
|
||||
exec "$REAL_JAVA" "\${ARGS[@]}"
|
||||
`;
|
||||
const wrapperScript = generateWrapperScript(loadWrapperConfig(), 'darwin', javaBin);
|
||||
fs.writeFileSync(javaWrapperPath, wrapperScript, { mode: 0o755 });
|
||||
await signPath(javaWrapperPath, false);
|
||||
console.log('Created java wrapper with --disable-sentry fix');
|
||||
console.log('Created java wrapper from config template');
|
||||
javaBin = javaWrapperPath;
|
||||
}
|
||||
} catch (signError) {
|
||||
@@ -352,6 +345,40 @@ exec "$REAL_JAVA" "\${ARGS[@]}"
|
||||
}
|
||||
}
|
||||
|
||||
// Windows: Create java wrapper to strip/inject JVM flags per wrapper config
|
||||
if (process.platform === 'win32' && javaBin && fs.existsSync(javaBin)) {
|
||||
try {
|
||||
const javaDir = path.dirname(javaBin);
|
||||
const javaOriginal = path.join(javaDir, 'java-original.exe');
|
||||
const javaWrapperPath = path.join(javaDir, 'java-wrapper.bat');
|
||||
|
||||
if (!fs.existsSync(javaOriginal)) {
|
||||
fs.copyFileSync(javaBin, javaOriginal);
|
||||
console.log('Backed up java.exe as java-original.exe');
|
||||
}
|
||||
|
||||
const wrapperScript = generateWrapperScript(loadWrapperConfig(), 'win32', null);
|
||||
fs.writeFileSync(javaWrapperPath, wrapperScript);
|
||||
console.log('Created Windows java wrapper from config template');
|
||||
javaBin = javaWrapperPath;
|
||||
} catch (wrapperError) {
|
||||
console.log('Notice: Windows java wrapper creation failed:', wrapperError.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Linux: Create java wrapper to strip/inject JVM flags per wrapper config
|
||||
if (process.platform === 'linux' && javaBin && fs.existsSync(javaBin)) {
|
||||
try {
|
||||
const javaWrapperPath = path.join(path.dirname(javaBin), 'java-wrapper');
|
||||
const wrapperScript = generateWrapperScript(loadWrapperConfig(), 'linux', javaBin);
|
||||
fs.writeFileSync(javaWrapperPath, wrapperScript, { mode: 0o755 });
|
||||
console.log('Created Linux java wrapper from config template');
|
||||
javaBin = javaWrapperPath;
|
||||
} catch (wrapperError) {
|
||||
console.log('Notice: Linux java wrapper creation failed:', wrapperError.message);
|
||||
}
|
||||
}
|
||||
|
||||
const args = [
|
||||
'--app-dir', gameLatest,
|
||||
'--java-exec', javaBin,
|
||||
@@ -436,6 +463,22 @@ exec "$REAL_JAVA" "\${ARGS[@]}"
|
||||
}
|
||||
}
|
||||
|
||||
// Kill any stalled game processes from a previous launch to prevent file locks
|
||||
// and "game already running" issues
|
||||
await killGameProcesses();
|
||||
|
||||
// Remove AOT cache: generated by official Hytale JRE, incompatible with F2P JRE.
|
||||
// Client adds -XX:AOTCache when this file exists, causing classloading failures.
|
||||
const aotCache = path.join(gameLatest, 'Server', 'HytaleServer.aot');
|
||||
if (fs.existsSync(aotCache)) {
|
||||
try {
|
||||
fs.unlinkSync(aotCache);
|
||||
console.log('Removed incompatible AOT cache (HytaleServer.aot)');
|
||||
} catch (aotErr) {
|
||||
console.warn('Could not remove AOT cache:', aotErr.message);
|
||||
}
|
||||
}
|
||||
|
||||
// DualAuth Agent: Set JAVA_TOOL_OPTIONS so java picks up -javaagent: flag
|
||||
// This enables runtime auth patching without modifying the server JAR
|
||||
const agentJar = path.join(gameLatest, 'Server', 'dualauth-agent.jar');
|
||||
|
||||
@@ -39,6 +39,41 @@ async function isGameRunning() {
|
||||
}
|
||||
}
|
||||
|
||||
// Force-kill stalled game processes to release file locks before repair/reinstall.
|
||||
// Cross-platform: Windows (taskkill/PowerShell), macOS (pkill), Linux (pkill).
|
||||
async function killGameProcesses() {
|
||||
const killed = [];
|
||||
|
||||
async function tryKill(command, label) {
|
||||
try {
|
||||
await execAsync(command);
|
||||
killed.push(label);
|
||||
} catch (_) { /* process not found is expected */ }
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// Kill client
|
||||
await tryKill('taskkill /F /IM "HytaleClient.exe" /T', 'HytaleClient.exe');
|
||||
// Kill java.exe instances running HytaleServer.jar via PowerShell
|
||||
// (Get-CimInstance replaces deprecated wmic, works on Windows 10+)
|
||||
await tryKill(
|
||||
'powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \\"name=\'java.exe\'\\" | Where-Object { $_.CommandLine -like \'*HytaleServer*\' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"',
|
||||
'java.exe(HytaleServer)'
|
||||
);
|
||||
} else {
|
||||
// macOS and Linux
|
||||
await tryKill('pkill -9 -f HytaleClient', 'HytaleClient');
|
||||
await tryKill('pkill -9 -f HytaleServer', 'HytaleServer');
|
||||
}
|
||||
|
||||
if (killed.length > 0) {
|
||||
console.log(`[GameManager] Force-killed stalled processes: ${killed.join(', ')}`);
|
||||
// Wait for OS to release file handles
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
}
|
||||
return killed;
|
||||
}
|
||||
|
||||
// Helper function to safely remove directory with retry logic
|
||||
async function safeRemoveDirectory(dirPath, maxRetries = 3) {
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
@@ -50,8 +85,13 @@ async function safeRemoveDirectory(dirPath, maxRetries = 3) {
|
||||
return; // Success, exit the loop
|
||||
} catch (error) {
|
||||
console.warn(`Attempt ${attempt}/${maxRetries} failed to remove ${dirPath}: ${error.message}`);
|
||||
|
||||
|
||||
if (attempt < maxRetries) {
|
||||
// On EPERM/EBUSY, try killing stalled game processes that hold file locks
|
||||
if (attempt === 1 && (error.code === 'EPERM' || error.code === 'EBUSY')) {
|
||||
console.log('Permission error detected, killing stalled game processes...');
|
||||
await killGameProcesses();
|
||||
}
|
||||
// Wait before retrying (exponential backoff)
|
||||
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
||||
console.log(`Waiting ${delay}ms before retry...`);
|
||||
@@ -833,11 +873,14 @@ async function repairGame(progressCallback, branchOverride = null) {
|
||||
progressCallback('Removing old game files...', 30, null, null, null);
|
||||
}
|
||||
|
||||
// Check if game is running before attempting to delete files
|
||||
// Kill stalled game processes before attempting to delete files
|
||||
const gameRunning = await isGameRunning();
|
||||
if (gameRunning) {
|
||||
console.warn('[RepairGame] Game appears to be running. This may cause permission errors during repair.');
|
||||
console.log('[RepairGame] Please close the game before repairing, or wait for the repair to complete.');
|
||||
console.warn('[RepairGame] Game processes detected. Force-killing to release file locks...');
|
||||
if (progressCallback) {
|
||||
progressCallback('Stopping stalled game processes...', 20, null, null, null);
|
||||
}
|
||||
await killGameProcesses();
|
||||
}
|
||||
|
||||
// Delete Game and Cache Directory with retry logic
|
||||
@@ -964,5 +1007,6 @@ module.exports = {
|
||||
installGame,
|
||||
uninstallGame,
|
||||
checkExistingGameInstallation,
|
||||
repairGame
|
||||
repairGame,
|
||||
killGameProcesses
|
||||
};
|
||||
|
||||
@@ -285,6 +285,27 @@ async function toggleMod(modId, modsPath) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function getModFiles(modId) {
|
||||
try {
|
||||
const response = await axios.get(`https://api.curseforge.com/v1/mods/${modId}/files`, {
|
||||
headers: {
|
||||
'x-api-key': API_KEY,
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
params: {
|
||||
pageSize: 20,
|
||||
sortOrder: 'desc'
|
||||
}
|
||||
});
|
||||
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching mod files:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function syncModsForCurrentProfile() {
|
||||
try {
|
||||
const activeProfile = profileManager.getActiveProfile();
|
||||
@@ -455,5 +476,6 @@ module.exports = {
|
||||
syncModsForCurrentProfile,
|
||||
generateModId,
|
||||
extractModName,
|
||||
extractVersion
|
||||
};
|
||||
extractVersion,
|
||||
getModFiles
|
||||
};
|
||||
@@ -7,16 +7,17 @@ const { getOS, getArch } = require('../utils/platformUtils');
|
||||
// Patches base URL fetched dynamically via multi-source fallback chain
|
||||
const AUTH_DOMAIN = process.env.HYTALE_AUTH_DOMAIN || 'auth.sanasol.ws';
|
||||
const PATCHES_CONFIG_SOURCES = [
|
||||
{ type: 'http', url: `https://${AUTH_DOMAIN}/api/patches-config`, name: 'auth-server' },
|
||||
{ type: 'http', url: 'https://htdwnldsan.top/patches-config', name: 'backup-http' },
|
||||
{ type: 'http', url: `https://${AUTH_DOMAIN}/api/patches-config`, name: 'primary' },
|
||||
{ type: 'http', url: 'https://htdwnldsan.top/patches-config', name: 'backup-1' },
|
||||
{ type: 'http', url: 'https://dl1.htdwnldsan.top/patches-config', name: 'backup-2' },
|
||||
{ type: 'doh', name: '_patches.htdwnldsan.top', name_label: 'dns-txt' },
|
||||
];
|
||||
const HARDCODED_FALLBACK = 'https://dl.vboro.de/patches';
|
||||
|
||||
// Non-Cloudflare mirrors for users where Cloudflare IPs are blocked (Russia, Ukraine, etc.)
|
||||
// These redirect to MEGA S3 which is not behind Cloudflare
|
||||
// Alternative mirrors (non-Cloudflare) for regions where CF is blocked
|
||||
const NON_CF_MIRRORS = [
|
||||
'https://htdwnldsan.top/patches', // Direct IP VPS → MEGA redirect
|
||||
'https://dl1.htdwnldsan.top',
|
||||
'https://htdwnldsan.top/patches',
|
||||
];
|
||||
|
||||
// Fallback: latest known build number if manifest is unreachable
|
||||
|
||||
@@ -9,7 +9,9 @@ const MAX_DOMAIN_LENGTH = 16;
|
||||
|
||||
// DualAuth ByteBuddy Agent (runtime class transformation, no JAR modification)
|
||||
const DUALAUTH_AGENT_URL = 'https://github.com/sanasol/hytale-auth-server/releases/latest/download/dualauth-agent.jar';
|
||||
const DUALAUTH_AGENT_VERSION_API = 'https://api.github.com/repos/sanasol/hytale-auth-server/releases/latest';
|
||||
const DUALAUTH_AGENT_FILENAME = 'dualauth-agent.jar';
|
||||
const DUALAUTH_AGENT_VERSION_FILE = 'dualauth-agent.version';
|
||||
|
||||
function getTargetDomain() {
|
||||
if (process.env.HYTALE_AUTH_DOMAIN) {
|
||||
@@ -511,30 +513,70 @@ class ClientPatcher {
|
||||
*/
|
||||
async ensureAgentAvailable(serverDir, progressCallback) {
|
||||
const agentPath = this.getAgentPath(serverDir);
|
||||
const versionPath = path.join(serverDir, DUALAUTH_AGENT_VERSION_FILE);
|
||||
|
||||
console.log('=== DualAuth Agent (ByteBuddy) ===');
|
||||
console.log(`Target: ${agentPath}`);
|
||||
|
||||
// Check if agent already exists and is valid
|
||||
// Check local version and whether file exists
|
||||
let localVersion = null;
|
||||
let agentExists = false;
|
||||
if (fs.existsSync(agentPath)) {
|
||||
try {
|
||||
const stats = fs.statSync(agentPath);
|
||||
if (stats.size > 1024) {
|
||||
console.log(`DualAuth Agent present (${(stats.size / 1024).toFixed(0)} KB)`);
|
||||
if (progressCallback) progressCallback('DualAuth Agent ready', 100);
|
||||
return { success: true, agentPath, alreadyExists: true };
|
||||
agentExists = true;
|
||||
if (fs.existsSync(versionPath)) {
|
||||
localVersion = fs.readFileSync(versionPath, 'utf8').trim();
|
||||
}
|
||||
} else {
|
||||
console.log('Agent file appears corrupt, re-downloading...');
|
||||
fs.unlinkSync(agentPath);
|
||||
}
|
||||
// File exists but too small - corrupt, re-download
|
||||
console.log('Agent file appears corrupt, re-downloading...');
|
||||
fs.unlinkSync(agentPath);
|
||||
} catch (e) {
|
||||
console.warn('Could not check agent file:', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates from GitHub
|
||||
let remoteVersion = null;
|
||||
let needsDownload = !agentExists;
|
||||
if (agentExists) {
|
||||
try {
|
||||
if (progressCallback) progressCallback('Checking for agent updates...', 5);
|
||||
const axios = require('axios');
|
||||
const resp = await axios.get(DUALAUTH_AGENT_VERSION_API, {
|
||||
timeout: 5000,
|
||||
headers: { 'Accept': 'application/vnd.github.v3+json' }
|
||||
});
|
||||
remoteVersion = resp.data.tag_name; // e.g. "v1.1.10"
|
||||
if (localVersion && localVersion === remoteVersion) {
|
||||
console.log(`DualAuth Agent up to date (${localVersion})`);
|
||||
if (progressCallback) progressCallback('DualAuth Agent ready', 100);
|
||||
return { success: true, agentPath, alreadyExists: true, version: localVersion };
|
||||
}
|
||||
console.log(`Agent update available: ${localVersion || 'unknown'} → ${remoteVersion}`);
|
||||
needsDownload = true;
|
||||
} catch (e) {
|
||||
// GitHub API failed - use existing agent if available
|
||||
console.warn(`Could not check for updates: ${e.message}`);
|
||||
if (agentExists) {
|
||||
console.log(`Using existing agent (${localVersion || 'unknown version'})`);
|
||||
if (progressCallback) progressCallback('DualAuth Agent ready', 100);
|
||||
return { success: true, agentPath, alreadyExists: true, version: localVersion };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!needsDownload) {
|
||||
if (progressCallback) progressCallback('DualAuth Agent ready', 100);
|
||||
return { success: true, agentPath, alreadyExists: true, version: localVersion };
|
||||
}
|
||||
|
||||
// Download agent from GitHub releases
|
||||
if (progressCallback) progressCallback('Downloading DualAuth Agent...', 20);
|
||||
console.log(`Downloading from: ${DUALAUTH_AGENT_URL}`);
|
||||
const action = agentExists ? 'Updating' : 'Downloading';
|
||||
if (progressCallback) progressCallback(`${action} DualAuth Agent...`, 20);
|
||||
console.log(`${action} from: ${DUALAUTH_AGENT_URL}`);
|
||||
|
||||
try {
|
||||
// Ensure server directory exists
|
||||
@@ -548,7 +590,7 @@ class ClientPatcher {
|
||||
const stream = await smartDownloadStream(DUALAUTH_AGENT_URL, (chunk, downloadedBytes, total) => {
|
||||
if (progressCallback && total) {
|
||||
const percent = 20 + Math.floor((downloadedBytes / total) * 70);
|
||||
progressCallback(`Downloading agent... ${(downloadedBytes / 1024).toFixed(0)} KB`, percent);
|
||||
progressCallback(`${action} agent... ${(downloadedBytes / 1024).toFixed(0)} KB`, percent);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -575,9 +617,13 @@ class ClientPatcher {
|
||||
}
|
||||
fs.renameSync(tmpPath, agentPath);
|
||||
|
||||
console.log(`DualAuth Agent downloaded (${(stats.size / 1024).toFixed(0)} KB)`);
|
||||
// Save version
|
||||
const version = remoteVersion || 'unknown';
|
||||
fs.writeFileSync(versionPath, version, 'utf8');
|
||||
|
||||
console.log(`DualAuth Agent ${agentExists ? 'updated' : 'downloaded'} (${(stats.size / 1024).toFixed(0)} KB, ${version})`);
|
||||
if (progressCallback) progressCallback('DualAuth Agent ready', 100);
|
||||
return { success: true, agentPath };
|
||||
return { success: true, agentPath, updated: agentExists, version };
|
||||
|
||||
} catch (downloadError) {
|
||||
console.error(`Failed to download DualAuth Agent: ${downloadError.message}`);
|
||||
@@ -586,6 +632,11 @@ class ClientPatcher {
|
||||
if (fs.existsSync(tmpPath)) {
|
||||
try { fs.unlinkSync(tmpPath); } catch (e) { /* ignore */ }
|
||||
}
|
||||
// If we had an existing agent, still use it
|
||||
if (agentExists) {
|
||||
console.log('Using existing agent despite update failure');
|
||||
return { success: true, agentPath, alreadyExists: true, version: localVersion };
|
||||
}
|
||||
return { success: false, error: downloadError.message };
|
||||
}
|
||||
}
|
||||
|
||||
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}` });
|
||||
});
|
||||
});
|
||||
}
|
||||
238
backend/utils/logCollector.js
Normal file
238
backend/utils/logCollector.js
Normal file
@@ -0,0 +1,238 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const zlib = require('zlib');
|
||||
const logger = require('../logger');
|
||||
|
||||
const MAX_FILE_SIZE = 2 * 1024 * 1024; // 2MB per file
|
||||
|
||||
/**
|
||||
* Get the HytaleSaves directory (game client logs)
|
||||
*/
|
||||
function getHytaleSavesDir() {
|
||||
const home = os.homedir();
|
||||
if (process.platform === 'win32') {
|
||||
return path.join(home, 'AppData', 'Local', 'HytaleSaves');
|
||||
} else if (process.platform === 'darwin') {
|
||||
return path.join(home, 'Library', 'Application Support', 'HytaleSaves');
|
||||
} else {
|
||||
return path.join(home, '.hytalesaves');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a log file, capping at MAX_FILE_SIZE (keeps tail/most recent lines)
|
||||
*/
|
||||
function readLogFile(filePath) {
|
||||
try {
|
||||
const stats = fs.statSync(filePath);
|
||||
if (stats.size <= MAX_FILE_SIZE) {
|
||||
return fs.readFileSync(filePath, 'utf8');
|
||||
}
|
||||
|
||||
// Read only the last MAX_FILE_SIZE bytes
|
||||
const fd = fs.openSync(filePath, 'r');
|
||||
const buffer = Buffer.alloc(MAX_FILE_SIZE);
|
||||
fs.readSync(fd, buffer, 0, MAX_FILE_SIZE, stats.size - MAX_FILE_SIZE);
|
||||
fs.closeSync(fd);
|
||||
|
||||
const content = buffer.toString('utf8');
|
||||
// Skip first partial line
|
||||
const firstNewline = content.indexOf('\n');
|
||||
const trimmed = firstNewline >= 0 ? content.substring(firstNewline + 1) : content;
|
||||
return `[... truncated ${stats.size - MAX_FILE_SIZE} bytes ...]\n` + trimmed;
|
||||
} catch (err) {
|
||||
return `[Error reading file: ${err.message}]`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files matching a date pattern from a directory
|
||||
*/
|
||||
function getFilesForDate(dir, dateStr, pattern) {
|
||||
if (!fs.existsSync(dir)) return [];
|
||||
|
||||
try {
|
||||
return fs.readdirSync(dir)
|
||||
.filter(f => f.includes(dateStr) && (pattern ? pattern.test(f) : true))
|
||||
.map(f => ({
|
||||
name: f,
|
||||
path: path.join(dir, f),
|
||||
mtime: fs.statSync(path.join(dir, f)).mtime
|
||||
}))
|
||||
.sort((a, b) => b.mtime - a.mtime);
|
||||
} catch (err) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ZIP BUILDER (pure Node.js, no dependencies)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* CRC32 lookup table
|
||||
*/
|
||||
const crc32Table = new Uint32Array(256);
|
||||
for (let i = 0; i < 256; i++) {
|
||||
let c = i;
|
||||
for (let j = 0; j < 8; j++) {
|
||||
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
|
||||
}
|
||||
crc32Table[i] = c;
|
||||
}
|
||||
|
||||
function crc32(buf) {
|
||||
let crc = 0xFFFFFFFF;
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
crc = crc32Table[(crc ^ buf[i]) & 0xFF] ^ (crc >>> 8);
|
||||
}
|
||||
return (crc ^ 0xFFFFFFFF) >>> 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ZIP file from an array of {name, content} entries
|
||||
* Uses DEFLATE compression via built-in zlib
|
||||
*/
|
||||
function createZipBuffer(files) {
|
||||
const localHeaders = [];
|
||||
const centralEntries = [];
|
||||
let offset = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const nameBytes = Buffer.from(file.name, 'utf8');
|
||||
const contentBytes = Buffer.from(file.content, 'utf8');
|
||||
const compressed = zlib.deflateRawSync(contentBytes);
|
||||
const crcVal = crc32(contentBytes);
|
||||
|
||||
// Local file header (30 bytes + filename)
|
||||
const local = Buffer.alloc(30);
|
||||
local.writeUInt32LE(0x04034b50, 0); // signature
|
||||
local.writeUInt16LE(20, 4); // version needed
|
||||
local.writeUInt16LE(0, 6); // flags
|
||||
local.writeUInt16LE(8, 8); // compression: DEFLATE
|
||||
local.writeUInt16LE(0, 10); // mod time
|
||||
local.writeUInt16LE(0, 12); // mod date
|
||||
local.writeUInt32LE(crcVal, 14); // crc32
|
||||
local.writeUInt32LE(compressed.length, 18); // compressed size
|
||||
local.writeUInt32LE(contentBytes.length, 22); // uncompressed size
|
||||
local.writeUInt16LE(nameBytes.length, 26); // filename length
|
||||
local.writeUInt16LE(0, 28); // extra field length
|
||||
|
||||
localHeaders.push(local, nameBytes, compressed);
|
||||
|
||||
// Central directory entry (46 bytes + filename)
|
||||
const central = Buffer.alloc(46);
|
||||
central.writeUInt32LE(0x02014b50, 0); // signature
|
||||
central.writeUInt16LE(20, 4); // version made by
|
||||
central.writeUInt16LE(20, 6); // version needed
|
||||
central.writeUInt16LE(0, 8); // flags
|
||||
central.writeUInt16LE(8, 10); // compression
|
||||
central.writeUInt16LE(0, 12); // mod time
|
||||
central.writeUInt16LE(0, 14); // mod date
|
||||
central.writeUInt32LE(crcVal, 16); // crc32
|
||||
central.writeUInt32LE(compressed.length, 20); // compressed size
|
||||
central.writeUInt32LE(contentBytes.length, 24); // uncompressed size
|
||||
central.writeUInt16LE(nameBytes.length, 28); // filename length
|
||||
central.writeUInt16LE(0, 30); // extra field length
|
||||
central.writeUInt16LE(0, 32); // comment length
|
||||
central.writeUInt16LE(0, 34); // disk number
|
||||
central.writeUInt16LE(0, 36); // internal attrs
|
||||
central.writeUInt32LE(0, 38); // external attrs
|
||||
central.writeUInt32LE(offset, 42); // local header offset
|
||||
|
||||
centralEntries.push(central, nameBytes);
|
||||
offset += 30 + nameBytes.length + compressed.length;
|
||||
}
|
||||
|
||||
const centralDirBuf = Buffer.concat(centralEntries);
|
||||
|
||||
// End of central directory (22 bytes)
|
||||
const eocd = Buffer.alloc(22);
|
||||
eocd.writeUInt32LE(0x06054b50, 0); // signature
|
||||
eocd.writeUInt16LE(0, 4); // disk number
|
||||
eocd.writeUInt16LE(0, 6); // cd disk number
|
||||
eocd.writeUInt16LE(files.length, 8); // entries on disk
|
||||
eocd.writeUInt16LE(files.length, 10); // total entries
|
||||
eocd.writeUInt32LE(centralDirBuf.length, 12); // cd size
|
||||
eocd.writeUInt32LE(offset, 16); // cd offset
|
||||
eocd.writeUInt16LE(0, 20); // comment length
|
||||
|
||||
return Buffer.concat([...localHeaders, centralDirBuf, eocd]);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Collect all relevant logs for submission
|
||||
* Returns { files: [{name, content}], meta: {username, platform, version} }
|
||||
*/
|
||||
function collectLogs() {
|
||||
const files = [];
|
||||
const today = new Date();
|
||||
const todayStr = today.toISOString().split('T')[0]; // YYYY-MM-DD
|
||||
|
||||
const yesterday = new Date(today);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const yesterdayStr = yesterday.toISOString().split('T')[0];
|
||||
|
||||
// 1. Launcher logs
|
||||
const launcherLogDir = logger.getLogDirectory();
|
||||
if (launcherLogDir && fs.existsSync(launcherLogDir)) {
|
||||
// Today's launcher logs
|
||||
const todayLogs = getFilesForDate(launcherLogDir, todayStr, /^launcher-.*\.log$/);
|
||||
for (const f of todayLogs) {
|
||||
files.push({ name: `launcher/${f.name}`, content: readLogFile(f.path) });
|
||||
}
|
||||
|
||||
// Most recent from yesterday (just one)
|
||||
const yesterdayLogs = getFilesForDate(launcherLogDir, yesterdayStr, /^launcher-.*\.log$/);
|
||||
if (yesterdayLogs.length > 0) {
|
||||
files.push({ name: `launcher/${yesterdayLogs[0].name}`, content: readLogFile(yesterdayLogs[0].path) });
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Game client logs
|
||||
const savesDir = getHytaleSavesDir();
|
||||
const clientLogDir = path.join(savesDir, 'Logs');
|
||||
if (fs.existsSync(clientLogDir)) {
|
||||
const clientLogs = getFilesForDate(clientLogDir, todayStr, /client\.log$/);
|
||||
for (const f of clientLogs) {
|
||||
files.push({ name: `client/${f.name}`, content: readLogFile(f.path) });
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Config snapshot
|
||||
const appDir = logger.getAppDir ? logger.getAppDir() : logger.getInstallPath();
|
||||
const configPath = path.join(appDir, 'config.json');
|
||||
if (fs.existsSync(configPath)) {
|
||||
try {
|
||||
const configContent = fs.readFileSync(configPath, 'utf8');
|
||||
files.push({ name: 'config.json', content: configContent });
|
||||
} catch (err) {
|
||||
files.push({ name: 'config.json', content: `[Error reading config: ${err.message}]` });
|
||||
}
|
||||
}
|
||||
|
||||
// Build metadata
|
||||
const { app } = require('electron');
|
||||
let username = 'unknown';
|
||||
try {
|
||||
const configFile = path.join(appDir, 'config.json');
|
||||
if (fs.existsSync(configFile)) {
|
||||
const cfg = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
||||
username = cfg.username || cfg.playerName || 'unknown';
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
return {
|
||||
files,
|
||||
meta: {
|
||||
username,
|
||||
platform: `${process.platform}-${process.arch}`,
|
||||
version: app.getVersion()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { collectLogs, createZipBuffer };
|
||||
@@ -164,8 +164,14 @@ function detectGpuLinux() {
|
||||
const isAmd = lowerLine.includes('amd') || lowerLine.includes('radeon') || vendorId === '1002';
|
||||
const isIntel = lowerLine.includes('intel') || vendorId === '8086';
|
||||
|
||||
// Intel Arc detection
|
||||
const isIntelArc = isIntel && (lowerName.includes('arc') || lowerName.includes('a770') || lowerName.includes('a750') || lowerName.includes('a380'));
|
||||
// Intel Arc discrete GPU detection
|
||||
// Discrete Arc cards have specific model numbers: A310, A380, A580, A750, A770, B570, B580
|
||||
// Integrated "Intel Arc Graphics" (Meteor Lake, Lunar Lake, Arrow Lake) have NO model suffix
|
||||
// and sit on bus 00 (slot 00:02.0) — these are iGPUs, not dGPUs
|
||||
const pciSlot = line.split(' ')[0] || '';
|
||||
const pciBus = parseInt(pciSlot.split(':')[0], 16) || 0;
|
||||
const hasArcModelNumber = isIntel && /\b[ab]\d{3}\b/i.test(lowerName);
|
||||
const isIntelArc = isIntel && (hasArcModelNumber || (lowerName.includes('arc') && pciBus > 0));
|
||||
|
||||
let vendor = 'unknown';
|
||||
if (isNvidia) vendor = 'nvidia';
|
||||
|
||||
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
|
||||
113
docs/FASTUTIL_CLASSLOADER_ISSUE.md
Normal file
113
docs/FASTUTIL_CLASSLOADER_ISSUE.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Singleplayer Server Crash: fastutil ClassNotFoundException
|
||||
|
||||
## Status: Open (user-specific, Feb 24 2026)
|
||||
|
||||
## Symptom
|
||||
|
||||
Singleplayer server crashes immediately after DualAuth Agent installs successfully:
|
||||
|
||||
```
|
||||
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 User
|
||||
|
||||
- Discord: ヅ𝚃 JAYED !
|
||||
- Platform: Windows (standard x86_64, NOT ARM)
|
||||
- Reproduces 100% on singleplayer, every attempt
|
||||
- Other users (including macOS/Linux) are NOT affected
|
||||
|
||||
## What Works
|
||||
|
||||
- Java wrapper correctly strips `-XX:+UseCompactObjectHeaders`
|
||||
- Java wrapper correctly injects `--disable-sentry`
|
||||
- DualAuth Agent v1.1.12 installs successfully (STATIC mode)
|
||||
- Multiplayer connections work fine
|
||||
- Repair and reinstall did NOT fix the issue
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
`fastutil` (`it.unimi.dsi.fastutil`) should be bundled inside `HytaleServer.jar` (fat JAR). The `ClassNotFoundException` means the JVM's app classloader cannot find it despite it being in the JAR.
|
||||
|
||||
### Ruled Out
|
||||
|
||||
- **Wrapper issue**: Wrapper is working correctly (confirmed in logs)
|
||||
- **UseCompactObjectHeaders**: Server also has `-XX:+IgnoreUnrecognizedVMOptions`, so unrecognized flags don't crash it
|
||||
- **DualAuth Agent**: Works for all other users; agent installs successfully before the crash
|
||||
- **Corrupted game files**: Repair/reinstall didn't help
|
||||
- **ARM64/Parallels**: User is on standard Windows, not ARM
|
||||
|
||||
### Likely Causes (user-specific)
|
||||
|
||||
1. **Antivirus interference** — Windows Defender or third-party AV blocking Java from reading classes out of JAR files, especially with `-javaagent` active
|
||||
2. **Corrupted/incompatible JRE** — bundled JRE might be broken on their system
|
||||
3. **File locking** — another process holding HytaleServer.jar open
|
||||
|
||||
## Debugging Steps (ask user)
|
||||
|
||||
1. **Does official Hytale singleplayer work?** (without F2P launcher)
|
||||
- Yes → something about our launch setup
|
||||
- No → their system/JRE issue
|
||||
|
||||
2. **Check antivirus** — add game directory to Windows Defender exclusions:
|
||||
- Settings → Windows Security → Virus & threat protection → Exclusions
|
||||
- Add their HytaleF2P install folder
|
||||
|
||||
3. **Verify fastutil is in the JAR**:
|
||||
```cmd
|
||||
jar tf "D:\path\to\Server\HytaleServer.jar" | findstr fastutil
|
||||
```
|
||||
- If output shows fastutil classes → JAR is fine, classloader issue
|
||||
- If no output → JAR is incomplete/corrupt (different from other users)
|
||||
|
||||
4. **Try without DualAuth agent** — rename `dualauth-agent.jar` in Server/ folder, retry singleplayer
|
||||
- If works → agent's classloader manipulation breaks fastutil on their setup
|
||||
- If still fails → unrelated to agent
|
||||
|
||||
5. **Check JRE version** — have them run:
|
||||
```cmd
|
||||
"D:\path\to\jre\latest\bin\java.exe" -version
|
||||
```
|
||||
|
||||
## Update (Feb 24): `-XX:+UseCompactObjectHeaders` stripping removed from defaults
|
||||
|
||||
Stripping this flag did NOT fix the issue. The server already has `-XX:+IgnoreUnrecognizedVMOptions` so unrecognized flags are harmless. The flag was removed from default `stripFlags` in `backend/core/config.js`.
|
||||
|
||||
## Using the Java Wrapper to Strip JVM Flags
|
||||
|
||||
If a user needs to strip a specific JVM flag (e.g., for debugging or compatibility), they can do it via the launcher UI:
|
||||
|
||||
1. Open **Settings** → scroll to **Java Wrapper Configuration**
|
||||
2. Under **JVM Flags to Remove**, type the flag (e.g. `-XX:+UseCompactObjectHeaders`) and click **Add**
|
||||
3. The flag will be stripped from all JVM invocations at launch time
|
||||
4. To inject custom arguments, use the **Arguments to Inject** section (with optional "Server Only" condition)
|
||||
5. **Restore Defaults** resets to empty strip flags + `--disable-sentry` (server only)
|
||||
|
||||
The wrapper generates platform-specific scripts at launch time:
|
||||
- **Windows**: `java-wrapper.bat` in `jre/latest/bin/`
|
||||
- **macOS/Linux**: `java-wrapper` shell script in the same directory
|
||||
|
||||
Config is stored in `config.json` under `javaWrapperConfig`:
|
||||
```json
|
||||
{
|
||||
"javaWrapperConfig": {
|
||||
"stripFlags": ["-XX:+SomeFlag"],
|
||||
"injectArgs": [
|
||||
{ "arg": "--some-arg", "condition": "server" },
|
||||
{ "arg": "--other-arg", "condition": "always" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- Java wrapper config: `backend/core/config.js` (stripFlags / injectArgs)
|
||||
- DualAuth Agent: v1.1.12, package `ws.sanasol.dualauth`
|
||||
- Game version at time of report: `2026.02.19-1a311a592`
|
||||
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 ""
|
||||
135
main.js
135
main.js
@@ -88,8 +88,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'
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1068,7 +1069,7 @@ ipcMain.handle('load-settings', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
const { getModsPath, loadInstalledMods, downloadMod, uninstallMod, toggleMod, getCurrentUuid, getAllUuidMappings, setUuidForUser, generateNewUuid, deleteUuidForUser, resetCurrentUserUuid } = require('./backend/launcher');
|
||||
const { getModsPath, loadInstalledMods, downloadMod, uninstallMod, toggleMod, getModFiles, getCurrentUuid, getAllUuidMappings, setUuidForUser, generateNewUuid, deleteUuidForUser, resetCurrentUserUuid } = require('./backend/launcher');
|
||||
const os = require('os');
|
||||
|
||||
ipcMain.handle('get-local-app-data', async () => {
|
||||
@@ -1118,6 +1119,15 @@ ipcMain.handle('download-mod', async (event, modInfo) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-mod-files', async (event, modId) => {
|
||||
try {
|
||||
return await getModFiles(modId);
|
||||
} catch (error) {
|
||||
console.error('Error getting mod files:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('uninstall-mod', async (event, modId, modsPath) => {
|
||||
try {
|
||||
return await uninstallMod(modId, modsPath);
|
||||
@@ -1407,6 +1417,83 @@ ipcMain.handle('get-recent-logs', async (event, maxLines = 100) => {
|
||||
|
||||
|
||||
|
||||
ipcMain.handle('send-logs', async () => {
|
||||
try {
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
const { collectLogs, createZipBuffer } = require('./backend/utils/logCollector');
|
||||
|
||||
const { files, meta } = collectLogs();
|
||||
if (files.length === 0) {
|
||||
return { success: false, error: 'No log files found' };
|
||||
}
|
||||
|
||||
// Create ZIP with individual log files
|
||||
const zipBuffer = createZipBuffer(files);
|
||||
|
||||
// Get auth server URL from core config
|
||||
const { getAuthServerUrl } = require('./backend/core/config');
|
||||
const authUrl = getAuthServerUrl();
|
||||
|
||||
// Build file names list
|
||||
const fileNames = files.map(f => f.name).join(',');
|
||||
|
||||
return await new Promise((resolve) => {
|
||||
const url = new URL(authUrl + '/logs/submit');
|
||||
const transport = url.protocol === 'https:' ? https : http;
|
||||
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
||||
path: url.pathname,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/zip',
|
||||
'Content-Length': zipBuffer.length,
|
||||
'X-Log-Username': meta.username || 'unknown',
|
||||
'X-Log-Platform': meta.platform || 'unknown',
|
||||
'X-Log-Version': meta.version || 'unknown',
|
||||
'X-Log-File-Count': String(files.length),
|
||||
'X-Log-Files': fileNames
|
||||
},
|
||||
timeout: 30000
|
||||
};
|
||||
|
||||
const req = transport.request(options, (res) => {
|
||||
let body = '';
|
||||
res.on('data', (chunk) => body += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const data = JSON.parse(body);
|
||||
if (res.statusCode === 200) {
|
||||
resolve({ success: true, id: data.id, message: data.message });
|
||||
} else {
|
||||
resolve({ success: false, error: data.error || `Server error ${res.statusCode}` });
|
||||
}
|
||||
} catch (e) {
|
||||
resolve({ success: false, error: `Invalid response: ${res.statusCode}` });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (err) => {
|
||||
resolve({ success: false, error: err.message });
|
||||
});
|
||||
|
||||
req.on('timeout', () => {
|
||||
req.destroy();
|
||||
resolve({ success: false, error: 'Request timed out' });
|
||||
});
|
||||
|
||||
req.write(zipBuffer);
|
||||
req.end();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error sending logs:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('open-logs-folder', async () => {
|
||||
try {
|
||||
const logDir = logger.getLogDirectory();
|
||||
@@ -1462,3 +1549,45 @@ ipcMain.handle('profile-update', async (event, id, updates) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Java Wrapper Config IPC
|
||||
ipcMain.handle('load-wrapper-config', () => {
|
||||
const { loadWrapperConfig } = require('./backend/launcher');
|
||||
return loadWrapperConfig();
|
||||
});
|
||||
|
||||
ipcMain.handle('save-wrapper-config', (event, config) => {
|
||||
try {
|
||||
const { saveWrapperConfig } = require('./backend/launcher');
|
||||
saveWrapperConfig(config);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error saving wrapper config:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('reset-wrapper-config', () => {
|
||||
try {
|
||||
const { resetWrapperConfig } = require('./backend/launcher');
|
||||
const config = resetWrapperConfig();
|
||||
return { success: true, config };
|
||||
} catch (error) {
|
||||
console.error('Error resetting wrapper config:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-default-wrapper-config', () => {
|
||||
const { getDefaultWrapperConfig } = require('./backend/launcher');
|
||||
return getDefaultWrapperConfig();
|
||||
});
|
||||
|
||||
ipcMain.handle('preview-wrapper-script', (event, config, platform) => {
|
||||
const { generateWrapperScript } = require('./backend/launcher');
|
||||
return generateWrapperScript(config || require('./backend/launcher').loadWrapperConfig(), platform || process.platform, '/path/to/java');
|
||||
});
|
||||
|
||||
ipcMain.handle('get-current-platform', () => {
|
||||
return process.platform;
|
||||
});
|
||||
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "hytale-f2p-launcher",
|
||||
"version": "2.2.0",
|
||||
"version": "2.4.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hytale-f2p-launcher",
|
||||
"version": "2.2.0",
|
||||
"version": "2.4.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.10",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hytale-f2p-launcher",
|
||||
"version": "2.3.7",
|
||||
"version": "2.4.4",
|
||||
"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",
|
||||
@@ -26,7 +26,6 @@
|
||||
"electron",
|
||||
"auto-update",
|
||||
"mod-manager"
|
||||
|
||||
],
|
||||
"maintainers": [
|
||||
{
|
||||
|
||||
10
preload.js
10
preload.js
@@ -45,6 +45,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
loadInstalledMods: (modsPath) => ipcRenderer.invoke('load-installed-mods', modsPath),
|
||||
downloadMod: (modInfo) => ipcRenderer.invoke('download-mod', modInfo),
|
||||
uninstallMod: (modId, modsPath) => ipcRenderer.invoke('uninstall-mod', modId, modsPath),
|
||||
getModFiles: (modId) => ipcRenderer.invoke('get-mod-files', modId),
|
||||
toggleMod: (modId, modsPath) => ipcRenderer.invoke('toggle-mod', modId, modsPath),
|
||||
selectModFiles: () => ipcRenderer.invoke('select-mod-files'),
|
||||
copyModFile: (sourcePath, modsPath) => ipcRenderer.invoke('copy-mod-file', sourcePath, modsPath),
|
||||
@@ -94,6 +95,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
getLogDirectory: () => ipcRenderer.invoke('get-log-directory'),
|
||||
openLogsFolder: () => ipcRenderer.invoke('open-logs-folder'),
|
||||
getRecentLogs: (maxLines) => ipcRenderer.invoke('get-recent-logs', maxLines),
|
||||
sendLogs: () => ipcRenderer.invoke('send-logs'),
|
||||
|
||||
// UUID Management methods
|
||||
getCurrentUuid: () => ipcRenderer.invoke('get-current-uuid'),
|
||||
@@ -103,6 +105,14 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
deleteUuidForUser: (username) => ipcRenderer.invoke('delete-uuid-for-user', username),
|
||||
resetCurrentUserUuid: () => ipcRenderer.invoke('reset-current-user-uuid'),
|
||||
|
||||
// Java Wrapper Config API
|
||||
loadWrapperConfig: () => ipcRenderer.invoke('load-wrapper-config'),
|
||||
saveWrapperConfig: (config) => ipcRenderer.invoke('save-wrapper-config', config),
|
||||
resetWrapperConfig: () => ipcRenderer.invoke('reset-wrapper-config'),
|
||||
getDefaultWrapperConfig: () => ipcRenderer.invoke('get-default-wrapper-config'),
|
||||
previewWrapperScript: (config, platform) => ipcRenderer.invoke('preview-wrapper-script', config, platform),
|
||||
getCurrentPlatform: () => ipcRenderer.invoke('get-current-platform'),
|
||||
|
||||
// Profile API
|
||||
profile: {
|
||||
create: (name) => ipcRenderer.invoke('profile-create', name),
|
||||
|
||||
134
server/README.md
Normal file
134
server/README.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Hytale F2P - Dedicated Server
|
||||
|
||||
Host your own Hytale server. The scripts handle everything automatically.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Java 25+** — [Windows installer](https://download.oracle.com/java/25/latest/jdk-25_windows-x64_bin.exe) | [Other platforms](https://adoptium.net/)
|
||||
- If you have the F2P launcher installed, its bundled Java will be used automatically
|
||||
- **Internet connection** for first launch (downloads ~3.5 GB of game files)
|
||||
- If you have the F2P launcher installed, game files are copied locally (no download needed)
|
||||
|
||||
## Video Guide
|
||||
|
||||
[](https://youtu.be/KvuXLH7SKvI)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Free Hosted Server (no PC required)
|
||||
|
||||
Use [play.hosting](https://play.hosting) to get a free Hytale server with F2P support:
|
||||
|
||||
1. Register at [play.hosting](https://play.hosting)
|
||||
2. Create a **Hytale** server
|
||||
3. Start the server once and wait for it to fully load
|
||||
4. Go to **Files** → open the `mods` folder
|
||||
5. Click **New** → **File via URL**
|
||||
6. Paste: `https://github.com/sanasol/hytale-auth-server/releases/latest/download/dualauth-agent.jar`
|
||||
7. Click **Query** and download the file
|
||||
8. Go to **Console** and **Restart** the server
|
||||
|
||||
Done — your F2P server is ready to join.
|
||||
|
||||
### Windows (self-hosted)
|
||||
|
||||
1. Download `start.bat` to an empty folder
|
||||
2. Double-click `start.bat`
|
||||
3. Done — server starts on port **5520**
|
||||
|
||||
### Linux / macOS (self-hosted)
|
||||
|
||||
```bash
|
||||
mkdir hytale-server && cd hytale-server
|
||||
curl -O https://git.sanhost.net/sanasol/hytale-f2p/raw/branch/develop/server/start.sh
|
||||
chmod +x start.sh
|
||||
./start.sh
|
||||
```
|
||||
|
||||
## What the scripts do
|
||||
|
||||
1. Search for the F2P launcher install (default paths + custom `installPath` from config)
|
||||
2. Use bundled Java from the launcher, or fall back to system Java (25+ required)
|
||||
3. Copy game files from the launcher install if available
|
||||
4. Download missing files: `HytaleServer.jar` (~150 MB), `Assets.zip` (~3.3 GB), `dualauth-agent.jar` (~5 MB)
|
||||
5. Check for updates on every launch (server, assets, and agent)
|
||||
6. Generate a persistent server ID
|
||||
7. Fetch authentication tokens
|
||||
8. Start the server with dual-auth support
|
||||
|
||||
## Connecting
|
||||
|
||||
- **Same PC**: Connect to `localhost:5520` or `127.0.0.1:5520`
|
||||
- **LAN**: Connect to your local IP (e.g. `192.168.1.x:5520`)
|
||||
- **Internet**: Forward port `5520` (TCP + UDP) on your router, friends connect to your public IP
|
||||
|
||||
### No public IP? Use playit.gg (recommended)
|
||||
|
||||
If you're behind CGNAT or can't port forward, [playit.gg](https://playit.gg) gives you a public address for free:
|
||||
|
||||
1. Go to [playit.gg](https://playit.gg) and create an account
|
||||
2. Download and run the playit agent
|
||||
3. Create a tunnel — select **Hytale** as the game type, local port `5520`
|
||||
4. Share the generated address with friends (e.g. `something.joinplayit.gg:12345`)
|
||||
|
||||
### Other options
|
||||
|
||||
- [Radmin VPN](https://www.radmin-vpn.com/) — virtual LAN, all players must install it
|
||||
- [ZeroTier](https://www.zerotier.com/) — same idea, create a network, friends join and connect via VPN IP
|
||||
|
||||
## Configuration
|
||||
|
||||
Set environment variables before running the script:
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `SERVER_NAME` | `My Hytale Server` | Server name shown in listings |
|
||||
| `BIND_ADDRESS` | `0.0.0.0:5520` | IP and port to listen on |
|
||||
| `JVM_XMX` | *(Java default)* | Max memory (e.g. `4G`, `8G`) |
|
||||
| `JVM_XMS` | *(Java default)* | Initial memory |
|
||||
| `AUTH_MODE` | `authenticated` | Auth mode (`authenticated` or `none`) |
|
||||
| `HYTALE_AUTH_DOMAIN` | `auth.sanasol.ws` | Auth server domain |
|
||||
| `DOWNLOAD_BASE` | `https://download.sanasol.ws/download` | File download URL |
|
||||
|
||||
**Example (Linux):**
|
||||
```bash
|
||||
SERVER_NAME="Epic Server" JVM_XMX=4G ./start.sh
|
||||
```
|
||||
|
||||
**Example (Windows):**
|
||||
```cmd
|
||||
set SERVER_NAME=Epic Server
|
||||
set JVM_XMX=4G
|
||||
start.bat
|
||||
```
|
||||
|
||||
## Files created
|
||||
|
||||
```
|
||||
your-folder/
|
||||
├── start.sh / start.bat # Startup script
|
||||
├── HytaleServer.jar # Game server (auto-downloaded)
|
||||
├── Assets.zip # Game assets (auto-downloaded)
|
||||
├── dualauth-agent.jar # Auth agent (auto-downloaded)
|
||||
├── .server-id # Persistent server UUID
|
||||
├── .versions/ # Version tracking for auto-updates
|
||||
├── Server/ # Server data (created by server)
|
||||
│ ├── config.json
|
||||
│ └── worlds/
|
||||
└── UserData/ # Player saves
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| `java not found` | Install the F2P launcher (includes Java) or install Java 25+ from [adoptium.net](https://adoptium.net/) |
|
||||
| Download fails | Check internet connection. Files can be downloaded manually from `https://download.sanasol.ws/download/` |
|
||||
| Port already in use | Change port: `BIND_ADDRESS=0.0.0.0:5521 ./start.sh` |
|
||||
| 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 |
|
||||
|
||||
## Community
|
||||
|
||||
Need help? Join the community: TG Group: https://t.me/sanhostnet | TG Channel: https://t.me/hf2p_og | Chat: https://chat.sanhost.net/invite/Tfz4jCK4
|
||||
<!-- Discord: https://discord.gg/Fhbb9Yk5WW -->
|
||||
17
server/commit-msg.txt
Normal file
17
server/commit-msg.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
## v2.4.0
|
||||
|
||||
### New Features
|
||||
- **Send Logs** — One-click button to submit launcher & game logs to support. Collects launcher logs, game client logs, and config snapshot into a ZIP, uploads to server, and returns an 8-character ID to share with support ([320ca54](https://git.sanhost.net/sanasol/hytale-f2p/commit/320ca547585c67d0773dba262612db5026378f52), [19c8991](https://git.sanhost.net/sanasol/hytale-f2p/commit/19c8991a44641ebbf44eec73a0ecd9db05241c49))
|
||||
- **Arabic (ar-SA) locale** with full RTL support (community contribution by @Yugurten) ([30929ee](https://git.sanhost.net/sanasol/hytale-f2p/commit/30929ee0da5a9c64e65869d6157bd705db3b80f0))
|
||||
- **One-click dedicated server scripts** for self-hosting ([552ec42](https://git.sanhost.net/sanasol/hytale-f2p/commit/552ec42d6c7e1e7d1a2803d284019ccae963f41e))
|
||||
|
||||
### Bug Fixes
|
||||
- Fix Intel Arc iGPU (Meteor Lake/Lunar Lake) on PCI bus 00 being misdetected as discrete GPU on dual-GPU Linux systems ([19c8991](https://git.sanhost.net/sanasol/hytale-f2p/commit/19c8991a44641ebbf44eec73a0ecd9db05241c49))
|
||||
- Fix stalled game processes blocking launcher operations — automatic process cleanup on repair and relaunch ([e14d56e](https://git.sanhost.net/sanasol/hytale-f2p/commit/e14d56ef4846423c1fd172d88334cb76193ee741))
|
||||
- Fix AOT cache crashes — stale cache cleared before game launch ([e14d56e](https://git.sanhost.net/sanasol/hytale-f2p/commit/e14d56ef4846423c1fd172d88334cb76193ee741))
|
||||
- Fix Arabic RTL CSS syntax ([fb90277](https://git.sanhost.net/sanasol/hytale-f2p/commit/fb90277be9cf5f0b8a90195a7d089273b6be082b))
|
||||
|
||||
### Other
|
||||
- Updated README with Forgejo URLs and server setup video ([a649bf1](https://git.sanhost.net/sanasol/hytale-f2p/commit/a649bf1fcc7cbb2cd0d9aa0160b07828a144b9dd), [66faa1b](https://git.sanhost.net/sanasol/hytale-f2p/commit/66faa1bb1e39575fecb462310af338d13b1cb183))
|
||||
|
||||
**Full changelog**: [v2.3.8...v2.4.0](https://git.sanhost.net/sanasol/hytale-f2p/compare/v2.3.8...v2.4.0)
|
||||
441
server/start.bat
Normal file
441
server/start.bat
Normal file
@@ -0,0 +1,441 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: ============================================================
|
||||
:: Hytale F2P Dedicated Server - One-Click Starter
|
||||
:: ============================================================
|
||||
:: Just double-click this file to start your server!
|
||||
::
|
||||
:: The script will:
|
||||
:: 1. Look for game files and Java in your F2P launcher install
|
||||
:: 2. Auto-download anything missing
|
||||
:: 3. Auto-update server, assets, and agent on each launch
|
||||
:: 4. Fetch auth tokens and start the server
|
||||
:: ============================================================
|
||||
|
||||
:: Configuration (edit these or set as environment variables)
|
||||
if not defined HYTALE_AUTH_DOMAIN set "HYTALE_AUTH_DOMAIN=auth.sanasol.ws"
|
||||
if not defined AUTH_SERVER set "AUTH_SERVER=https://%HYTALE_AUTH_DOMAIN%"
|
||||
if not defined SERVER_NAME set "SERVER_NAME=My Hytale Server"
|
||||
if not defined ASSETS_PATH set "ASSETS_PATH=.\Assets.zip"
|
||||
if not defined BIND_ADDRESS set "BIND_ADDRESS=0.0.0.0:5520"
|
||||
if not defined AUTH_MODE set "AUTH_MODE=authenticated"
|
||||
if not defined DOWNLOAD_BASE set "DOWNLOAD_BASE=https://download.sanasol.ws/download"
|
||||
|
||||
:: File names
|
||||
set "AGENT_JAR=dualauth-agent.jar"
|
||||
set "SERVER_JAR=HytaleServer.jar"
|
||||
set "AGENT_URL=https://github.com/sanasol/hytale-auth-server/releases/latest/download/dualauth-agent.jar"
|
||||
set "AGENT_VERSION_API=https://api.github.com/repos/sanasol/hytale-auth-server/releases/latest"
|
||||
set "VERSION_DIR=.versions"
|
||||
|
||||
echo ============================================================
|
||||
echo Hytale F2P Dedicated Server
|
||||
echo ============================================================
|
||||
echo.
|
||||
|
||||
:: --- Prerequisite Checks ---
|
||||
|
||||
where curl >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] curl is required but not found
|
||||
echo [ERROR] curl comes with Windows 10+. Update Windows or install curl.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if not exist "%VERSION_DIR%" mkdir "%VERSION_DIR%"
|
||||
|
||||
:: --- Find Local F2P Launcher Install ---
|
||||
|
||||
set "F2P_DIR="
|
||||
set "F2P_BASE=%USERPROFILE%\AppData\Local\HytaleF2P"
|
||||
set "F2P_CONFIG=%F2P_BASE%\config.json"
|
||||
set "JAVA_CMD=java"
|
||||
|
||||
:: Check config.json for custom installPath
|
||||
set "F2P_CUSTOM_BASE="
|
||||
if exist "%F2P_CONFIG%" (
|
||||
for /f "delims=" %%p in ('powershell -Command "try { $c = Get-Content '%F2P_CONFIG%' | ConvertFrom-Json; if ($c.installPath) { $c.installPath.Trim() + '\HytaleF2P' } } catch {}" 2^>nul') do (
|
||||
set "F2P_CUSTOM_BASE=%%p"
|
||||
)
|
||||
)
|
||||
|
||||
:: Search for game files: custom path first, then default
|
||||
if defined F2P_CUSTOM_BASE (
|
||||
if exist "!F2P_CUSTOM_BASE!\release\package\game\latest" (
|
||||
set "F2P_DIR=!F2P_CUSTOM_BASE!\release\package\game\latest"
|
||||
) else if exist "!F2P_CUSTOM_BASE!\pre-release\package\game\latest" (
|
||||
set "F2P_DIR=!F2P_CUSTOM_BASE!\pre-release\package\game\latest"
|
||||
)
|
||||
)
|
||||
|
||||
if not defined F2P_DIR (
|
||||
if exist "%F2P_BASE%\release\package\game\latest" (
|
||||
set "F2P_DIR=%F2P_BASE%\release\package\game\latest"
|
||||
) else if exist "%F2P_BASE%\pre-release\package\game\latest" (
|
||||
set "F2P_DIR=%F2P_BASE%\pre-release\package\game\latest"
|
||||
)
|
||||
)
|
||||
|
||||
:: --- Find Java from F2P launcher ---
|
||||
|
||||
:: Check config.json for custom javaPath
|
||||
if exist "%F2P_CONFIG%" (
|
||||
for /f "delims=" %%j in ('powershell -Command "try { $c = Get-Content '%F2P_CONFIG%' | ConvertFrom-Json; if ($c.javaPath -and (Test-Path $c.javaPath)) { $c.javaPath.Trim() } } catch {}" 2^>nul') do (
|
||||
set "JAVA_CMD=%%j"
|
||||
echo [INFO] Found Java in F2P config: %%j
|
||||
)
|
||||
)
|
||||
|
||||
:: Check bundled JRE if no custom javaPath found
|
||||
if "!JAVA_CMD!"=="java" (
|
||||
set "F2P_JRE_BASE="
|
||||
if defined F2P_CUSTOM_BASE (
|
||||
if exist "!F2P_CUSTOM_BASE!\release\package\jre\latest\bin\java.exe" (
|
||||
set "F2P_JRE_BASE=!F2P_CUSTOM_BASE!\release\package\jre\latest"
|
||||
) else if exist "!F2P_CUSTOM_BASE!\pre-release\package\jre\latest\bin\java.exe" (
|
||||
set "F2P_JRE_BASE=!F2P_CUSTOM_BASE!\pre-release\package\jre\latest"
|
||||
)
|
||||
)
|
||||
if not defined F2P_JRE_BASE (
|
||||
if exist "%F2P_BASE%\release\package\jre\latest\bin\java.exe" (
|
||||
set "F2P_JRE_BASE=%F2P_BASE%\release\package\jre\latest"
|
||||
) else if exist "%F2P_BASE%\pre-release\package\jre\latest\bin\java.exe" (
|
||||
set "F2P_JRE_BASE=%F2P_BASE%\pre-release\package\jre\latest"
|
||||
)
|
||||
)
|
||||
if defined F2P_JRE_BASE (
|
||||
set "JAVA_CMD=!F2P_JRE_BASE!\bin\java.exe"
|
||||
echo [INFO] Found Java in F2P launcher: !JAVA_CMD!
|
||||
)
|
||||
)
|
||||
|
||||
:: Verify java exists
|
||||
"!JAVA_CMD!" -version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
where java >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] Java is not installed and no F2P launcher JRE found
|
||||
echo.
|
||||
echo Options:
|
||||
echo 1. Install the F2P launcher first ^(it includes Java^)
|
||||
echo 2. Download Java 25: https://download.oracle.com/java/25/latest/jdk-25_windows-x64_bin.exe
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
set "JAVA_CMD=java"
|
||||
)
|
||||
|
||||
:: Check Java version
|
||||
for /f "tokens=3 delims= " %%v in ('"!JAVA_CMD!" -version 2^>^&1 ^| findstr /i "version"') do (
|
||||
set "JAVA_VER_RAW=%%~v"
|
||||
)
|
||||
if defined JAVA_VER_RAW (
|
||||
for /f "tokens=1 delims=." %%m in ("!JAVA_VER_RAW!") do set "JAVA_MAJOR=%%m"
|
||||
)
|
||||
|
||||
echo [INFO] Java: !JAVA_VER_RAW! ^(!JAVA_CMD!^)
|
||||
|
||||
if defined JAVA_MAJOR (
|
||||
if !JAVA_MAJOR! LSS 25 (
|
||||
echo [ERROR] Java !JAVA_MAJOR! detected. Java 25+ is REQUIRED.
|
||||
echo The DualAuth agent requires Java 25 ^(class file version 69^).
|
||||
echo.
|
||||
echo Download Java 25: https://download.oracle.com/java/25/latest/jdk-25_windows-x64_bin.exe
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
:: --- Copy game files from F2P install ---
|
||||
|
||||
if defined F2P_DIR (
|
||||
echo [INFO] Found F2P launcher game files: !F2P_DIR!
|
||||
|
||||
if not exist "%SERVER_JAR%" (
|
||||
if exist "!F2P_DIR!\Server\HytaleServer.jar" (
|
||||
echo [INFO] Found HytaleServer.jar in F2P launcher
|
||||
echo [INFO] Copying from: !F2P_DIR!\Server\HytaleServer.jar
|
||||
copy "!F2P_DIR!\Server\HytaleServer.jar" "%SERVER_JAR%" >nul
|
||||
echo [INFO] Copied successfully
|
||||
)
|
||||
)
|
||||
|
||||
if not exist "%ASSETS_PATH%" (
|
||||
if exist "!F2P_DIR!\Assets.zip" (
|
||||
echo [INFO] Found Assets.zip in F2P launcher
|
||||
echo [INFO] Copying from: !F2P_DIR!\Assets.zip
|
||||
copy "!F2P_DIR!\Assets.zip" "%ASSETS_PATH%" >nul
|
||||
echo [INFO] Copied successfully
|
||||
)
|
||||
)
|
||||
|
||||
echo.
|
||||
) else (
|
||||
echo [INFO] No F2P launcher install found, will download files
|
||||
echo.
|
||||
)
|
||||
|
||||
:: --- Download / Update HytaleServer.jar ---
|
||||
|
||||
set "JAR_URL=%DOWNLOAD_BASE%/HytaleServer.jar"
|
||||
set "JAR_VERSION_FILE=%VERSION_DIR%\HytaleServer.jar.version"
|
||||
|
||||
if not exist "%SERVER_JAR%" (
|
||||
echo [INFO] HytaleServer.jar not found, downloading...
|
||||
echo [INFO] Expected size: ~150 MB
|
||||
curl -fL --progress-bar -o "%SERVER_JAR%.tmp" "%JAR_URL%" --connect-timeout 15 --max-time 3600
|
||||
if exist "%SERVER_JAR%.tmp" (
|
||||
move /y "%SERVER_JAR%.tmp" "%SERVER_JAR%" >nul
|
||||
echo [INFO] HytaleServer.jar downloaded
|
||||
for /f "delims=" %%h in ('powershell -Command "try { (Invoke-WebRequest -Uri '%JAR_URL%' -Method Head -UseBasicParsing).Headers['ETag'] } catch {}" 2^>nul') do (
|
||||
echo %%h>"%JAR_VERSION_FILE%"
|
||||
)
|
||||
) else (
|
||||
echo [ERROR] Failed to download HytaleServer.jar
|
||||
echo [ERROR] Check your internet connection
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
) else (
|
||||
echo [INFO] Checking for HytaleServer.jar updates...
|
||||
set "LOCAL_JAR_VER="
|
||||
if exist "%JAR_VERSION_FILE%" set /p LOCAL_JAR_VER=<"%JAR_VERSION_FILE%"
|
||||
|
||||
set "REMOTE_JAR_VER="
|
||||
for /f "delims=" %%h in ('powershell -Command "try { (Invoke-WebRequest -Uri '%JAR_URL%' -Method Head -UseBasicParsing -TimeoutSec 10).Headers['ETag'] } catch { '' }" 2^>nul') do (
|
||||
set "REMOTE_JAR_VER=%%h"
|
||||
)
|
||||
|
||||
if defined REMOTE_JAR_VER (
|
||||
if "!LOCAL_JAR_VER!"=="!REMOTE_JAR_VER!" (
|
||||
echo [INFO] HytaleServer.jar is up to date
|
||||
) else (
|
||||
echo [INFO] HytaleServer.jar update available, downloading...
|
||||
curl -fL --progress-bar -o "%SERVER_JAR%.tmp" "%JAR_URL%" --connect-timeout 15 --max-time 3600
|
||||
if exist "%SERVER_JAR%.tmp" (
|
||||
move /y "%SERVER_JAR%.tmp" "%SERVER_JAR%" >nul
|
||||
echo !REMOTE_JAR_VER!>"%JAR_VERSION_FILE%"
|
||||
echo [INFO] HytaleServer.jar updated
|
||||
) else (
|
||||
echo [WARN] Update failed, using existing HytaleServer.jar
|
||||
)
|
||||
)
|
||||
) else (
|
||||
echo [INFO] Could not check for updates, using existing HytaleServer.jar
|
||||
)
|
||||
)
|
||||
|
||||
:: --- Download / Update Assets.zip ---
|
||||
|
||||
set "ASSETS_URL=%DOWNLOAD_BASE%/Assets.zip"
|
||||
set "ASSETS_VERSION_FILE=%VERSION_DIR%\Assets.zip.version"
|
||||
|
||||
if not exist "%ASSETS_PATH%" (
|
||||
echo [INFO] Assets.zip not found, downloading...
|
||||
echo [INFO] Expected size: ~3.3 GB - this will take a while
|
||||
curl -fL --progress-bar -o "%ASSETS_PATH%.tmp" "%ASSETS_URL%" --connect-timeout 15 --max-time 7200
|
||||
if exist "%ASSETS_PATH%.tmp" (
|
||||
move /y "%ASSETS_PATH%.tmp" "%ASSETS_PATH%" >nul
|
||||
echo [INFO] Assets.zip downloaded
|
||||
for /f "delims=" %%h in ('powershell -Command "try { (Invoke-WebRequest -Uri '%ASSETS_URL%' -Method Head -UseBasicParsing).Headers['ETag'] } catch {}" 2^>nul') do (
|
||||
echo %%h>"%ASSETS_VERSION_FILE%"
|
||||
)
|
||||
) else (
|
||||
echo [ERROR] Failed to download Assets.zip
|
||||
echo [ERROR] Check your internet connection
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
) else (
|
||||
echo [INFO] Checking for Assets.zip updates...
|
||||
set "LOCAL_ASSETS_VER="
|
||||
if exist "%ASSETS_VERSION_FILE%" set /p LOCAL_ASSETS_VER=<"%ASSETS_VERSION_FILE%"
|
||||
|
||||
set "REMOTE_ASSETS_VER="
|
||||
for /f "delims=" %%h in ('powershell -Command "try { (Invoke-WebRequest -Uri '%ASSETS_URL%' -Method Head -UseBasicParsing -TimeoutSec 10).Headers['ETag'] } catch { '' }" 2^>nul') do (
|
||||
set "REMOTE_ASSETS_VER=%%h"
|
||||
)
|
||||
|
||||
if defined REMOTE_ASSETS_VER (
|
||||
if "!LOCAL_ASSETS_VER!"=="!REMOTE_ASSETS_VER!" (
|
||||
echo [INFO] Assets.zip is up to date
|
||||
) else (
|
||||
echo [INFO] Assets.zip update available, downloading...
|
||||
echo [INFO] This is a large file ^(~3.3 GB^), please be patient
|
||||
curl -fL --progress-bar -o "%ASSETS_PATH%.tmp" "%ASSETS_URL%" --connect-timeout 15 --max-time 7200
|
||||
if exist "%ASSETS_PATH%.tmp" (
|
||||
move /y "%ASSETS_PATH%.tmp" "%ASSETS_PATH%" >nul
|
||||
echo !REMOTE_ASSETS_VER!>"%ASSETS_VERSION_FILE%"
|
||||
echo [INFO] Assets.zip updated
|
||||
) else (
|
||||
echo [WARN] Update failed, using existing Assets.zip
|
||||
)
|
||||
)
|
||||
) else (
|
||||
echo [INFO] Could not check for updates, using existing Assets.zip
|
||||
)
|
||||
)
|
||||
|
||||
:: --- Download / Update DualAuth Agent ---
|
||||
|
||||
set "AGENT_VERSION_FILE=%VERSION_DIR%\dualauth-agent.jar.version"
|
||||
|
||||
if not exist "%AGENT_JAR%" (
|
||||
echo [INFO] Downloading DualAuth Agent...
|
||||
curl -fL -# -o "%AGENT_JAR%.tmp" "%AGENT_URL%" --connect-timeout 15 --max-time 120
|
||||
if exist "%AGENT_JAR%.tmp" (
|
||||
move /y "%AGENT_JAR%.tmp" "%AGENT_JAR%" >nul
|
||||
echo [INFO] DualAuth Agent downloaded
|
||||
for /f "delims=" %%v in ('powershell -Command "try { $r = Invoke-RestMethod -Uri '%AGENT_VERSION_API%' -TimeoutSec 10; $r.tag_name } catch { '' }" 2^>nul') do (
|
||||
echo %%v>"%AGENT_VERSION_FILE%"
|
||||
)
|
||||
) else (
|
||||
echo [ERROR] Failed to download DualAuth Agent
|
||||
echo [ERROR] Download manually: %AGENT_URL%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
) else (
|
||||
echo [INFO] Checking for DualAuth Agent updates...
|
||||
set "LOCAL_AGENT_VER="
|
||||
if exist "%AGENT_VERSION_FILE%" set /p LOCAL_AGENT_VER=<"%AGENT_VERSION_FILE%"
|
||||
|
||||
set "REMOTE_AGENT_VER="
|
||||
for /f "delims=" %%v in ('powershell -Command "try { $r = Invoke-RestMethod -Uri '%AGENT_VERSION_API%' -TimeoutSec 10; $r.tag_name } catch { '' }" 2^>nul') do (
|
||||
set "REMOTE_AGENT_VER=%%v"
|
||||
)
|
||||
|
||||
if defined REMOTE_AGENT_VER (
|
||||
if "!LOCAL_AGENT_VER!"=="!REMOTE_AGENT_VER!" (
|
||||
echo [INFO] DualAuth Agent up to date ^(!LOCAL_AGENT_VER!^)
|
||||
) else (
|
||||
echo [INFO] Agent update: !LOCAL_AGENT_VER! -^> !REMOTE_AGENT_VER!
|
||||
curl -fL -# -o "%AGENT_JAR%.tmp" "%AGENT_URL%" --connect-timeout 15 --max-time 120
|
||||
if exist "%AGENT_JAR%.tmp" (
|
||||
move /y "%AGENT_JAR%.tmp" "%AGENT_JAR%" >nul
|
||||
echo !REMOTE_AGENT_VER!>"%AGENT_VERSION_FILE%"
|
||||
echo [INFO] DualAuth Agent updated
|
||||
) else (
|
||||
echo [WARN] Agent update failed, using existing
|
||||
)
|
||||
)
|
||||
) else (
|
||||
echo [INFO] Could not check agent updates, using existing
|
||||
)
|
||||
)
|
||||
|
||||
:: --- Final Checks ---
|
||||
|
||||
if not exist "%SERVER_JAR%" (
|
||||
echo [ERROR] HytaleServer.jar not found
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
if not exist "%ASSETS_PATH%" (
|
||||
echo [ERROR] Assets.zip not found
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
if not exist "%AGENT_JAR%" (
|
||||
echo [ERROR] dualauth-agent.jar not found
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: --- Generate or Load Server ID ---
|
||||
|
||||
set "SERVER_ID_FILE=.server-id"
|
||||
if exist "%SERVER_ID_FILE%" (
|
||||
set /p SERVER_ID=<"%SERVER_ID_FILE%"
|
||||
echo [INFO] Server ID: !SERVER_ID!
|
||||
) else (
|
||||
for /f "delims=" %%i in ('powershell -Command "[guid]::NewGuid().ToString()"') do set "SERVER_ID=%%i"
|
||||
echo !SERVER_ID!>"%SERVER_ID_FILE%"
|
||||
echo [INFO] Generated server ID: !SERVER_ID!
|
||||
)
|
||||
|
||||
:: --- Fetch Server Tokens ---
|
||||
|
||||
echo.
|
||||
echo [INFO] Fetching server tokens from %AUTH_SERVER%...
|
||||
|
||||
set "TEMP_RESPONSE=%TEMP%\hytale_auth_%RANDOM%.json"
|
||||
|
||||
curl -s -X POST "%AUTH_SERVER%/server/auto-auth" ^
|
||||
-H "Content-Type: application/json" ^
|
||||
-d "{\"server_id\": \"!SERVER_ID!\", \"server_name\": \"%SERVER_NAME%\"}" ^
|
||||
--connect-timeout 10 ^
|
||||
--max-time 30 ^
|
||||
-o "%TEMP_RESPONSE%" 2>nul
|
||||
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] Failed to connect to auth server at %AUTH_SERVER%
|
||||
del "%TEMP_RESPONSE%" 2>nul
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
findstr /C:"sessionToken" "%TEMP_RESPONSE%" >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] Invalid response from auth server:
|
||||
type "%TEMP_RESPONSE%"
|
||||
del "%TEMP_RESPONSE%" 2>nul
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: Extract tokens using PowerShell
|
||||
for /f "delims=" %%i in ('powershell -Command "$j = Get-Content '%TEMP_RESPONSE%' | ConvertFrom-Json; $j.sessionToken"') do set "SESSION_TOKEN=%%i"
|
||||
for /f "delims=" %%i in ('powershell -Command "$j = Get-Content '%TEMP_RESPONSE%' | ConvertFrom-Json; $j.identityToken"') do set "IDENTITY_TOKEN=%%i"
|
||||
|
||||
del "%TEMP_RESPONSE%" 2>nul
|
||||
|
||||
if "!SESSION_TOKEN!"=="" (
|
||||
echo [ERROR] Could not extract session token from response
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
if "!IDENTITY_TOKEN!"=="" (
|
||||
echo [ERROR] Could not extract identity token from response
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [INFO] Tokens received successfully
|
||||
|
||||
:: --- Start Server ---
|
||||
|
||||
set "JAVA_ARGS="
|
||||
if defined JVM_XMS set "JAVA_ARGS=!JAVA_ARGS! -Xms%JVM_XMS%"
|
||||
if defined JVM_XMX set "JAVA_ARGS=!JAVA_ARGS! -Xmx%JVM_XMX%"
|
||||
|
||||
echo.
|
||||
echo ============================================================
|
||||
echo Starting Hytale Server
|
||||
echo Name: %SERVER_NAME%
|
||||
echo Bind: %BIND_ADDRESS%
|
||||
echo Java: !JAVA_CMD!
|
||||
echo Agent: %AGENT_JAR%
|
||||
echo ============================================================
|
||||
echo.
|
||||
|
||||
"!JAVA_CMD!" %JAVA_ARGS% -javaagent:"%AGENT_JAR%" -jar "%SERVER_JAR%" ^
|
||||
--assets "%ASSETS_PATH%" ^
|
||||
--bind "%BIND_ADDRESS%" ^
|
||||
--auth-mode "%AUTH_MODE%" ^
|
||||
--disable-sentry ^
|
||||
--session-token "!SESSION_TOKEN!" ^
|
||||
--identity-token "!IDENTITY_TOKEN!" ^
|
||||
%*
|
||||
|
||||
echo.
|
||||
echo ============================================================
|
||||
echo Server stopped. Exit code: %ERRORLEVEL%
|
||||
echo ============================================================
|
||||
pause
|
||||
|
||||
endlocal
|
||||
516
server/start.sh
Executable file
516
server/start.sh
Executable file
@@ -0,0 +1,516 @@
|
||||
#!/bin/bash
|
||||
# ============================================================
|
||||
# Hytale F2P Dedicated Server - One-Click Starter
|
||||
# ============================================================
|
||||
# Just run: ./start.sh
|
||||
#
|
||||
# The script will:
|
||||
# 1. Look for game files in your F2P launcher install
|
||||
# 2. Auto-download anything missing
|
||||
# 3. Auto-update server, assets, and agent on each launch
|
||||
# 4. Fetch auth tokens and start the server
|
||||
# ============================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration (edit these or set as environment variables)
|
||||
HYTALE_AUTH_DOMAIN="${HYTALE_AUTH_DOMAIN:-auth.sanasol.ws}"
|
||||
AUTH_SERVER="${AUTH_SERVER:-https://$HYTALE_AUTH_DOMAIN}"
|
||||
SERVER_NAME="${SERVER_NAME:-My Hytale Server}"
|
||||
BIND_ADDRESS="${BIND_ADDRESS:-0.0.0.0:5520}"
|
||||
AUTH_MODE="${AUTH_MODE:-authenticated}"
|
||||
|
||||
# Download URLs
|
||||
DOWNLOAD_BASE="${DOWNLOAD_BASE:-https://download.sanasol.ws/download}"
|
||||
AGENT_URL="https://github.com/sanasol/hytale-auth-server/releases/latest/download/dualauth-agent.jar"
|
||||
AGENT_VERSION_API="https://api.github.com/repos/sanasol/hytale-auth-server/releases/latest"
|
||||
|
||||
# File names (in current directory)
|
||||
AGENT_JAR="dualauth-agent.jar"
|
||||
SERVER_JAR="HytaleServer.jar"
|
||||
ASSETS_FILE="Assets.zip"
|
||||
ASSETS_PATH="${ASSETS_PATH:-./Assets.zip}"
|
||||
VERSION_DIR=".versions"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Hytale F2P Dedicated Server"
|
||||
echo "============================================================"
|
||||
echo ""
|
||||
|
||||
# --- Prerequisite Checks ---
|
||||
|
||||
if ! command -v curl &>/dev/null; then
|
||||
echo "[ERROR] curl is required but not found"
|
||||
echo " Install: sudo apt install curl"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$VERSION_DIR"
|
||||
|
||||
# --- Find Local F2P Launcher Install ---
|
||||
|
||||
get_f2p_default_dir() {
|
||||
case "$(uname -s)" in
|
||||
Darwin) echo "$HOME/Library/Application Support/HytaleF2P" ;;
|
||||
Linux) echo "$HOME/.hytalef2p" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Read a JSON string field from config.json using available tools
|
||||
read_config_field() {
|
||||
local config_file="$1" field="$2"
|
||||
if [ ! -f "$config_file" ]; then return 1; fi
|
||||
|
||||
if command -v python3 &>/dev/null; then
|
||||
python3 -c "
|
||||
import json
|
||||
try:
|
||||
c = json.load(open('$config_file'))
|
||||
v = c.get('$field', '').strip()
|
||||
if v: print(v)
|
||||
except: pass
|
||||
" 2>/dev/null
|
||||
elif command -v jq &>/dev/null; then
|
||||
jq -r ".$field // empty" "$config_file" 2>/dev/null
|
||||
else
|
||||
grep -o "\"$field\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" "$config_file" 2>/dev/null | cut -d'"' -f4
|
||||
fi
|
||||
}
|
||||
|
||||
find_f2p_install() {
|
||||
local default_app_dir
|
||||
default_app_dir=$(get_f2p_default_dir) || return 1
|
||||
|
||||
local search_dirs=()
|
||||
|
||||
# Check config.json for custom installPath
|
||||
local config_file="$default_app_dir/config.json"
|
||||
local custom_path
|
||||
custom_path=$(read_config_field "$config_file" "installPath")
|
||||
if [ -n "$custom_path" ]; then
|
||||
local custom_f2p="$custom_path/HytaleF2P"
|
||||
if [ -d "$custom_f2p" ]; then
|
||||
search_dirs+=("$custom_f2p")
|
||||
fi
|
||||
fi
|
||||
|
||||
# Always also check default location
|
||||
search_dirs+=("$default_app_dir")
|
||||
|
||||
for base in "${search_dirs[@]}"; do
|
||||
for branch in "release" "pre-release"; do
|
||||
local game_dir="$base/$branch/package/game/latest"
|
||||
if [ -d "$game_dir" ]; then
|
||||
echo "$game_dir"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Find bundled JRE from F2P launcher install
|
||||
find_f2p_java() {
|
||||
local default_app_dir
|
||||
default_app_dir=$(get_f2p_default_dir) || return 1
|
||||
|
||||
local search_dirs=()
|
||||
|
||||
# Check config.json for custom javaPath first
|
||||
local config_file="$default_app_dir/config.json"
|
||||
local custom_java
|
||||
custom_java=$(read_config_field "$config_file" "javaPath")
|
||||
if [ -n "$custom_java" ] && [ -x "$custom_java" ]; then
|
||||
echo "$custom_java"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check custom installPath
|
||||
local custom_path
|
||||
custom_path=$(read_config_field "$config_file" "installPath")
|
||||
if [ -n "$custom_path" ]; then
|
||||
local custom_f2p="$custom_path/HytaleF2P"
|
||||
[ -d "$custom_f2p" ] && search_dirs+=("$custom_f2p")
|
||||
fi
|
||||
|
||||
search_dirs+=("$default_app_dir")
|
||||
|
||||
for base in "${search_dirs[@]}"; do
|
||||
for branch in "release" "pre-release"; do
|
||||
local jre_dir="$base/$branch/package/jre/latest"
|
||||
# Standard path
|
||||
if [ -x "$jre_dir/bin/java" ]; then
|
||||
echo "$jre_dir/bin/java"
|
||||
return 0
|
||||
fi
|
||||
# macOS bundle path
|
||||
if [ -x "$jre_dir/Contents/Home/bin/java" ]; then
|
||||
echo "$jre_dir/Contents/Home/bin/java"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
copy_from_f2p() {
|
||||
local file="$1" local_path="$2" f2p_path="$3"
|
||||
|
||||
if [ -f "$local_path" ]; then
|
||||
return 1 # Already exists locally
|
||||
fi
|
||||
|
||||
if [ -f "$f2p_path" ]; then
|
||||
echo "[INFO] Found $file in F2P launcher install"
|
||||
echo "[INFO] Copying from: $f2p_path"
|
||||
cp "$f2p_path" "$local_path"
|
||||
echo "[INFO] Copied successfully"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- Detect Java ---
|
||||
|
||||
JAVA_CMD="java"
|
||||
|
||||
# Try F2P bundled JRE first
|
||||
F2P_JAVA=$(find_f2p_java 2>/dev/null) || true
|
||||
if [ -n "$F2P_JAVA" ]; then
|
||||
echo "[INFO] Found Java in F2P launcher: $F2P_JAVA"
|
||||
JAVA_CMD="$F2P_JAVA"
|
||||
elif ! command -v java &>/dev/null; then
|
||||
echo "[ERROR] Java is not installed and no F2P launcher JRE found"
|
||||
echo ""
|
||||
echo " Options:"
|
||||
echo " 1. Install the F2P launcher first (it includes Java)"
|
||||
echo " 2. Install Java 25+ manually:"
|
||||
echo " Windows: https://download.oracle.com/java/25/latest/jdk-25_windows-x64_bin.exe"
|
||||
echo " macOS: brew install openjdk"
|
||||
echo " Ubuntu/Debian: sudo apt install openjdk-25-jre"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Java version
|
||||
JAVA_FULL=$("$JAVA_CMD" -version 2>&1 | head -1)
|
||||
JAVA_VER=$(echo "$JAVA_FULL" | grep -oP '(?<=")\d+' 2>/dev/null || echo "$JAVA_FULL" | sed 's/.*"\([0-9]*\).*/\1/')
|
||||
echo "[INFO] Java: $JAVA_FULL"
|
||||
if [ -n "$JAVA_VER" ] && [ "$JAVA_VER" -lt 25 ] 2>/dev/null; then
|
||||
echo "[ERROR] Java $JAVA_VER detected. Java 25+ is REQUIRED."
|
||||
echo " The DualAuth agent requires Java 25 (class file version 69)."
|
||||
echo ""
|
||||
if [ -n "$F2P_JAVA" ]; then
|
||||
echo " Your F2P launcher JRE is outdated. Update the launcher to get a newer Java."
|
||||
else
|
||||
echo " Install Java 25+:"
|
||||
echo " Windows: https://download.oracle.com/java/25/latest/jdk-25_windows-x64_bin.exe"
|
||||
echo " macOS: brew install openjdk"
|
||||
echo " Ubuntu/Debian: sudo apt install openjdk-25-jre"
|
||||
fi
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Find F2P Game Files ---
|
||||
|
||||
F2P_DIR=""
|
||||
if F2P_DIR=$(find_f2p_install 2>/dev/null); then
|
||||
echo "[INFO] Found F2P launcher game files: $F2P_DIR"
|
||||
|
||||
# Try to copy HytaleServer.jar from F2P install
|
||||
copy_from_f2p "HytaleServer.jar" "$SERVER_JAR" "$F2P_DIR/Server/HytaleServer.jar" || true
|
||||
|
||||
# Try to copy Assets.zip from F2P install
|
||||
copy_from_f2p "Assets.zip" "$ASSETS_PATH" "$F2P_DIR/Assets.zip" || true
|
||||
|
||||
echo ""
|
||||
else
|
||||
echo "[INFO] No F2P launcher install found, will download files"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# --- Download / Update Functions ---
|
||||
|
||||
get_remote_version() {
|
||||
local url="$1"
|
||||
local headers
|
||||
headers=$(curl -sI -L "$url" --connect-timeout 10 --max-time 15 2>/dev/null | tr -d '\r')
|
||||
local etag
|
||||
etag=$(echo "$headers" | grep -i "^etag:" | tail -1 | sed 's/^[^:]*: *//' | tr -d '"')
|
||||
if [ -n "$etag" ]; then printf '%s' "$etag"; return 0; fi
|
||||
local lastmod
|
||||
lastmod=$(echo "$headers" | grep -i "^last-modified:" | tail -1 | sed 's/^[^:]*: *//')
|
||||
if [ -n "$lastmod" ]; then printf '%s' "$lastmod"; return 0; fi
|
||||
local length
|
||||
length=$(echo "$headers" | grep -i "^content-length:" | tail -1 | sed 's/^[^:]*: *//')
|
||||
if [ -n "$length" ]; then printf 'size:%s' "$length"; return 0; fi
|
||||
return 1
|
||||
}
|
||||
|
||||
needs_update() {
|
||||
local url="$1" dest="$2" name="$3"
|
||||
local version_file="${VERSION_DIR}/${name}.version"
|
||||
|
||||
if [ ! -f "$dest" ]; then
|
||||
echo "[INFO] $name not found, will download"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "[INFO] Checking for $name updates..."
|
||||
local remote_version
|
||||
remote_version=$(get_remote_version "$url" 2>/dev/null) || true
|
||||
if [ -z "$remote_version" ]; then
|
||||
echo "[INFO] Could not check for updates, using existing $name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local local_version=""
|
||||
[ -f "$version_file" ] && local_version=$(cat "$version_file" 2>/dev/null)
|
||||
|
||||
if [ "$remote_version" = "$local_version" ]; then
|
||||
echo "[INFO] $name is up to date"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -n "$local_version" ]; then
|
||||
echo "[INFO] $name update available"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
save_version() {
|
||||
local url="$1" name="$2"
|
||||
local version_file="${VERSION_DIR}/${name}.version"
|
||||
local ver
|
||||
ver=$(get_remote_version "$url" 2>/dev/null) || true
|
||||
[ -n "$ver" ] && printf '%s\n' "$ver" > "$version_file"
|
||||
}
|
||||
|
||||
download_file() {
|
||||
local url="$1" dest="$2" name="$3" expected_mb="${4:-0}"
|
||||
local tmp="${dest}.tmp"
|
||||
|
||||
echo "[INFO] Downloading $name..."
|
||||
[ "$expected_mb" -gt 0 ] 2>/dev/null && echo "[INFO] Expected size: ~${expected_mb} MB"
|
||||
|
||||
for attempt in 1 2 3; do
|
||||
rm -f "$tmp" 2>/dev/null || true
|
||||
|
||||
if [ "$expected_mb" -gt 50 ] 2>/dev/null; then
|
||||
curl -fL --progress-bar -o "$tmp" "$url" --connect-timeout 15 --max-time 3600 2>&1
|
||||
else
|
||||
curl -fL -# -o "$tmp" "$url" --connect-timeout 15 --max-time 300 2>&1
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ] && [ -f "$tmp" ]; then
|
||||
local size
|
||||
size=$(stat -c%s "$tmp" 2>/dev/null || stat -f%z "$tmp" 2>/dev/null || echo 0)
|
||||
if [ "$size" -gt 1000 ]; then
|
||||
mv -f "$tmp" "$dest"
|
||||
local mb=$((size / 1024 / 1024))
|
||||
echo "[INFO] $name downloaded (${mb} MB)"
|
||||
return 0
|
||||
fi
|
||||
echo "[WARN] $name download too small (${size} bytes), retrying..."
|
||||
fi
|
||||
|
||||
echo "[WARN] Download attempt $attempt failed, retrying..."
|
||||
rm -f "$tmp" 2>/dev/null || true
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "[ERROR] Failed to download $name after 3 attempts"
|
||||
rm -f "$tmp" 2>/dev/null || true
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- Download / Update Server Files ---
|
||||
|
||||
# HytaleServer.jar
|
||||
JAR_URL="${DOWNLOAD_BASE}/HytaleServer.jar"
|
||||
if needs_update "$JAR_URL" "$SERVER_JAR" "HytaleServer.jar"; then
|
||||
if download_file "$JAR_URL" "$SERVER_JAR" "HytaleServer.jar" "150"; then
|
||||
save_version "$JAR_URL" "HytaleServer.jar"
|
||||
else
|
||||
if [ ! -f "$SERVER_JAR" ]; then
|
||||
echo "[ERROR] HytaleServer.jar is required. Check your internet connection."
|
||||
exit 1
|
||||
fi
|
||||
echo "[WARN] Update failed, using existing HytaleServer.jar"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Assets.zip
|
||||
ASSETS_URL="${DOWNLOAD_BASE}/Assets.zip"
|
||||
if needs_update "$ASSETS_URL" "$ASSETS_PATH" "Assets.zip"; then
|
||||
echo "[INFO] Assets.zip is large (~3.3 GB), this may take a while..."
|
||||
if download_file "$ASSETS_URL" "$ASSETS_PATH" "Assets.zip" "3300"; then
|
||||
save_version "$ASSETS_URL" "Assets.zip"
|
||||
else
|
||||
if [ ! -f "$ASSETS_PATH" ]; then
|
||||
echo "[ERROR] Assets.zip is required. Check your internet connection."
|
||||
exit 1
|
||||
fi
|
||||
echo "[WARN] Update failed, using existing Assets.zip"
|
||||
fi
|
||||
fi
|
||||
|
||||
# DualAuth Agent (uses GitHub releases API for version tracking)
|
||||
check_agent_update() {
|
||||
if [ -f "$AGENT_JAR" ]; then
|
||||
local agent_size
|
||||
agent_size=$(stat -c%s "$AGENT_JAR" 2>/dev/null || stat -f%z "$AGENT_JAR" 2>/dev/null || echo 0)
|
||||
if [ "$agent_size" -lt 10000 ]; then
|
||||
echo "[WARN] Agent JAR seems corrupt (${agent_size} bytes), re-downloading..."
|
||||
rm -f "$AGENT_JAR"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local version_file="${VERSION_DIR}/${AGENT_JAR}.version"
|
||||
local local_version=""
|
||||
[ -f "$version_file" ] && local_version=$(cat "$version_file" 2>/dev/null)
|
||||
|
||||
echo "[INFO] Checking for DualAuth Agent updates..."
|
||||
local remote_version
|
||||
remote_version=$(curl -sf "$AGENT_VERSION_API" --connect-timeout 5 --max-time 10 2>/dev/null | grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4)
|
||||
|
||||
if [ -n "$remote_version" ]; then
|
||||
if [ "$local_version" = "$remote_version" ]; then
|
||||
echo "[INFO] DualAuth Agent up to date ($local_version)"
|
||||
return 1
|
||||
else
|
||||
echo "[INFO] Agent update: ${local_version:-unknown} -> $remote_version"
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
echo "[INFO] Could not check agent updates, using existing"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
AGENT_REMOTE_VERSION=""
|
||||
if check_agent_update; then
|
||||
echo "[INFO] Downloading DualAuth Agent..."
|
||||
if curl -fL -# -o "${AGENT_JAR}.tmp" "$AGENT_URL" --connect-timeout 15 --max-time 120 2>&1 && [ -f "${AGENT_JAR}.tmp" ]; then
|
||||
dl_size=$(stat -c%s "${AGENT_JAR}.tmp" 2>/dev/null || stat -f%z "${AGENT_JAR}.tmp" 2>/dev/null || echo 0)
|
||||
if [ "$dl_size" -gt 10000 ]; then
|
||||
mv -f "${AGENT_JAR}.tmp" "$AGENT_JAR"
|
||||
AGENT_REMOTE_VERSION=$(curl -sf "$AGENT_VERSION_API" --connect-timeout 5 --max-time 10 2>/dev/null | grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4)
|
||||
[ -n "$AGENT_REMOTE_VERSION" ] && printf '%s\n' "$AGENT_REMOTE_VERSION" > "${VERSION_DIR}/${AGENT_JAR}.version"
|
||||
echo "[INFO] DualAuth Agent ready (${AGENT_REMOTE_VERSION:-latest})"
|
||||
else
|
||||
echo "[WARN] Downloaded agent too small, discarding"
|
||||
rm -f "${AGENT_JAR}.tmp"
|
||||
fi
|
||||
else
|
||||
rm -f "${AGENT_JAR}.tmp" 2>/dev/null
|
||||
if [ -f "$AGENT_JAR" ]; then
|
||||
echo "[WARN] Agent update failed, using existing"
|
||||
else
|
||||
echo "[ERROR] Failed to download DualAuth Agent"
|
||||
echo "[ERROR] Download manually: $AGENT_URL"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Final Checks ---
|
||||
|
||||
for required in "$SERVER_JAR" "$ASSETS_PATH" "$AGENT_JAR"; do
|
||||
if [ ! -f "$required" ]; then
|
||||
echo "[ERROR] Required file missing: $required"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# --- Generate Server ID ---
|
||||
|
||||
SERVER_ID_FILE=".server-id"
|
||||
if [ -f "$SERVER_ID_FILE" ]; then
|
||||
SERVER_ID=$(cat "$SERVER_ID_FILE")
|
||||
echo "[INFO] Server ID: $SERVER_ID"
|
||||
else
|
||||
SERVER_ID=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || uuidgen 2>/dev/null || python3 -c "import uuid; print(uuid.uuid4())" 2>/dev/null)
|
||||
if [ -z "$SERVER_ID" ]; then
|
||||
echo "[ERROR] Could not generate UUID. Install uuidgen or python3."
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$SERVER_ID" > "$SERVER_ID_FILE"
|
||||
echo "[INFO] Generated server ID: $SERVER_ID"
|
||||
fi
|
||||
|
||||
# --- Fetch Tokens ---
|
||||
|
||||
echo ""
|
||||
echo "[INFO] Fetching server tokens from $AUTH_SERVER..."
|
||||
|
||||
TEMP_RESPONSE=$(mktemp)
|
||||
curl -s -X POST "$AUTH_SERVER/server/auto-auth" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"server_id\": \"$SERVER_ID\", \"server_name\": \"$SERVER_NAME\"}" \
|
||||
--connect-timeout 10 \
|
||||
--max-time 30 \
|
||||
-o "$TEMP_RESPONSE"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "[ERROR] Failed to connect to auth server at $AUTH_SERVER"
|
||||
rm -f "$TEMP_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q "sessionToken" "$TEMP_RESPONSE" 2>/dev/null; then
|
||||
echo "[ERROR] Invalid response from auth server:"
|
||||
cat "$TEMP_RESPONSE"
|
||||
rm -f "$TEMP_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract tokens (python3 > jq > grep fallback)
|
||||
if command -v python3 &>/dev/null; then
|
||||
SESSION_TOKEN=$(python3 -c "import json,sys; print(json.load(open('$TEMP_RESPONSE'))['sessionToken'])")
|
||||
IDENTITY_TOKEN=$(python3 -c "import json,sys; print(json.load(open('$TEMP_RESPONSE'))['identityToken'])")
|
||||
elif command -v jq &>/dev/null; then
|
||||
SESSION_TOKEN=$(jq -r '.sessionToken' "$TEMP_RESPONSE")
|
||||
IDENTITY_TOKEN=$(jq -r '.identityToken' "$TEMP_RESPONSE")
|
||||
else
|
||||
SESSION_TOKEN=$(grep -o '"sessionToken":"[^"]*"' "$TEMP_RESPONSE" | cut -d'"' -f4)
|
||||
IDENTITY_TOKEN=$(grep -o '"identityToken":"[^"]*"' "$TEMP_RESPONSE" | cut -d'"' -f4)
|
||||
fi
|
||||
|
||||
rm -f "$TEMP_RESPONSE"
|
||||
|
||||
if [ -z "$SESSION_TOKEN" ] || [ -z "$IDENTITY_TOKEN" ]; then
|
||||
echo "[ERROR] Could not extract tokens from auth server response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[INFO] Tokens received successfully"
|
||||
|
||||
# --- Start Server ---
|
||||
|
||||
JAVA_ARGS=""
|
||||
[ -n "${JVM_XMS:-}" ] && JAVA_ARGS="$JAVA_ARGS -Xms$JVM_XMS"
|
||||
[ -n "${JVM_XMX:-}" ] && JAVA_ARGS="$JAVA_ARGS -Xmx$JVM_XMX"
|
||||
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
echo " Starting Hytale Server"
|
||||
echo " Name: $SERVER_NAME"
|
||||
echo " Bind: $BIND_ADDRESS"
|
||||
echo " Agent: $AGENT_JAR"
|
||||
echo "============================================================"
|
||||
echo ""
|
||||
|
||||
exec "$JAVA_CMD" $JAVA_ARGS -javaagent:"$AGENT_JAR" -jar "$SERVER_JAR" \
|
||||
--assets "$ASSETS_PATH" \
|
||||
--bind "$BIND_ADDRESS" \
|
||||
--auth-mode "$AUTH_MODE" \
|
||||
--disable-sentry \
|
||||
--session-token "$SESSION_TOKEN" \
|
||||
--identity-token "$IDENTITY_TOKEN" \
|
||||
"$@"
|
||||
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