chore(history): squash 2 commit(s) from 2026-09-04

- refactor: удалить launcher и backend — развитие только мода
- feat: автор loki5512344 везде, чужие атрибуции убраны, .cbcfg → .lvcfg с легаси-чтением + тесты
This commit is contained in:
loki5512344 2026-09-04 17:54:49 +02:00
parent 53ff8f8f8e
commit e504859777
1456 changed files with 1752 additions and 12478 deletions

11
.gitignore vendored
View file

@ -11,12 +11,6 @@ gradlew.bat
*.iml *.iml
.vscode/ .vscode/
# Rust
*/target/
# Go
*/vendor/
# OS # OS
.DS_Store .DS_Store
Thumbs.db Thumbs.db
@ -37,4 +31,7 @@ ref/
*.jar *.jar
*.rar *.rar
*.zip *.zip
backend/server
# IDE/Build artifacts
.factorypath
**/bin/

38
TODO.md
View file

@ -1,36 +1,30 @@
# LoVisual — структура проекта # LoVisual
Репозиторий разбит на 3 независимых компонента. **У каждого свой TODO, корневой TODO больше не ведётся.** Проект состоит из одного компонента — мода.
| Компонент | Стек | TODO | | Компонент | Стек | TODO |
|-----------|------|------| |-----------|------|------|
| `combatant-client-26.2/` | Java 25, Fabric (MC 26.2) | [`combatant-client-26.2/TODO.md`](combatant-client-26.2/TODO.md) | | `combatant-client-26.2/` | Java 25, Fabric (MC 26.2) | [`combatant-client-26.2/TODO.md`](combatant-client-26.2/TODO.md) |
| `backend/` | Rust 2024 + axum | [`backend/TODO.md`](backend/TODO.md) |
| `launcher/` | Rust 2024 + Slint | [`launcher/TODO.md`](launcher/TODO.md) | > Лаунчер (Rust + Slint) и backend (Rust + axum) удалены из проекта — развитие только мода.
--- ---
## Общие правила для Rust-компонентов (backend + launcher) ## Правила (для мода, продублированы в его TODO)
- KISS / DRY / SOLID - KISS / DRY / SOLID
- Макс. 200 строк на файл - Макс. 200 строк на файл (фасады-делегаты исключение)
- Макс. 3 файла на папку - Макс. 3 файла на папку
- Обязательно: `cargo fmt` + `cargo clippy --all-targets` + `cargo test` - Никакого статика, никаких this-escape, никаких мёртвых миксинов
- Запрещено `#[allow(dead_code)]` - Без пустышек; каждый этап компилируется (`./gradlew build`)
- Запрещено `unsafe` в собственной логике - Тесты обязательны для новых модулей (JUnit)
- Без пустышек: модуль создаётся только с рабочей реализацией
- Каждый этап компилируется
--- ---
## Статус ## Статус
- [x] Фаза 1: Ребрендинг `combatant` → `lovisual`
### Backend - [x] Фаза 2: Выкинуть Runtime/JarReplacement + Panic
- [x] Каркас: config, /health, graceful shutdown, CORS - [x] Фаза 3: Разбивка 12 файлов-гигантов
- [ ] Auth (argon2 + JWT), профили, конфиги, IRC — см. `backend/TODO.md` - [x] Фаза 3.5: Удаление читерских модулей (легит-направление)
- [x] Фаза 4: Security (BackdoorProtection, StaffTracker)
### Launcher - [x] Фаза 5: Нормализация модулей (ленивый Minecraft, GameClock)
- [x] Каркас: окно логина, фоновая проверка /health - [ ] Фаза 6: Тесты (EventBus, конфиг, утилиты) — см. `combatant-client-26.2/TODO.md`
- [ ] Auth, dashboard, settings, launch — см. `launcher/TODO.md`
### Мод (combatant-client-26.2)
- [ ] Полный rewrite: ребрендинг (1464 `combatant`), разбивка 12 файлов >1500 строк, выкинуть Runtime/JarReplacement, тесты — см. `combatant-client-26.2/TODO.md`

2199
backend/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,44 +0,0 @@
[package]
name = "lovisual-backend"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"
description = "LoVisual backend: auth, profiles, friends, cloud configs, IRC"
license = "GPL-3.0"
[dependencies]
axum = "0.8.9"
tokio = { version = "1.53", features = ["full"] }
tower-http = { version = "0.7", features = ["cors", "trace", "fs"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlx = { version = "0.9", features = ["runtime-tokio", "sqlite", "chrono"] }
chrono = { version = "0.4", features = ["serde"] }
thiserror = "2"
anyhow = "1"
argon2 = "0.5"
jsonwebtoken = "11"
uuid = { version = "1", features = ["v4", "serde"] }
[dev-dependencies]
tower = { version = "0.5", features = ["util"] }
[lints.rust]
unsafe_code = "forbid"
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

View file

@ -1,51 +0,0 @@
# LoVisual Backend (Rust + axum)
## Правила (ОБЯЗАТЕЛЬНЫЕ, нарушение = откат)
- **KISS / DRY / SOLID** — простая структура, без оверинжиниринга
- **Макс. 200 строк на файл** — превышение = разбить на модули
- **Макс. 3 файла на папку** — больше = новая подпапка по смыслу
- **Обязательное прохождение:** `cargo fmt` + `cargo clippy --all-targets` (ноль ошибок) + `cargo test`
- **Запрещено `#[allow(dead_code)]`** — мёртвый код удаляется, не прячется
- **Запрещено `unsafe`** (в собственной логике; lints: `unsafe_code = "forbid"`)
- **Без пустышек** — модуль создаётся только с рабочей реализацией
- **Каждый этап компилируется** — перед переходом к следующему
- **Ошибки JSON-декода не игнорировать** — всегда `.context()`/`?`
- **`user_id` из токена, не из тела запроса**
---
## Сделано
- [x] Каркас: main.rs + server/{mod,config} + routes/{mod,health}
- [x] `GET /health` + graceful shutdown (SIGTERM/SIGINT)
- [x] Config из env (LOVISUAL_HOST/PORT/CORS_ORIGINS), валидация на старте
- [x] CORS (настраиваемые origins) + tower TraceLayer
- [x] Rust 2024 edition, актуальные версии пакетов
---
## Фаза 1: DB (SQLite, sqlx)
- [ ] `src/db/mod.rs` — подключение, миграции
- [ ] `src/db/users.rs` — таблица users
- [ ] `src/db/configs.rs` — облачные конфиги
- [ ] `src/db/profiles.rs` — профили, playtime, friends
## Фаза 2: Auth
- [ ] `src/auth/mod.rs` — argon2 + JWT (jsonwebtoken)
- [ ] `src/auth/handlers.rs` — register/login/logout
- [ ] `src/auth/middleware.rs` — Bearer token → 401, не panic
## Фаза 3: Profile API
- [ ] `src/profile/mod.rs` — get/update/playtime/search/friends
## Фаза 4: Configs API
- [ ] `src/config_api/mod.rs` — upload/download
- [ ] 16-byte share keys (минимум)
## Фаза 5: IRC
- [ ] `src/irc/mod.rs` — WebSocket хаб (axum + tokio-tungstenite)
## Фаза 6: Middleware
- [ ] RateLimit — с cleanup-задачей (без утечки)
- [ ] Request logging через tracing
### Проверка после каждой фазы: `cargo fmt && cargo clippy --all-targets && cargo test`

View file

@ -1,30 +0,0 @@
//! LoVisual backend server entry point.
mod routes;
mod server;
use std::net::SocketAddr;
use anyhow::Context as _;
use server::config::Config;
use server::serve;
fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "lovisual_backend=info,tower_http=info".into()),
)
.init();
let config = Config::from_env().context("failed to load config")?;
let addr: SocketAddr = config.listen_addr();
tracing::info!(%addr, "starting lovisual backend");
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.context("failed to build tokio runtime")?
.block_on(serve(config))
}

View file

@ -1,47 +0,0 @@
//! Health/liveness endpoints.
use axum::Router;
use axum::routing::get;
use serde::Serialize;
/// Health response body.
#[derive(Debug, Serialize)]
pub struct Health {
pub status: &'static str,
pub version: &'static str,
}
/// Build the health router.
pub fn router() -> Router {
Router::new().route("/health", get(health))
}
async fn health() -> axum::Json<Health> {
axum::Json(Health {
status: "ok",
version: env!("CARGO_PKG_VERSION"),
})
}
#[cfg(test)]
mod tests {
use axum::body::Body;
use axum::http::{Request, StatusCode};
use tower::ServiceExt as _;
use super::*;
#[tokio::test]
async fn health_returns_ok() {
let response = router()
.oneshot(
Request::builder()
.uri("/health")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
}

View file

@ -1,13 +0,0 @@
//! Route definitions.
//!
//! Top-level router; each domain (auth, profile, configs, irc) will add its
//! own sub-router here.
pub mod health;
use axum::Router;
/// Compose the full route tree.
pub fn router() -> Router {
Router::new().merge(health::router())
}

View file

@ -1,120 +0,0 @@
//! Runtime configuration, loaded from environment variables.
//!
//! Parsed into validated types at the boundary; invalid values fail fast
//! at startup instead of blowing up mid-request.
use std::env;
use std::net::{IpAddr, SocketAddr};
use anyhow::{Context as _, Result, bail};
use axum::http::HeaderValue;
use tower_http::cors::AllowOrigin;
/// Application configuration.
#[derive(Debug, Clone)]
pub struct Config {
pub host: IpAddr,
pub port: u16,
pub cors_origins: AllowOrigin,
}
impl Config {
/// Default host for local development.
pub const DEFAULT_HOST: &'static str = "127.0.0.1";
/// Default port for local development.
pub const DEFAULT_PORT: u16 = 8765;
/// Load configuration from the environment.
pub fn from_env() -> Result<Self> {
Self::from_vars(
env::var("LOVISUAL_HOST").ok(),
env::var("LOVISUAL_PORT").ok(),
env::var("LOVISUAL_CORS_ORIGINS").ok(),
)
}
/// Build config from raw option values (injectable for tests).
fn from_vars(host: Option<String>, port: Option<String>, cors: Option<String>) -> Result<Self> {
let host = host
.unwrap_or_else(|| Self::DEFAULT_HOST.to_owned())
.parse()
.context("LOVISUAL_HOST is not a valid IP address")?;
let port: u16 = match port {
Some(p) => p
.parse()
.context("LOVISUAL_PORT is not a valid port number")?,
None => Self::DEFAULT_PORT,
};
let origins = parse_origins(&cors)?;
Ok(Self {
host,
port,
cors_origins: origins,
})
}
/// Resolved listen address.
pub fn listen_addr(&self) -> SocketAddr {
SocketAddr::new(self.host, self.port)
}
}
fn parse_origins(raw: &Option<String>) -> Result<AllowOrigin> {
let Some(raw) = raw else {
// No origins configured: allow same-origin requests only.
return Ok(AllowOrigin::any());
};
let origins = raw
.split(',')
.map(str::trim)
.filter(|s| !s.is_empty())
.collect::<Vec<_>>();
if origins.is_empty() {
bail!("LOVISUAL_CORS_ORIGINS is set but contains no origins");
}
Ok(AllowOrigin::list(
origins
.into_iter()
.map(|o| o.parse::<HeaderValue>().context("invalid CORS origin"))
.collect::<Result<Vec<_>, _>>()?,
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_origins_allows_any() {
assert!(parse_origins(&None).is_ok());
}
#[test]
fn empty_list_origin_string_fails() {
assert!(parse_origins(&Some(String::from(",,"))).is_err());
}
#[test]
fn invalid_port_fails() {
let result = Config::from_vars(None, Some("not-a-port".into()), None);
assert!(result.is_err());
}
#[test]
fn invalid_host_fails() {
let result = Config::from_vars(Some("not-an-ip".into()), None, None);
assert!(result.is_err());
}
#[test]
fn defaults_apply_when_empty() {
let config = Config::from_vars(None, None, None).unwrap();
assert_eq!(
config.listen_addr(),
SocketAddr::from(([127, 0, 0, 1], 8765))
);
}
}

View file

@ -1,57 +0,0 @@
//! Server assembly: router, listener, graceful shutdown.
use anyhow::Context as _;
use axum::Router;
use tokio::net::TcpListener;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
use super::routes;
use config::Config;
pub mod config;
/// Build the application router for a given config.
pub fn app(config: &Config) -> Router {
Router::new()
.merge(routes::router())
.layer(CorsLayer::new().allow_origin(config.cors_origins.clone()))
.layer(TraceLayer::new_for_http())
}
/// Bind and serve the application until a shutdown signal arrives.
pub async fn serve(config: Config) -> anyhow::Result<()> {
let listener = TcpListener::bind(config.listen_addr())
.await
.context("failed to bind listener")?;
axum::serve(listener, app(&config))
.with_graceful_shutdown(shutdown_signal())
.await
.context("server error")
}
/// Resolves on SIGTERM/SIGINT.
async fn shutdown_signal() {
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("failed to install ctrl-c handler");
};
#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to install SIGTERM handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}

View file

@ -3,7 +3,7 @@
# LoVisual # LoVisual
Minecraft utility client for PvP and automation. Minecraft utility client for PvP and automation.
Fork of [Combatant Client], built on ideas from LiquidBounce, Meteor, and ThunderHack. Developed and maintained by loki5512344.
![Java](https://img.shields.io/badge/Java-25+-orange?style=flat-square&logo=openjdk&logoColor=white) ![Java](https://img.shields.io/badge/Java-25+-orange?style=flat-square&logo=openjdk&logoColor=white)
![Fabric](https://img.shields.io/badge/Fabric-1.21.5-dimgrey?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczp3ZXQvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA2LjAtYzAwMCA3OS4xNjUyNzcsIDIwMjAvMTAvMTUtMTc6NDg6MzIgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiLz4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4AAAAAAAAAAAAAACnyRQAABO9JREFUSA2FVutvFFUUP+fOrB1K29JQWCAq0gei1PZBNk1KqWA0loaE+IVEfTDxP/iB+JHEL5r4hchfICFEYoiJD0yMCRqDNjWKmlhKS1sKxdLd7Ux3d86PM3f37uws7REmOTP3nnPu79x77jnnXohzrzj2dxN+dmME5OASIF8B8hUgR4HcDYATJCLHLly5A8gFQC4BcgLkOSAnQZ4M2TwBwSpx1MFSxFEF5Kx3JXKs4vvdDZBrAXINkG6Q00D2gDwDZBcifE2n62BcJIUAnwQYA5gEGAf4BOB9gI8AXgd4AuBxRERzWq7Vh/x69eKz5cpSq9ODiLgLEVdPALVqrx4B+ArgMmJ1FM92G5D77p94Ynx8fLBarQ4A/BcY1QE5CbAMsKLi/wjAz5ubm7+vra29sLW19ReA0gCqN0H2LyL2b9269RYiDiPiU8SFrgK0AtxsffO7X6+8sLy8/Eq1Wn0a5McAvU4H+RAg70HEeYiYQcQmREwjYh0AE4iYCMK1CHCv/eDA3qXfr/QNDw/fQ8TviKD7hYSU4mAQ4PdKpfLRhx9+eEsRtYi4JYQvIGLiEJEERJxAxH7LdgRgh7k2NtZ/+/Z0f7FY7EHEHxHxBRHADgAC0A/QaN+7t3r69OkjQ0NDMwBjAOqUjkQER4CMjoyM3N27dy8WCoVXhVYBnm3XWAXo3b1798Fms3k7l8s9CsDLV0XUXYBcR0Sy6enpS8YYSzQh4rVW63hBdqPRmAHAJEKIEFIADYhYo2lttVoXms3m/wB4+OF1Tjvn3PPnz8+k02kHEe8i4hn7WwC+DhQKuVxuUhGjKMIDRscTEHFPKTWZz+df9E1QSsmYu7i4eBQA/78uUPp7uZFIpZZqtYFsNot7e3v4+PFj3N3dxUqlgvl8Ppb4PwEixwAOAcDm5uY7S0tLr8XaKl6eTqexr68Pr127hoODgzg2Noa5XA7z+Tzm83kcGhrCa9eu4cDAgC8nGcbYOETEWxK01kLce2ZmZtznuxBXFvx8Po+lUgk/+OAD/Oqrr/Dzzz/Hjz/+GD/99FM8f/48njlzBpeWlnBpaQkzmQwCQNQK3KvWs2fPhr3FS8zn87i+vo4AKb/YOzs7+MUXX+Cff/6J8/PzOD09jW+88Qa+/fbb+MYbb+Dk5CTAexEMgMog0lAKQkH99ddfcWpqComkH374QcaDg4N4+vRpHBgYwGQyiblcDsfGxjCRSMi7D14CwEWA3RiP++7fvy/jnZ0d3N7elnGz2ZRxKpXCdDqNQgg8cOAA7NmzB5PJJGoNMQKwIT3wf29paUlgb2xsoLV2n3K5jLdv38YffvgBZ2Zm8Pvvv8dffvkFV1dXcWFhAQH4EiDHAHStVsNerycrT09P4/LysnP20qVLeOHChW4GJKVUKpUyxlAmk0GlFGqt/x8A7mGz2fRBEN68efOk/KPRaODc3Jxw5eeffw4ATr65uSnjnZ0dtNZiKpVCY0w3rTGNRgMB+BTAdACg1mKMYbVaLZbL5TMnTpyQ0N5++20sFAqyv7e3h0opyePR0VEsFAqYy+U4JiYf2d3de10p9bS8DgBypVKpVwaLRn0AOwD35H2ot3J+fp7T6TQXi0WenJzkr7/+mt99913+5JNPRBD/+9//5EuXLvGZM2fkTK1W+1Qp9VIQBHcAeA5gRSm1c/DgwesCbk4p9bTkrRDCGIMLCwsYhiGu/G+IcByr1Wo/m80WlVI7Us+fOw7HZ/KlAqSFEFgsFh9Lp9M7ADWAKUQ8Ii8Rx4mnbAaYU0pd10r/Z4zZFSLWnHP3bNu+KKR/EoJ3RA==) ![Fabric](https://img.shields.io/badge/Fabric-1.21.5-dimgrey?style=flat-square&logo=data:image/png;base64,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)
@ -21,7 +21,7 @@ Fork of [Combatant Client], built on ideas from LiquidBounce, Meteor, and Thunde
LoVisual is an open-source utility and PvP client for Minecraft. It provides configurable combat, movement, visual, automation, HUD, and UI features with a custom ClickGUI, theme system, and addon API. LoVisual is an open-source utility and PvP client for Minecraft. It provides configurable combat, movement, visual, automation, HUD, and UI features with a custom ClickGUI, theme system, and addon API.
This repository is a **fork** of [Combatant Client], which drew inspiration from [LiquidBounce], [Meteor Client], and [ThunderHack Recode]. The goal is to continue development, add new features, and maintain compatibility with the latest Minecraft versions. LoVisual is developed and maintained by [loki5512344](https://github.com/loki5512344). The goal is to keep adding new features and maintain compatibility with the latest Minecraft versions.
### Quick Start ### Quick Start
@ -77,7 +77,7 @@ This repository is a **fork** of [Combatant Client], which drew inspiration from
LoVisual — открытый утилитарный и PvP-клиент для Minecraft. Включает настраиваемые боевые, двигательные, визуальные и автоматизированные модули, кастомный ClickGUI, систему тем и API для аддонов. LoVisual — открытый утилитарный и PvP-клиент для Minecraft. Включает настраиваемые боевые, двигательные, визуальные и автоматизированные модули, кастомный ClickGUI, систему тем и API для аддонов.
Этот репозиторий является **форком** [Combatant Client], который в свою очередь черпал вдохновение из [LiquidBounce], [Meteor Client] и [ThunderHack Recode]. Цель — продолжать разработку, добавлять новые возможности и поддерживать актуальные версии Minecraft. LoVisual разрабатывается и поддерживается [loki5512344](https://github.com/loki5512344). Цель — добавлять новые возможности и поддерживать актуальные версии Minecraft.
### Быстрый старт ### Быстрый старт
@ -147,49 +147,6 @@ GNU General Public License v3.0. See [LICENSE](LICENSE).
## TODO ## TODO
### Фаза 1: Переименование (Rebrand) План разработки и актуальный статус ведутся в [TODO.md](TODO.md).
- [ ] Переименовать package `combatant` → `dev.loki.lovisual`
- [ ] Переименовать mod ID `combatant` → `lovisual`
- [ ] Сменить название клиента везде (Combatant → LoVisual)
- [ ] Обновить `fabric.mod.json`, `mixins.json`
- [ ] Обновить все нативные сообщения и debug-строки
### Фаза 2: Косметические изменения [loki5512344]: https://github.com/loki5512344
- [ ] Заменить `.cbcfg` → `.lvisual`, путь `config/combatant/` → `config/lovisual/`
- [ ] Префикс команд оставить `@`
- [ ] Новый splash/watermark, иконки, логотип
- [ ] Переписать `theme` под LoVisual-стиль
### Фаза 3: Рефакторинг
- [ ] Выкинуть ненужное: дебажные фичи, лишние интеграции (iris/sodium compat оставить)
- [ ] Разбить большие файлы (> 1500 строк): Scaffold, KillAura, AutoCrystal, Renderer2D
- [ ] Привести структуру к единому стандарту (нейминг, форматирование)
### Фаза 4: Модули
- [ ] Добавить модули из `ref/modules/` (Sprint, Velocity, AntiAFK, AutoRespawn, AutoFish, ...)
- [ ] Переписать IRC-модуль (WebSocket)
- [ ] Сделать облачные конфиги (CloudAPI)
### Фаза 5: Backend (Go)
- [ ] Написать бэкенд с нуля (Go + chi + SQLite)
- [ ] Auth (bcrypt, JWT), профили, друзья
- [ ] WebSocket хаб для IRC
- [ ] REST API для облачных конфигов
### Фаза 6: Лаунчер (Rust)
- [ ] Написать лаунчер с нуля (Rust + egui)
- [ ] Логин/регистрация, дашборд, настройки
- [ ] Авто-запуск Minecraft с модом
- [ ] Авто-обновление
### Фаза 7: Polish
- [ ] Звуки (49 звуков)
- [ ] Анимации (SpringAnimator)
- [ ] 6 стилей HUD
- [ ] Load-экран
- [ ] Главное меню (MainMenu)
[Combatant Client]: https://github.com/pivosos2007/combatant
[LiquidBounce]: https://github.com/CCBlueX/LiquidBounce
[Meteor Client]: https://github.com/MeteorDevelopment/meteor-client
[ThunderHack Recode]: https://github.com/Pan4ur/ThunderHack-Recode

View file

@ -20,7 +20,7 @@
### 1. Ребрендинг не закончен (пункт 2) ### 1. Ребрендинг не закончен (пункт 2)
- 288 файлов / **1464 упоминания** `combatant` в main-коде - 288 файлов / **1464 упоминания** `combatant` в main-коде
- Классы: `CombatantRenderSystem`, `CombatantRenderEngineBootstrap`, `CombatantRenderPipelines`, `CombatantVulkanBackend`, `CombatantFrameGraph`, `CombatantTerrainVertex` и др. - Классы: `CombatantRenderSystem`, `CombatantRenderEngineBootstrap`, `CombatantRenderPipelines`, `CombatantVulkanBackend`, `CombatantFrameGraph`, `CombatantTerrainVertex` и др.
- Заголовки файлов: «This file is part of the Combatant Client distribution» / `copyright (c) pivosos2007` - Заголовки файлов: «This file is part of the Combatant Client distribution» / копирайт изначального автора
### 2. Файлы-гиганты (пункт 3) — 12 файлов > 1500 строк ### 2. Файлы-гиганты (пункт 3) — 12 файлов > 1500 строк
| Файл | Строк | | Файл | Строк |

View file

@ -389,7 +389,7 @@ publishing {
repositories { repositories {
maven { maven {
name = "GitHubPackages" name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/pivosos2007/lovisual-client") url = uri("https://maven.pkg.github.com/loki5512344/lovisual-client")
credentials { credentials {
username = findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR") ?: "" username = findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR") ?: ""
password = findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN") ?: System.getenv("GH_TOKEN") ?: "" password = findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN") ?: System.getenv("GH_TOKEN") ?: ""

View file

@ -6,7 +6,7 @@ minecraft_version=26.2
loader_version=0.19.3 loader_version=0.19.3
# Mod Properties # Mod Properties
mod_version=0.1.1 mod_version=0.1.1
maven_group=pivosos2007 maven_group=dev.loki
archives_base_name=lovisual archives_base_name=lovisual
# Dependencies # Dependencies
# check this on https://modmuss50.me/fabric.html # check this on https://modmuss50.me/fabric.html

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */
@ -332,7 +332,6 @@ public class LoVisual implements ClientModInitializer {
// === ТИКИ === // === ТИКИ ===
ClientTickEvents.START_CLIENT_TICK.register(client -> { ClientTickEvents.START_CLIENT_TICK.register(client -> {
if (client.player == null) return; if (client.player == null) return;
if (!Lifecycle.isActive()) return; if (!Lifecycle.isActive()) return;
@ -377,7 +376,6 @@ public class LoVisual implements ClientModInitializer {
ctx.submitNodeCollector() ctx.submitNodeCollector()
)); ));
// === HUD 2D OVERLAYS === // === HUD 2D OVERLAYS ===
HudElementRegistry.addFirst( HudElementRegistry.addFirst(

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */
@ -436,7 +436,6 @@ public enum ConfigSerializer {
} }
} }
private static JsonLookup findJsonElement(ConfigObject obj, JsonObject root, String valueName) { private static JsonLookup findJsonElement(ConfigObject obj, JsonObject root, String valueName) {
if (root == null || valueName == null) return JsonLookup.missing(); if (root == null || valueName == null) return JsonLookup.missing();
JsonElement direct = root.get(valueName); JsonElement direct = root.get(valueName);

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,13 +1,12 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */
package dev.loki.lovisual.config.profile; package dev.loki.lovisual.config.profile;
import dev.loki.lovisual.features.theme.Theme; import dev.loki.lovisual.features.theme.Theme;
import dev.loki.lovisual.config.ConfigObject; import dev.loki.lovisual.config.ConfigObject;
import dev.loki.lovisual.config.values.ConfigValue; import dev.loki.lovisual.config.values.ConfigValue;

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,13 +1,12 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */
package dev.loki.lovisual.config.profile; package dev.loki.lovisual.config.profile;
import dev.loki.lovisual.features.theme.Theme; import dev.loki.lovisual.features.theme.Theme;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import dev.loki.lovisual.config.ConfigObject; import dev.loki.lovisual.config.ConfigObject;

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */
@ -20,15 +20,25 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
public final class ConfigProfileStorage { public final class ConfigProfileStorage {
public static final String EXTENSION = ".cbcfg"; public static final String EXTENSION = ".lvcfg";
private static final Path ROOT = ConfigPaths.profilesRoot(); public static final String LEGACY_EXTENSION = ".cbcfg";
private final Path root;
private final ConfigProfileBinaryCodec codec = new ConfigProfileBinaryCodec(); private final ConfigProfileBinaryCodec codec = new ConfigProfileBinaryCodec();
public ConfigProfileStorage() {
this(ConfigPaths.profilesRoot());
}
ConfigProfileStorage(Path root) {
this.root = root;
}
public static String sanitizeFileName(String name) { public static String sanitizeFileName(String name) {
if (name == null) return "config"; if (name == null) return "config";
String base = name.trim().toLowerCase(Locale.ROOT); String base = name.trim().toLowerCase(Locale.ROOT);
if (base.endsWith(EXTENSION)) base = base.substring(0, base.length() - EXTENSION.length()); base = stripExtension(base, EXTENSION);
base = stripExtension(base, LEGACY_EXTENSION);
if (base.isEmpty()) return "config"; if (base.isEmpty()) return "config";
StringBuilder out = new StringBuilder(base.length()); StringBuilder out = new StringBuilder(base.length());
for (int i = 0; i < base.length(); i++) { for (int i = 0; i < base.length(); i++) {
@ -42,12 +52,17 @@ public final class ConfigProfileStorage {
return out.length() == 0 ? "config" : out.toString(); return out.length() == 0 ? "config" : out.toString();
} }
private static String stripExtension(String value, String extension) {
if (value.endsWith(extension)) return value.substring(0, value.length() - extension.length());
return value;
}
public Path root() { public Path root() {
return ROOT; return root;
} }
public Path directory(ConfigProfileType type) { public Path directory(ConfigProfileType type) {
return ROOT.resolve(type.folderName()); return root.resolve(type.folderName());
} }
public List<ConfigProfileMeta> list(ConfigProfileType type) { public List<ConfigProfileMeta> list(ConfigProfileType type) {
@ -56,7 +71,7 @@ public final class ConfigProfileStorage {
Path dir = directory(type); Path dir = directory(type);
if (!Files.isDirectory(dir)) return out; if (!Files.isDirectory(dir)) return out;
try (var stream = Files.list(dir)) { try (var stream = Files.list(dir)) {
stream.filter(path -> Files.isRegularFile(path) && path.getFileName().toString().endsWith(EXTENSION)) stream.filter(path -> Files.isRegularFile(path) && hasProfileExtension(path))
.forEach(path -> { .forEach(path -> {
try { try {
ConfigProfileMeta meta = codec.readMeta(Files.readAllBytes(path)); ConfigProfileMeta meta = codec.readMeta(Files.readAllBytes(path));
@ -72,12 +87,16 @@ public final class ConfigProfileStorage {
return out; return out;
} }
private static boolean hasProfileExtension(Path path) {
String name = path.getFileName().toString().toLowerCase(Locale.ROOT);
return name.endsWith(EXTENSION) || name.endsWith(LEGACY_EXTENSION);
}
public ImportResult importProfileFile(Path source, boolean moveSource) throws IOException { public ImportResult importProfileFile(Path source, boolean moveSource) throws IOException {
if (source == null) throw new IllegalArgumentException("profile path is null"); if (source == null) throw new IllegalArgumentException("profile path is null");
if (!Files.isRegularFile(source)) throw new IOException("profile is not a regular file: " + source); if (!Files.isRegularFile(source)) throw new IOException("profile is not a regular file: " + source);
String fileName = source.getFileName() == null ? "" : source.getFileName().toString(); if (!hasProfileExtension(source)) {
if (!fileName.toLowerCase(Locale.ROOT).endsWith(EXTENSION)) { throw new IOException("not a " + EXTENSION + " or " + LEGACY_EXTENSION + " profile: " + source);
throw new IOException("not a " + EXTENSION + " profile: " + source);
} }
byte[] bytes = Files.readAllBytes(source); byte[] bytes = Files.readAllBytes(source);
@ -98,7 +117,7 @@ public final class ConfigProfileStorage {
} }
public ConfigProfileSnapshot read(ConfigProfileType type, String idOrName) throws IOException { public ConfigProfileSnapshot read(ConfigProfileType type, String idOrName) throws IOException {
Path path = file(type, idOrName); Path path = resolveExisting(type, idOrName);
return codec.read(Files.readAllBytes(path)); return codec.read(Files.readAllBytes(path));
} }
@ -116,8 +135,9 @@ public final class ConfigProfileStorage {
} }
public boolean delete(ConfigProfileType type, String idOrName) throws IOException { public boolean delete(ConfigProfileType type, String idOrName) throws IOException {
Path path = file(type, idOrName); boolean deleted = Files.deleteIfExists(file(type, idOrName));
return Files.deleteIfExists(path); deleted |= Files.deleteIfExists(legacyFile(type, idOrName));
return deleted;
} }
public ConfigProfileSnapshot rename(ConfigProfileType type, String idOrName, String newName) throws IOException { public ConfigProfileSnapshot rename(ConfigProfileType type, String idOrName, String newName) throws IOException {
@ -125,7 +145,7 @@ public final class ConfigProfileStorage {
ConfigProfileSnapshot old = read(type, idOrName); ConfigProfileSnapshot old = read(type, idOrName);
ConfigProfileMeta oldMeta = old.meta(); ConfigProfileMeta oldMeta = old.meta();
ConfigProfileMeta newMeta = oldMeta.withName(newName.trim(), System.currentTimeMillis()); ConfigProfileMeta newMeta = oldMeta.withName(newName.trim(), System.currentTimeMillis());
Path oldPath = file(type, oldMeta.getId()); Path oldPath = resolveExisting(type, oldMeta.getId());
Path newPath = file(type, newMeta.getId()); Path newPath = file(type, newMeta.getId());
if (!oldMeta.getId().equals(newMeta.getId()) && Files.exists(newPath)) { if (!oldMeta.getId().equals(newMeta.getId()) && Files.exists(newPath)) {
throw new IOException("profile already exists: " + newMeta.name()); throw new IOException("profile already exists: " + newMeta.name());
@ -137,24 +157,42 @@ public final class ConfigProfileStorage {
} }
public boolean exists(ConfigProfileType type, String idOrName) { public boolean exists(ConfigProfileType type, String idOrName) {
return Files.exists(file(type, idOrName)); return Files.exists(file(type, idOrName)) || Files.exists(legacyFile(type, idOrName));
} }
/** Canonical storage path — always uses the current {@link #EXTENSION}. */
public Path file(ConfigProfileType type, String idOrName) { public Path file(ConfigProfileType type, String idOrName) {
String id = sanitizeFileName(idOrName); String id = sanitizeFileName(idOrName);
if (!id.endsWith(EXTENSION)) id += EXTENSION; if (id.endsWith(LEGACY_EXTENSION)) {
id = stripExtension(id, LEGACY_EXTENSION) + EXTENSION;
} else if (!id.endsWith(EXTENSION)) {
id += EXTENSION;
}
return directory(type).resolve(id); return directory(type).resolve(id);
} }
private Path legacyFile(ConfigProfileType type, String idOrName) {
String id = sanitizeFileName(idOrName);
if (!id.endsWith(LEGACY_EXTENSION)) id += LEGACY_EXTENSION;
return directory(type).resolve(id);
}
private Path resolveExisting(ConfigProfileType type, String idOrName) {
Path canonical = file(type, idOrName);
if (Files.exists(canonical)) return canonical;
Path legacy = legacyFile(type, idOrName);
return Files.exists(legacy) ? legacy : canonical;
}
private void normalizeLooseProfiles() { private void normalizeLooseProfiles() {
if (!Files.isDirectory(ROOT)) return; if (!Files.isDirectory(root)) return;
List<Path> candidates = new ArrayList<>(); List<Path> candidates = new ArrayList<>();
try (var stream = Files.walk(ROOT)) { try (var stream = Files.walk(root)) {
stream.filter(Files::isRegularFile) stream.filter(Files::isRegularFile)
.filter(path -> path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(EXTENSION)) .filter(ConfigProfileStorage::hasProfileExtension)
.forEach(candidates::add); .forEach(candidates::add);
} catch (IOException e) { } catch (IOException e) {
DebugLog.error("Failed to scan loose config profiles %s", e, ROOT.toAbsolutePath()); DebugLog.error("Failed to scan loose config profiles %s", e, root.toAbsolutePath());
return; return;
} }

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of the LoVisual Client distribution. * This file is part of the LoVisual Client distribution.
* Copyright (c) 2026 pivosos2007. * Copyright (c) 2026 loki5512344.
* *
* Licensed under the GNU General Public License v3.0. * Licensed under the GNU General Public License v3.0.
*/ */

Some files were not shown because too many files have changed in this diff Show more