chore(history): squash 10 commit(s) from 2026-07-31
- feat: Фаза 1 ребрендинг combatant → lovisual + восстановление config - refactor: Фаза 2 — выкинуть Runtime/JarReplacement, заменить на Lifecycle - refactor: Фаза 3.5 — удалить читерские модули, ESP под легит - refactor: Фаза 3 — разбить Renderer2D (3379→2150 фасад + 14 хелперов ≤200) - refactor: Фаза 3 — разбить BetterChatRenderer (2870→210 фасад + 21 хелпер ≤200) - refactor: Фаза 3 — разбить UnifiedSettingRenderer (2798→208 фасад + 24 хелпера ≤200) - refactor: Фаза 3 — разбить ClickGuiRenderer (1887→542 фасад + 15 хелперов ≤200) - refactor: Фаза 3 — разбить NameTags (1791→438 фасад + 11 хелперов ≤200) - refactor: Фаза 3 — разбить TargetHud (1655→340 фасад + 11 хелперов ≤200) - refactor: Фаза 3 — разбить ESP (1538→269 фасад + 11 хелперов ≤200)
This commit is contained in:
parent
8084ac50b3
commit
52d87368c9
1595 changed files with 35857 additions and 51145 deletions
6855
launcher/Cargo.lock
generated
Normal file
6855
launcher/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
36
launcher/Cargo.toml
Normal file
36
launcher/Cargo.toml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
[package]
|
||||
name = "lovisual-launcher"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
rust-version = "1.85"
|
||||
description = "LoVisual launcher: login, dashboard, Minecraft launch"
|
||||
license = "GPL-3.0"
|
||||
|
||||
[dependencies]
|
||||
slint = "1.17"
|
||||
tokio = { version = "1.53", features = ["full"] }
|
||||
reqwest = { version = "0.13", features = ["json", "rustls"], default-features = false }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
anyhow = "1"
|
||||
dirs = "6"
|
||||
|
||||
[build-dependencies]
|
||||
slint-build = "1.17"
|
||||
|
||||
[lints.rust]
|
||||
dead_code = "deny"
|
||||
|
||||
[lints.clippy]
|
||||
correctness = "deny"
|
||||
suspicious = "deny"
|
||||
style = "deny"
|
||||
complexity = "deny"
|
||||
perf = "deny"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = true
|
||||
37
launcher/TODO.md
Normal file
37
launcher/TODO.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# LoVisual Launcher (Rust + Slint)
|
||||
|
||||
## Правила (ОБЯЗАТЕЛЬНЫЕ, нарушение = откат)
|
||||
- **KISS / DRY / SOLID** — простая структура, без оверинжиниринга
|
||||
- **Макс. 200 строк на файл** — превышение = разбить на модули
|
||||
- **Макс. 3 файла на папку** — больше = новая подпапка по смыслу
|
||||
- **Обязательное прохождение:** `cargo fmt` + `cargo clippy --all-targets` (ноль ошибок) + `cargo test`
|
||||
- **Запрещено `#[allow(dead_code)]`** — мёртвый код удаляется, не прячется
|
||||
- **unsafe запрещён в нашем коде** (сгенерированный Slint-код — исключение, его не трогаем)
|
||||
- **Без пустышек** — модуль создаётся только с рабочей реализацией
|
||||
- **Каждый этап компилируется** — перед переходом к следующему
|
||||
|
||||
---
|
||||
|
||||
## Сделано
|
||||
- [x] Каркас: main.rs + api/ (health check в фоне)
|
||||
- [x] Окно логина (Slint): логин/пароль/статус
|
||||
- [x] Rust 2024 edition, актуальные версии пакетов
|
||||
- [x] Фоновая проверка `/health` бэкенда, статус в UI
|
||||
|
||||
---
|
||||
|
||||
## Фаза 1: API клиент (`src/api/`)
|
||||
- [ ] `auth.rs` — login/register (reqwest, таймауты, rename_all)
|
||||
- [ ] `profile.rs` — get/update/playtime/version check
|
||||
- [ ] Версия лаунчера читается из Cargo.toml (не хардкод)
|
||||
|
||||
## Фаза 2: Экраны (Slint)
|
||||
- [ ] login — реальный логин/регистрация через backend
|
||||
- [ ] dashboard — профиль, playtime, version check
|
||||
- [ ] settings — кастомная команда запуска (ЧИТАЕТСЯ на Play, не stale)
|
||||
|
||||
## Фаза 3: Launch
|
||||
- [ ] `src/launch/mod.rs` — запуск Minecraft (команда из настроек, защита от пустой строки)
|
||||
- [ ] `src/session/mod.rs` — save_session только при логине, удалять при logout
|
||||
|
||||
### Проверка после каждой фазы: `cargo fmt && cargo clippy --all-targets && cargo test`
|
||||
3
launcher/build.rs
Normal file
3
launcher/build.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
slint_build::compile("ui/main.slint").expect("failed to compile slint ui");
|
||||
}
|
||||
35
launcher/src/api/mod.rs
Normal file
35
launcher/src/api/mod.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
//! Minimal client for the LoVisual backend API.
|
||||
|
||||
use anyhow::Context as _;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// Default backend base URL for local development.
|
||||
pub const DEFAULT_API_URL: &str = "http://127.0.0.1:8765";
|
||||
|
||||
/// Health response from `GET /health`.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Health {
|
||||
pub status: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
/// Fetch backend health status.
|
||||
pub async fn health(base_url: &str) -> anyhow::Result<Health> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(5))
|
||||
.build()
|
||||
.context("failed to build http client")?;
|
||||
|
||||
let url = format!("{}/health", base_url.trim_end_matches('/'));
|
||||
let response = client
|
||||
.get(&url)
|
||||
.send()
|
||||
.await
|
||||
.context("health request failed")?;
|
||||
response
|
||||
.error_for_status()
|
||||
.context("health returned error status")?
|
||||
.json()
|
||||
.await
|
||||
.context("invalid health response")
|
||||
}
|
||||
52
launcher/src/main.rs
Normal file
52
launcher/src/main.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
//! LoVisual launcher entry point.
|
||||
|
||||
mod api;
|
||||
|
||||
slint::include_modules!();
|
||||
|
||||
use slint::ComponentHandle;
|
||||
|
||||
fn main() -> Result<(), slint::PlatformError> {
|
||||
let ui = MainWindow::new()?;
|
||||
|
||||
spawn_backend_status_check(ui.as_weak());
|
||||
|
||||
let weak = ui.as_weak();
|
||||
ui.on_login(move || {
|
||||
let ui = weak.unwrap();
|
||||
let login = ui.get_login_input().trim().to_string();
|
||||
let password = ui.get_password_input().trim().to_string();
|
||||
|
||||
if login.is_empty() || password.is_empty() {
|
||||
ui.set_status("Заполните логин и пароль".into());
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: auth against backend, save session, transition to dashboard.
|
||||
ui.set_status(format!("Вход выполнен как {login}").into());
|
||||
});
|
||||
|
||||
ui.run()
|
||||
}
|
||||
|
||||
/// Ping backend health in background and surface the result in the UI.
|
||||
fn spawn_backend_status_check(weak: slint::Weak<MainWindow>) {
|
||||
std::thread::spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("failed to build tokio runtime");
|
||||
|
||||
let status = runtime.block_on(api::health(api::DEFAULT_API_URL));
|
||||
let message = match status {
|
||||
Ok(health) => format!("Backend v{}: {}", health.version, health.status),
|
||||
Err(error) => format!("Backend недоступен: {error}"),
|
||||
};
|
||||
|
||||
let _ = slint::invoke_from_event_loop(move || {
|
||||
if let Some(ui) = weak.upgrade() {
|
||||
ui.set_status(message.clone().into());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
63
launcher/ui/main.slint
Normal file
63
launcher/ui/main.slint
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { VerticalBox, LineEdit, Button } from "std-widgets.slint";
|
||||
|
||||
export component MainWindow inherits Window {
|
||||
title: "LoVisual Launcher";
|
||||
width: 480px;
|
||||
height: 320px;
|
||||
|
||||
in-out property <string> status: "Disconnected";
|
||||
|
||||
VerticalBox {
|
||||
spacing: 12px;
|
||||
padding: 24px;
|
||||
|
||||
Text {
|
||||
text: "LoVisual Launcher";
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
horizontal-alignment: center;
|
||||
color: #00bfff;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Войдите в аккаунт, чтобы запустить Minecraft";
|
||||
font-size: 13px;
|
||||
horizontal-alignment: center;
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
VerticalBox {
|
||||
spacing: 8px;
|
||||
|
||||
Text { text: "Логин"; }
|
||||
LineEdit {
|
||||
text <=> login-input;
|
||||
placeholder-text: "username or email";
|
||||
}
|
||||
|
||||
Text { text: "Пароль"; }
|
||||
LineEdit {
|
||||
text <=> password-input;
|
||||
input-type: InputType.password;
|
||||
placeholder-text: "••••••••";
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Войти";
|
||||
primary: true;
|
||||
clicked => root.login();
|
||||
}
|
||||
|
||||
Text {
|
||||
text: status;
|
||||
horizontal-alignment: center;
|
||||
color: #ff6b6b;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
callback login();
|
||||
in-out property <string> login-input: "";
|
||||
in-out property <string> password-input: "";
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue