Исправлена корректная загрузка локализации при обновлении игры.
Исправлено уведомление ошибки, о несуществующей ошибке.
Переехали на новый сервер, скорость скачивания через "p####i" должна работать у всех и в несколько раз быстрее.
Стабильная версия лаунчера 1.4.7 вышла из стадии бета-тестирования.
• Добавлена 4-я цифра для критических патчей.
• Переработана вся система работы с путями.
• Обнаружена критическая ошибка в игре, при работе с путями. Интегрировали свою уникальную систему виртуализации в лаунчер, при её активации - игра будет гарантированно работать, игнорируя проблемные пути и символы. (Удачи другим лаунчерам сделать подобное 😆)
• Добавлена виртуализация для запуска локального сервера.
• Исправлено...
понял спасибо@TixTodi, извини, я не в той теме ответил. Да, жди пожалуйста онлайнфикса. Скоро всё будет работать. Поддержка лаунчера тут: HytaleNet Launcher Support
• Переписана вся логика лаунчера, исправлены все старые проблемы с установкой, скачиванием и запуском.
• Ускорена работа скачивания, функция докачивания файлов при проблемах.
• Ускорена работа патчей (снова).
• Ускорена проверка ресурсов игры при запуске.Исправлены все проблемы с русификацией.
• Добавили кнопку "Исправить клиент" в настройки, если игра сломалась.
• Наш новый надёжный патч, теперь не обязательно обновлять лаунчер при выходе новой версии игры.
• Изменили дизайн, сделали его...
#[cfg(windows)]
use std::ffi::OsStr;
#[cfg(windows)]
use std::os::windows::ffi::OsStrExt;
#[cfg(windows)]
use std::ptr;
#[cfg(windows)]
use winapi::um::fileapi::GetLogicalDrives;
#[cfg(windows)]
use winapi::shared::minwindef::DWORD;
#[cfg(windows)]
use winapi::um::winnt::LPCWSTR;
#[cfg(windows)]
extern "system" {
fn DefineDosDeviceW(
dwFlags: DWORD,
lpDeviceName: LPCWSTR,
lpTargetPath: LPCWSTR,
) -> i32;
}
#[cfg(windows)]
const DDD_REMOVE_DEFINITION: u32 = 0x00000002;
#[cfg(windows)]
pub fn find_available_drive_letter() -> Result<char, String> {
let drives = unsafe { GetLogicalDrives() };
let mut occupied = Vec::new();
for i in 0..26 {
let bit = 1 << i;
if (drives & bit) != 0 {
let letter = (b'A' + i as u8) as char;
occupied.push(letter);
}
}
eprintln!("[VIRTUALIZATION] Occupied drive letters: {:?}", occupied);
let preferred = ['Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D'];
for &letter in preferred.iter() {
let bit = 1 << (letter as u8 - b'A');
if (drives & bit) == 0 {
eprintln!("[VIRTUALIZATION] Found available drive letter: {}", letter);
return Ok(letter);
}
}
Err("Нет свободных букв дисков. Отключите ненужные диски (сетевые, USB, виртуальные) в Проводнике и попробуйте снова.".to_string())
}
#[cfg(not(windows))]
pub fn find_available_drive_letter() -> Result<char, String> {
Err("Path virtualization is only available on Windows".to_string())
}
#[cfg(windows)]
fn to_wide_string(s: &str) -> Vec<u16> {
OsStr::new(s)
.encode_wide()
.chain(Some(0))
.collect()
}
#[cfg(windows)]
pub fn create_virtual_drive(drive_letter: char, target_path: &str) -> Result<(), String> {
use std::path::Path;
let drive_str = format!("{}:", drive_letter);
let drive_wide = to_wide_string(&drive_str);
let target_wide = to_wide_string(target_path);
eprintln!("[VIRTUALIZATION] Creating virtual drive {} -> {}", drive_str, target_path);
let target_path_obj = Path::new(target_path);
if !target_path_obj.exists() {
eprintln!("[VIRTUALIZATION] ERROR: Target path does not exist: {}", target_path);
return Err(format!("Target path does not exist: {}", target_path));
}
if !target_path_obj.is_dir() {
eprintln!("[VIRTUALIZATION] ERROR: Target path is not a directory: {}", target_path);
return Err(format!("Target path is not a directory: {}", target_path));
}
eprintln!("[VIRTUALIZATION] Target path validated, calling DefineDosDeviceW...");
let result = unsafe {
DefineDosDeviceW(
0,
drive_wide.as_ptr(),
target_wide.as_ptr(),
)
};
if result == 0 {
let error = unsafe { winapi::um::errhandlingapi::GetLastError() };
eprintln!("[VIRTUALIZATION] DefineDosDeviceW failed with error code: {}", error);
eprintln!("[VIRTUALIZATION] Drive: {}, Target: {}", drive_str, target_path);
eprintln!("[VIRTUALIZATION] Target path exists: {}, is_dir: {}", target_path_obj.exists(), target_path_obj.is_dir());
return Err(format!("Failed to create virtual drive: Windows error {} (check if path is valid and drive letter free)", error));
}
eprintln!("[VIRTUALIZATION] Virtual drive {} created successfully", drive_str);
Ok(())
}
#[cfg(not(windows))]
pub fn create_virtual_drive(_drive_letter: char, _target_path: &str) -> Result<(), String> {
Err("Path virtualization is only available on Windows".to_string())
}
#[cfg(windows)]
pub fn remove_virtual_drive(drive_letter: char) -> Result<(), String> {
let drive_str = format!("{}:", drive_letter);
let drive_wide = to_wide_string(&drive_str);
eprintln!("[VIRTUALIZATION] Removing virtual drive {}", drive_str);
let result = unsafe {
DefineDosDeviceW(
DDD_REMOVE_DEFINITION,
drive_wide.as_ptr(),
ptr::null(),
)
};
if result == 0 {
let error = unsafe { winapi::um::errhandlingapi::GetLastError() };
return Err(format!("Failed to remove virtual drive: Windows error {}", error));
}
eprintln!("[VIRTUALIZATION] Virtual drive {} removed successfully", drive_str);
Ok(())
}
#[cfg(not(windows))]
pub fn remove_virtual_drive(_drive_letter: char) -> Result<(), String> {
Err("Path virtualization is only available on Windows".to_string())
}
pub fn get_virtual_path(drive_letter: char, relative_path: &str) -> String {
if relative_path.is_empty() {
format!("{}:\\", drive_letter)
} else {
let normalized = relative_path.trim_start_matches('\\').trim_start_matches('/');
format!("{}:\\{}", drive_letter, normalized.replace('/', "\\"))
}
}#[tauri::command]
#[cfg(windows)]
async fn setup_path_virtualization(target_path: String) -> Result<String, String> {
use crate::virtualization;
use crate::config::Config;
eprintln!("[VIRTUALIZATION] setup_path_virtualization called with target_path: {}", target_path);
let drive_letter = virtualization::find_available_drive_letter().map_err(|e| {
eprintln!("[VIRTUALIZATION] ERROR: {}", e);
e
})?;
eprintln!("[VIRTUALIZATION] Found available drive letter: {}", drive_letter);
virtualization::create_virtual_drive(drive_letter, &target_path).map_err(|e| {
eprintln!("[VIRTUALIZATION] ERROR in create_virtual_drive: {}", e);
e
})?;
eprintln!("[VIRTUALIZATION] Loading config to save virtualization settings...");
let mut config = Config::load().await.map_err(|e| {
eprintln!("[VIRTUALIZATION] ERROR loading config: {}", e);
format!("Failed to load config: {}", e)
})?;
config.path_virtualization_enabled = true;
config.virtual_drive_letter = Some(format!("{}", drive_letter));
config.virtual_drive_target = Some(target_path.clone());
eprintln!("[VIRTUALIZATION] Saving config with virtualization enabled...");
config.save().await.map_err(|e| {
eprintln!("[VIRTUALIZATION] ERROR saving config: {}", e);
format!("Failed to save config: {}", e)
})?;
eprintln!("[VIRTUALIZATION] setup_path_virtualization completed successfully, drive: {}:", drive_letter);
Ok(format!("{}:", drive_letter))
}По вашим просьбам, вернули вам выбор старых релизов игры.
Исправили редкую ошибку создания портативной версии, напоминаем что её создать можно вручную, прописав в названии лаунчера слово "portable".
• Исправлено множество редких ошибок во время обновления игры.
• Исправлена неправильная логика IO патчинга игры, процесс ускорен.
• Исправлена ошибка сохранения версии при обновлениях, заменены индексы на их названия.
• Исправлена ошибка, когда лончер мог скачать битый файл со смещением.
• Исправлено множество внутренних исключений в работе с файлами.
• Исправлено зависание интерфейса при работе с большими файлами.
• Ускорены все проверки клиента игры, сильно ускорен её запуск.
• Переписана...
• Добавлены флаги запуска локального сервера, автоматический пресет настроек.
• Перенесена папка всех данных сервера в%appdata%\HLauncher\ServerDataдля безопасного хранения миров и данных локального сервера.
• Исправлена установка игры, если путь файлов сверх пределов длинный.
• Исправлено отображение выбранной версии в углу.
• Исправлено отображение иконки и цвета текста на отключенных кнопках.