fix: comprehensive UUID/username persistence bug fixes (#252)

* fix: comprehensive UUID/username persistence bug fixes

Major fixes for UUID/skin reset issues that caused players to lose cosmetics:

Core fixes:
- Username rename now preserves UUID (atomic rename, not new identity)
- Atomic config writes with backup/recovery system
- Case-insensitive UUID lookup with case-preserving storage
- Pre-launch validation blocks play if no username configured
- Removed saveUsername calls from launch/install flows

UUID Modal fixes:
- Fixed isCurrent badge showing on wrong user
- Added switch identity button to change between saved usernames
- Fixed custom UUID input using unsaved DOM username
- UUID list now refreshes when player name changes
- Enabled copy/paste in custom UUID input field

UI/UX improvements:
- Added translation keys for switch username functionality
- CSS user-select fix for UUID input fields
- Allowed Ctrl+V/C/X/A shortcuts in Electron

Files: config.js, gameLauncher.js, gameManager.js, playerManager.js,
launcher.js, settings.js, main.js, preload.js, style.css, en.json

See UUID_BUGS_FIX_PLAN.md for detailed bug list (18 bugs, 16 fixed)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(i18n): add switch username translations to all locales

Added translation keys for username switching functionality:
- notifications.noUsername
- notifications.switchUsernameSuccess
- notifications.switchUsernameFailed
- notifications.playerNameTooLong
- confirm.switchUsernameTitle
- confirm.switchUsernameMessage
- confirm.switchUsernameButton

Languages updated: de-DE, es-ES, fr-FR, id-ID, pl-PL, pt-BR, ru-RU, sv-SE, tr-TR

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: move UUID_BUGS_FIX_PLAN.md to docs folder

* docs: update UUID_BUGS_FIX_PLAN with complete fix details

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Alex
2026-02-01 04:01:58 +07:00
committed by GitHub
parent 6fbf37422f
commit 62430fe8f0
21 changed files with 1564 additions and 181 deletions

View File

@@ -194,27 +194,81 @@ window.switchProfile = async (id) => {
export async function launch() {
if (isDownloading || (playBtn && playBtn.disabled)) return;
let playerName = 'Player';
if (window.SettingsAPI && window.SettingsAPI.getCurrentPlayerName) {
playerName = window.SettingsAPI.getCurrentPlayerName();
} else if (playerNameInput && playerNameInput.value.trim()) {
playerName = playerNameInput.value.trim();
// ==========================================================================
// STEP 1: Check launch readiness from backend (single source of truth)
// ==========================================================================
let launchState = null;
let playerName = null;
try {
if (window.electronAPI && window.electronAPI.checkLaunchReady) {
launchState = await window.electronAPI.checkLaunchReady();
playerName = launchState?.username;
} else if (window.electronAPI && window.electronAPI.loadUsername) {
// Fallback to loadUsername if checkLaunchReady not available
playerName = await window.electronAPI.loadUsername();
launchState = { ready: !!playerName, hasUsername: !!playerName, username: playerName, issues: [] };
}
} catch (error) {
console.error('[Launcher] Error checking launch readiness:', error);
}
let javaPath = '';
if (window.SettingsAPI && window.SettingsAPI.getCurrentJavaPath) {
javaPath = window.SettingsAPI.getCurrentJavaPath();
// Validate launch readiness
if (!launchState?.ready || !playerName) {
const issues = launchState?.issues || ['No username configured'];
const errorMsg = window.i18n
? window.i18n.t('errors.noUsername')
: 'Please set your username in Settings before playing.';
console.error('[Launcher] Launch blocked:', issues.join(', '));
// Show error to user
if (window.LauncherUI && window.LauncherUI.showError) {
window.LauncherUI.showError(errorMsg);
} else {
alert(errorMsg);
}
// Navigate to settings if possible
if (window.LauncherUI && window.LauncherUI.showPage) {
window.LauncherUI.showPage('settings-page');
window.LauncherUI.setActiveNav('settings');
}
return;
}
// Warn if using default 'Player' name (shouldn't happen with new logic, but keep as safety)
if (playerName === 'Player') {
console.warn('[Launcher] Warning: Using default username "Player"');
}
console.log(`[Launcher] Launching game for: "${playerName}"`);
// ==========================================================================
// STEP 2: Load other settings from backend
// ==========================================================================
let javaPath = '';
try {
if (window.electronAPI && window.electronAPI.loadJavaPath) {
javaPath = await window.electronAPI.loadJavaPath() || '';
}
} catch (error) {
console.error('[Launcher] Error loading Java path:', error);
}
let gpuPreference = 'auto';
try {
if (window.electronAPI && window.electronAPI.loadGpuPreference) {
gpuPreference = await window.electronAPI.loadGpuPreference();
}
} catch (error) {
console.error('Error loading GPU preference:', error);
console.error('[Launcher] Error loading GPU preference:', error);
}
// ==========================================================================
// STEP 3: Start launch process
// ==========================================================================
if (window.LauncherUI) window.LauncherUI.showProgress();
isDownloading = true;
if (playBtn) {
@@ -227,8 +281,9 @@ export async function launch() {
if (window.LauncherUI) window.LauncherUI.updateProgress({ message: startingMsg });
if (window.electronAPI && window.electronAPI.launchGame) {
// Pass playerName from config - backend will validate again
const result = await window.electronAPI.launchGame(playerName, javaPath, '', gpuPreference);
isDownloading = false;
if (window.LauncherUI) {
@@ -243,7 +298,35 @@ export async function launch() {
}, 500);
}
} else {
console.error('Launch failed:', result.error);
console.error('[Launcher] Launch failed:', result.error);
// Handle specific error cases
if (result.needsUsername) {
const errorMsg = window.i18n
? window.i18n.t('errors.noUsername')
: 'Please set your username in Settings before playing.';
if (window.LauncherUI && window.LauncherUI.showError) {
window.LauncherUI.showError(errorMsg);
} else {
alert(errorMsg);
}
// Navigate to settings
if (window.LauncherUI && window.LauncherUI.showPage) {
window.LauncherUI.showPage('settings-page');
window.LauncherUI.setActiveNav('settings');
}
} else if (result.error) {
// Show generic error
const errorMsg = window.i18n
? window.i18n.t('errors.launchFailed').replace('{error}', result.error)
: `Launch failed: ${result.error}`;
if (window.LauncherUI && window.LauncherUI.showError) {
window.LauncherUI.showError(errorMsg);
}
}
}
} else {
isDownloading = false;
@@ -260,7 +343,13 @@ export async function launch() {
window.LauncherUI.hideProgress();
}
resetPlayButton();
console.error('Launch error:', error);
console.error('[Launcher] Launch error:', error);
// Show error to user
const errorMsg = error.message || 'Unknown launch error';
if (window.LauncherUI && window.LauncherUI.showError) {
window.LauncherUI.showError(errorMsg);
}
}
}

View File

@@ -446,10 +446,27 @@ async function savePlayerName() {
return;
}
await window.electronAPI.saveUsername(playerName);
const result = await window.electronAPI.saveUsername(playerName);
// Check if save was successful
if (result && result.success === false) {
console.error('[Settings] Failed to save username:', result.error);
const errorMsg = window.i18n
? window.i18n.t('notifications.playerNameSaveFailed')
: `Failed to save player name: ${result.error || 'Unknown error'}`;
showNotification(errorMsg, 'error');
return;
}
const successMsg = window.i18n ? window.i18n.t('notifications.playerNameSaved') : 'Player name saved successfully';
showNotification(successMsg, 'success');
// Refresh UUID display since it may have changed for the new username
await loadCurrentUuid();
// Also refresh the UUID list to update which entry is marked as current
await loadAllUuids();
} catch (error) {
console.error('Error saving player name:', error);
const errorMsg = window.i18n ? window.i18n.t('notifications.playerNameSaveFailed') : 'Failed to save player name';
@@ -573,11 +590,26 @@ export function getCurrentJavaPath() {
}
/**
* Get current player name from UI input
* Returns null if no name is set (caller must handle this)
* NOTE: launcher.js now loads username directly from backend config
* This function is used for display purposes only
*/
export function getCurrentPlayerName() {
if (settingsPlayerName && settingsPlayerName.value.trim()) {
return settingsPlayerName.value.trim();
}
return 'Player';
// Return null instead of 'Player' - caller must handle missing username
return null;
}
/**
* Get current player name with fallback for display purposes only
* DO NOT use this for launching game - use backend loadUsername() instead
*/
export function getCurrentPlayerNameForDisplay() {
return getCurrentPlayerName() || 'Player';
}
window.openGameLocation = openGameLocation;
@@ -587,6 +619,7 @@ document.addEventListener('DOMContentLoaded', initSettings);
window.SettingsAPI = {
getCurrentJavaPath,
getCurrentPlayerName,
getCurrentPlayerNameForDisplay,
reloadBranch: loadVersionBranch
};
@@ -729,6 +762,9 @@ async function loadAllUuids() {
</div>
<div class="uuid-item-actions">
${mapping.isCurrent ? '<div class="uuid-item-current-badge">Current</div>' : ''}
${!mapping.isCurrent ? `<button class="uuid-item-btn switch" onclick="switchToUsername('${escapeHtml(mapping.username)}')" title="Switch to this identity">
<i class="fas fa-user-check"></i>
</button>` : ''}
<button class="uuid-item-btn copy" onclick="copyUuid('${mapping.uuid}')" title="Copy UUID">
<i class="fas fa-copy"></i>
</button>
@@ -813,7 +849,17 @@ async function setCustomUuid() {
async function performSetCustomUuid(uuid) {
try {
if (window.electronAPI && window.electronAPI.setUuidForUser) {
const username = getCurrentPlayerName();
// IMPORTANT: Use saved username from config, not unsaved DOM input
// This prevents setting UUID for wrong user if username field was edited but not saved
let username = null;
if (window.electronAPI.loadUsername) {
username = await window.electronAPI.loadUsername();
}
if (!username) {
const msg = window.i18n ? window.i18n.t('notifications.noUsername') : 'No username configured. Please save your username first.';
showNotification(msg, 'error');
return;
}
const result = await window.electronAPI.setUuidForUser(username, uuid);
if (result.success) {
@@ -850,6 +896,73 @@ window.copyUuid = async function (uuid) {
}
};
/**
* Switch to a different username/UUID identity
* This changes the active username to use that username's UUID
*/
window.switchToUsername = async function (username) {
try {
const message = window.i18n
? window.i18n.t('confirm.switchUsernameMessage').replace('{username}', username)
: `Switch to username "${username}"? This will change your active player identity.`;
const title = window.i18n ? window.i18n.t('confirm.switchUsernameTitle') : 'Switch Identity';
const confirmBtn = window.i18n ? window.i18n.t('confirm.switchUsernameButton') : 'Switch';
const cancelBtn = window.i18n ? window.i18n.t('common.cancel') : 'Cancel';
showCustomConfirm(
message,
title,
async () => {
await performSwitchToUsername(username);
},
null,
confirmBtn,
cancelBtn
);
} catch (error) {
console.error('Error in switchToUsername:', error);
const msg = window.i18n ? window.i18n.t('notifications.switchUsernameFailed') : 'Failed to switch username';
showNotification(msg, 'error');
}
};
async function performSwitchToUsername(username) {
try {
if (!window.electronAPI || !window.electronAPI.saveUsername) {
throw new Error('API not available');
}
const result = await window.electronAPI.saveUsername(username);
if (result && result.success === false) {
throw new Error(result.error || 'Failed to save username');
}
// Update the username input field
if (settingsPlayerName) {
settingsPlayerName.value = username;
}
// Refresh the current UUID display
await loadCurrentUuid();
// Refresh the UUID list to show new "Current" badge
await loadAllUuids();
const msg = window.i18n
? window.i18n.t('notifications.switchUsernameSuccess').replace('{username}', username)
: `Switched to "${username}" successfully!`;
showNotification(msg, 'success');
} catch (error) {
console.error('Error switching username:', error);
const msg = window.i18n
? window.i18n.t('notifications.switchUsernameFailed')
: `Failed to switch username: ${error.message}`;
showNotification(msg, 'error');
}
}
window.deleteUuid = async function (username) {
try {
const message = window.i18n ? window.i18n.t('confirm.deleteUuidMessage').replace('{username}', username) : `Are you sure you want to delete the UUID for "${username}"? This action cannot be undone.`;

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Mod konnte nicht gelöscht werden: {error}",
"modsModNotFound": "Mod-Informationen nicht gefunden",
"hwAccelSaved": "Hardware-Beschleunigungseinstellung gespeichert",
"hwAccelSaveFailed": "Hardware-Beschleunigungseinstellung konnte nicht gespeichert werden"
"hwAccelSaveFailed": "Hardware-Beschleunigungseinstellung konnte nicht gespeichert werden",
"noUsername": "Kein Benutzername konfiguriert. Bitte speichere zuerst deinen Benutzernamen.",
"switchUsernameSuccess": "Erfolgreich zu \"{username}\" gewechselt!",
"switchUsernameFailed": "Benutzername konnte nicht gewechselt werden",
"playerNameTooLong": "Spielername darf maximal 16 Zeichen haben"
},
"confirm": {
"defaultTitle": "Aktion bestätigen",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Löschen",
"uninstallGameTitle": "Spiel deinstallieren",
"uninstallGameMessage": "Möchtest du Hytale wirklich deinstallieren? Alle Spieldateien werden gelöscht.",
"uninstallGameButton": "Deinstallieren"
"uninstallGameButton": "Deinstallieren",
"switchUsernameTitle": "Identität wechseln",
"switchUsernameMessage": "Zu Benutzername \"{username}\" wechseln? Dies ändert deine aktuelle Spieleridentität.",
"switchUsernameButton": "Wechseln"
},
"progress": {
"initializing": "Initialisiere...",

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Failed to delete mod: {error}",
"modsModNotFound": "Mod information not found",
"hwAccelSaved": "Hardware acceleration setting saved",
"hwAccelSaveFailed": "Failed to save hardware acceleration setting"
"hwAccelSaveFailed": "Failed to save hardware acceleration setting",
"noUsername": "No username configured. Please save your username first.",
"switchUsernameSuccess": "Switched to \"{username}\" successfully!",
"switchUsernameFailed": "Failed to switch username",
"playerNameTooLong": "Player name must be 16 characters or less"
},
"confirm": {
"defaultTitle": "Confirm action",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Delete",
"uninstallGameTitle": "Uninstall game",
"uninstallGameMessage": "Are you sure you want to uninstall Hytale? All game files will be deleted.",
"uninstallGameButton": "Uninstall"
"uninstallGameButton": "Uninstall",
"switchUsernameTitle": "Switch Identity",
"switchUsernameMessage": "Switch to username \"{username}\"? This will change your current player identity.",
"switchUsernameButton": "Switch"
},
"progress": {
"initializing": "Initializing...",

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Error al eliminar mod: {error}",
"modsModNotFound": "Información del mod no encontrada",
"hwAccelSaved": "Configuración de aceleración por hardware guardada",
"hwAccelSaveFailed": "Error al guardar la configuración de aceleración por hardware"
"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.",
"switchUsernameSuccess": "¡Cambiado a \"{username}\" con éxito!",
"switchUsernameFailed": "Error al cambiar nombre de usuario",
"playerNameTooLong": "El nombre del jugador debe tener 16 caracteres o menos"
},
"confirm": {
"defaultTitle": "Confirmar acción",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Eliminar",
"uninstallGameTitle": "Desinstalar juego",
"uninstallGameMessage": "¿Estás seguro de que quieres desinstalar Hytale? Se eliminarán todos los archivos del juego.",
"uninstallGameButton": "Desinstalar"
"uninstallGameButton": "Desinstalar",
"switchUsernameTitle": "Cambiar identidad",
"switchUsernameMessage": "¿Cambiar al nombre de usuario \"{username}\"? Esto cambiará tu identidad de jugador actual.",
"switchUsernameButton": "Cambiar"
},
"progress": {
"initializing": "Inicializando...",

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Échec de la suppression du mod: {error}",
"modsModNotFound": "Informations du mod introuvables",
"hwAccelSaved": "Paramètre d'accélération matérielle sauvegardé",
"hwAccelSaveFailed": "Échec de la sauvegarde du paramètre d'accélération matérielle"
"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.",
"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"
},
"confirm": {
"defaultTitle": "Confirmer l'action",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Supprimer",
"uninstallGameTitle": "Désinstaller le jeu",
"uninstallGameMessage": "Êtes-vous sûr de vouloir désinstaller Hytale? Tous les fichiers du jeu seront supprimés.",
"uninstallGameButton": "Désinstaller"
"uninstallGameButton": "Désinstaller",
"switchUsernameTitle": "Changer d'identité",
"switchUsernameMessage": "Basculer vers le nom d'utilisateur \"{username}\"? Cela changera votre identité de joueur actuelle.",
"switchUsernameButton": "Changer"
},
"progress": {
"initializing": "Initialisation...",

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Gagal menghapus mod: {error}",
"modsModNotFound": "Informasi mod tidak ditemukan",
"hwAccelSaved": "Pengaturan akselerasi perangkat keras disimpan",
"hwAccelSaveFailed": "Gagal menyimpan pengaturan akselerasi perangkat keras"
"hwAccelSaveFailed": "Gagal menyimpan pengaturan akselerasi perangkat keras",
"noUsername": "Nama pengguna belum dikonfigurasi. Silakan simpan nama pengguna terlebih dahulu.",
"switchUsernameSuccess": "Berhasil beralih ke \"{username}\"!",
"switchUsernameFailed": "Gagal beralih nama pengguna",
"playerNameTooLong": "Nama pemain harus 16 karakter atau kurang"
},
"confirm": {
"defaultTitle": "Konfirmasi tindakan",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Hapus",
"uninstallGameTitle": "Hapus instalasi game",
"uninstallGameMessage": "Apakah kamu yakin ingin menghapus instalasi Hytale? Semua file game akan dihapus.",
"uninstallGameButton": "Hapus Instalasi"
"uninstallGameButton": "Hapus Instalasi",
"switchUsernameTitle": "Ganti Identitas",
"switchUsernameMessage": "Beralih ke nama pengguna \"{username}\"? Ini akan mengubah identitas pemain saat ini.",
"switchUsernameButton": "Ganti"
},
"progress": {
"initializing": "Menginisialisasi...",

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Nie udało się usunąć moda: {error}",
"modsModNotFound": "Nie znaleziono informacji o modzie",
"hwAccelSaved": "Zapisano ustawienie przyspieszenia sprzętowego",
"hwAccelSaveFailed": "Nie udało się zapisać ustawienia 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.",
"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"
},
"confirm": {
"defaultTitle": "Potwierdź działanie",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Usuń",
"uninstallGameTitle": "Odinstaluj grę",
"uninstallGameMessage": "Czy na pewno chcesz odinstalować Hytale? Wszystkie pliki gry zostaną usunięte.",
"uninstallGameButton": "Odinstaluj"
"uninstallGameButton": "Odinstaluj",
"switchUsernameTitle": "Zmień tożsamość",
"switchUsernameMessage": "Przełączyć na nazwę użytkownika \"{username}\"? To zmieni Twoją aktualną tożsamość gracza.",
"switchUsernameButton": "Przełącz"
},
"progress": {
"initializing": "Inicjalizacja...",

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Falha ao excluir mod: {error}",
"modsModNotFound": "Informações do mod não encontradas",
"hwAccelSaved": "Configuração de aceleração de hardware salva",
"hwAccelSaveFailed": "Falha ao salvar configuração de aceleração de hardware"
"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.",
"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"
},
"confirm": {
"defaultTitle": "Confirmar ação",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Excluir",
"uninstallGameTitle": "Desinstalar jogo",
"uninstallGameMessage": "Tem certeza de que deseja desinstalar Hytale? Todos os arquivos do jogo serão excluídos.",
"uninstallGameButton": "Desinstalar"
"uninstallGameButton": "Desinstalar",
"switchUsernameTitle": "Trocar Identidade",
"switchUsernameMessage": "Trocar para o nome de usuário \"{username}\"? Isso mudará sua identidade de jogador atual.",
"switchUsernameButton": "Trocar"
},
"progress": {
"initializing": "Inicializando...",

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Не получилось удалить мод: {error}",
"modsModNotFound": "Информация по моду не найдена",
"hwAccelSaved": "Настройка аппаратного ускорения сохранена!",
"hwAccelSaveFailed": "Не удалось сохранить настройку аппаратного ускорения"
"hwAccelSaveFailed": "Не удалось сохранить настройку аппаратного ускорения",
"noUsername": "Имя пользователя не настроено. Пожалуйста, сначала сохраните имя пользователя.",
"switchUsernameSuccess": "Успешно переключено на \"{username}\"!",
"switchUsernameFailed": "Не удалось переключить имя пользователя",
"playerNameTooLong": "Имя игрока должно быть не более 16 символов"
},
"confirm": {
"defaultTitle": "Подтвердить действие",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Удалить",
"uninstallGameTitle": "Удалить игру",
"uninstallGameMessage": "Вы уверены, что хотите удалить Hytale? Все данные игры будут безвозвратно удалены!",
"uninstallGameButton": "Удалить"
"uninstallGameButton": "Удалить",
"switchUsernameTitle": "Сменить личность",
"switchUsernameMessage": "Переключиться на имя пользователя \"{username}\"? Это изменит вашу текущую личность игрока.",
"switchUsernameButton": "Переключить"
},
"progress": {
"initializing": "Инициализация...",

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Misslyckades med att ta bort modd: {error}",
"modsModNotFound": "Moddinformation hittades inte",
"hwAccelSaved": "Hårdvaruaccelerationsinställning sparad",
"hwAccelSaveFailed": "Misslyckades med att spara hårdvaruaccelerationsinställning"
"hwAccelSaveFailed": "Misslyckades med att spara hårdvaruaccelerationsinställning",
"noUsername": "Inget användarnamn konfigurerat. Vänligen spara ditt användarnamn först.",
"switchUsernameSuccess": "Bytte till \"{username}\" framgångsrikt!",
"switchUsernameFailed": "Misslyckades med att byta användarnamn",
"playerNameTooLong": "Spelarnamnet måste vara 16 tecken eller mindre"
},
"confirm": {
"defaultTitle": "Bekräfta åtgärd",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Ta bort",
"uninstallGameTitle": "Avinstallera spel",
"uninstallGameMessage": "Är du säker på att du vill avinstallera Hytale? Alla spelfiler kommer att tas bort.",
"uninstallGameButton": "Avinstallera"
"uninstallGameButton": "Avinstallera",
"switchUsernameTitle": "Byt identitet",
"switchUsernameMessage": "Byta till användarnamn \"{username}\"? Detta kommer att ändra din nuvarande spelaridentitet.",
"switchUsernameButton": "Byt"
},
"progress": {
"initializing": "Initierar...",

View File

@@ -211,7 +211,11 @@
"modsDeleteFailed": "Mod silinemedi: {error}",
"modsModNotFound": "Mod bilgileri bulunamadı",
"hwAccelSaved": "Donanım hızlandırma ayarı kaydedildi",
"hwAccelSaveFailed": "Donanım hızlandırma ayarı kaydedilemedi"
"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.",
"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"
},
"confirm": {
"defaultTitle": "Eylemi onayla",
@@ -226,7 +230,10 @@
"deleteUuidButton": "Sil",
"uninstallGameTitle": "Oyunu kaldır",
"uninstallGameMessage": "Hytale'yi kaldırmak istediğinizden emin misiniz? Tüm oyun dosyaları silinecektir.",
"uninstallGameButton": "Kaldır"
"uninstallGameButton": "Kaldır",
"switchUsernameTitle": "Kimlik Değiştir",
"switchUsernameMessage": "\"{username}\" kullanıcı adına geçilsin mi? Bu mevcut oyuncu kimliğinizi değiştirecektir.",
"switchUsernameButton": "Değiştir"
},
"progress": {
"initializing": "Başlatılıyor...",

View File

@@ -5236,6 +5236,21 @@ select.settings-input option {
font-family: 'JetBrains Mono', monospace;
font-size: 0.875rem;
letter-spacing: 0.5px;
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
user-select: text !important;
pointer-events: auto !important;
}
/* Ensure all input fields allow text selection and paste */
input[type="text"].uuid-input,
#customUuidInput {
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
user-select: text !important;
pointer-events: auto !important;
}
.uuid-btn {
@@ -5623,6 +5638,12 @@ select.settings-input option {
color: #ef4444;
}
.uuid-item-btn.switch:hover {
background: rgba(59, 130, 246, 0.2);
border-color: rgba(59, 130, 246, 0.4);
color: #3b82f6;
}
@media (max-width: 600px) {
.uuid-modal-content {
width: 95vw;