- feat(accounts): persist device links with opaque hashed tokens, list and revoke endpoints - feat(frontend): app shell, routing and landing page with the chat-command hero - feat(frontend): Cyrillic-first fonts (Unbounded, Onest, JetBrains Mono); add i18next and motion - docs: free mod, bilingual site, one-click download, theme editor, public profiles, rich landing in plans - feat(accounts): internal gRPC AuthenticateDevice guarded by internal key - feat(frontend): ru/en i18n with typed per-feature dictionaries and language switch - feat(accounts): GET /me profile endpoint - feat(gateway): scaffold crate with config validation and health check - feat(gateway): reverse proxy to accounts and configs services - feat(gateway): resolve identity once from access JWT or device token via gRPC - feat(gateway): per-route and global rate limits with Retry-After - feat(gateway): CORS for the site origin; docs for gateway and internal contract - feat(configs): scaffold service with schema, config validation and health check - feat(configs): four config slots per account with list, get and save - feat(configs): permanent share codes with regenerate and public load-by-code - feat(accounts): GetPublicProfiles gRPC for showcase author info - style(accounts,common): apply rustfmt to existing sources - feat(configs): public showcase with publish, browse, detail and copy-to-slot - feat(backend): public profile endpoint and showcase author filter - fix(gateway): silence clippy collapsible-if and needless-ref warnings - docs(backend): configs-service implemented; Подсистема 1 backend complete - feat(mod): add Optimize module skeleton with OptimizeState holder - feat(mod): gate glass blur behind Optimize no_glass knob - feat(mod): cut MotionBlur and DoF sample counts behind lite_post knob - feat(mod): trim procedural sky noise behind lite_sky knob - feat(mod): drop fade gradients and digit rolls behind lean_hud knob - docs(todo): mark Optimize module phase 9.2 complete - refactor(mod): drop dead Renderer2D compatibility shims - refactor(mod): prune unreachable Renderer2D overload towers - refactor(mod): remove unused Renderer2D overloads and imports - docs(todo): mark Renderer2D giant-splitting done (2179 to 1597) - refactor(mod): extract shader id constants from LoVisualRenderPipelines - docs(todo): record registry wave 2026-09-25 (Renderer2D, pipelines) - refactor(mod): move Renderer2D instance state into base class - refactor(mod): extract Renderer2DRounded drawing family - refactor(mod): extract Renderer2DPath connector and chamfer family - refactor(mod): extract Renderer2DShapes circle line and texture primitives - refactor(mod): extract Renderer2DGlass and Renderer2DItem families - refactor(mod): prune Renderer2D imports after facade split - docs(todo): record Renderer2D facade inheritance split (1597 to 475) - docs: easter eggs — .env honeypot, konami troll mode, devtools banner, IDDQD config, breakable 404 block, 418 teapot - feat(mod): introduce surface style system core (SurfaceStyle, StyleSpec, StyleConfig, SurfaceRenderer) - refactor(mod): delegate HudRenderUtil liquid glass draws to SurfaceRenderer (dedupe glass constants) - refactor(mod): route bespoke glass call sites through SurfaceRenderer.plateSpec - feat(mod): add Auto option to HUD bg effects via shared HudBgStyles resolution - feat(mod): flat fallback for no-glass optimize mode and persist global HUD config - feat(mod): default HUD bg effects to Auto so the global surface style drives widgets - feat(mod): add global cycle-style hotkey with surface style notification - feat(mod): add surface style swatch strip under the global style picker - feat(gateway): reject ambiguous paths and answer .env probes with a honeypot - fix(gateway): charge failed credentials against the rate limit, allow stale ones on /auth - feat(frontend): ClickGui theme pipeline generated from the mod, live site theming - feat(frontend): landing v2 hero — voxel/particle backdrop, live ClickGui, theme strip - docs(todo): drop the FPS A/B measurement from phase 9.3, close phase 9 - feat(gateway): answer /coffee with a 418 teapot - feat(frontend): land the rest of landing v2 — HUD, module wall, showcase, FAQ, footer - feat(frontend): one-click download from GitHub releases, changelog page, release CI - feat(frontend): theme editor with live ClickGui preview, mod-compatible export and share links - fix(frontend): landing HUD playground now shows real mod widgets (fps, coordinates, module list, keybinds, ping) - style(frontend): apply ClickGui glass effect to landing HUD playground widgets - fix(frontend): prevent color field row overflow in theme editor grid - fix(frontend): never attach stale bearer token to /auth/* requests - fix(configs): unpublish/publish can no longer bypass moderation - refactor(accounts): shrink auth/handlers.rs under the 250-line cap - fix(accounts): tolerate concurrent refresh without killing every session - fix(gateway): minor hardening from the backend review - feat(configs): IDDQD easter egg config
59 KiB
LoVisual Mod (combatant-client-26.2) — план переписи
Мод — форк Combatant Client, переписываем под LoVisual.
Правила (ОБЯЗАТЕЛЬНЫЕ, нарушение = откат)
- KISS / DRY / SOLID — простая структура, без оверинжиниринга
- Макс. 200 строк на файл (было >1500) — превышение = разбить на модули
- Макс. 4 файла на папку — больше = новая подпапка по смыслу
- Публичный API можно менять свободно (решение владельца 2026-09-05): KISS важнее обратной совместимости; при рефакторинге разрешено менять сигнатуры, удалять делегирование и мёртвый код, обновляя вызывающих
- Коммиты — на английском (решение 2026-09-23, действует с этого момента вперёд; более старые коммиты на русском не переписываем)
- Никакого статика —
Minecraft.getInstance()только лениво, никогда в поле - Никаких this-escape — конструктор не публикует
this - Никаких мёртвых миксинов — миксин только если реально нужен;
cancellable=trueтолько при реальном cancel - Без пустышек — файл без реализации не создаётся
- Каждый этап компилируется —
./gradlew buildперед переходом к следующему - Тесты обязательны для новых модулей (JUnit)
СТАТУС ПРОБЛЕМ (что вижу сейчас)
1. Ребрендинг не закончен (пункт 2)
- 288 файлов / 1464 упоминания
combatantв main-коде - Классы:
CombatantRenderSystem,CombatantRenderEngineBootstrap,CombatantRenderPipelines,CombatantVulkanBackend,CombatantFrameGraph,CombatantTerrainVertexи др. - Заголовки файлов: «This file is part of the Combatant Client distribution» / копирайт изначального автора
2. Файлы-гиганты (пункт 3) — 12 файлов > 1500 строк
| Файл | Строк |
|---|---|
| render/engine/renderer/Renderer2D.java | 3379 |
| features/gui/chat/BetterChatRenderer.java | 2870 |
| features/gui/clickgui/settings/UnifiedSettingRenderer.java | 2798 |
| features/module/modules/player/Scaffold.java | 2416 |
| features/module/modules/combat/AttributeSwap.java | 1922 |
| features/gui/clickgui/ClickGuiRenderer.java | 1900 |
| features/module/modules/visuals/NameTags.java | 1791 |
| features/module/modules/combat/ElytraTarget.java | 1711 |
| features/gui/hud/draggable/impl/TargetHud.java | 1687 |
| features/module/modules/visuals/ESP.java | 1520 |
| features/module/modules/combat/AutoCrystal.java | 1516 |
| features/gui/hud/nondraggable/impl/CustomBar.java | 1496 |
3. Runtime / JarReplacement (пункт 4)
runtime/(ClientRuntime, RuntimeGate, ClientRuntimeState, SOFT_PANIC/JAR_REPLACEMENT_PANIC, shutdown/resume-участники)runtime/loader/(JarReplacementCoordinator, LoaderBridge, PanicOnlyJarReplacementCoordinator)- Моё предложение: выкинуть. Живая замена jar в памяти — источник classloader-утечек, хрупкая, для клиента не нужна (обновление делает лаунчер). Заменить на простой Lifecycle.
4. Security (пункт 7)
features/security/BackdoorProtection— на деле защита от SSRF/translate-инъекций (порт ExploitPreventer, MIT). Полезное, ноenum-синглтон со статикой.features/relations/StaffTracker+StaffHeuristicsMatcher— детект «стафа» по эвристикам TAB. Сомнительно, решить оставить/выкинуть.
5. Тесты (пункт 6)
- 5 тестов на ~213k строк. Добавлять по мере переписи модулей.
Фаза 1: Ребрендинг (пункт 2)
- Восстановлен
config/-пакет (55+3 файла) изref/zip/combatant-client-26.2 (1).zip— он отсутствовал в форке, без него проект не собирался. Переименованcombatant.client.config→dev.loki.lovisual.config.compileJava✓ - Заменить
combatant→lovisualв main-коде (1464 вхождения, 288 файлов) - Переименовать классы
Combatant*→LoVisual*(23 шт., включаяIrisCombatantFrameHooks→IrisLoVisualFrameHooks) - Заголовки файлов → LoVisual, пересобрать копирайты (GPLv3 форка)
- Проверить
fabric.mod.json/lovisual.mixins.jsonна остатки - Проверить строки/сообщения/логи на «combatant»
- JNI-имена в native:
Java_combatant_client_→Java_dev_loki_lovisual_(34 шт.) + FindClass-пути
Проверка: rg -i combatant src → 0 ✓, ./gradlew build ✓ (1406 файлов изменено)
Фаза 2: Выкинуть Runtime/JarReplacement (пункт 4)
Вместе с рантаймом удалить и модуль Panic (
features/module/modules/misc/Panic.java), интерфейсRuntimeControlModule, командуRuntimeCommandи все ветки на них вModuleManager/Module. Panic — единственная реализацияRuntimeControlModule, без него они становятся мёртвым кодом.
- Удалить
runtime/иruntime/loader/(14 файлов) - Удалить
Panic.java+RuntimeControlModule.java+RuntimeCommand.java - Убрать ветки
instanceof RuntimeControlModuleиtickRuntimeControllers()из ModuleManager/Module - Убрать
RuntimeGateиз точек входа (LoVisual.java, модули, рендер) — заменён наLifecycle - Заменить на простой Lifecycle-класс (
dev.loki.lovisual.Lifecycle: INIT → ACTIVE → SHUTDOWN, гейтыcanRunRender/Hud/ShaderBridge+RenderResourceReadiness) - Panic-фичи удалены;
ModuleManager.disableAll()оставлен как простая утилита - Мёртвые cleanup-методы удалены (
shutdownForRuntime,clearRuntimeReferences,VanillaChatRuntimeCleanup, i18n-ключи panic/runtime)
Проверка: ./gradlew build ✓, тесты ✓ (13), rg runtime-подсистема → 0
Фаза 3: Разбивка файлов-гигантов (пункт 3) — ЗАВЕРШЕНА
Стратегия: публичный фасад исключён из лимита ≤200 (делегирует ~все публичные методы), вся логика вынесена в package-private final-хелперы в том же пакете (плоско), ≤200 строк каждый. 0 вызывающих файлов изменено. AttributeSwap/Scaffold/ElytraTarget/ AutoCrystal удалены в Фазе 3.5 — их разбивать не нужно.
- Renderer2D (3379) → фасад 2150 + 14 хелперов (Blur, Chamfered, Circles, Connectors, Items, Lines, LiquidGlass, Orbiz, Polygons, Rects, RectsExtra, RectsGlow, SmokeFill, Textures)
- BetterChatRenderer (2870) → фасад 210 + 21 хелпер (LayoutEngine, Viewport, FrameRenderer, MessageRenderer, GlyphRenderer, InputRenderer, Suggestion*, InputHandler, MouseClick, ContextMenu, Tooltip, Draw/Font/EditBox/Segment/CommandMerge + 3 model-файла)
- UnifiedSettingRenderer (2798) → фасад 208 + 24 хелпера (по типам настроек + цвет-редактор из 9 классов: Renderer, SquareRenderer, PresetRenderer/Handler, HexInput/State, Drag, Layout, Math)
- ClickGuiRenderer (1887) → фасад 542 + 15 хелперов (Input, Keyboard, RenderEngine, Lifecycle, Screen, TextBatch, DrawHelpers, KeyBind, Editor, PickerIcon, TopLayer, TabBar, TabState + Model + Fonts)
- NameTags (1791) → фасад 438 + 11 хелперов (Renderer, Plate, LayoutEngine, Face, Equipment, Opponent, Effects, LabelBuilder, EnchantFormatter, Cache + Model)
- TargetHud (1655) → фасад 340 + 11 хелперов (Renderer, TargetResolver, Panel, Face, Content, Text, Arc, Equipment, Effects, Formatter + Model)
- ESP (1538) → фасад 269 + 11 хелперов (EntityCollector, ShaderRenderer/Mask/Color, 2D/3D, SearchOverlay, Outline, MeshBuilder, ColorUtil + Model)
- CustomBar (1495) → фасад 426 + 11 хелперов (XpBar, JumpBar, Locator*×7, Resolver + Model)
Проверка: после каждого файла ./gradlew build ✓; хелперы ≤200, фасады исключены;
0 вызывающих файлов изменено во всей Фазе 3
Долг (файлы >200 вне списка гигантов, в т.ч. новые фасады):
Renderer3D 433, Module 1181, LoVisualRenderPipelines 1329, DraggableHudElementRegistry 1449,
Predictions 1363, DynamicIsland 1255, CustomHealthBar 412, CustomHotbar 547, HudNotifier 1116,
Triangulator 1102, BetterButtons 1033, Themes 1012, SvgMeshBackend 1014, UiPrimitiveRenderer 1042,
BetterTooltips 948, CustomTabList 829, InventorySwap 1009, ModuleManager 521, LoVisual 441,
а также многие HUD/module/rendering-файлы 200–800 строк. Разбирать по желанию.
Фаза 3.5: Удаление читерских модулей (легит-направление)
Пользователь решил: LoVisual — легитное приложение. Все читерские модули удаляются.
- combat: удалены AttributeSwap, AutoAnchor, AutoAttack, AutoBed, AutoBow, AutoCrystal, Backtrack, Criticals, ElytraTarget, Hitbox, KillAura, KTLeave, MaceKill. Оставлены: ProjectilePuncher, PvpCooldowns, Reach, RubberHand, SpearAssist, TPSSync
- movement: удалены ВСЕ модули (AirJump, AutoDodge, ElytraFly, ElytraHelper, Flight, Freeze, InventoryMove, NoFall, NoPush, NoStun, Parkour, Phase, ReverseStep, SafeWalk, Speed, Sprint, Step, Strafe, SuperFirework, TargetStrafe, Timer, Velocity, WindJump) — 22 файла
- player: удалены AirPlace, AutoArmor, AutoEat, AutoTotem, Scaffold, XCarry. Оставлены: AutoTool, ClickPearl, FakeLag, NoDelay, NoInteract, ShitDropper
- Осиротевшие хелпер-пакеты: удалены
combat/autocrystal/(17),combat/autobed/(9),combat/autoanchor/(9),util/block/scaffold/(10),BlockPlacer+ тест ScaffoldPlacementGateTest - Enum-заглушки:
KillAura.RaycastMode/RotationTiming/AngleSmoothMode→ локальные enum в ProjectilePuncher/Reach;Criticals.SelectionMode→ удалено;Hitbox-зависимость убрана из ESP - ESP переписан под легит: новые настройки
only_visible(LOS-луч от камеры),skip_invisible,only_armor(без брони не подсвечивать) — по умолчаниюtrue, только для игроков
Проверка: ./gradlew build ✓, тесты ✓ (13), остатки только в комментариях/строках
Фаза 4: Security (пункт 7)
- BackdoorProtection: убрать
enum-синглтон, сделать инстансный сервис, ленивыйMinecraft.getInstance() - Решить судьбу StaffTracker/StaffHeuristics — согласовать и выкинуть или переписать (решение: StaffHeuristics удалён, StaffTracker+StaffData оставлены, переведены на INSTANCE-паттерн)
- Покрыть логику фильтров тестами (BackdoorProtectionTest, 11 тестов)
Фаза 5: Модули — нормализация под правила
- Прочесать все 131 модуль: убрать
static final Minecraftв полях (6 шт. уже найдено) (LegacyHudNotifier, ScissorFunction, ShitDropper, ProtocolUtil, CooldownRender, ClickGuiRenderer.MC + 7 файлов-потребителей ClickGui-пакета — всё переведено на ленивыйMinecraft.getInstance()) - Проверить модули на
System.currentTimeMillis()→ заменить на игровое время (16 файлов модулей → новыйutil/time/GameClock.millis(), откат на стенное время вне мира) - Разнести addon/events/mixininterface по смыслу — ОТЛОЖЕНО: blast-radius 54/41/88 файлов-импортёров, чистый import-churn без функциональной ценности; делать только отдельным скриптовым проходом
Фаза 6: Тесты (пункт 6)
- Юнит-тесты: EventBus (12), конфиг (values: NumberValue/Boolean/String/Set/ItemIdSet/BooleanMap, ModeValue, EnumValue, RGB/RGBA color; profile: DiffEngine, ValueCodec), утилиты (LegacyTextUtil — 12). Игровые классы без моков: тесты только на чистую логику.
- По мере переписи модулей — тест на каждый (Текущий итог: 292 теста, все зелёные)
Фаза 7: QoL-модули из Delta (легит) — ПЛАН, обсуждаем
Референс:
ref/delta-26.2-main(чит-клиент, код обфусцирован — берём только идеи, переписываем под наши правила; у Delta 114 модулей, у нас 63). Уже перенесено: SeeInvisibles→InvisESP, HandsShader→Chams/Noise, AtmoDawnFog, ShulkerPreview, Animations, ItemPhysic (из WorldTweaks), HoldMyItems (фикс ordinal-бага, математика в EaseCurves). Реф-исходники: ref/hmi-src, ref/soupvisuals-src.
Решения по обсуждению (2026-09-05)
- Wasted — НЕ делаем
- TapeMouse — делаем (пользователь: AFK-фермы, не чит-софт)
- DeathCoords — делаем
- ItemScroller / Arrows — НЕ делаем
- ColorPlus ≈ наш PostFX — PostFX восстановлен и починен (дефолтный пресет был near-identity → добавлен Cinematic + тесты)
- Правило папок: максимум 4 файла (было 3)
Перенос (список пользователя)
- StreamerMode (misc) — маскирует ники-подобные токены и названия серверов в чате
(общая точка NameProtect: ChatComponentMixin.addMessage), скины заменяются на
стандартные (AbstractClientPlayerMixin.getSkin). Чистая логика —
util/text/NameMasker(тест NameMaskerTest). - LockSlot (player, ~36) — запрет выброса из выбранных слотов 1–9 (анти-случайный Q), опция «только в PVP», предупреждение в чат. Точка: перехват drop-пакета.
- AutoRespawn (player, ~28) — авто-возрождение после смерти (tick на death screen).
- AutoLeave (player) — выход по триггерам: низкое ХП, N игроков в радиусе
(друзья игнорируются опцией), задержка + предупреждение. Выход через
mc.disconnect(null, false); чистая state-machineLeavePolicy(AutoLeaveTest). - AutoAccept (player) — авто-ответ на ТП/клан-запросы: паттерны фраз (SetValue),
команда ответа, «только друзья» (PlayerRelations), задержка, кулдаун ≥500мс.
Пакетный путь: PvpChatEvent (handleSystemChat/handlePlayerChat), отправка
connection.sendCommand(...). Чистый парсерTeleportRequestParser(AutoAcceptTest). - SoundReducer (player, ~42) — РЕШЕНО: развитие NoSound, а не новый модуль.
Режимы Mute (старое поведение через ClientPacketListenerMixin) и Reduce —
новая громкость 1–100% через SoundEngineMixin (@Redirect getVolume в
SoundEngine.play). Чистая математика
NoSound.resolveScale(NoSoundTest). - Wasted (misc, ~100) — ОТМЕНЕНО (решение пользователя)
- ThirdPerson (player) — свободный обзор от 3-го лица по удержанию бинда (L, настраивается), движение игрока не поворачивается. MouseMixin.turnPlayer копит orbit-yaw/pitch, CameraMixin.update (TAIL) ставит камеру за игрока по орбите (+ cull-frustum). Орбиты сбрасываются, когда бинд отпущен.
- UseTracker (player) — уведомления: мои тотемы (N осталось), тотемы других, эффекты оппонентов (группа: invisibility/speed/strength/regeneration/fire_res/weakness). Пакетный путь: ClientboundEntityEventPacket (event 35) + ClientboundUpdateMobEffectPacket. Тест UseTrackerTest.
- NoServerPack (misc) — авто-отклонение принудительного серверного ресурспака (спрашивать/отклонять всегда). Точка: ClientCommonPacketListenerImplMixin.
- NoFriendDamage (combat, ~19) — отмена атаки, если цель — друг (PlayerRelations).
- TapeMouse — ОСТАВЛЕН (решение владельца 2026-09-08): автоклик по таймеру (Attack/Use) для AFK-ферм, интервал, опция «только при зажатии». Считаем допустимым исключением из Фазы 3.5 по явному решению владельца.
Кандидаты из отчёта (не в списке пользователя — обсудить)
- DeathCoords (~27) — координаты последней смерти в чат. Дёшево, полезно.
- ItemScroller (misc) — shift-перекладка предметов колёсиком в контейнерах:
QUICK_MOVE клик по слоту под курсором, иначе слот хотбара (опция).
Точка: AbstractContainerScreenMixin.mouseScrolled; чистая логика
ScrollMovePolicy(ScrollMovePolicyTest). - Arrows / off-screen индикаторы — ОТМЕНЕНО (решение владельца 2026-09-08).
Проверка после каждого модуля: ./gradlew build ✓, тесты на чистую логику ✓
Фаза 7.5: Команды — префикс % + автокомплит
- Префикс команд сменён
@→%(старый@принимается как legacy). Источник истины —features/command/CommandPrefix(CHAR/STRING/isCommandLike/strip); вызовы обновлены: CommandManager.handle/suggest, ScreenMixin (RunCommand), BetterChat (TabHandler, SuggestionState, SuggestionRenderer, ContextMenuHandler), usage-строки и RunCommand-ссылки всех команд (help/modules и др.). - Аргументный автокомплит дополнен usage-подсказками: если
suggest()команды не дал вариантов на позиции — показывается подсказка вида%toggle <module>(features/command/UsageHints, парсит<...>/[...]из usage-строки). Существующий UI автокомплита BetterChat (стрелки/Tab/клик) работает как раньше.
Проверка: ./gradlew build ✓, ./gradlew test ✓ (CommandPrefixTest, UsageHintsTest,
CommandDiscoveryTest обновлён)
Фаза 8: Структура «как у Delta» (маленькие файлы) — ПЛАН
Правило ≤200 уже есть (Фаза 1–3), но долг: ~240 файлов >200 строк. У Delta файлы компактные и слои разделены: api / client(features, ui, utils, processors) / inject(accessors, invokers, mixin). Наша пакетная структура сопоставима — проблема в размере файлов.
- Топ-гигантов к разбивке (после Фазы 3): LoVisualRenderPipelines 1527, DraggableHudElementRegistry 1449, Predictions 1363, DynamicIsland 1254, Module 1181, HudNotifier 1116, AltManager 1115, Triangulator 1102, TargetESP 1061, UiPrimitiveRenderer 1042
- Стратегия: как в Фазе 3 — фасад + package-private хелперы ≤200, 0 изменений вызывающих,
./gradlew buildпосле каждого; по мере касания файлов в Фазе 7 — разбивать на ходу - Facades >200 (Renderer2D 2150 и т.п.) — оставить как есть (исключение по правилам)
- Не смешивать с Фазой 7: разбивка структуры — отдельными проходами, иначе diff-каша
Проверка: ./gradlew test зелёный
Фаза 8.5: Аудит структуры (правила ≤200 строк / ≤4 файла) — ПОЛНЫЙ СПИСОК НА РЕФАКТОРИНГ
Снимок 2026-09-09 (
src/main/java, 1459 файлов / 197k строк). Гейт перед Фазой 9.2 (Optimize): движковые файлы, которые Optimize будет трогать, должны быть разбиты заранее. 9.1 (мёртвые грузы) от этого НЕ зависит — идёт своим порядком. Стратегия та же, что Фаза 3/8: фасад + package-private хелперы ≤200, 0 правок вызывающих, билд после каждого файла. Фасад-исключение (Renderer2D) — см. оговорку ниже.
Итоговая картина
- Файлы >200 строк: 244 шт. → >800 строк: 26 | 400–800: 78 | 201–400: 140
- Папки >4 файлов: 86 шт.
- Разбивка больших файлов по зонам: features/gui — 73 | render/engine — 54 | features/module — 35 | util — ~35 | mixins — 3 | config — ~6 | прочее — остальное
8.5.1 Папки >4 файлов — топ должников (разбить на подпапки по смыслу)
mixins/— 108 файлов → leaf-пакеты (entity/blaze3d/input/world/network/gui/hud/render), mixins.json перегенерирован, FQN-строки LoVisualMixinPlugin обновлены. Коммиты 94d5af7, 3c76817.mixins/accessors/— 45 → 12 групп (entity/renderer/world/state/packet/inventory/...). Коммит 3c76817. Также разбиты iris/sodium/security на подпакеты.features/module/modules/visuals/— 67 → esp/nametags/hats/effects/weather/scene/camera/ player/items/fx + lyrics/lrc. Конфиг-id НЕ менялись (проверено). Коммит 073ac26.features/gui/clickgui/settings/— 51 → core/bool/mode/slider/text/text-list/group/bind/ color(+render/preset/hex)/i18n/render/skin/owner/cooldown/util. Коммит 9db1fec.features/gui/hud/draggable/impl/— 40 → stats/metrics/status/combat/panels/targethud(+/render, render/parts)/scripted + hudnotifier/model, targethud-модель свёрнута в класс с record-ами. Коммит 6b89e40.events/impl/— 32 → input/movement/world/network/entity/pvp/render/player; events/ верхний уровень: core(Event,EventBus,Events) + meta(EventHandler,UsedImplicitly).features/gui/chat/— 31 →render/,suggestions/,menu/,model/.render/engine/uniform/impl/— 28 → по потребителям (uniform/impl/pipeline/,uniform/impl/effects/).render/engine/renderer/ui/— 28: верхний уровень разнести по уже существующимdraw/,runtime/, добавитьblur/,batches/.render/engine/text/— 24 иtext/backend/— 12 →text/разбить наtext/render/,text/layout/,text/msdf/.render/engine/renderer/ui/runtime/script/— 23,ui/draw/— 23 → внутри по 4-файловым кластерам.features/gui/hud/nondraggable/impl/— 20 (+impl/tab/) →impl/overlays/,impl/tablist/.features/gui/clickgui/— 20 на верхнем уровне → всё в существующиеlayout/,sections/,picker/,protocol/,util/.render/engine/command/— 17,render/helpers/— 16,features/command/impl/— 16,config/values/— 16 → по 2-3 подпапки (типам:values/numeric/,values/color/…).render/engine/profiler/— 14,modules/player/— 14,features/gui/hud/— 14,addon/— 14 — средние: раскидать при касании.- Хвост (5–13 файлов, ~55 папок):
util/pvp13,util/projectile13,render/iris13,config/profile13,features/module/misc12,features/module12,config12 и т.д. — ЗАКРЫТ выделенным проходом (коммиты 8846d53…303b17e, волна 8.5.1 завершена). Снимок после волны: 0 папок >4 файлов вsrc/main/java(скан 2026-09-10). Замечания: command-discovery рекурсивный (ClassGraph acceptPackages), конфиг-id и ключи i18n не менялись; package-private члены, ставшие кросс-пакетными, расширены до public/protected; svgMsdfTool-импорты починены после split svg.
8.5.2 Файлы-гиганты >600 строк — приоритетная очередь разбивки (53 файла)
Формат: файл (строк) → идея разбивки. Порядок сверху вниз. Facade-оговорка: Renderer2D остаётся фасадом-делегатом (правило Фазы 8), но его вспомогательные классы (kawase/blur/glass) — вынести из OrderedUiBatcher (935) в
ui/blur/.
- Renderer2D 2179→1597 → ГОТОВО 7b848ddd+464f7868+098c5515 (2026-09-25): ФАСАД;
далее 1597→475 наследованием (волна 2026-09-25 ниже)
(исключение по правилу Фазы 8) — делегирование оставлено; −234 мёртвые шимы
совместимости, −271 недостижимые перегрузки liquidGlass/item/Corners,
−115 прочие мёртвые перегрузки+импорты; макс. тело метода 35 строк (блоков >200 нет),
хелперы в
renderer/renderer2d/(Rects2D/Strokes2D/…), build+test ✓ - LoVisualAltManagerScreen 1113 → ГОТОВО (2026-09-10): 11 файлов
mainmenu/alt/(Screen 132, Controller 200, Ops 59, Anims 108 + auth/ 171 + render/ 157/156/139/100 + render/cards/ 178/166), все ≤200, build+test ✓ - UiPrimitiveRenderer 1053 → ГОТОВО (2026-09-10): фасад 188 (коннекторы) + render/primitive/ 4 (Context 153, Props 158, Painter 145, GlowPainter 147) + render/rect/ 2 (RectPainter 150, CornerPainter 150), build+test ✓
- BetterButtons 1032 → ГОТОВО 7e86f8d: 388 + 7×≤200 (state/hover/parallax/queue/render) — extraction+tooltip+render →
hud/nondraggable/impl/buttons/ - InventorySwap 1031 → УДАЛЁН 5acafc8: чит-инфра, заменён ShitEsp + хотбар-only — по стратегиям свапа; чистая логика отдельными классами с тестами
- SvgMeshBackend 1014 → ГОТОВО c11ebe3/aafe7de: 1020→267 + Parser181/Geometry173/Texture223/Render35/Cache25 — parser/triangulate/cache разнести
- Themes 1012 → ГОТОВО e51a610: 1012→562 + 3×163 пресета — реестр тем в
theme/presets/(по 4 темы/файл), дефолты отдельно - WorldParticles 991 → ГОТОВО 1d757a6+5a463d7e: 999→821→260 (фасад) + ambient/render/ 3×≤182
(Batches/Bubble/Funnel) + ambient/particle/ 197 + geometry/ доработаны (offset/multiplyAlpha/STACKS) —
типы частиц в
visuals/effects/ambient/ - Module 989 → ГОТОВО c8da336: 1031→139 + 4×≤134 (ext/) — базовый класс: settings-механику вынести в
module/ext/(осторожно, общий предок!) - LoVisualAddonManagerScreen 989 → ГОТОВО de56b4b9: 989→112 + 10 файлов mainmenu/addon/ (Controller 164, Ops 112, PanelAnims 56, auth/AddonScanState 52 + render/ 115/162/183 + render/cards/ 79/88), все ≤200 — по образцу AltManager
- Chams 976 → ГОТОВО 78b5335: 985→129 + 4×≤195 — режимы рендера в подклассы-политики (фасад модуля тонкий)
- ClickGuiPickerState 962 → ГОТОВО 234f0d9: 970→166 + 4×≤140; CombatProtocolHeuristicsEditorState 932→79 +4×≤192 03ae4a6 — state/actions хелперы
- ModulesMenuScreen 960 → ГОТОВО 234f0d9: 966→145 + 4×≤178 — рендер категорий отдельно от состояния
- UiMeshGeometry 954 → ГОТОВО sub-agent: 956→99 + 4×≤135 — по примитивам геометрии
- BetterTooltips 953 → ГОТОВО 0b8ea41: 960→181 +4×≤199; OrderedUiBatcher 935 → ГОТОВО 6e0bf7f: 944→188 (→ batches + blur), Scoreboard 924, Rain 893 → ГОТОВО sub-agent: 903→159 +4×≤162; CompactHudStatModel 893 → ГОТОВО 8979140: 894→125 +4×≤175; TazikHat 859 ✓ ✓ закрыт портированием в Cosmetics built-in (2026-09-11, см. Фазу 7), ShaderPatchCompiler 843 → ГОТОВО 2b55fbc: 843→66+4×≤174; OnlineRelationPlayerPickerComponent 837→74+4×≤192 17fc665; CustomTabList 830 → ГОТОВО 9e9b71d: 834→136+4×≤166, GameRendererMixin 830 → ГОТОВО f45f46e: 830→344 (только хуки+Host/BobInvoker) + mixins/render/main/handlers/ 4×≤200 (ProjectionHelper/WorldEngine/WorldPost/WorldDebug), Predictions 805 → ГОТОВО (см. хвост)
- Хвост 400–800: ЧАСТИЧНО — ConfigProfilesComponent 799→125 d95bfec, HitEffect 770→120 5e1ce05, PlayerMovementSimulation 757→157 4b253d1, ProjectilePuncher 747→104 fba4f7d, MediaPlayer 739→174 8ce3927, Crosshair 737→143 4cf0970, Itemizer 699→193/Cooldowns 707→151 11245ac, Potions 617→176 2e4912a, Admins 702→118 f18cb96, Keybinds 611→157 cc18feb, ModuleList 499→181 52327dc, Radar 392→161 f580373, Fps 309→168 5df2cc0, Memory 304→151 de7cc2b, Tps 339→154 e167f0c, Ping 309→145 15d04cb, SpeedBps 334→156 b78a638, GameTime 314→150 922d3bc, SystemTime 300→136 3cb6e40, Coordinates 337→174 9412982, Armor 324→162 932639d, Inventory 476→153 f4124f4, BlockHighlight 692→271+4×≤200 9f76e5a5 (scene/highlight/), см. /tmp-снимок в истории команд: ConfigProfilesComponent 791, HitEffect 770, PlayerMovementSimulation 755, ProjectilePuncher 746, MediaPlayer 739, Crosshair 737, MeshBuilder 729, SettingsPanelComponent 703, Admins 702, Cooldowns 701, Itemizer 699, LocalPlayerMixin 687, Trails 681, ItemBatchRenderer 673, CosmeticModel 671, MenuScreen 667, GlStencilFramebufferSupport 663, ConfigSerializer 663, DynamicIsland 661, ReimaginedVisual 660, RelationsComponent 659, UiStyle 640, Potions 617, ModuleComponent 612, MsdfFont 606, Keybinds 603, MediaSessionService 594 и др.)
- Волна 2026-09-24 (HUD/settings): NameTags 453→223 (nametags/impl/ Keys+Collector+Projection) b1414d7a, MainSettingsComponent 448→291 (+Scroll+Model) 3f0d452f, LinuxMediaSession 441→148 3d11bb4e, CustomTextRenderer 229→195 (GradientTexts), CustomBar 438→185 (+CustomBarModel константы +bars/impl/BarSettings, forwardеры удалены, mixins правлены на реальные рендереры) 4e442fb7, CustomHealthBar 406→146 (+bars/impl/HealthBarPainter+HealthScriptRenderer, dead-code удалён) a3415435. bars/ закрыт: все файлы ≤200, impl/ = 3 файла.
- Волна 2026-09-25 (реестры): Renderer2D 2179→1597 (см. строку 8.5.2 выше, 7b848ddd+464f7868+098c5515), LoVisualRenderPipelines 432→311 9577ed3d (SHADER_* 105 констант → pipeline/ShaderIds 130, мёртвые импорты удалены; логика ≤26 строк, фасад-исключение), build+test ✓.
- Волна 2026-09-25 (наследование фасада): Renderer2D 1597→475 (leaf), цепочка Renderer2D extends Item(124) extends Glass(183) extends Shapes(313) extends Path(245) extends Rounded(315) extends Base(23); API вызывающих не менялся (0 правок callers, static-import ITEM_OVERLAY_* и Renderer2D.LiquidGlassPreset работают по наследству); renderer/facade/ = 4 файла, facade/shape/ = 2 ✓ лимит; Base.self() для this→Renderer2D-делегатов; семейства >200 — фасад-части (исключение TODO:Facades); 37c7c410+64e23ef8+9ae72eeb+2b8c40a3+6204017b+ 9fffc58d, build+test+checkstyle ✓.
8.5.3 201–400 строк (~140 файлов) — проход «касаться = чинить»
- Не делать отдельным марафоном: при правке файла по другой фазе — первым коммитом разбивка на ≤200, вторым — целевое изменение (иначе diff-каша, правило Фазы 8)
8.5.4 Порядок и критерии готовности
- Очередь: 8.5.1 крупные папки (mixins, visuals, clickgui/settings, hud impl) → 8.5.2 гиганты >800 → далее по убыванию. Каждая папка/файл = отдельный коммит. → 8.5.1 ЗАВЕРШЕНА (2026-09-10): 0 папок >4 файлов.
- После каждого шага:
./gradlew build✓,./gradlew test✓; конфиг-id модулей и ключи сеттингов НЕ ломаем (совместимость .lvcfg); скрипты-переименования пакетов не трогают string-literals id — проверять grep'ом после moves. - Критерий закрытия для старта 9.2: ноль файлов >200 в путях, которые трогает Optimize (Renderer2D-секции blur/glass, OrderedUiBatcher, HUD-имплементации bgEffect, MsdfFont/CustomTextRenderer), и ноль папок >4 в этих же пакетах. Полный ноль по всем 244/86 — долгий хвост, 9.2 им не блокируется. → 2026-09-24: MsdfFont 155 ✓, OrderedUiBatcher 180 ✓, CustomTextRenderer 229→195 ✓ (glyph-pass вынесен в GradientTexts). Остаток гейта — bgEffect-HUD >200: TargetHud 333, Triangulator 304, Scoreboard 233 — ВСЕ фасады Фазы 3 с helper-пакетами (targethud/render/, triangulator/{render,Solver,Tracker,View}, panels/scoreboard/) → исключение по правилу фасадов. ГЕЙТ 9.2 ЗАКРЫТ.
- Обновить этот раздел при подходе к волне (отмечать [x], доснимать счётчики).
Фаза 9: Оптимизация FPS (аудит 2026-09-09) — ПЛАН
Диагноз: у клиента есть «налог каждый кадр», который платится даже при выключенных модулях (друг: просадка FPS на чистом конфиге, Windows). Решение владельца: мёртвые грузы — ЧИНИМ в коде; авто-деградации по FPS НЕ ДЕЛАЕМ; вместо неё — ручной модуль Optimize (включил → порезали стекло/blur/тяжёлые проходы).
9.1 Мёртвые грузы (чиним в коде всегда, независимо от модулей) — приоритет
- glGetError на каждом render pass — ГОТОВО 2026-09-11 (коммит 137121e): гейт по clip-active || stencil-debug —
GlStencilShapeClipBackend.endRenderPass(:140) иbeginRenderPass(:98/:104/:110/:124) дёргаютcheckGlErrors(:428-434) на КАЖДЫЙ проход (миксинCommandEncoderMixin:102-112висит на submitRenderPass → это и все ванильные/Sodium проходы).glGetError= синк с Windows-драйвером, несколько мс/кадр. Фикс: проверять только еслиmode != DISABLED || renderPassAttachmentRequired, либо под dev-флагом; в релизе — no-op. lovisual$isVulkanBackend()на каждый submitRenderPass — ГОТОВО (c9a65b1), кэш Boolean (CommandEncoderMixin:66-74) —getDeviceInfo().backendName().toLowerCase()заново на кадр×проходы. Фикс: вычислить один раз, закэшировать boolean.- PreTranslucentDepth — полноэкранный depth-copy каждый кадр без нужды — ГОТОВО (b747b47): demand-флаг requestForNextFrame()/takeRequested() у потребителей getDepthView(For), capture в vanilla- и Iris-путях по запросу (1-кадр задержка, фолбэк на live view) —
LevelRendererMixin:275-283гейтится только «нет Iris». Фикс: гейтneedsPreTranslucentDepthCapture()по образцуWorldSceneDepth(LevelRendererMixin:160-218); потребители:PostProcessGraphResources:61,LoVisualVisuals:79,WorldPassCompiler:174. - copyMainColor каждый кадр ДО гейта — ГОТОВО (118eb2a): ранний return по liquidGlassBlurRequested —
UiBlurResources.captureWorldSource(:116-123): полноэкранный color-copy делается до проверкиliquidGlassBlurRequested(:122). Фикс: переставить — copy только когда blur реально запрошен. - Строки профайлера при выключенном профайлере — ГОТОВО (aa55c4d): 2-arg scope()/section()/postPass(prefix,suffix) в ProfilerPhase/RenderProfiler2D/3D/RenderCostProfiler (DEV-гейт конкатенации) — конкатенации на call-site
каждый кадр:
PostProcessGraph:59("graph:" + phase),ModuleManager:219-221/:290("3d:module:" + m.name()). Фикс: ленивые supplier'ы / ранний isEnabled-check. - Мелочь, по ходу:
new PoseStack/new Matrix4f×2/new Frustum×2 в кадре (GameRendererMixin:203-239/:341-347/:609-617) — закэшить переиспользуемые копии. Frustum-часть отменена (da6525c): @Invoker("calculateFrustum") не резолвится на remapped-рантайме = краш; scratch-кэш матриц/PoseStack остался.
9.2 Модуль Optimize (misc) — ручной рубильник экономии
⛔ ГЕЙТ: не начинать, пока не закрыта Фаза 8.5 (рефакторинг-аудитStructure ниже) — Optimize трогает Renderer2D/OrderedUiBatcher/HUD-имплементации, все они >200 строк. Идея владельца: один тумблер «хочу максимум FPS». НИКАКОГО автовключения по FPS. Пока активен — движок и HUD работают в лёгком режиме.
- Каркас — ГОТОВО ade82f06:
modules/misc/optimize/Optimize.java(в подпапке — лимитcheckFolderLimit=4 файла вmisc/), 4 bool-тумблера (default on), тестOptimizeTest. Отклонения от плана при реализации: -no_glass— 9246e010: не мутим bgEffect (конфиг элементов неизменяем) и не ищемprepareSharedBlurInternal(метода больше нет). Гейты:Blur2D.source()→ null при no_glass (вместе сFpsOptimizer.isGlassDisabled→ все blurRect/glassBlurRect мимо) + ранние return вUiBlurResources.requestLiquidGlassBlur/requestBeforeNextShapeClip(флаги не ставятся →captureWorldSourceearly-out → нет полноэкранного copy, kawase и prewarm). Побочный эффект: гасит и низкоконтрастный blur текста (осознанно — «все glass/blur off» по замыслу тумблера). -lite_post— 52463246: MotionBlurSHADER_TAPS=11 → LITE_TAPS=5(план «15→5» устарел — тапов уже 11),historyValid = hasHistory && !lite(temporal-история off = ещё ~10 neighborhood-fetch'ей в шейдерный early-out; цена: хвостов истории нет, остаётся чисто пространственный блюр). DoF:taps = lite ? LOW : quality(LOW=8; план «32→8» устарел — квалити 8/14/22). -lite_sky— c403cb53:SkyboxShaderPasses.LITE_SKY_LAYER_MASK=0xF00F(базовые слои 0-3 + звёзды 12-15, чистит nebula/curtains/bursts/polar/water/…),layerMask &=при включённом тумблере; вreimagined_skybox.frag4 безусловных fbm + bursts-блок обёрнуты в layer-гейты; pulsar/thunder fbm 4→2 октавы черезu_Thunder.w(ранее всегда 0). ТестSkyboxShaderPassesTest, все 3 шейдера проходятglslangValidator -S frag. -lean_hud— 8746074d:GradientTexts.horizontalFade→ plain-текст (без per-glyphnew Stringи gradient-лямбд; marquee-clipped — clip остаётся, fade-градиенты гаснут) в Custom и Vanilla-реализациях; Fps digit-roll off (FpsAnimator.widerText/digitProgress→ settled-прогресс). Не в scope:GradientTexts.gradient/quadGradient(визуал списка модулей), аллокация вHorizontalFade.glyphAdvance(её просто обходит lean-гейт). - Механизм «как движок узнаёт» — ГОТОВО ade82f06:
render/engine/optimize/ OptimizeState.java(enum-utility, 4 volatile boolean,apply/reset, чистый статик без состояния модуля), движок читает геттеры, модуль пушит в onEnable/onFrame. - Взаимодействие с 9.1 — ГОТОВО: фиксы 9.1 не завязаны на Optimize и продолжают работать всегда; Optimize только добавляет гейты сверху.
- i18n en_us + ru_ru — ГОТОВО ade82f06:
module.optimize.*+setting.optimize.*(4 ключа, симметрия en/ru проверена). - Нотификация при первом включении — ГОТОВО ade82f06: once-флаг в инстансе модуля +
Notifier.info("Optimize: " + summaryOf(...))со списком включённых тумблеров.
9.3 Проверка
./gradlew buildпосле каждого пункта;./gradlew testзелёный — выполнялось после каждого из 5 коммитов 9.2 (итог: build+test зелёные на 8746074d).- Порядок: 9.1 (по одному пункту на коммит) → 9.2 — 9.1 закрыта (коммиты выше), 9.2 закрыта ade82f06..8746074d.
- A/B-мерка до/после 9.1 на чистом конфиге — УБРАНА из плана (решение владельца 2026-09-25): требует запуска Minecraft на Windows, задачей не является.
Фаза 10: Заход по референсам из Downloads (2026-09-15)
Пользователь скинул 5 архивов, распакованы в
/tmp/lv_refs/. Порядок выполнения — сверху вниз, по одному коммиту на пункт,./gradlew build+testпосле каждого.
- TrollfaceMask — почини "не работает" (
/tmp/lv_refs/trollface/TrollfaceMask.java, референс Moonward 1.21.4). Наш модуль:trollface/TrollfaceMask.java. Причина бага: биллборд ориентировался к камере (headPos→camPos); от первого лица камера почти совпадает по вертикали с головой → view≈(0,1,0) → right=view×up вырождается в 0 → плоскость ложится горизонтально, не видна. Режимы «Друзей»/«Всех» брали толькоTargetManager.getTarget()(сущность под прицелом) — маска рисовалась максимум на одном игроке. Фикс: ориентация поgetViewVector()цели вместо камеры, переборmc.level.players()с фильтром по режиму (себя/friend via PlayerRelations/все). Коммит e7e0ff2. - Rain — сравнение с двумя референсами (2026-09-15, вывод: у нас уже паритет/лучше).
Референс 1
/tmp/lv_refs/rainfull/(Cataclysm client) — нашRain.java(894 строки) уже является портом этого самого класса (Drop/Splash/Droplet/Mist, presetDensity/Wind/Mass, drawStreaks/Ripples/Crowns/Droplets/Mist — 1:1 совпадающие константы и формулы), ПЛЮС у нас уже есть их wet-surface screen-space постфх (WetSurfaceUniforms+ шейдерassets/lovisual/shaders/wet_surface.frag, pipelineWET_SURFACE, реализован какPostProcessPassв самом Rain.java) — то, что в референсе вынесено в отдельныйWetSurfaceRenderer. Улучшать нечего, разница только в деталях реализации пайплайна. Референс 2/tmp/lv_refs/mokriyzhir/(WetWorld.java, пакетez.redux...) — это ОТДЕЛЬНЫЙ самостоятельный модуль "Wet World" из чужого чит-клиента (Category.VISUALS), НЕ часть дождя: мочит поверхности всего мира по реальной погоде сервера (world.getRainGradient), с настройками Quality (Low/Medium/High/Ultra), Reflection Strength, Wet Darkening, Ripples+Speed, Rain Only, учитывает fog (интеграция с их модулем Fog: цвет/дистанция/яркость подмешивается в шейдер). У нас такого отдельного всегда-активного "мир мокрый по факту дождя" модуля нет — наш wet-surface жёстко завязан на модуль Rain (работает только когда сам Rain включён). Если захочется — можно вынести wet-surface-pass Rain.java в отдельный модуль WetWorld с привязкой кmc.level.isRaining()+getRainLevel(tickDelta)вместоonlyWhenRainingгалки, плюс quality- пресет и fog-интеграцию. НЕ делаю сейчас — не запрошено явно, чисто идея на будущее. - WetWorld — сделан отдельным модулем (2026-09-15), шейдер Rain пофикшен.
Третий референс
/tmp/lv_refs/wetworld2/(ru.white...WetWorld+WetWorldPipeline+wet_world.vsh/fsh) оказался технически лучшим из трёх: настоящий SSR с бинарным рефайнментом пересечения, dithering шага луча, реконструкция нормали по соседям в буфере глубины (не screen-space derivative), fbm-небо с sun specular, fog-интеграция. Проверка нашегоwet_surface.frag(«может кривые») подтвердила баги: 1)normalFromDepthсчитался черезdFdx/dFdy(viewPos)— рвётся на границах глубины, шумная/блочная нормаль -> дрожащий край лужи; 2) рябь была МЁРТВЫМ КОДОМ:vec2 perturb = vec2(dFdx(ripple), dFdy(ripple)) * 0.0;— умножение на 0 полностью гасило эффект, а сама рябь добавлялась как сырое значение волны вместо её наклона. Исправлено (коммит 0986ce6): нормаль — по 4 соседям depth-буфера (как в лучшем референсе), рябь — аналитический градиент (разность высоты в соседних точках), добавлен bin-рефайнмент SSR-пересечения + dithering, небо дополнено fbm-облаками/sun specular, затемнение вынесено в отдельный uniform-параметр (было зашито константой 0.82). Новый модульvisuals/weather/WetWorld.java: мочит мир по фактуmc.level.getRainLevel(), независимо от партиклов Rain и тумблераwet_ground— Quality (Low/Medium/High/Ultra -> шаги/дистанция/bin-рефайнмент SSR), Reflection Strength, Darkening, Ripples+Speed, Rain Only. Переиспользует общийWET_SURFACEpipeline/WetSurfaceUniforms(при одновременном включении с Rain.wet_ground эффект стекается — задокументировано в коде). Fog-интеграция референса НЕ перенесена (нет доступа к чужому Fog-модулю/данным тумана в нашем движке в этом заходе) — можно добавить отдельно, если понадобится. - HitEffect — интегрирован ImpactRing (2026-09-15).
Референс
/tmp/lv_refs/myasomaybe/ImpactRing/—JumpWaveRenderer/JumpGlowRendererиспользуют захват сцены + собственный низкоуровневый RHI-пайплайн с SSR-варпом (кастомный шейдер сэмплит скопированный цветовой буфер по кольцу) — тяжеловесная техника не в духе нашего движка. Взята идея визуала (кольцо + вертикальная стена свечения в точке удара), реализована через уже существующий движок: обычные аддитивные квады через MeshBuilder/Renderer3D (WORLD_COLORED_ADDITIVE+WORLD_COLORED_ADDITIVE_DEPTH), без захвата сцены и отдельного шейдера — консистентно с уже существующими WaveEffect/HitBubble в том же файле. НовыйEffectMode.IMPACTв HitEffect: настройки impact_radius/ impact_duration/impact_glow_height, срабатывает как WAVE (по удару), рендер — расширяющееся кольцо (сегменты, fade к центру) + полупрозрачная вертикальная стена по контуру круга, затухающая вверх и во времени. Коммит 61c478e../gradlew build+test✓. - HoldMyItems — сверка с HMI.zip (2026-09-15), портирован grip.
Референс
/tmp/lv_refs/hmi/(Delta HMI): обёртка обфусцированного чит-клиента над СТОРОННИМ jar-модомholdmyitems-5.1.1+26.2.jar+ своиSwingAnimations(десяток стилей свинга, weapon-inspect a-la CS:GO karambit-spin, "Pretty Hands" режимы). Полный набор НЕ портирован — фича-крип чит-клиента, конфликтует с KISS и с правилом «без стороннего jar в сборке» (Delta буквально бандлит чужой мод и синхронизирует с ним настройки черезOriginalHmiBridge). Взята единственная самоценная и самодостаточная идея — grip: лёгкий хват предметов с рукоятью (мечи/инструменты/трезубец/палки — см.heldAlongTheFist) наискось в кулаке вместо строго вертикального ванильного equip. Портировано (коммит b257fde):HeldAlongFist(чистый предикат по ItemTags),HandPoseMath.grip(...)(чистая функция позы, тесты gripMirrorsBySide/ gripExtendPullsCloserAndAcrossFist), настройкиgrip_enabled+6 slider'ов в HoldMyItems (дефолты как у референсного пресета "HMI"), проброс ItemStack через HandDynamics.apply/ItemInHandRendererMixin../gradlew build+testзелёные.
Проверка после каждого пункта: ./gradlew build ✓, ./gradlew test ✓, TODO.md отмечен
Фаза 11: Аудит attack-визуалов — sacura 1.21.4 / GodWeer-26.2 (2026-09-22)
Референсы распакованы в
ref/clients/sacura-1.21.4иref/clients/godweer-26.2. Вывод по большинству эффектов: у нас уже полноценнее (больше режимов, единый render engine с пайплайнами вместо сырого GL). Ниже — конкретные пробелы, которые реально стоит доделать; всё остальное сравнённое (HitEffect, KillEffect, JumpCircles, Hitboxes, ItemPhysic, ViewModel offsets) уже на уровне или выше обоих референсов.
- ViewModel —
Customswing-режим. sacura (SwingAnimationsModule) имеет режим "Кастомная" с explicit слайдерами угол/наклон. У нас 8 фиксированных пресетов (Hammer/Chop/Arc/Double/Shake/Shove/Slash/Thrust), но нет свободно настраиваемого. Также посмотреть на sacura-режимы "Возвращение"/"Возвращение к себе"/"Круговая" — возможно стоят портирования как доп. пресеты, если визуально отличаются от наших. - HitEffect — опциональный distortion/glow post-process слой. godweer
(
HitEffectRenderer) кроме частиц кладёт screen-space distortion+glow слайдеры. У нас частицы (Bubbles/Wave/Impact) есть, post-process слоя над ними нет. ПаттернPostProcessPassуже есть (см.TotemFX), можно переиспользовать — но не блокирующая, это чистый апгрейд поверх уже более богатой системы. - JumpCircles — easing на появление/исчезновение. sacura использует
EaseBackInдля пружинки при спавне/затухании круга. Проверитьrender/engine/math— если easing-утилита уже есть, подключить к текущему fade вместо линейного. - KillEffect — through-walls поведение. godweer (
PopEffect) имеет явный toggle "сквозь стены" для beam/взрыв эффектов. Проверить DepthMode у Lightning/Orthodox beam в нашем KillEffect — сейчас режется окклюзией или нет; если режется и это не специально — добавить toggle по аналогии. - Trails — живое сравнение с sacura
TrailsModule. Не разбирался по коду в этом заходе (визуальный эффект, решение лучше принимать посмотрев обе стороны вживую в игре, а не по исходникам).