chore(history): squash 16 commit(s) from 2026-09-09
- docs: Cosmetics Overhaul design spec (slot-based cosmetics + built-in head/back modules) - chore: drop design doc, implementing directly from TODO.md - feat: Cosmetics module overhaul — 8 body-zone slots + 3D preview screen - feat: AlwaysVisibleHand module + KineticLyrics text scale & timing offset - feat: KillEffect Portal/Tornado procedural shader modes - feat: KineticLyrics sync — millisecond position prediction (playerctl-style) - feat: SkyShader Nebula mode, BlockHighlight shader outlines, crosshairs - feat: CombatLog module — damage dealt/taken log with DPS stats - refactor(8.5.1): regroup mixins/ 108 files into domain leaf packages (<=4 each) + regenerate mixins.json - refactor(8.5.1): split accessors into 12 groups, iris/sodium/security into leaves, fix plugin FQNs + mixins.json - refactor(8.5.1): split visuals/ 67 files into esp/nametags/hats/effects/weather/scene/camera/player/items/fx packages; lyrics->lrc; member visibility fixes; configs untouched (module ids preserved) - refactor(8.5.1): split features/gui/clickgui/settings 51 files into 18 leaf packages (core/color/slider/text/bind/...); visibility promotion only, no behavior changes - refactor(8.5.1): split features/gui/hud/draggable/impl 40 files into stats/metrics/status/combat/panels/targethud/scripted leaves; TargetHudModel records nested; visibility-only - refactor(8.5.1): split events tree (impl 32 -> 8 domains, top -> core/meta); TODO 8.5 progress notes - refactor(8.5.1): split features/gui/chat 31 files into model/render/text/input/suggestions/menu/messages leaves - fix(8.5.1 audit): delete dead unregistered ItemRenderStateLayerAccessor, drop ghost sodium FQNs from plugin, remove vestigial visuals.* star import
This commit is contained in:
parent
707820e418
commit
fb6e6eb1d3
690 changed files with 5923 additions and 2774 deletions
|
|
@ -241,3 +241,161 @@
|
|||
- [ ] Не смешивать с Фазой 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 файлов — топ должников (разбить на подпапки по смыслу)
|
||||
- [x] `mixins/` — 108 файлов → leaf-пакеты (entity/blaze3d/input/world/network/gui/hud/render),
|
||||
mixins.json перегенерирован, FQN-строки LoVisualMixinPlugin обновлены. Коммиты 94d5af7, 3c76817.
|
||||
- [x] `mixins/accessors/` — 45 → 12 групп (entity/renderer/world/state/packet/inventory/...). Коммит 3c76817.
|
||||
Также разбиты iris/sodium/security на подпакеты.
|
||||
- [x] `features/module/modules/visuals/` — 67 → esp/nametags/hats/effects/weather/scene/camera/
|
||||
player/items/fx + lyrics/lrc. Конфиг-id НЕ менялись (проверено). Коммит 073ac26.
|
||||
- [x] `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.
|
||||
- [x] `features/gui/hud/draggable/impl/` — 40 → stats/metrics/status/combat/panels/targethud(+/render,
|
||||
render/parts)/scripted + hudnotifier/model, targethud-модель свёрнута в класс с record-ами. Коммит 6b89e40.
|
||||
- [x] `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/pvp` 13, `util/projectile` 13, `render/iris` 13,
|
||||
`config/profile` 13, `features/module/misc` 12, `features/module` 12, `config` 12 и т.д. —
|
||||
НЕ выделенный проход, а «касаться = чинить» (см. 8.5.4).
|
||||
|
||||
### 8.5.2 Файлы-гиганты >600 строк — приоритетная очередь разбивки (53 файла)
|
||||
> Формат: файл (строк) → идея разбивки. Порядок сверху вниз. Facade-оговорка:
|
||||
> Renderer2D остаётся фасадом-делегатом (правило Фазы 8), но его вспомогательные
|
||||
> классы (kawase/blur/glass) — вынести из OrderedUiBatcher (935) в `ui/blur/`.
|
||||
- [ ] Renderer2D 2166 — ФАСАД (исключение по правилу Фазы 8): оставить делегирование,
|
||||
но блоки >200 строк переносить в package-private хелперы в `renderer/renderer2d/`
|
||||
(частично уже есть Rects2D/Strokes2D)
|
||||
- [ ] LoVisualAltManagerScreen 1113 — экран+список+диалоги → 4 файла `mainmenu/alt/`
|
||||
- [ ] UiPrimitiveRenderer 1042 — по типам примитивов (rect/round/line/text-batch)
|
||||
- [ ] BetterButtons 1032 — extraction+tooltip+render → `hud/nondraggable/impl/buttons/`
|
||||
- [ ] InventorySwap 1031 — по стратегиям свапа; чистая логика отдельными классами с тестами
|
||||
- [ ] SvgMeshBackend 1014 — parser/triangulate/cache разнести
|
||||
- [ ] Themes 1012 — реестр тем в `theme/presets/` (по 4 темы/файл), дефолты отдельно
|
||||
- [ ] WorldParticles 991 — типы частиц в `visuals/effects/particles/`
|
||||
- [ ] Module 989 — базовый класс: settings-механику вынести в `module/ext/` (осторожно, общий предок!)
|
||||
- [ ] LoVisualAddonManagerScreen 989 — как AltManager
|
||||
- [ ] Chams 976 — режимы рендера в подклассы-политики (фасад модуля тонкий)
|
||||
- [ ] ClickGuiPickerState 962 + CombatProtocolHeuristicsEditorState 932 — state/actions хелперы
|
||||
- [ ] ModulesMenuScreen 960 — рендер категорий отдельно от состояния
|
||||
- [ ] UiMeshGeometry 954 — по примитивам геометрии
|
||||
- [ ] BetterTooltips 953, OrderedUiBatcher 935 (→ batches + blur), Scoreboard 924,
|
||||
Rain 893 (+CompactHudStatModel 893), TazikHat 859 (обещан в Фазе 7 — разбить при портировании),
|
||||
ShaderPatchCompiler 843, OnlineRelationPlayerPickerComponent 837, CustomTabList 830,
|
||||
GameRendererMixin 810 (только хуки; тело вынести в `render/engine/entry/`-классы —
|
||||
ВНИМАНИЕ: 9.1 тоже его трогает, координировать), Predictions 805
|
||||
- [ ] Хвост 400–800 (~78 файлов, см. /tmp-снимок в истории команд: ConfigProfilesComponent 791,
|
||||
HitEffect 770, PlayerMovementSimulation 755, ProjectilePuncher 746, MediaPlayer 739,
|
||||
Crosshair 737, MeshBuilder 729, SettingsPanelComponent 703, Admins 702, Cooldowns 701,
|
||||
Itemizer 699, BlockHighlight 692, 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 и др.)
|
||||
|
||||
### 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 → далее по убыванию. Каждая папка/файл = отдельный коммит.
|
||||
- [ ] После каждого шага: `./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 им не блокируется.
|
||||
- [ ] Обновить этот раздел при подходе к волне (отмечать [x], доснимать счётчики).
|
||||
|
||||
## Фаза 9: Оптимизация FPS (аудит 2026-09-09) — ПЛАН
|
||||
> Диагноз: у клиента есть «налог каждый кадр», который платится даже при
|
||||
> выключенных модулях (друг: просадка FPS на чистом конфиге, Windows).
|
||||
> Решение владельца: мёртвые грузы — ЧИНИМ в коде; авто-деградации по FPS НЕ ДЕЛАЕМ;
|
||||
> вместо неё — ручной модуль **Optimize** (включил → порезали стекло/blur/тяжёлые проходы).
|
||||
|
||||
### 9.1 Мёртвые грузы (чиним в коде всегда, независимо от модулей) — приоритет
|
||||
- [ ] **glGetError на каждом render pass** — `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**
|
||||
(`CommandEncoderMixin:66-74`) — `getDeviceInfo().backendName().toLowerCase()`
|
||||
заново на кадр×проходы. Фикс: вычислить один раз, закэшировать boolean.
|
||||
- [ ] **PreTranslucentDepth — полноэкранный depth-copy каждый кадр без нужды** —
|
||||
`LevelRendererMixin:275-283` гейтится только «нет Iris». Фикс: гейт
|
||||
`needsPreTranslucentDepthCapture()` по образцу `WorldSceneDepth`
|
||||
(`LevelRendererMixin:160-218`); потребители: `PostProcessGraphResources:61`,
|
||||
`LoVisualVisuals:79`, `WorldPassCompiler:174`.
|
||||
- [ ] **copyMainColor каждый кадр ДО гейта** — `UiBlurResources.captureWorldSource`
|
||||
(`:116-123`): полноэкранный color-copy делается до проверки
|
||||
`liquidGlassBlurRequested` (`:122`). Фикс: переставить — copy только когда
|
||||
blur реально запрошен.
|
||||
- [ ] **Строки профайлера при выключенном профайлере** — конкатенации на 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`) — закэшить переиспользуемые копии.
|
||||
|
||||
### 9.2 Модуль **Optimize** (misc) — ручной рубильник экономии
|
||||
> ⛔ ГЕЙТ: не начинать, пока не закрыта Фаза 8.5 (рефакторинг-аудитStructure ниже) —
|
||||
> Optimize трогает Renderer2D/OrderedUiBatcher/HUD-имплементации, все они >200 строк.
|
||||
> Идея владельца: один тумблер «хочу максимум FPS». НИКАКОГО автовключения по FPS.
|
||||
> Пока активен — движок и HUD работают в лёгком режиме.
|
||||
- [ ] Каркас: `modules/misc/Optimize.java` (≤200 строк, тест `OptimizeTest` на чистую логику).
|
||||
Настройки (bool, дефолт on при включении модуля):
|
||||
- `no_glass` — все glass/blur-эффекты HUD off: форсить bgEffect=None у всех
|
||||
HUD-элементов (Fps сейчас по умолчанию "Blur", `Fps:70`), сделать
|
||||
`Renderer2D.requestLiquidGlassBlur*` no-op, пропускать kawase-цепочку в
|
||||
`OrderedUiBatcher.prepareSharedBlurInternal`.
|
||||
- `lite_post` — резать тапы тяжёлых пост-проходов: MotionBlur 15→5
|
||||
(+ neighborhood 10→0), DoF 32→8 — через uniform, без переписывания шейдеров
|
||||
(альтернатива жёстче: временно деактивировать проходы — решить на реализации, KISS).
|
||||
- `lite_sky` — процедурные скайбоксы (reimagined_skybox / pulsar / thunder,
|
||||
~20 fbm-оценок на пиксель) — пропускать noise-слои.
|
||||
- `lean_hud` — off для per-frame аллокационных прибамбасов: градиенты/бегущие
|
||||
строки (`CustomTextRenderer:387` new String на глиф), анимации цифр.
|
||||
- [ ] Механизм «как движок узнаёт»: НЕ тыкать статику модуля в лоб — сделать
|
||||
`OptimizeState` (boolean + getter, обновление при enable/disable), читать через
|
||||
него в движке (паттерн уже есть: `ReimaginedVisual.needsWorldSceneDepthCapture()`).
|
||||
- [ ] Взаимодействие с 9.1: фиксы 9.1 НЕ отменяются модулем — copy/ошибки чиним всегда;
|
||||
Optimize режет только то, что имеет визуальную цену.
|
||||
- [ ] i18n en_us + ru_ru обязательно: `module.optimize.*`, `setting.optimize.*`.
|
||||
- [ ] Нотификация при первом включении (chat/HUD): что именно порезано.
|
||||
|
||||
### 9.3 Проверка
|
||||
- [ ] A/B-мерка до/после 9.1 на чистом конфиге (F3/DebugHud, пустой мир, 1080p):
|
||||
целевой эффект — минус 2 полноэкранных copy/кадр и минус N glGetError-синков
|
||||
(N = проходов за кадр).
|
||||
- [ ] `./gradlew build` после каждого пункта; `./gradlew test` зелёный.
|
||||
- [ ] Порядок: 9.1 (по одному пункту на коммит) → 9.2.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"enabled": false
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
package dev.loki.lovisual;
|
||||
|
||||
import dev.loki.lovisual.events.impl.GameTickEvent;
|
||||
import dev.loki.lovisual.events.impl.render.GameTickEvent;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
|
|
@ -25,7 +25,7 @@ import dev.loki.lovisual.events.Events;
|
|||
import dev.loki.lovisual.features.account.AccountConfig;
|
||||
import dev.loki.lovisual.features.command.CommandManager;
|
||||
import dev.loki.lovisual.features.gui.clickgui.ClickGuiRenderer;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.I18nPreflightContributors;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.i18n.I18nPreflightContributors;
|
||||
import dev.loki.lovisual.features.gui.hud.HudElements;
|
||||
import dev.loki.lovisual.features.gui.hud.HudRenderSpace;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.DraggableHudElementRegistry;
|
||||
|
|
@ -72,6 +72,7 @@ import dev.loki.lovisual.util.pvp.PvpTargetTracker;
|
|||
import dev.loki.lovisual.util.pvp.PvpTracker;
|
||||
import dev.loki.lovisual.util.target.TargetManager;
|
||||
import dev.loki.lovisual.util.time.TimerController;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.bind.KeyBindSetting;
|
||||
|
||||
public class LoVisual implements ClientModInitializer {
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ package dev.loki.lovisual.addon;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
record AddonDescriptor(
|
||||
public record AddonDescriptor(
|
||||
String id,
|
||||
String name,
|
||||
String version,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
final class AddonRegistration {
|
||||
public final class AddonRegistration {
|
||||
final AddonDescriptor descriptor;
|
||||
final EntrypointContainer<LoVisualAddon> entrypoint;
|
||||
final List<String> modules = new ArrayList<>();
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import java.nio.file.Path;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
final class AddonStateStore {
|
||||
public final class AddonStateStore {
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
private AddonStateStore() {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import net.fabricmc.loader.api.ModContainer;
|
|||
|
||||
import java.nio.file.Path;
|
||||
|
||||
final class LoVisualAddonContextImpl implements LoVisualAddonContext {
|
||||
public final class LoVisualAddonContextImpl implements LoVisualAddonContext {
|
||||
private final AddonRegistration registration;
|
||||
|
||||
LoVisualAddonContextImpl(AddonRegistration registration) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ package dev.loki.lovisual.addon;
|
|||
import dev.loki.lovisual.api.v0.addon.LoVisualAddonRuntimeContext;
|
||||
import dev.loki.lovisual.api.v0.client.LoVisualClientApi;
|
||||
|
||||
final class LoVisualAddonRuntimeContextImpl implements LoVisualAddonRuntimeContext {
|
||||
public final class LoVisualAddonRuntimeContextImpl implements LoVisualAddonRuntimeContext {
|
||||
private final String addonId;
|
||||
|
||||
LoVisualAddonRuntimeContextImpl(String addonId) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import dev.loki.lovisual.features.module.ModuleAutoLoader;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class LoVisualModuleLoadContextImpl implements LoVisualModuleLoadContext {
|
||||
public final class LoVisualModuleLoadContextImpl implements LoVisualModuleLoadContext {
|
||||
private final AddonRegistration registration;
|
||||
private final List<String> pendingExclusions = new ArrayList<>();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import dev.loki.lovisual.config.values.ConfigValue;
|
|||
import dev.loki.lovisual.config.values.ModeValue;
|
||||
import dev.loki.lovisual.features.module.Module;
|
||||
|
||||
final class ModuleExtensionContextImpl implements ModuleExtensionContext {
|
||||
public final class ModuleExtensionContextImpl implements ModuleExtensionContext {
|
||||
private final String addonId;
|
||||
private final Module module;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
package dev.loki.lovisual.api.v0.addon;
|
||||
|
||||
import dev.loki.lovisual.events.UsedImplicitly;
|
||||
import dev.loki.lovisual.events.meta.UsedImplicitly;
|
||||
import dev.loki.lovisual.features.command.ClientCommand;
|
||||
import dev.loki.lovisual.api.v0.clickgui.LoVisualClickGuiSection;
|
||||
import dev.loki.lovisual.features.gui.hud.AbstractHudElement;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
package dev.loki.lovisual.api.v0.addon;
|
||||
|
||||
import dev.loki.lovisual.events.UsedImplicitly;
|
||||
import dev.loki.lovisual.events.meta.UsedImplicitly;
|
||||
import dev.loki.lovisual.features.module.Module;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import xaero.hud.path.XaeroPath;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class XaeroWaypointRuntime {
|
||||
public final class XaeroWaypointRuntime {
|
||||
private XaeroWaypointRuntime() {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
package dev.loki.lovisual.config;
|
||||
|
||||
import dev.loki.lovisual.config.values.*;
|
||||
import dev.loki.lovisual.config.values.*;
|
||||
import dev.loki.lovisual.render.iris.IrisRuntime;
|
||||
import dev.loki.lovisual.util.logging.DebugLog;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ package dev.loki.lovisual.config;
|
|||
|
||||
import dev.loki.lovisual.config.common.CommonSettingSchema;
|
||||
import dev.loki.lovisual.config.values.*;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.TextListSetting;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.text.list.TextListSetting;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
package dev.loki.lovisual.config.common;
|
||||
|
||||
import dev.loki.lovisual.config.common.impl.*;
|
||||
import dev.loki.lovisual.features.module.modules.visuals.esp.ESP;
|
||||
|
||||
public enum CommonSettingSchemas {
|
||||
;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
enum ConfigProfileValueCodec {
|
||||
public enum ConfigProfileValueCodec {
|
||||
;
|
||||
private static final byte NULL = 0;
|
||||
private static final byte BOOLEAN = 1;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import dev.loki.lovisual.events.meta.EventHandler;
|
||||
|
||||
public final class EventBus {
|
||||
private static final Subscriber[] EMPTY_SUBSCRIBERS = new Subscriber[0];
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
package dev.loki.lovisual.events;
|
||||
import dev.loki.lovisual.events.EventBus;
|
||||
|
||||
public enum Events {
|
||||
;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.entity;
|
||||
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import dev.loki.lovisual.events.Event;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.input;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.input.KeyEvent;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.input;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.input;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import lombok.Getter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.input;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import lombok.Getter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.movement;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.movement;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import lombok.Getter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.movement;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import lombok.Getter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.movement;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.network;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.network;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.network;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import lombok.Getter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.network;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
|
|
@ -5,13 +5,13 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.player;
|
||||
|
||||
import dev.loki.lovisual.config.SettingDef;
|
||||
import dev.loki.lovisual.config.SettingOwner;
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.Setting;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.SettingFactory;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.core.Setting;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.core.SettingFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.player;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import lombok.Getter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.player;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.player;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import lombok.Getter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.pvp;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.pvp;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.pvp;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import dev.loki.lovisual.events.Event;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.pvp;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.render;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.render;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import lombok.Getter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.render;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.renderer.state.LightmapRenderState;
|
||||
|
|
@ -13,6 +13,7 @@ import net.minecraft.util.Mth;
|
|||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3fc;
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import dev.loki.lovisual.features.module.modules.visuals.player.FullBright;
|
||||
|
||||
public class LightmapModifyEvent extends Event {
|
||||
private static final float EPSILON = 1.0e-4f;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.render;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import dev.loki.lovisual.render.engine.svg.SvgRegistry;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.world;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.world;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.world;
|
||||
|
||||
import dev.loki.lovisual.events.Event;
|
||||
import lombok.Getter;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events.impl;
|
||||
package dev.loki.lovisual.events.impl.world;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
|
@ -5,12 +5,13 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events;
|
||||
package dev.loki.lovisual.events.meta;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import dev.loki.lovisual.events.meta.UsedImplicitly;
|
||||
|
||||
@UsedImplicitly
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.events;
|
||||
package dev.loki.lovisual.events.meta;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
package dev.loki.lovisual.features.command;
|
||||
|
||||
import dev.loki.lovisual.events.UsedImplicitly;
|
||||
import dev.loki.lovisual.events.meta.UsedImplicitly;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import java.util.Comparator;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
final class CommandUtils {
|
||||
public final class CommandUtils {
|
||||
private CommandUtils() {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import dev.loki.lovisual.features.module.Module;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.panels.ModuleList;
|
||||
|
||||
@CommandInfo(
|
||||
id = "hide",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import dev.loki.lovisual.features.command.CommandContext;
|
|||
import dev.loki.lovisual.features.command.CommandInfo;
|
||||
import dev.loki.lovisual.features.command.CommandOutput;
|
||||
import dev.loki.lovisual.util.player.NetworkStatsUtil;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.stats.Ping;
|
||||
|
||||
@CommandInfo(
|
||||
id = "ping",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import dev.loki.lovisual.util.player.NetworkStatsUtil;
|
|||
import net.minecraft.client.multiplayer.ServerData;
|
||||
|
||||
import java.util.Locale;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.stats.Ping;
|
||||
|
||||
@CommandInfo(
|
||||
id = "serverinfo",
|
||||
|
|
|
|||
|
|
@ -2,54 +2,89 @@ package dev.loki.lovisual.features.cosmetic;
|
|||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
import dev.loki.lovisual.features.module.Modules;
|
||||
import dev.loki.lovisual.features.module.modules.visuals.cosmetics.CosmeticDisplay;
|
||||
|
||||
public final class CosmeticAttachmentTransforms {
|
||||
private CosmeticAttachmentTransforms() {
|
||||
}
|
||||
|
||||
public static void apply(PoseStack matrices, CosmeticType type, CosmeticModel model) {
|
||||
if (type == CosmeticType.PET) {
|
||||
// TODO: replace with CosmeticAnimator.getPetHoverOffset() once the animator is ported.
|
||||
float bob = 0f;
|
||||
matrices.mulPose(Axis.YP.rotationDegrees(180f));
|
||||
matrices.translate(0.45f, 2.15f + bob, 0.12f);
|
||||
matrices.scale(0.55f, 0.55f, 0.55f);
|
||||
return;
|
||||
/**
|
||||
* Base attachment transform for a slot, then the user's per-slot offset/rotation/scale
|
||||
* override from {@link CosmeticDisplay} (accessory slots only).
|
||||
*/
|
||||
public static void apply(PoseStack matrices, CosmeticSlot slot, CosmeticModel model) {
|
||||
applyBase(matrices, slot);
|
||||
if (slot.isAccessory()) {
|
||||
applyUserOverride(matrices, slot);
|
||||
}
|
||||
if (type == CosmeticType.ACCESSORY && model != null) {
|
||||
String folder = model.getFolderName().toLowerCase();
|
||||
if (folder.contains("witch") || folder.contains("hat")) {
|
||||
}
|
||||
|
||||
private static void applyUserOverride(PoseStack matrices, CosmeticSlot slot) {
|
||||
CosmeticDisplay module = Modules.get(CosmeticDisplay.class);
|
||||
if (module == null) return;
|
||||
CosmeticDisplay.SlotOffsetSettings offsets = module.offsetsFor(slot);
|
||||
if (offsets == null) return;
|
||||
|
||||
matrices.translate(offsets.offsetX().get(), offsets.offsetY().get(), offsets.offsetZ().get());
|
||||
matrices.mulPose(Axis.YP.rotationDegrees(offsets.rotation().get()));
|
||||
float scale = offsets.scale().get();
|
||||
matrices.scale(scale, scale, scale);
|
||||
}
|
||||
|
||||
private static void applyBase(PoseStack matrices, CosmeticSlot slot) {
|
||||
switch (slot) {
|
||||
case PET -> {
|
||||
// TODO: replace with CosmeticAnimator.getPetHoverOffset() once the animator is ported.
|
||||
float bob = 0f;
|
||||
matrices.mulPose(Axis.YP.rotationDegrees(180f));
|
||||
matrices.translate(0.45f, 2.15f + bob, 0.12f);
|
||||
matrices.scale(0.55f, 0.55f, 0.55f);
|
||||
}
|
||||
case HEAD -> {
|
||||
matrices.translate(0f, 1.72f, 0f);
|
||||
matrices.scale(0.52f, 0.52f, 0.52f);
|
||||
} else if (folder.contains("wing")) {
|
||||
}
|
||||
case SHOULDERS -> {
|
||||
matrices.translate(0f, 1.45f, 0f);
|
||||
matrices.scale(0.5f, 0.5f, 0.5f);
|
||||
}
|
||||
case BACK -> {
|
||||
matrices.translate(0f, 0.1f, -0.3f);
|
||||
matrices.mulPose(Axis.YP.rotationDegrees(180f));
|
||||
matrices.scale(0.85f, 0.85f, 0.85f);
|
||||
} else if (folder.contains("wheel")) {
|
||||
}
|
||||
case WAIST -> {
|
||||
matrices.translate(0f, 0.55f, 0.25f);
|
||||
matrices.scale(0.55f, 0.55f, 0.55f);
|
||||
} else {
|
||||
}
|
||||
case LEGS -> {
|
||||
matrices.translate(0f, 0.3f, 0f);
|
||||
matrices.scale(0.5f, 0.5f, 0.5f);
|
||||
}
|
||||
case HELD -> {
|
||||
matrices.translate(0f, 1.3f, 0f);
|
||||
matrices.scale(0.5f, 0.5f, 0.5f);
|
||||
}
|
||||
case MODEL -> {
|
||||
// no base transform: full-body model replaces the player 1:1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyPreview(PoseStack matrices, CosmeticModel model) {
|
||||
if (model == null) return;
|
||||
String folder = model.getFolderName().toLowerCase();
|
||||
if (model.getType() == CosmeticType.PET) {
|
||||
CosmeticSlot slot = model.getSlot();
|
||||
if (slot == CosmeticSlot.PET) {
|
||||
matrices.translate(6f, 22f, 1f);
|
||||
matrices.scale(1.8f, 1.8f, 1.8f);
|
||||
} else if (model.getType() == CosmeticType.ACCESSORY) {
|
||||
if (folder.contains("witch") || folder.contains("hat")) {
|
||||
matrices.translate(0f, 20f, 0f);
|
||||
} else if (folder.contains("wing")) {
|
||||
matrices.translate(0f, 2f, -4f);
|
||||
matrices.scale(1.3f, 1.3f, 1.3f);
|
||||
} else if (folder.contains("wheel")) {
|
||||
matrices.translate(0f, 6f, 3f);
|
||||
}
|
||||
} else if (slot == CosmeticSlot.HEAD) {
|
||||
matrices.translate(0f, 20f, 0f);
|
||||
} else if (slot == CosmeticSlot.BACK) {
|
||||
matrices.translate(0f, 2f, -4f);
|
||||
matrices.scale(1.3f, 1.3f, 1.3f);
|
||||
} else if (slot == CosmeticSlot.WAIST) {
|
||||
matrices.translate(0f, 6f, 3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
package dev.loki.lovisual.features.cosmetic;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Holds which cosmetic (model/pet/accessory) the local user currently has equipped.
|
||||
* Holds which cosmetic the local user currently has equipped, per slot.
|
||||
* MODEL/PET are single-active slots; the six accessory slots (HEAD/SHOULDERS/
|
||||
* BACK/WAIST/LEGS/HELD) are independent and can all be active at once.
|
||||
* Rendering and animation live in separate classes (not part of this core pass).
|
||||
*/
|
||||
public final class CosmeticManager {
|
||||
private static final CosmeticManager INSTANCE = new CosmeticManager();
|
||||
|
||||
private CosmeticModel activeModel;
|
||||
private CosmeticModel activePet;
|
||||
private CosmeticModel activeAccessory;
|
||||
private final Map<CosmeticSlot, CosmeticModel> active = new EnumMap<>(CosmeticSlot.class);
|
||||
|
||||
private CosmeticManager() {
|
||||
}
|
||||
|
|
@ -23,52 +26,55 @@ public final class CosmeticManager {
|
|||
* (by id) against the freshly loaded models.
|
||||
*/
|
||||
public void init() {
|
||||
String modelId = activeModel != null ? activeModel.getId() : null;
|
||||
String petId = activePet != null ? activePet.getId() : null;
|
||||
String accessoryId = activeAccessory != null ? activeAccessory.getId() : null;
|
||||
Map<CosmeticSlot, String> equippedIds = new EnumMap<>(CosmeticSlot.class);
|
||||
for (Map.Entry<CosmeticSlot, CosmeticModel> entry : active.entrySet()) {
|
||||
if (entry.getValue() != null) {
|
||||
equippedIds.put(entry.getKey(), entry.getValue().getId());
|
||||
}
|
||||
}
|
||||
|
||||
for (CosmeticModel model : CosmeticRepository.get().all()) {
|
||||
model.resetTexture();
|
||||
}
|
||||
CosmeticRepository.get().reload();
|
||||
|
||||
active.clear();
|
||||
CosmeticRepository repo = CosmeticRepository.get();
|
||||
activeModel = modelId != null ? repo.get(modelId) : null;
|
||||
activePet = petId != null ? repo.get(petId) : null;
|
||||
activeAccessory = accessoryId != null ? repo.get(accessoryId) : null;
|
||||
for (Map.Entry<CosmeticSlot, String> entry : equippedIds.entrySet()) {
|
||||
CosmeticModel resolved = repo.get(entry.getValue());
|
||||
if (resolved != null) {
|
||||
active.put(entry.getKey(), resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CosmeticModel getActive(CosmeticType type) {
|
||||
return switch (type) {
|
||||
case PET -> activePet;
|
||||
case ACCESSORY -> activeAccessory;
|
||||
case MODEL -> activeModel;
|
||||
};
|
||||
public CosmeticModel getActive(CosmeticSlot slot) {
|
||||
return active.get(slot);
|
||||
}
|
||||
|
||||
public void setActive(CosmeticType type, CosmeticModel model) {
|
||||
public void setActive(CosmeticSlot slot, CosmeticModel model) {
|
||||
if (model != null) {
|
||||
CosmeticModel resolved = CosmeticRepository.get().get(model.getId());
|
||||
if (resolved != null) {
|
||||
model = resolved;
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case PET -> activePet = model;
|
||||
case ACCESSORY -> activeAccessory = model;
|
||||
case MODEL -> activeModel = model;
|
||||
if (model == null) {
|
||||
active.remove(slot);
|
||||
} else {
|
||||
active.put(slot, model);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear(CosmeticType type) {
|
||||
setActive(type, null);
|
||||
public void clear(CosmeticSlot slot) {
|
||||
setActive(slot, null);
|
||||
}
|
||||
|
||||
public boolean hasAnyActive() {
|
||||
return activeModel != null || activePet != null || activeAccessory != null;
|
||||
return !active.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return activeModel != null;
|
||||
return active.get(CosmeticSlot.MODEL) != null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public class CosmeticModel {
|
|||
private final String accentColor;
|
||||
private final String folderName;
|
||||
private final CosmeticType type;
|
||||
private final CosmeticSlot slot;
|
||||
private final File externalDirectory;
|
||||
@Setter private String source = "Своя";
|
||||
|
||||
|
|
@ -63,18 +64,27 @@ public class CosmeticModel {
|
|||
private CosmeticTextureSlot fallbackSlot;
|
||||
|
||||
private CosmeticModel(String id, String name, String description, String accentColor,
|
||||
String folderName, CosmeticType type, File externalDirectory) {
|
||||
String folderName, CosmeticType type, CosmeticSlot slot, File externalDirectory) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.accentColor = accentColor;
|
||||
this.folderName = folderName;
|
||||
this.type = type;
|
||||
this.slot = slot != null ? slot : defaultSlotFor(type, folderName);
|
||||
this.externalDirectory = externalDirectory;
|
||||
}
|
||||
|
||||
private static CosmeticSlot defaultSlotFor(CosmeticType type, String folderName) {
|
||||
return switch (type) {
|
||||
case MODEL -> CosmeticSlot.MODEL;
|
||||
case PET -> CosmeticSlot.PET;
|
||||
case ACCESSORY -> CosmeticSlot.guessFromFolderName(folderName);
|
||||
};
|
||||
}
|
||||
|
||||
public CosmeticModel withType(CosmeticType newType) {
|
||||
CosmeticModel copy = new CosmeticModel(id, name, description, accentColor, folderName, newType, externalDirectory);
|
||||
CosmeticModel copy = new CosmeticModel(id, name, description, accentColor, folderName, newType, null, externalDirectory);
|
||||
copy.rootBones.addAll(rootBones);
|
||||
copy.renderRoots.addAll(renderRoots);
|
||||
copy.boneMap.putAll(boneMap);
|
||||
|
|
@ -141,7 +151,8 @@ public class CosmeticModel {
|
|||
: avatar.has("type") ? avatar.get("type").getAsString() : null
|
||||
);
|
||||
|
||||
CosmeticModel model = new CosmeticModel(catalogId, name, desc, color, folderName, type, externalDir);
|
||||
CosmeticSlot slot = avatar.has("slot") ? CosmeticSlot.fromJson(avatar.get("slot").getAsString()) : null;
|
||||
CosmeticModel model = new CosmeticModel(catalogId, name, desc, color, folderName, type, slot, externalDir);
|
||||
|
||||
InputStream modelStream = openModelStream(externalDir, folderName);
|
||||
if (modelStream == null) {
|
||||
|
|
@ -392,6 +403,7 @@ public class CosmeticModel {
|
|||
parent.getAccentColor(),
|
||||
petFolder,
|
||||
CosmeticType.PET,
|
||||
null,
|
||||
null
|
||||
);
|
||||
pet.source = parent.source;
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ public final class CosmeticRenderer {
|
|||
private CosmeticRenderer() {
|
||||
}
|
||||
|
||||
public static void renderOnPlayer(Renderer3D renderer, Player player, CosmeticType type, float tickDelta) {
|
||||
public static void renderOnPlayer(Renderer3D renderer, Player player, CosmeticSlot slot, float tickDelta) {
|
||||
if (renderer == null || player == null) return;
|
||||
|
||||
CosmeticModel model = CosmeticManager.get().getActive(type);
|
||||
CosmeticModel model = CosmeticManager.get().getActive(slot);
|
||||
if (model == null) return;
|
||||
|
||||
model.loadTexture();
|
||||
|
|
@ -45,7 +45,7 @@ public final class CosmeticRenderer {
|
|||
|
||||
PoseStack matrices = new PoseStack();
|
||||
matrices.translate(pos.x, pos.y, pos.z);
|
||||
CosmeticAttachmentTransforms.apply(matrices, type, model);
|
||||
CosmeticAttachmentTransforms.apply(matrices, slot, model);
|
||||
matrices.mulPose(Axis.YP.rotationDegrees(180f - bodyYaw));
|
||||
|
||||
Map<Integer, List<PendingFace>> byTexture = new HashMap<>();
|
||||
|
|
@ -58,14 +58,14 @@ public final class CosmeticRenderer {
|
|||
Vec3 cam = RenderState.cameraPos;
|
||||
|
||||
for (Map.Entry<Integer, List<PendingFace>> entry : byTexture.entrySet()) {
|
||||
CosmeticTextureSlot slot = model.getTextureSlot(entry.getKey());
|
||||
if (slot == null) continue;
|
||||
if (!slot.isLoaded()) {
|
||||
model.ensureTextureSlotLoaded(slot);
|
||||
CosmeticTextureSlot textureSlot = model.getTextureSlot(entry.getKey());
|
||||
if (textureSlot == null) continue;
|
||||
if (!textureSlot.isLoaded()) {
|
||||
model.ensureTextureSlotLoaded(textureSlot);
|
||||
}
|
||||
if (!slot.isLoaded()) continue;
|
||||
if (!textureSlot.isLoaded()) continue;
|
||||
|
||||
AbstractTexture texture = mc.getTextureManager().getTexture(slot.getId());
|
||||
AbstractTexture texture = mc.getTextureManager().getTexture(textureSlot.getId());
|
||||
if (texture == null || texture.getTextureView() == null || texture.getSampler() == null) continue;
|
||||
|
||||
Renderer3D.BatchBindings bindings = Renderer3D.BatchBindings.none()
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ public final class CosmeticRepository {
|
|||
return catalog.values().stream().filter(m -> m.getType() == type).toList();
|
||||
}
|
||||
|
||||
public List<CosmeticModel> bySlot(CosmeticSlot slot) {
|
||||
return catalog.values().stream().filter(m -> m.getSlot() == slot).toList();
|
||||
}
|
||||
|
||||
public CosmeticModel get(String id) {
|
||||
return catalog.get(id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
package dev.loki.lovisual.features.cosmetic;
|
||||
|
||||
/**
|
||||
* Where a cosmetic is worn. MODEL/PET are whole-body/companion slots (one active
|
||||
* item each); the remaining six are independent accessory slots that can all be
|
||||
* active simultaneously (e.g. a hat + wings + a belt at once).
|
||||
*/
|
||||
public enum CosmeticSlot {
|
||||
MODEL("model", "Модель"),
|
||||
PET("pet", "Питомец"),
|
||||
HEAD("head", "Голова"),
|
||||
SHOULDERS("shoulders", "Плечи"),
|
||||
BACK("back", "Спина"),
|
||||
WAIST("waist", "Пояс"),
|
||||
LEGS("legs", "Ноги"),
|
||||
HELD("held", "В руке");
|
||||
|
||||
private final String id;
|
||||
private final String displayName;
|
||||
|
||||
CosmeticSlot(String id, String displayName) {
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public boolean isAccessory() {
|
||||
return this != MODEL && this != PET;
|
||||
}
|
||||
|
||||
public static CosmeticSlot fromJson(String raw) {
|
||||
if (raw == null || raw.isBlank()) return null;
|
||||
return switch (raw.trim().toLowerCase()) {
|
||||
case "model", "модель" -> MODEL;
|
||||
case "pet", "pets", "питомец", "питомцы" -> PET;
|
||||
case "head", "hat", "голова", "шляпа" -> HEAD;
|
||||
case "shoulders", "shoulder", "плечи" -> SHOULDERS;
|
||||
case "back", "спина" -> BACK;
|
||||
case "waist", "belt", "пояс" -> WAIST;
|
||||
case "legs", "leg", "ноги" -> LEGS;
|
||||
case "held", "hand", "рука" -> HELD;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
/** Best-effort slot guess from the model's folder name (legacy accessories without a "slot" key). */
|
||||
public static CosmeticSlot guessFromFolderName(String folderName) {
|
||||
if (folderName == null) return HELD;
|
||||
String folder = folderName.toLowerCase();
|
||||
if (folder.contains("witch") || folder.contains("hat")) return HEAD;
|
||||
if (folder.contains("wing")) return BACK;
|
||||
if (folder.contains("wheel")) return WAIST;
|
||||
return HELD;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
|
||||
import net.minecraft.network.chat.HoverEvent;
|
||||
import net.minecraft.network.chat.Style;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Shared package-private records extracted from {@link BetterChatRenderer}.
|
||||
* The only public-facing record kept nested on the facade is {@link BetterChatRenderer.Layout}.
|
||||
* Records that own non-trivial rendering logic ({@code FrameLayout}, {@code ContextMenu}) live in
|
||||
* their own files ({@link BetterChatFrameModel}, {@link BetterChatContextMenuModel}).
|
||||
*/
|
||||
final class BetterChatModel {
|
||||
private BetterChatModel() {
|
||||
}
|
||||
|
||||
record MousePos(double rawX, double rawY, double fx, double fy) {
|
||||
}
|
||||
|
||||
record InputLine(int start, int end, float width) {
|
||||
}
|
||||
|
||||
record Segment(String text, Style style) {
|
||||
}
|
||||
|
||||
record VisualLine(ChatLine message, int messageIndex, int messageGroup, int startChar, int endChar, List<Glyph> glyphs) {
|
||||
}
|
||||
|
||||
record Glyph(
|
||||
float x0,
|
||||
float x1,
|
||||
int charIndex,
|
||||
int charEndExclusive,
|
||||
int color,
|
||||
HoverEvent hover,
|
||||
String font,
|
||||
String text,
|
||||
Style style
|
||||
) {
|
||||
}
|
||||
|
||||
record GlyphBox(
|
||||
float x0,
|
||||
float y0,
|
||||
float x1,
|
||||
float y1,
|
||||
int charIndex,
|
||||
int charEndExclusive,
|
||||
int color,
|
||||
String font,
|
||||
HoverEvent hover,
|
||||
String text,
|
||||
Style style
|
||||
) {
|
||||
}
|
||||
|
||||
record CachedLine(int startChar, int endChar, List<Glyph> glyphs) {
|
||||
}
|
||||
|
||||
record CachedMessageLayout(float fontSize, float maxWidth, List<CachedLine> lines) {
|
||||
}
|
||||
|
||||
record FrameLine(ChatLine message, int messageIndex, int messageGroup, float y0, float y1, List<GlyphBox> glyphs) {
|
||||
}
|
||||
|
||||
record MessageBubble(ChatLine message, int messageGroup, float x, float y, float w, float h, FrameLine firstLine, FrameLine lastLine) {
|
||||
}
|
||||
|
||||
record MessageClipBounds(float x, float y, float w, float h) {
|
||||
}
|
||||
|
||||
record PickResult(FrameLine line, GlyphBox glyph) {
|
||||
}
|
||||
|
||||
record LayoutResult(List<VisualLine> lines, int messagesSeen) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import dev.loki.lovisual.features.command.CommandOutput;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
import dev.loki.lovisual.render.engine.color.RenderColor;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.text.TextRenderer;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.client.gui.screens.ChatScreen;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import dev.loki.lovisual.util.text.TextSelection;
|
||||
|
||||
/**
|
||||
* Facade for the BetterChat HUD renderer. This enum singleton is now a thin delegation point:
|
||||
* it owns the shared per-frame state and constants read/written by the package-private helpers,
|
||||
* and re-exports the original public API exactly as before so no caller changes.
|
||||
*/
|
||||
public enum BetterChatRenderer {
|
||||
;
|
||||
|
||||
// ---- Constants (package-private; read by helpers) ----
|
||||
static final int MAX_MESSAGE_CHARS = 256;
|
||||
static final float PADDING = 8f;
|
||||
static final float LINE_SPACING = 3f;
|
||||
static final float RADIUS = 24f;
|
||||
static final float MESSAGE_PAD_X = 8f;
|
||||
static final float MESSAGE_PAD_Y = 5f;
|
||||
static final float MESSAGE_GAP = 4f;
|
||||
static final float MESSAGE_INPUT_GAP = 8f;
|
||||
static final float MESSAGE_RADIUS = 9f;
|
||||
static final float MESSAGE_TS_GAP = 8f;
|
||||
static final float INPUT_RADIUS = 12f;
|
||||
static final float SEARCH_ICON_WIDTH = 34f;
|
||||
static final float SEARCH_ICON_HEIGHT = 22f;
|
||||
static final float NEW_MESSAGE_REVEAL_SECONDS = 0.34f;
|
||||
static final float NEW_MESSAGE_SLIDE_PX = 18f;
|
||||
static final int MAX_HISTORY = 32000;
|
||||
static final float CHAT_TOOLTIP_OFFSET_X = 6f;
|
||||
static final float CHAT_TOOLTIP_OFFSET_Y = 12f;
|
||||
static final double DRAG_SELECT_THRESHOLD = 4.0;
|
||||
static final RenderColor TMP_COLOR = new RenderColor(0xFFFFFFFF);
|
||||
|
||||
// ---- Layout caches (package-private) ----
|
||||
static final IdentityHashMap<ChatLine, BetterChatModel.CachedMessageLayout> MESSAGE_LAYOUT_CACHE = new IdentityHashMap<>();
|
||||
static final IdentityHashMap<ChatLine, List<BetterChatModel.Segment>> SEGMENT_CACHE = new IdentityHashMap<>();
|
||||
static final IdentityHashMap<CommandOutput.MessageLine, ChatLine> COMMAND_LINE_CACHE = new IdentityHashMap<>();
|
||||
static final TextSelection selection = new TextSelection();
|
||||
|
||||
// ---- Persistent UI state (package-private) ----
|
||||
static Renderer2D renderer;
|
||||
static float lastRenderWidth = 0f;
|
||||
static float lastRenderHeight = 0f;
|
||||
static int scrollOffsetLines = 0;
|
||||
static float smoothScrollOffsetLines = 0f;
|
||||
static int maxScrollLines = 0;
|
||||
static float lastFontSizeForUi = 16f;
|
||||
static List<ChatLine> lastMessages = Collections.emptyList();
|
||||
static boolean draggingScrollbar = false;
|
||||
static float sbTrackX, sbTrackY, sbTrackW, sbTrackH;
|
||||
static float sbThumbY, sbThumbH;
|
||||
static float sbDragOffset = 0f;
|
||||
static long lastScrollInteractionMs = 0L;
|
||||
static boolean sbEnabled = false;
|
||||
static int sbTotalLines = 0;
|
||||
static int sbVisibleLines = 0;
|
||||
static float inputBoxX = 0f, inputBoxY = 0f, inputBoxW = 0f, inputBoxH = 0f;
|
||||
static boolean hasInputBox = false;
|
||||
static boolean suggestActive = false;
|
||||
static float suggestX, suggestY, suggestW, suggestH, suggestItemH;
|
||||
static float suggestRowsX, suggestRowsY, suggestRowsW, suggestRowsH;
|
||||
static int suggestStart, suggestVisible, suggestTotal;
|
||||
static net.minecraft.client.gui.components.CommandSuggestions.SuggestionsList suggestWindow;
|
||||
static List<Suggestion> suggestEntries = Collections.emptyList();
|
||||
static boolean lastHoverWasOutsideSuggest = true;
|
||||
static boolean suggestHasScrollbar = false;
|
||||
static boolean suggestDraggingScrollbar = false;
|
||||
static float suggestTrackX, suggestTrackY, suggestTrackW, suggestTrackH, suggestThumbH;
|
||||
static int suggestCurrentStart = -1;
|
||||
static int suggestCurrentSelection = -1;
|
||||
static double lastMouseFx = 0.0;
|
||||
static double lastMouseFy = 0.0;
|
||||
static boolean lastMouseValid = false;
|
||||
static boolean tooltipCaptured = false;
|
||||
static String lastSuggestInput = "";
|
||||
static int lastSuggestCursor = -1;
|
||||
static int lastSuggestSelStart = -1;
|
||||
static int lastSuggestSelEnd = -1;
|
||||
static CommandSuggestorBridge.SuggestionSnapshot lastSnapshot = null;
|
||||
static long debugPerfWindowStartNs = 0L;
|
||||
static long debugPerfAccumLayoutNs = 0L;
|
||||
static long debugPerfAccumBuildNs = 0L;
|
||||
static int debugPerfAccumFrames = 0;
|
||||
static BetterChatFrameModel.FrameLayout frame = BetterChatFrameModel.FrameLayout.empty();
|
||||
static BetterChatContextMenuModel.ContextMenu contextMenu = BetterChatContextMenuModel.ContextMenu.closed();
|
||||
static boolean selecting = false;
|
||||
static boolean searchHit = false;
|
||||
static float searchRectX = 0f, searchRectY = 0f, searchRectW = 0f, searchRectH = 0f;
|
||||
static boolean searchHotkeyDown = false;
|
||||
static float chatHitX = 0f, chatHitY = 0f, chatHitW = 0f, chatHitH = 0f;
|
||||
static String hoverEntityUuid = null;
|
||||
static ChatScreen storedScreen;
|
||||
static BetterChatStore previewStore;
|
||||
static int lastSuggestionHash = 0;
|
||||
static boolean leftPending = false;
|
||||
static double leftDownFx = 0.0, leftDownFy = 0.0;
|
||||
static int leftDownMsgIndex = -1;
|
||||
static int leftDownCharIndex = -1;
|
||||
static Style leftDownStyle = null;
|
||||
|
||||
// ---- Per-frame transient context (package-private; populated by the phased helpers) ----
|
||||
static GuiGraphicsExtractor ctxGui;
|
||||
static float ctxBaseX, ctxBaseY;
|
||||
static BetterChat ctxSettings;
|
||||
static net.minecraft.client.Minecraft ctxMc;
|
||||
static boolean ctxPreview, ctxChatOpen, ctxActiveSurface;
|
||||
static boolean ctxShowTs, ctxHasSeconds, ctxTipDate, ctxTipUnix, ctxAutoFollow;
|
||||
static int ctxTsColor, ctxTotalMessages, ctxVisibleLimit, ctxMessagesSeen;
|
||||
static float ctxWidth, ctxContentWidth, ctxTsWidth;
|
||||
static float ctxLineHeight, ctxBoxHeight, ctxLineYOffset, ctxTargetVisible;
|
||||
static float ctxStableBoxHeight, ctxFixedInputY, ctxFrameY;
|
||||
static int[] ctxMessageGroups;
|
||||
static List<BetterChatModel.VisualLine> ctxAllLines;
|
||||
static List<BetterChatModel.VisualLine> ctxVisible;
|
||||
static long ctxPerfT0, ctxPerfT1;
|
||||
static ChatHoverUtil.HoverTip ctxHoverTip;
|
||||
|
||||
// ---- Public API (delegating) ----
|
||||
|
||||
public static void markLayoutDirty() {
|
||||
BetterChatLayoutEngine.markLayoutDirty();
|
||||
}
|
||||
|
||||
public static void renderEngine(Renderer2D rendererIn,
|
||||
TextRenderer fallback,
|
||||
GuiGraphicsExtractor ctx,
|
||||
int screenW,
|
||||
float baseX,
|
||||
float baseY) {
|
||||
BetterChatFrameRenderer.renderEngine(rendererIn, fallback, ctx, screenW, baseX, baseY);
|
||||
}
|
||||
|
||||
public static void renderCapturedTooltip(GuiGraphicsExtractor ctx) {
|
||||
BetterChatTooltipRenderer.renderCapturedTooltip(ctx);
|
||||
}
|
||||
|
||||
public static boolean onScroll(double delta) {
|
||||
return BetterChatInputHandler.onScroll(delta);
|
||||
}
|
||||
|
||||
public static boolean onMouseButton(double mx, double my, int button, boolean pressed) {
|
||||
return BetterChatMouseClickHandler.onMouseButton(mx, my, button, pressed);
|
||||
}
|
||||
|
||||
public static void onMouseMove(double mx, double my) {
|
||||
BetterChatInputHandler.onMouseMove(mx, my);
|
||||
}
|
||||
|
||||
public static void copySelectionToClipboard() {
|
||||
BetterChatContextMenuHandler.copySelectionToClipboard();
|
||||
}
|
||||
|
||||
public static void resetScroll() {
|
||||
BetterChatInputHandler.resetScroll();
|
||||
}
|
||||
|
||||
public static void applyAutoPrefixIfNeeded(EditBox field) {
|
||||
// Compact BetterChat no longer owns chat-prefix chips; hook kept for compatibility.
|
||||
}
|
||||
|
||||
public static void setSuggestionContext(ChatScreen screen, GuiGraphicsExtractor context, int mouseX, int mouseY) {
|
||||
storedScreen = screen;
|
||||
}
|
||||
|
||||
public static void onNewMessage() {
|
||||
BetterChatInputHandler.onNewMessage();
|
||||
}
|
||||
|
||||
public static float getLastWidth() {
|
||||
return lastRenderWidth;
|
||||
}
|
||||
|
||||
public static float getLastHeight() {
|
||||
return lastRenderHeight;
|
||||
}
|
||||
|
||||
public static boolean contains(float mx, float my) {
|
||||
return BetterChatInputHandler.contains(mx, my);
|
||||
}
|
||||
|
||||
public static boolean isInteractive(float mx, float my) {
|
||||
return BetterChatInputHandler.isInteractive(mx, my);
|
||||
}
|
||||
|
||||
public record Layout(float x, float y) {
|
||||
}
|
||||
}
|
||||
|
|
@ -12,13 +12,17 @@ import net.minecraft.util.Mth;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatFrameModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
|
||||
/**
|
||||
* Second render phase: turns the laid-out visual lines into a viewport + frame layout
|
||||
* (bubbles/lines) and animates smooth scrolling. Writes the resulting {@link BetterChatFrameModel.FrameLayout}
|
||||
* back onto the facade and populates the per-frame sizing context used by every later phase.
|
||||
*/
|
||||
final class BetterChatViewport {
|
||||
public final class BetterChatViewport {
|
||||
private BetterChatViewport() {
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +71,7 @@ final class BetterChatViewport {
|
|||
return safe;
|
||||
}
|
||||
|
||||
static void composeFrame() {
|
||||
public static void composeFrame() {
|
||||
List<BetterChatModel.VisualLine> allLines = BetterChatRenderer.ctxAllLines;
|
||||
BetterChatModel.LayoutResult layoutResult = new BetterChatModel.LayoutResult(allLines, BetterChatRenderer.ctxMessagesSeen);
|
||||
List<ChatLine> messages = BetterChatRenderer.lastMessages;
|
||||
|
|
|
|||
|
|
@ -5,25 +5,25 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.input;
|
||||
|
||||
import dev.loki.lovisual.mixins.accessors.TextFieldWidgetAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.widget.TextFieldWidgetAccessor;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
/**
|
||||
* EditBox selection introspection + word-boundary helper, independent of the chat renderer.
|
||||
*/
|
||||
final class BetterChatEditBoxUtil {
|
||||
public final class BetterChatEditBoxUtil {
|
||||
private BetterChatEditBoxUtil() {
|
||||
}
|
||||
|
||||
static int selectionStart(EditBox field, int fallback) {
|
||||
public static int selectionStart(EditBox field, int fallback) {
|
||||
if (field == null) return fallback;
|
||||
return Mth.clamp(field.getCursorPosition(), 0, field.getValue().length());
|
||||
}
|
||||
|
||||
static int selectionEnd(EditBox field, int fallback) {
|
||||
public static int selectionEnd(EditBox field, int fallback) {
|
||||
if (field == null) return fallback;
|
||||
try {
|
||||
return Mth.clamp(((TextFieldWidgetAccessor) field).lovisual$getSelectionEnd(), 0, field.getValue().length());
|
||||
|
|
@ -35,12 +35,12 @@ final class BetterChatEditBoxUtil {
|
|||
}
|
||||
}
|
||||
|
||||
static void setCursorSafe(EditBox field, int pos) {
|
||||
public static void setCursorSafe(EditBox field, int pos) {
|
||||
if (field == null) return;
|
||||
field.moveCursorTo(Mth.clamp(pos, 0, field.getValue().length()), false);
|
||||
}
|
||||
|
||||
static String wordAt(String text, int charIndex) {
|
||||
public static String wordAt(String text, int charIndex) {
|
||||
if (text == null || text.isEmpty()) return "";
|
||||
int idx = Mth.clamp(charIndex, 0, Math.max(0, text.length() - 1));
|
||||
int start = idx;
|
||||
|
|
@ -5,23 +5,28 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.input;
|
||||
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.ChatScreen;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatMouseClickHandler;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatSuggestionState;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatDrawUtil;
|
||||
|
||||
/**
|
||||
* Chat-surface interaction: scroll, drag-selection movement, scrollbar drag and hit-testing.
|
||||
* The mouse-button dispatch lives in {@link BetterChatMouseClickHandler}.
|
||||
*/
|
||||
final class BetterChatInputHandler {
|
||||
public final class BetterChatInputHandler {
|
||||
private BetterChatInputHandler() {
|
||||
}
|
||||
|
||||
static BetterChatModel.MousePos resolveMouse(double rawX, double rawY) {
|
||||
public static BetterChatModel.MousePos resolveMouse(double rawX, double rawY) {
|
||||
return new BetterChatModel.MousePos(rawX, rawY, rawX, rawY);
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +63,7 @@ final class BetterChatInputHandler {
|
|||
return new BetterChatModel.PickResult(bestLine, glyph);
|
||||
}
|
||||
|
||||
static boolean onScroll(double delta) {
|
||||
public static boolean onScroll(double delta) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (!BetterChat.isActive()) return false;
|
||||
if (!(ClientScreen.current() instanceof ChatScreen)) return false;
|
||||
|
|
@ -81,7 +86,7 @@ final class BetterChatInputHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
static void onMouseMove(double mx, double my) {
|
||||
public static void onMouseMove(double mx, double my) {
|
||||
if (!BetterChat.isActive()) return;
|
||||
BetterChatModel.MousePos mouse = resolveMouse(mx, my);
|
||||
double fx = mouse.fx();
|
||||
|
|
@ -116,7 +121,7 @@ final class BetterChatInputHandler {
|
|||
BetterChatRenderer.selection.updateCaret(pick.line().messageIndex(), selectionCaretIndex(pick.glyph()));
|
||||
}
|
||||
|
||||
static void resetScroll() {
|
||||
public static void resetScroll() {
|
||||
BetterChatRenderer.scrollOffsetLines = 0;
|
||||
BetterChatRenderer.smoothScrollOffsetLines = 0f;
|
||||
BetterChatRenderer.draggingScrollbar = false;
|
||||
|
|
@ -124,14 +129,14 @@ final class BetterChatInputHandler {
|
|||
BetterChatRenderer.sbDragOffset = 0f;
|
||||
}
|
||||
|
||||
static void onNewMessage() {
|
||||
public static void onNewMessage() {
|
||||
if (BetterChatRenderer.scrollOffsetLines > 0 && BetterChatRenderer.scrollOffsetLines <= 2) {
|
||||
BetterChatRenderer.scrollOffsetLines = 0;
|
||||
BetterChatRenderer.smoothScrollOffsetLines = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isInteractive(float mx, float my) {
|
||||
public static boolean isInteractive(float mx, float my) {
|
||||
boolean inChat = BetterChatRenderer.chatHitW > 0f && mx >= BetterChatRenderer.chatHitX && mx <= BetterChatRenderer.chatHitX + BetterChatRenderer.chatHitW && my >= BetterChatRenderer.chatHitY && my <= BetterChatRenderer.chatHitY + BetterChatRenderer.chatHitH;
|
||||
boolean inSearch = BetterChatRenderer.searchHit && mx >= BetterChatRenderer.searchRectX && mx <= BetterChatRenderer.searchRectX + BetterChatRenderer.searchRectW && my >= BetterChatRenderer.searchRectY && my <= BetterChatRenderer.searchRectY + BetterChatRenderer.searchRectH;
|
||||
boolean inInput = BetterChatRenderer.hasInputBox && mx >= BetterChatRenderer.inputBoxX && mx <= BetterChatRenderer.inputBoxX + BetterChatRenderer.inputBoxW && my >= BetterChatRenderer.inputBoxY && my <= BetterChatRenderer.inputBoxY + BetterChatRenderer.inputBoxH;
|
||||
|
|
@ -141,11 +146,11 @@ final class BetterChatInputHandler {
|
|||
return inChat || inSearch || inInput || inSuggest || inContext || inScrollbar;
|
||||
}
|
||||
|
||||
static boolean contains(float mx, float my) {
|
||||
public static boolean contains(float mx, float my) {
|
||||
return isInteractive(mx, my);
|
||||
}
|
||||
|
||||
static void updateChatHit() {
|
||||
public static void updateChatHit() {
|
||||
float hitBottom = BetterChatRenderer.hasInputBox ? BetterChatRenderer.inputBoxY + BetterChatRenderer.inputBoxH : BetterChatRenderer.ctxFixedInputY;
|
||||
BetterChatRenderer.chatHitX = BetterChatRenderer.ctxBaseX;
|
||||
BetterChatRenderer.chatHitY = BetterChatRenderer.frame.y();
|
||||
|
|
@ -153,7 +158,7 @@ final class BetterChatInputHandler {
|
|||
BetterChatRenderer.chatHitH = Math.max(BetterChatRenderer.ctxBoxHeight, hitBottom - BetterChatRenderer.chatHitY);
|
||||
}
|
||||
|
||||
static void clearChatHit() {
|
||||
public static void clearChatHit() {
|
||||
BetterChatRenderer.chatHitX = BetterChatRenderer.chatHitY = BetterChatRenderer.chatHitW = BetterChatRenderer.chatHitH = 0f;
|
||||
BetterChatRenderer.lastMouseValid = false;
|
||||
}
|
||||
|
|
@ -4,8 +4,8 @@
|
|||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
import dev.loki.lovisual.mixins.accessors.ChatScreenAccessor;
|
||||
package dev.loki.lovisual.features.gui.chat.input;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.render.engine.text.FontInfo;
|
||||
import dev.loki.lovisual.render.engine.text.Fonts;
|
||||
import dev.loki.lovisual.render.engine.text.TextRenderer;
|
||||
|
|
@ -17,13 +17,19 @@ import net.minecraft.client.gui.screens.ChatScreen;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import static dev.loki.lovisual.features.theme.Theme.theme;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatEditBoxUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.messages.BetterChatSearch;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatDrawUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatFontUtil;
|
||||
/**
|
||||
* Input-field rendering: wrapping, caret, selection highlight, char counter and search chip.
|
||||
*/
|
||||
final class BetterChatInputRenderer {
|
||||
public final class BetterChatInputRenderer {
|
||||
private BetterChatInputRenderer() {
|
||||
}
|
||||
static void renderInput(Minecraft mc, float fontSize, float boxW, float x, float inputY) {
|
||||
public static void renderInput(Minecraft mc, float fontSize, float boxW, float x, float inputY) {
|
||||
if (!(ClientScreen.current() instanceof ChatScreen chatScreen)) return;
|
||||
EditBox field = ((ChatScreenAccessor) chatScreen).getChatField();
|
||||
if (field == null) return;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.input;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
|
|
@ -14,16 +14,24 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.gui.screens.ChatScreen;
|
||||
import net.minecraft.util.Mth;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatInputHandler;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatContextMenuHandler;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatContextMenuModel;
|
||||
import dev.loki.lovisual.features.gui.chat.messages.BetterChatSearch;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatSuggestionState;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatDrawUtil;
|
||||
|
||||
/**
|
||||
* Mouse-button dispatch over the chat surface: scrollbar drag, suggest rows, search icon,
|
||||
* context menu and click/selection logic. Delegates textual actions to {@link BetterChatContextMenuHandler}.
|
||||
*/
|
||||
final class BetterChatMouseClickHandler {
|
||||
public final class BetterChatMouseClickHandler {
|
||||
private BetterChatMouseClickHandler() {
|
||||
}
|
||||
|
||||
static boolean onMouseButton(double mx, double my, int button, boolean pressed) {
|
||||
public static boolean onMouseButton(double mx, double my, int button, boolean pressed) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (!BetterChat.isActive()) return false;
|
||||
if (!(ClientScreen.current() instanceof ChatScreen)) return false;
|
||||
|
|
@ -4,11 +4,11 @@
|
|||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.menu;
|
||||
import dev.loki.lovisual.features.command.CommandManager;
|
||||
import dev.loki.lovisual.features.command.CommandPrefix;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
import dev.loki.lovisual.mixins.accessors.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import dev.loki.lovisual.util.text.ChatNameUtil;
|
||||
import dev.loki.lovisual.util.text.ClipboardUtil;
|
||||
|
|
@ -22,13 +22,20 @@ import net.minecraft.util.Mth;
|
|||
import net.minecraft.util.Util;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatEditBoxUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatContextMenuModel;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatTooltipRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatFontUtil;
|
||||
/**
|
||||
* Right-click context menu construction, click-event handling and chat prefill actions.
|
||||
*/
|
||||
final class BetterChatContextMenuHandler {
|
||||
public final class BetterChatContextMenuHandler {
|
||||
private BetterChatContextMenuHandler() {
|
||||
}
|
||||
static void openContextMenu(BetterChatModel.PickResult pick, double mx, double my) {
|
||||
public static void openContextMenu(BetterChatModel.PickResult pick, double mx, double my) {
|
||||
List<BetterChatContextMenuModel.ContextMenu.MenuEntry> entries = new ArrayList<>();
|
||||
ChatLine msg = pick.line().message();
|
||||
String full = msg.text().getString();
|
||||
|
|
@ -69,7 +76,7 @@ final class BetterChatContextMenuHandler {
|
|||
}
|
||||
BetterChatRenderer.contextMenu = BetterChatContextMenuModel.ContextMenu.open((float) mx, (float) my, entries);
|
||||
}
|
||||
static void copySelectionToClipboard() {
|
||||
public static void copySelectionToClipboard() {
|
||||
String selected = selectedText();
|
||||
if (selected.isEmpty()) return;
|
||||
ClipboardUtil.copy(selected);
|
||||
|
|
@ -98,7 +105,7 @@ final class BetterChatContextMenuHandler {
|
|||
}
|
||||
return out.toString();
|
||||
}
|
||||
static boolean handleClickEvent(net.minecraft.network.chat.Style style, boolean ctrlDown) {
|
||||
public static boolean handleClickEvent(net.minecraft.network.chat.Style style, boolean ctrlDown) {
|
||||
if (style == null) return false;
|
||||
ClickEvent evt = style.getClickEvent();
|
||||
if (evt == null) return false;
|
||||
|
|
@ -142,7 +149,7 @@ final class BetterChatContextMenuHandler {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
static boolean tryPrefillTellFromClick(int msgIndex, int charIndex) {
|
||||
public static boolean tryPrefillTellFromClick(int msgIndex, int charIndex) {
|
||||
if (msgIndex < 0 || msgIndex >= BetterChatRenderer.lastMessages.size()) return false;
|
||||
String full = BetterChatRenderer.lastMessages.get(msgIndex).text().getString();
|
||||
List<String> nickCandidates = ChatNameUtil.extractNicks(full);
|
||||
|
|
@ -5,27 +5,30 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.menu;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static dev.loki.lovisual.features.theme.Theme.theme;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatDrawUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatFontUtil;
|
||||
|
||||
/**
|
||||
* Right-click context menu record + entry record, extracted from the renderer facade.
|
||||
* Rendering leans on {@link BetterChatDrawUtil} and {@link BetterChatFontUtil}.
|
||||
*/
|
||||
final class BetterChatContextMenuModel {
|
||||
public final class BetterChatContextMenuModel {
|
||||
private BetterChatContextMenuModel() {
|
||||
}
|
||||
|
||||
record ContextMenu(boolean open, float x, float y, List<MenuEntry> entries) {
|
||||
public record ContextMenu(boolean open, float x, float y, List<MenuEntry> entries) {
|
||||
private static final float MENU_PAD_X = 8f;
|
||||
private static final float MENU_PAD_Y = 7f;
|
||||
private static final float MENU_RADIUS = 9f;
|
||||
|
||||
static ContextMenu closed() {
|
||||
public static ContextMenu closed() {
|
||||
return new ContextMenu(false, 0, 0, Collections.emptyList());
|
||||
}
|
||||
|
||||
|
|
@ -37,13 +40,13 @@ final class BetterChatContextMenuModel {
|
|||
return Math.max(20f, fontSize + 7f);
|
||||
}
|
||||
|
||||
boolean contains(double mx, double my) {
|
||||
public boolean contains(double mx, double my) {
|
||||
if (!open) return false;
|
||||
float fontSize = BetterChatRenderer.lastFontSizeForUi * 0.92f;
|
||||
return mx >= x && mx <= x + width(fontSize) && my >= y && my <= y + height(fontSize);
|
||||
}
|
||||
|
||||
MenuEntry pick(double my) {
|
||||
public MenuEntry pick(double my) {
|
||||
if (!open) return null;
|
||||
float fontSize = BetterChatRenderer.lastFontSizeForUi * 0.92f;
|
||||
float itemH = itemHeight(fontSize);
|
||||
|
|
@ -58,7 +61,7 @@ final class BetterChatContextMenuModel {
|
|||
return null;
|
||||
}
|
||||
|
||||
void render(float mouseX, float mouseY, float fontSize) {
|
||||
public void render(float mouseX, float mouseY, float fontSize) {
|
||||
if (!open || entries.isEmpty()) return;
|
||||
float fs = Math.max(12f, fontSize * 0.92f);
|
||||
float itemH = itemHeight(fs);
|
||||
|
|
@ -106,7 +109,7 @@ final class BetterChatContextMenuModel {
|
|||
return MENU_PAD_Y * 2f + entries.size() * itemHeight(fontSize);
|
||||
}
|
||||
|
||||
record MenuEntry(String label, Runnable action) {
|
||||
public record MenuEntry(String label, Runnable action) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.menu;
|
||||
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.BetterTooltips;
|
||||
import dev.loki.lovisual.util.text.ChatNameUtil;
|
||||
|
|
@ -25,17 +25,22 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static dev.loki.lovisual.features.gui.chat.ChatHoverUtil.HoverTip;
|
||||
import static dev.loki.lovisual.features.gui.chat.text.util.ChatHoverUtil.HoverTip;
|
||||
import static dev.loki.lovisual.features.theme.Theme.theme;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatEditBoxUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatSuggestionState;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.ChatHoverUtil;
|
||||
|
||||
/**
|
||||
* Tooltip capture/render, timestamp formatting and post-pick hover inference.
|
||||
*/
|
||||
final class BetterChatTooltipRenderer {
|
||||
public final class BetterChatTooltipRenderer {
|
||||
private BetterChatTooltipRenderer() {
|
||||
}
|
||||
|
||||
static String formatTimestamp(long ms, boolean withSeconds) {
|
||||
public static String formatTimestamp(long ms, boolean withSeconds) {
|
||||
LocalDateTime dt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ms), ZoneId.systemDefault());
|
||||
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(withSeconds ? "HH:mm:ss" : "HH:mm");
|
||||
return dt.format(fmt);
|
||||
|
|
@ -46,7 +51,7 @@ final class BetterChatTooltipRenderer {
|
|||
return dt.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));
|
||||
}
|
||||
|
||||
static HoverTip buildTimestampTooltip(long ms, boolean showDate, boolean showUnix) {
|
||||
public static HoverTip buildTimestampTooltip(long ms, boolean showDate, boolean showUnix) {
|
||||
List<ChatHoverUtil.ColoredLine> lines = new ArrayList<>();
|
||||
LocalDateTime dt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ms), ZoneId.systemDefault());
|
||||
if (showDate) {
|
||||
|
|
@ -62,7 +67,7 @@ final class BetterChatTooltipRenderer {
|
|||
return new HoverTip(lines, null, false, net.minecraft.world.item.ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
static void captureTooltip(HoverTip tip, float x, float y, GuiGraphicsExtractor ctx) {
|
||||
public static void captureTooltip(HoverTip tip, float x, float y, GuiGraphicsExtractor ctx) {
|
||||
if (tip == null || ctx == null) return;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null || mc.getWindow() == null) return;
|
||||
|
|
@ -81,7 +86,7 @@ final class BetterChatTooltipRenderer {
|
|||
BetterChatRenderer.tooltipCaptured = true;
|
||||
}
|
||||
|
||||
static void renderCapturedTooltip(GuiGraphicsExtractor ctx) {
|
||||
public static void renderCapturedTooltip(GuiGraphicsExtractor ctx) {
|
||||
if (!BetterChatRenderer.tooltipCaptured) return;
|
||||
BetterTooltips.renderTooltipWithContext(ctx);
|
||||
BetterTooltips.resetTooltipScaleOverride();
|
||||
|
|
@ -103,7 +108,7 @@ final class BetterChatTooltipRenderer {
|
|||
/**
|
||||
* Mirror of the original post-pick hover/suggest-inference block (lines 466-518).
|
||||
*/
|
||||
static void resolveHover(BetterChatModel.PickResult hover, Minecraft mc) {
|
||||
public static void resolveHover(BetterChatModel.PickResult hover, Minecraft mc) {
|
||||
boolean overSuggestWindow = BetterChatSuggestionState.isInsideSuggestWindow(
|
||||
BetterChatRenderer.lastMouseFx, BetterChatRenderer.lastMouseFy,
|
||||
BetterChatRenderer.lastMouseFx, BetterChatRenderer.lastMouseFy);
|
||||
|
|
@ -5,19 +5,20 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.messages;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
|
||||
final class BetterChatMessageGrouping {
|
||||
public final class BetterChatMessageGrouping {
|
||||
private static final String BOX_PREFIXES = "┏┓┗┛┃│║╔╗╚╝╭╮╰╯┌┐└┘┣┫├┤╠╣";
|
||||
|
||||
private BetterChatMessageGrouping() {
|
||||
}
|
||||
|
||||
static int[] groupIds(List<ChatLine> messages) {
|
||||
public static int[] groupIds(List<ChatLine> messages) {
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
return new int[0];
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.messages;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
import dev.loki.lovisual.features.gui.hud.HudRenderUtil;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
|
|
@ -14,15 +14,23 @@ import net.minecraft.util.Mth;
|
|||
import java.util.List;
|
||||
import static dev.loki.lovisual.render.engine.text.TextGlyphFallback.isSvgFontKey;
|
||||
import static dev.loki.lovisual.features.theme.Theme.theme;
|
||||
import static dev.loki.lovisual.features.gui.chat.ChatHoverUtil.HoverTip;
|
||||
import static dev.loki.lovisual.features.gui.chat.text.util.ChatHoverUtil.HoverTip;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatTooltipRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatFrameModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.text.BetterChatGlyphRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatDrawUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatFontUtil;
|
||||
/**
|
||||
* Message-area drawing: glass bubbles, selection rectangles, timestamp overlays and glyph pickup.
|
||||
* Extracted from {@link BetterChatRenderer}; behavior unchanged.
|
||||
*/
|
||||
final class BetterChatMessageRenderer {
|
||||
public final class BetterChatMessageRenderer {
|
||||
private BetterChatMessageRenderer() {
|
||||
}
|
||||
static float fade(float ageSeconds) {
|
||||
public static float fade(float ageSeconds) {
|
||||
BetterChat cfg = BetterChat.get();
|
||||
float life = cfg != null ? cfg.fadeSeconds() : 12f;
|
||||
if (life <= 0f) return 0f;
|
||||
|
|
@ -34,7 +42,7 @@ final class BetterChatMessageRenderer {
|
|||
float inv = 1f - t;
|
||||
return inv * inv * inv;
|
||||
}
|
||||
static float newMessageReveal(ChatLine message) {
|
||||
public static float newMessageReveal(ChatLine message) {
|
||||
if (message == null) return 1f;
|
||||
float age = message.ageSeconds();
|
||||
if (age <= 0f) return 0f;
|
||||
|
|
@ -43,7 +51,7 @@ final class BetterChatMessageRenderer {
|
|||
float inv = 1f - t;
|
||||
return 1f - inv * inv * inv;
|
||||
}
|
||||
static BetterChatModel.PickResult drawPhase() {
|
||||
public static BetterChatModel.PickResult drawPhase() {
|
||||
BetterChatFrameModel.FrameLayout frame = BetterChatRenderer.frame;
|
||||
boolean activeChatSurface = BetterChatRenderer.ctxActiveSurface;
|
||||
boolean showTs = BetterChatRenderer.ctxShowTs;
|
||||
|
|
@ -5,13 +5,16 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.messages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatStore;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
|
||||
/**
|
||||
* Minimal search state & async search runner for BetterChat, styled like ClickGuiSearch.
|
||||
|
|
@ -5,12 +5,12 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.messages;
|
||||
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import dev.loki.lovisual.features.command.CommandManager;
|
||||
import dev.loki.lovisual.features.command.CommandPrefix;
|
||||
import dev.loki.lovisual.mixins.accessors.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
|
|
@ -18,6 +18,9 @@ import net.minecraft.client.gui.screens.ChatScreen;
|
|||
import net.minecraft.client.input.KeyEvent;
|
||||
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatEditBoxUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatSuggestionState;
|
||||
|
||||
/**
|
||||
* Tab / arrow-key cycling through the custom {@code @}-command suggestion popup.
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat.model;
|
||||
|
||||
import net.minecraft.network.chat.HoverEvent;
|
||||
import net.minecraft.network.chat.Style;
|
||||
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatContextMenuModel;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatFrameModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
|
||||
/**
|
||||
* Shared package-private records extracted from {@link BetterChatRenderer}.
|
||||
* The only public-facing record kept nested on the facade is {@link BetterChatRenderer.Layout}.
|
||||
* Records that own non-trivial rendering logic ({@code FrameLayout}, {@code ContextMenu}) live in
|
||||
* their own files ({@link BetterChatFrameModel}, {@link BetterChatContextMenuModel}).
|
||||
*/
|
||||
public final class BetterChatModel {
|
||||
private BetterChatModel() {
|
||||
}
|
||||
|
||||
public record MousePos(double rawX, double rawY, double fx, double fy) {
|
||||
}
|
||||
|
||||
public record InputLine(int start, int end, float width) {
|
||||
}
|
||||
|
||||
public record Segment(String text, Style style) {
|
||||
}
|
||||
|
||||
public record VisualLine(ChatLine message, int messageIndex, int messageGroup, int startChar, int endChar, List<Glyph> glyphs) {
|
||||
}
|
||||
|
||||
public record Glyph(
|
||||
float x0,
|
||||
float x1,
|
||||
int charIndex,
|
||||
int charEndExclusive,
|
||||
int color,
|
||||
HoverEvent hover,
|
||||
String font,
|
||||
String text,
|
||||
Style style
|
||||
) {
|
||||
}
|
||||
|
||||
public record GlyphBox(
|
||||
float x0,
|
||||
float y0,
|
||||
float x1,
|
||||
float y1,
|
||||
int charIndex,
|
||||
int charEndExclusive,
|
||||
int color,
|
||||
String font,
|
||||
HoverEvent hover,
|
||||
String text,
|
||||
Style style
|
||||
) {
|
||||
}
|
||||
|
||||
public record CachedLine(int startChar, int endChar, List<Glyph> glyphs) {
|
||||
}
|
||||
|
||||
public record CachedMessageLayout(float fontSize, float maxWidth, List<CachedLine> lines) {
|
||||
}
|
||||
|
||||
public record FrameLine(ChatLine message, int messageIndex, int messageGroup, float y0, float y1, List<GlyphBox> glyphs) {
|
||||
}
|
||||
|
||||
public record MessageBubble(ChatLine message, int messageGroup, float x, float y, float w, float h, FrameLine firstLine, FrameLine lastLine) {
|
||||
}
|
||||
|
||||
public record MessageClipBounds(float x, float y, float w, float h) {
|
||||
}
|
||||
|
||||
public record PickResult(FrameLine line, GlyphBox glyph) {
|
||||
}
|
||||
|
||||
public record LayoutResult(List<VisualLine> lines, int messagesSeen) {
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,14 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.model;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
|
||||
public final class BetterChatStore {
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.model;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
|
@ -30,6 +30,10 @@ import java.io.FileWriter;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatStore;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatHoverCache;
|
||||
|
||||
/**
|
||||
* Per-world chat storage + async Gson persistence.
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.model;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
|
|
@ -5,28 +5,30 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.render;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
|
||||
/**
|
||||
* Frame layout record: builds screen-space line/bubble geometry from laid-out visual lines.
|
||||
* Pure geometry + picking, no rendering or font work.
|
||||
*/
|
||||
final class BetterChatFrameModel {
|
||||
public final class BetterChatFrameModel {
|
||||
private BetterChatFrameModel() {
|
||||
}
|
||||
|
||||
record FrameLayout(float x, float y, float w, float h, List<BetterChatModel.FrameLine> lines, List<BetterChatModel.MessageBubble> bubbles) {
|
||||
public record FrameLayout(float x, float y, float w, float h, List<BetterChatModel.FrameLine> lines, List<BetterChatModel.MessageBubble> bubbles) {
|
||||
static FrameLayout empty() {
|
||||
return new FrameLayout(0, 0, 0, 0, Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
static FrameLayout build(List<BetterChatModel.VisualLine> lines, float x, float y, float w, float h, float fontSize, float lineHeight, float yOffset, float timestampReserve) {
|
||||
public static FrameLayout build(List<BetterChatModel.VisualLine> lines, float x, float y, float w, float h, float fontSize, float lineHeight, float yOffset, float timestampReserve) {
|
||||
List<BetterChatModel.FrameLine> list = new ArrayList<>(lines.size());
|
||||
float cursorY = y + BetterChatRenderer.MESSAGE_PAD_Y + yOffset;
|
||||
int lastMessageGroup = Integer.MIN_VALUE;
|
||||
|
|
@ -90,7 +92,7 @@ final class BetterChatFrameModel {
|
|||
return bubbles;
|
||||
}
|
||||
|
||||
BetterChatModel.MessageClipBounds messageClipBounds() {
|
||||
public BetterChatModel.MessageClipBounds messageClipBounds() {
|
||||
if (lines.isEmpty() || h <= 0f) return null;
|
||||
float clipX = x + BetterChatRenderer.PADDING;
|
||||
float clipW = Math.max(1f, w - BetterChatRenderer.PADDING * 2f);
|
||||
|
|
@ -101,7 +103,7 @@ final class BetterChatFrameModel {
|
|||
return mx >= x && mx <= x + w && my >= y && my <= y + h;
|
||||
}
|
||||
|
||||
BetterChatModel.PickResult pick(double mx, double my) {
|
||||
public BetterChatModel.PickResult pick(double mx, double my) {
|
||||
if (!contains(mx, my)) return null;
|
||||
for (BetterChatModel.FrameLine line : lines) {
|
||||
if (my < line.y0() || my > line.y1() + BetterChatRenderer.LINE_SPACING) continue;
|
||||
|
|
@ -5,18 +5,30 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.render;
|
||||
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.text.TextRenderer;
|
||||
import dev.loki.lovisual.render.helpers.SystemCursor;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import dev.loki.lovisual.features.gui.chat.BetterChatViewport;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatInputHandler;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatInputRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatTooltipRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.messages.BetterChatMessageRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.messages.BetterChatSearch;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatSuggestionRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatSuggestionState;
|
||||
import dev.loki.lovisual.features.gui.chat.text.BetterChatLayoutEngine;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatDrawUtil;
|
||||
|
||||
/**
|
||||
* Single entry point for the chat HUD render pass; orchestrates the layout, message, input,
|
||||
* suggestion, scrollbar and tooltip phases. All real work lives in the other package-private helpers.
|
||||
*/
|
||||
final class BetterChatFrameRenderer {
|
||||
public final class BetterChatFrameRenderer {
|
||||
private BetterChatFrameRenderer() {
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat.render;
|
||||
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import dev.loki.lovisual.features.command.CommandOutput;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
import dev.loki.lovisual.render.engine.color.RenderColor;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.text.TextRenderer;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.client.gui.screens.ChatScreen;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import dev.loki.lovisual.util.text.TextSelection;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatInputHandler;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatMouseClickHandler;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatContextMenuHandler;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatContextMenuModel;
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatTooltipRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatStore;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatFrameModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatFrameRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.CommandSuggestorBridge;
|
||||
import dev.loki.lovisual.features.gui.chat.text.BetterChatLayoutEngine;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.ChatHoverUtil;
|
||||
|
||||
/**
|
||||
* Facade for the BetterChat HUD renderer. This enum singleton is now a thin delegation point:
|
||||
* it owns the shared per-frame state and constants read/written by the package-private helpers,
|
||||
* and re-exports the original public API exactly as before so no caller changes.
|
||||
*/
|
||||
public enum BetterChatRenderer {
|
||||
;
|
||||
|
||||
// ---- Constants (package-private; read by helpers) ----
|
||||
public static final int MAX_MESSAGE_CHARS = 256;
|
||||
public static final float PADDING = 8f;
|
||||
public static final float LINE_SPACING = 3f;
|
||||
public static final float RADIUS = 24f;
|
||||
public static final float MESSAGE_PAD_X = 8f;
|
||||
public static final float MESSAGE_PAD_Y = 5f;
|
||||
public static final float MESSAGE_GAP = 4f;
|
||||
public static final float MESSAGE_INPUT_GAP = 8f;
|
||||
public static final float MESSAGE_RADIUS = 9f;
|
||||
public static final float MESSAGE_TS_GAP = 8f;
|
||||
public static final float INPUT_RADIUS = 12f;
|
||||
public static final float SEARCH_ICON_WIDTH = 34f;
|
||||
public static final float SEARCH_ICON_HEIGHT = 22f;
|
||||
public static final float NEW_MESSAGE_REVEAL_SECONDS = 0.34f;
|
||||
public static final float NEW_MESSAGE_SLIDE_PX = 18f;
|
||||
public static final int MAX_HISTORY = 32000;
|
||||
public static final float CHAT_TOOLTIP_OFFSET_X = 6f;
|
||||
public static final float CHAT_TOOLTIP_OFFSET_Y = 12f;
|
||||
public static final double DRAG_SELECT_THRESHOLD = 4.0;
|
||||
public static final RenderColor TMP_COLOR = new RenderColor(0xFFFFFFFF);
|
||||
|
||||
// ---- Layout caches (package-private) ----
|
||||
public static final IdentityHashMap<ChatLine, BetterChatModel.CachedMessageLayout> MESSAGE_LAYOUT_CACHE = new IdentityHashMap<>();
|
||||
public static final IdentityHashMap<ChatLine, List<BetterChatModel.Segment>> SEGMENT_CACHE = new IdentityHashMap<>();
|
||||
public static final IdentityHashMap<CommandOutput.MessageLine, ChatLine> COMMAND_LINE_CACHE = new IdentityHashMap<>();
|
||||
public static final TextSelection selection = new TextSelection();
|
||||
|
||||
// ---- Persistent UI state (package-private) ----
|
||||
public static Renderer2D renderer;
|
||||
public static float lastRenderWidth = 0f;
|
||||
public static float lastRenderHeight = 0f;
|
||||
public static int scrollOffsetLines = 0;
|
||||
public static float smoothScrollOffsetLines = 0f;
|
||||
public static int maxScrollLines = 0;
|
||||
public static float lastFontSizeForUi = 16f;
|
||||
public static List<ChatLine> lastMessages = Collections.emptyList();
|
||||
public static boolean draggingScrollbar = false;
|
||||
public static float sbTrackX, sbTrackY, sbTrackW, sbTrackH;
|
||||
public static float sbThumbY, sbThumbH;
|
||||
public static float sbDragOffset = 0f;
|
||||
public static long lastScrollInteractionMs = 0L;
|
||||
public static boolean sbEnabled = false;
|
||||
public static int sbTotalLines = 0;
|
||||
public static int sbVisibleLines = 0;
|
||||
public static float inputBoxX = 0f, inputBoxY = 0f, inputBoxW = 0f, inputBoxH = 0f;
|
||||
public static boolean hasInputBox = false;
|
||||
public static boolean suggestActive = false;
|
||||
public static float suggestX, suggestY, suggestW, suggestH, suggestItemH;
|
||||
public static float suggestRowsX, suggestRowsY, suggestRowsW, suggestRowsH;
|
||||
public static int suggestStart, suggestVisible, suggestTotal;
|
||||
public static net.minecraft.client.gui.components.CommandSuggestions.SuggestionsList suggestWindow;
|
||||
public static List<Suggestion> suggestEntries = Collections.emptyList();
|
||||
public static boolean lastHoverWasOutsideSuggest = true;
|
||||
public static boolean suggestHasScrollbar = false;
|
||||
public static boolean suggestDraggingScrollbar = false;
|
||||
public static float suggestTrackX, suggestTrackY, suggestTrackW, suggestTrackH, suggestThumbH;
|
||||
public static int suggestCurrentStart = -1;
|
||||
public static int suggestCurrentSelection = -1;
|
||||
public static double lastMouseFx = 0.0;
|
||||
public static double lastMouseFy = 0.0;
|
||||
public static boolean lastMouseValid = false;
|
||||
public static boolean tooltipCaptured = false;
|
||||
public static String lastSuggestInput = "";
|
||||
public static int lastSuggestCursor = -1;
|
||||
public static int lastSuggestSelStart = -1;
|
||||
public static int lastSuggestSelEnd = -1;
|
||||
public static CommandSuggestorBridge.SuggestionSnapshot lastSnapshot = null;
|
||||
public static long debugPerfWindowStartNs = 0L;
|
||||
public static long debugPerfAccumLayoutNs = 0L;
|
||||
public static long debugPerfAccumBuildNs = 0L;
|
||||
public static int debugPerfAccumFrames = 0;
|
||||
public static BetterChatFrameModel.FrameLayout frame = BetterChatFrameModel.FrameLayout.empty();
|
||||
public static BetterChatContextMenuModel.ContextMenu contextMenu = BetterChatContextMenuModel.ContextMenu.closed();
|
||||
public static boolean selecting = false;
|
||||
public static boolean searchHit = false;
|
||||
public static float searchRectX = 0f, searchRectY = 0f, searchRectW = 0f, searchRectH = 0f;
|
||||
public static boolean searchHotkeyDown = false;
|
||||
public static float chatHitX = 0f, chatHitY = 0f, chatHitW = 0f, chatHitH = 0f;
|
||||
public static String hoverEntityUuid = null;
|
||||
public static ChatScreen storedScreen;
|
||||
public static BetterChatStore previewStore;
|
||||
public static int lastSuggestionHash = 0;
|
||||
public static boolean leftPending = false;
|
||||
public static double leftDownFx = 0.0, leftDownFy = 0.0;
|
||||
public static int leftDownMsgIndex = -1;
|
||||
public static int leftDownCharIndex = -1;
|
||||
public static Style leftDownStyle = null;
|
||||
|
||||
// ---- Per-frame transient context (package-private; populated by the phased helpers) ----
|
||||
public static GuiGraphicsExtractor ctxGui;
|
||||
public static float ctxBaseX, ctxBaseY;
|
||||
public static BetterChat ctxSettings;
|
||||
public static net.minecraft.client.Minecraft ctxMc;
|
||||
public static boolean ctxPreview, ctxChatOpen, ctxActiveSurface;
|
||||
public static boolean ctxShowTs, ctxHasSeconds, ctxTipDate, ctxTipUnix, ctxAutoFollow;
|
||||
public static int ctxTsColor, ctxTotalMessages, ctxVisibleLimit, ctxMessagesSeen;
|
||||
public static float ctxWidth, ctxContentWidth, ctxTsWidth;
|
||||
public static float ctxLineHeight, ctxBoxHeight, ctxLineYOffset, ctxTargetVisible;
|
||||
public static float ctxStableBoxHeight, ctxFixedInputY, ctxFrameY;
|
||||
public static int[] ctxMessageGroups;
|
||||
public static List<BetterChatModel.VisualLine> ctxAllLines;
|
||||
public static List<BetterChatModel.VisualLine> ctxVisible;
|
||||
public static long ctxPerfT0, ctxPerfT1;
|
||||
public static ChatHoverUtil.HoverTip ctxHoverTip;
|
||||
|
||||
// ---- Public API (delegating) ----
|
||||
|
||||
public static void markLayoutDirty() {
|
||||
BetterChatLayoutEngine.markLayoutDirty();
|
||||
}
|
||||
|
||||
public static void renderEngine(Renderer2D rendererIn,
|
||||
TextRenderer fallback,
|
||||
GuiGraphicsExtractor ctx,
|
||||
int screenW,
|
||||
float baseX,
|
||||
float baseY) {
|
||||
BetterChatFrameRenderer.renderEngine(rendererIn, fallback, ctx, screenW, baseX, baseY);
|
||||
}
|
||||
|
||||
public static void renderCapturedTooltip(GuiGraphicsExtractor ctx) {
|
||||
BetterChatTooltipRenderer.renderCapturedTooltip(ctx);
|
||||
}
|
||||
|
||||
public static boolean onScroll(double delta) {
|
||||
return BetterChatInputHandler.onScroll(delta);
|
||||
}
|
||||
|
||||
public static boolean onMouseButton(double mx, double my, int button, boolean pressed) {
|
||||
return BetterChatMouseClickHandler.onMouseButton(mx, my, button, pressed);
|
||||
}
|
||||
|
||||
public static void onMouseMove(double mx, double my) {
|
||||
BetterChatInputHandler.onMouseMove(mx, my);
|
||||
}
|
||||
|
||||
public static void copySelectionToClipboard() {
|
||||
BetterChatContextMenuHandler.copySelectionToClipboard();
|
||||
}
|
||||
|
||||
public static void resetScroll() {
|
||||
BetterChatInputHandler.resetScroll();
|
||||
}
|
||||
|
||||
public static void applyAutoPrefixIfNeeded(EditBox field) {
|
||||
// Compact BetterChat no longer owns chat-prefix chips; hook kept for compatibility.
|
||||
}
|
||||
|
||||
public static void setSuggestionContext(ChatScreen screen, GuiGraphicsExtractor context, int mouseX, int mouseY) {
|
||||
storedScreen = screen;
|
||||
}
|
||||
|
||||
public static void onNewMessage() {
|
||||
BetterChatInputHandler.onNewMessage();
|
||||
}
|
||||
|
||||
public static float getLastWidth() {
|
||||
return lastRenderWidth;
|
||||
}
|
||||
|
||||
public static float getLastHeight() {
|
||||
return lastRenderHeight;
|
||||
}
|
||||
|
||||
public static boolean contains(float mx, float my) {
|
||||
return BetterChatInputHandler.contains(mx, my);
|
||||
}
|
||||
|
||||
public static boolean isInteractive(float mx, float my) {
|
||||
return BetterChatInputHandler.isInteractive(mx, my);
|
||||
}
|
||||
|
||||
public record Layout(float x, float y) {
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.suggestions;
|
||||
|
||||
import dev.loki.lovisual.features.command.CommandOutput;
|
||||
|
||||
|
|
@ -13,16 +13,18 @@ import java.util.ArrayList;
|
|||
import java.util.Comparator;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
|
||||
/**
|
||||
* Merges command-output lines into the visible chat tail, sharing the identity-keyed
|
||||
* command-line cache held on {@link BetterChatRenderer}.
|
||||
*/
|
||||
final class BetterChatCommandMerge {
|
||||
public final class BetterChatCommandMerge {
|
||||
private BetterChatCommandMerge() {
|
||||
}
|
||||
|
||||
static List<ChatLine> mergeCommandOutput(List<ChatLine> storeLines,
|
||||
public static List<ChatLine> mergeCommandOutput(List<ChatLine> storeLines,
|
||||
List<CommandOutput.MessageLine> commandLines,
|
||||
int needed) {
|
||||
if (commandLines == null || commandLines.isEmpty()) {
|
||||
|
|
@ -4,22 +4,28 @@
|
|||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.suggestions;
|
||||
import dev.loki.lovisual.features.command.CommandPrefix;
|
||||
import dev.loki.lovisual.mixins.accessors.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.render.helpers.SystemCursor;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.util.Mth;
|
||||
import java.util.List;
|
||||
import static dev.loki.lovisual.features.theme.Theme.theme;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatEditBoxUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatSuggestionState;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.CommandSuggestorBridge;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatDrawUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatFontUtil;
|
||||
/**
|
||||
* Draws the autocomplete suggestion popup above the input field each frame.
|
||||
*/
|
||||
final class BetterChatSuggestionRenderer {
|
||||
public final class BetterChatSuggestionRenderer {
|
||||
private BetterChatSuggestionRenderer() {
|
||||
}
|
||||
static void renderSuggestions() {
|
||||
public static void renderSuggestions() {
|
||||
BetterChatRenderer.suggestActive = false;
|
||||
BetterChatRenderer.suggestHasScrollbar = false;
|
||||
if (BetterChatRenderer.storedScreen == null) return;
|
||||
|
|
@ -5,12 +5,12 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.suggestions;
|
||||
|
||||
import dev.loki.lovisual.features.command.CommandManager;
|
||||
import dev.loki.lovisual.features.command.CommandPrefix;
|
||||
import dev.loki.lovisual.mixins.accessors.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.SuggestionWindowAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.SuggestionWindowAccessor;
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
|
|
@ -24,36 +24,41 @@ import java.util.List;
|
|||
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_PRESS;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetKey;
|
||||
import dev.loki.lovisual.features.gui.chat.input.BetterChatEditBoxUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.messages.BetterChatSearch;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatSuggestionRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.CommandSuggestorBridge;
|
||||
|
||||
/**
|
||||
* Autocomplete suggestion window state: hotkey, snapshot capture, window dragging and hit-test.
|
||||
* Rendering lives in {@link BetterChatSuggestionRenderer}.
|
||||
*/
|
||||
final class BetterChatSuggestionState {
|
||||
public final class BetterChatSuggestionState {
|
||||
private BetterChatSuggestionState() {
|
||||
}
|
||||
|
||||
static boolean isInsideSuggestRows(double mx, double my) {
|
||||
public static boolean isInsideSuggestRows(double mx, double my) {
|
||||
if (!BetterChatRenderer.suggestActive) return false;
|
||||
return mx >= BetterChatRenderer.suggestRowsX && mx <= BetterChatRenderer.suggestRowsX + BetterChatRenderer.suggestRowsW
|
||||
&& my >= BetterChatRenderer.suggestRowsY && my <= BetterChatRenderer.suggestRowsY + BetterChatRenderer.suggestRowsH;
|
||||
}
|
||||
|
||||
static boolean isInsideSuggestRows(double mx, double my, double rawX, double rawY) {
|
||||
public static boolean isInsideSuggestRows(double mx, double my, double rawX, double rawY) {
|
||||
return isInsideSuggestRows(mx, my) || isInsideSuggestRows(rawX, rawY);
|
||||
}
|
||||
|
||||
static boolean isInsideSuggestWindow(double mx, double my) {
|
||||
public static boolean isInsideSuggestWindow(double mx, double my) {
|
||||
if (!BetterChatRenderer.suggestActive) return false;
|
||||
return mx >= BetterChatRenderer.suggestX && mx <= BetterChatRenderer.suggestX + BetterChatRenderer.suggestW
|
||||
&& my >= BetterChatRenderer.suggestY && my <= BetterChatRenderer.suggestY + BetterChatRenderer.suggestH;
|
||||
}
|
||||
|
||||
static boolean isInsideSuggestWindow(double mx, double my, double rawX, double rawY) {
|
||||
public static boolean isInsideSuggestWindow(double mx, double my, double rawX, double rawY) {
|
||||
return isInsideSuggestWindow(mx, my) || isInsideSuggestWindow(rawX, rawY);
|
||||
}
|
||||
|
||||
static boolean isInsideSuggestScrollbar(double mx, double my) {
|
||||
public static boolean isInsideSuggestScrollbar(double mx, double my) {
|
||||
if (!BetterChatRenderer.suggestActive || !BetterChatRenderer.suggestHasScrollbar) return false;
|
||||
float hitPad = 4f;
|
||||
return mx >= BetterChatRenderer.suggestTrackX - hitPad &&
|
||||
|
|
@ -62,7 +67,7 @@ final class BetterChatSuggestionState {
|
|||
my <= BetterChatRenderer.suggestTrackY + BetterChatRenderer.suggestTrackH + hitPad;
|
||||
}
|
||||
|
||||
static void resetSuggestionTracking() {
|
||||
public static void resetSuggestionTracking() {
|
||||
BetterChatRenderer.lastSuggestInput = "";
|
||||
BetterChatRenderer.lastSuggestCursor = -1;
|
||||
BetterChatRenderer.lastSuggestSelStart = -1;
|
||||
|
|
@ -76,7 +81,7 @@ final class BetterChatSuggestionState {
|
|||
BetterChatRenderer.suggestCurrentSelection = -1;
|
||||
}
|
||||
|
||||
static char commandPrefix(String text) {
|
||||
public static char commandPrefix(String text) {
|
||||
if (text == null) return 0;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
|
|
@ -101,7 +106,7 @@ final class BetterChatSuggestionState {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void applySuggestionWindowState(int start, int selection) {
|
||||
public static void applySuggestionWindowState(int start, int selection) {
|
||||
if (BetterChatRenderer.suggestWindow == null) return;
|
||||
SuggestionWindowAccessor acc = (SuggestionWindowAccessor) BetterChatRenderer.suggestWindow;
|
||||
acc.setLoVisual$inWindowIndex(Math.max(0, start));
|
||||
|
|
@ -110,7 +115,7 @@ final class BetterChatSuggestionState {
|
|||
}
|
||||
}
|
||||
|
||||
static void applySuggestion(int idx) {
|
||||
public static void applySuggestion(int idx) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (!(ClientScreen.current() instanceof ChatScreen chatScreen)) return;
|
||||
EditBox field = ((ChatScreenAccessor) chatScreen).getChatField();
|
||||
|
|
@ -134,7 +139,7 @@ final class BetterChatSuggestionState {
|
|||
}
|
||||
}
|
||||
|
||||
static void scrollSuggestToPosition(double my) {
|
||||
public static void scrollSuggestToPosition(double my) {
|
||||
if (!BetterChatRenderer.suggestHasScrollbar || BetterChatRenderer.suggestVisible <= 0 || BetterChatRenderer.suggestTotal <= BetterChatRenderer.suggestVisible)
|
||||
return;
|
||||
float trackSpan = BetterChatRenderer.suggestTrackH - BetterChatRenderer.suggestThumbH;
|
||||
|
|
@ -160,7 +165,7 @@ final class BetterChatSuggestionState {
|
|||
return new CommandSuggestorBridge.SuggestionSnapshot(0, 0, 0, 0, texts, suggestions, selection, start, visible);
|
||||
}
|
||||
|
||||
static void updateSearchHotkey(Minecraft mc, boolean chatOpen) {
|
||||
public static void updateSearchHotkey(Minecraft mc, boolean chatOpen) {
|
||||
if (!chatOpen || mc == null || mc.getWindow() == null) {
|
||||
BetterChatRenderer.searchHotkeyDown = false;
|
||||
return;
|
||||
|
|
@ -177,7 +182,7 @@ final class BetterChatSuggestionState {
|
|||
BetterChatRenderer.searchHotkeyDown = down;
|
||||
}
|
||||
|
||||
static void toggleSearchMode() {
|
||||
public static void toggleSearchMode() {
|
||||
if (BetterChatSearch.isActive() || BetterChatSearch.hasQuery()) {
|
||||
BetterChatSearch.deactivate();
|
||||
} else {
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.suggestions;
|
||||
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
|
|
@ -16,15 +16,16 @@ import net.minecraft.client.gui.components.EditBox;
|
|||
import net.minecraft.client.gui.screens.ChatScreen;
|
||||
import net.minecraft.client.multiplayer.PlayerInfo;
|
||||
import net.minecraft.client.renderer.Rect2i;
|
||||
import dev.loki.lovisual.mixins.accessors.ChatInputSuggestorAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.ChatScreenSuggestorAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.SuggestionWindowAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.ChatInputSuggestorAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.ChatScreenAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.ChatScreenSuggestorAccessor;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.SuggestionWindowAccessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
|
||||
/**
|
||||
* Reflection helpers for vanilla ChatInputSuggestor; lets us draw suggestions in custom style.
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.text;
|
||||
|
||||
import dev.loki.lovisual.features.command.CommandOutput;
|
||||
import dev.loki.lovisual.render.engine.svg.SvgRenderOptions;
|
||||
|
|
@ -15,15 +15,20 @@ import dev.loki.lovisual.util.time.GameClock;
|
|||
import net.minecraft.network.chat.Component;
|
||||
|
||||
import java.util.List;
|
||||
import dev.loki.lovisual.features.gui.chat.messages.BetterChatMessageRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatDrawUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatFontUtil;
|
||||
|
||||
/**
|
||||
* Glyph + command-prefix gradient rendering for laid-out frame lines.
|
||||
*/
|
||||
final class BetterChatGlyphRenderer {
|
||||
public final class BetterChatGlyphRenderer {
|
||||
private BetterChatGlyphRenderer() {
|
||||
}
|
||||
|
||||
static void renderGlyphs(List<BetterChatModel.FrameLine> lines, float fontSize, float lineHeight, boolean activeChatSurface) {
|
||||
public static void renderGlyphs(List<BetterChatModel.FrameLine> lines, float fontSize, float lineHeight, boolean activeChatSurface) {
|
||||
if (lines == null || lines.isEmpty()) return;
|
||||
float scale = BetterChatFontUtil.scaleForSize(fontSize);
|
||||
TextRenderer current = null;
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.text;
|
||||
import dev.loki.lovisual.features.command.CommandOutput;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.DraggableHudElementRegistry;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
|
|
@ -17,10 +17,22 @@ import net.minecraft.network.chat.Component;
|
|||
import net.minecraft.util.Mth;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
final class BetterChatLayoutEngine {
|
||||
import dev.loki.lovisual.features.gui.chat.menu.BetterChatContextMenuModel;
|
||||
import dev.loki.lovisual.features.gui.chat.messages.BetterChatMessageGrouping;
|
||||
import dev.loki.lovisual.features.gui.chat.messages.BetterChatSearch;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatStore;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatStoreManager;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatCommandMerge;
|
||||
import dev.loki.lovisual.features.gui.chat.suggestions.BetterChatSuggestionState;
|
||||
import dev.loki.lovisual.features.gui.chat.text.BetterChatLineLayout;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatFontUtil;
|
||||
public final class BetterChatLayoutEngine {
|
||||
private BetterChatLayoutEngine() {
|
||||
}
|
||||
static void markLayoutDirty() {
|
||||
public static void markLayoutDirty() {
|
||||
}
|
||||
static BetterChatModel.LayoutResult layoutLinesTailCached(List<ChatLine> messages, int[] messageGroups, float fontSize, float maxWidth, int maxLines) {
|
||||
if (messages.isEmpty()) return new BetterChatModel.LayoutResult(Collections.emptyList(), 0);
|
||||
|
|
@ -57,7 +69,7 @@ final class BetterChatLayoutEngine {
|
|||
/**
|
||||
* @return false when there is nothing to render this frame (caller must bail before draw phase).
|
||||
*/
|
||||
static boolean prepareFrame(GuiGraphicsExtractor ctx, int screenW, float baseX, float baseY) {
|
||||
public static boolean prepareFrame(GuiGraphicsExtractor ctx, int screenW, float baseX, float baseY) {
|
||||
BetterChatRenderer.ctxGui = ctx;
|
||||
BetterChatRenderer.ctxBaseX = baseX;
|
||||
BetterChatRenderer.ctxBaseY = baseY;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.text;
|
||||
|
||||
import net.minecraft.network.chat.HoverEvent;
|
||||
import net.minecraft.network.chat.Style;
|
||||
|
|
@ -17,12 +17,17 @@ import dev.loki.lovisual.render.engine.text.TextGlyphFallback;
|
|||
import dev.loki.lovisual.render.engine.text.TextRenderer;
|
||||
|
||||
import static dev.loki.lovisual.features.theme.Theme.theme;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.model.ChatLine;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.text.BetterChatSegmentUtil;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatFontUtil;
|
||||
|
||||
/**
|
||||
* Builds per-message wrapped glyph lines (cached on identity maps held by the facade).
|
||||
* Extracted from {@link BetterChatRenderer}; behavior unchanged.
|
||||
*/
|
||||
final class BetterChatLineLayout {
|
||||
public final class BetterChatLineLayout {
|
||||
private BetterChatLineLayout() {
|
||||
}
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.text;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.HoverEvent;
|
||||
|
|
@ -20,11 +20,14 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
|
||||
import static dev.loki.lovisual.features.theme.Theme.theme;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatModel;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatStoreManager;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatHoverCache;
|
||||
|
||||
/**
|
||||
* Segment flattening and per-segment color resolution for the chat line layout.
|
||||
*/
|
||||
final class BetterChatSegmentUtil {
|
||||
public final class BetterChatSegmentUtil {
|
||||
private BetterChatSegmentUtil() {
|
||||
}
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.text.util;
|
||||
|
||||
import dev.loki.lovisual.features.gui.clickgui.layout.screen.settings.SettingsGuiPalette;
|
||||
import dev.loki.lovisual.features.gui.hud.HudRenderUtil;
|
||||
|
|
@ -15,31 +15,32 @@ import dev.loki.lovisual.render.helpers.SystemCursor;
|
|||
import net.minecraft.util.Mth;
|
||||
|
||||
import static dev.loki.lovisual.features.theme.Theme.theme;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
|
||||
/**
|
||||
* Low-level draw primitives + color helpers shared by the chat renderer helpers.
|
||||
*/
|
||||
final class BetterChatDrawUtil {
|
||||
public final class BetterChatDrawUtil {
|
||||
private BetterChatDrawUtil() {
|
||||
}
|
||||
|
||||
static int withAlpha(int argb, int alpha) {
|
||||
public static int withAlpha(int argb, int alpha) {
|
||||
return (argb & 0x00FFFFFF) | ((alpha & 0xFF) << 24);
|
||||
}
|
||||
|
||||
static int mulAlpha(int argb, float a) {
|
||||
public static int mulAlpha(int argb, float a) {
|
||||
int alpha = (int) (((argb >>> 24) & 0xFF) * a);
|
||||
return (argb & 0x00FFFFFF) | (Mth.clamp(alpha, 0, 255) << 24);
|
||||
}
|
||||
|
||||
static void applyColor(int argb) {
|
||||
public static void applyColor(int argb) {
|
||||
BetterChatRenderer.TMP_COLOR.a = (argb >>> 24) & 0xFF;
|
||||
BetterChatRenderer.TMP_COLOR.r = (argb >>> 16) & 0xFF;
|
||||
BetterChatRenderer.TMP_COLOR.g = (argb >>> 8) & 0xFF;
|
||||
BetterChatRenderer.TMP_COLOR.b = argb & 0xFF;
|
||||
}
|
||||
|
||||
static void drawRoundedRect(float x, float y, float w, float h, float radius, int argb) {
|
||||
public static void drawRoundedRect(float x, float y, float w, float h, float radius, int argb) {
|
||||
if (BetterChatRenderer.renderer == null) return;
|
||||
if (((argb >>> 24) & 0xFF) <= 0) return;
|
||||
BetterChat cfg = BetterChat.get();
|
||||
|
|
@ -47,7 +48,7 @@ final class BetterChatDrawUtil {
|
|||
HudRenderUtil.drawHudBackground(BetterChatRenderer.renderer, x, y, w, h, radius, 1.1f, argb, useGradient);
|
||||
}
|
||||
|
||||
static void drawRoundedRectStrokeGradient(float x, float y, float w, float h, float radius, float thickness,
|
||||
public static void drawRoundedRectStrokeGradient(float x, float y, float w, float h, float radius, float thickness,
|
||||
int startArgb, int endArgb) {
|
||||
if (BetterChatRenderer.renderer == null) return;
|
||||
if (((startArgb >>> 24) & 0xFF) <= 0 && ((endArgb >>> 24) & 0xFF) <= 0) return;
|
||||
|
|
@ -69,7 +70,7 @@ final class BetterChatDrawUtil {
|
|||
}
|
||||
}
|
||||
|
||||
static void drawGlassPill(float x, float y, float w, float h, float radius, float alpha, boolean softPanel) {
|
||||
public static void drawGlassPill(float x, float y, float w, float h, float radius, float alpha, boolean softPanel) {
|
||||
if (BetterChatRenderer.renderer == null || w <= 0f || h <= 0f) return;
|
||||
BetterChat settings = BetterChat.get();
|
||||
float drawAlpha = Mth.clamp(alpha, 0.0f, 1.0f);
|
||||
|
|
@ -107,7 +108,7 @@ final class BetterChatDrawUtil {
|
|||
}
|
||||
}
|
||||
|
||||
static void drawLiquidHoverPill(float x, float y, float w, float h, float radius, float alpha) {
|
||||
public static void drawLiquidHoverPill(float x, float y, float w, float h, float radius, float alpha) {
|
||||
if (BetterChatRenderer.renderer == null || w <= 0f || h <= 0f || alpha <= 0.001f) return;
|
||||
flushRenderer();
|
||||
BetterChatRenderer.renderer.liquidGlassRect(
|
||||
|
|
@ -130,7 +131,7 @@ final class BetterChatDrawUtil {
|
|||
withAlpha(theme().accent(), 0x34));
|
||||
}
|
||||
|
||||
static void drawScrollbarGlass(float trackX, float trackY, float trackW, float trackH,
|
||||
public static void drawScrollbarGlass(float trackX, float trackY, float trackW, float trackH,
|
||||
float thumbY, float thumbH, boolean hot, int trackAlpha, int thumbAlpha) {
|
||||
if (BetterChatRenderer.renderer == null || trackW <= 0f || trackH <= 0f || thumbH <= 0f) return;
|
||||
SettingsGuiPalette palette = SettingsGuiPalette.current();
|
||||
|
|
@ -143,7 +144,7 @@ final class BetterChatDrawUtil {
|
|||
BetterChatRenderer.renderer.roundedRectGradientQuad(trackX, thumbY, trackW, thumbH, radius, 1.0f, handleA, handleB, handleB, handleA);
|
||||
}
|
||||
|
||||
static void renderScrollbar(float boxX, float boxY, float boxW, float boxH,
|
||||
public static void renderScrollbar(float boxX, float boxY, float boxW, float boxH,
|
||||
int startLine, int visibleLines, int totalLines, double mouseX, double mouseY) {
|
||||
if (visibleLines <= 0 || totalLines <= visibleLines) {
|
||||
BetterChatRenderer.sbEnabled = false;
|
||||
|
|
@ -177,7 +178,7 @@ final class BetterChatDrawUtil {
|
|||
drawScrollbarGlass(trackX, trackY, trackW, trackH, thumbY, thumbH, hot, trackAlpha, thumbAlpha);
|
||||
}
|
||||
|
||||
static boolean isScrollbarHovered(double mx, double my) {
|
||||
public static boolean isScrollbarHovered(double mx, double my) {
|
||||
float hitPad = 5f;
|
||||
return BetterChatRenderer.sbEnabled &&
|
||||
mx >= BetterChatRenderer.sbTrackX - hitPad &&
|
||||
|
|
@ -186,7 +187,7 @@ final class BetterChatDrawUtil {
|
|||
my <= BetterChatRenderer.sbTrackY + BetterChatRenderer.sbTrackH + hitPad;
|
||||
}
|
||||
|
||||
static void scrollToPosition(double my) {
|
||||
public static void scrollToPosition(double my) {
|
||||
if (!BetterChatRenderer.sbEnabled || BetterChatRenderer.sbVisibleLines <= 0 || BetterChatRenderer.sbTotalLines <= BetterChatRenderer.sbVisibleLines)
|
||||
return;
|
||||
float trackSpan = BetterChatRenderer.sbTrackH - BetterChatRenderer.sbThumbH;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.text.util;
|
||||
|
||||
import dev.loki.lovisual.render.engine.text.FontInfo;
|
||||
import dev.loki.lovisual.render.engine.text.Fonts;
|
||||
|
|
@ -14,20 +14,22 @@ import dev.loki.lovisual.render.engine.text.TextRenderer;
|
|||
import dev.loki.lovisual.render.engine.text.VanillaTextRenderer;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.util.Mth;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatDrawUtil;
|
||||
|
||||
/**
|
||||
* Font resolution, glyph sizing and plain-text draw helpers for the chat renderer.
|
||||
* All public-facing metrics share the renderer singleton held on {@link BetterChatRenderer}.
|
||||
*/
|
||||
final class BetterChatFontUtil {
|
||||
public final class BetterChatFontUtil {
|
||||
private BetterChatFontUtil() {
|
||||
}
|
||||
|
||||
static TextRenderer getInterRegular() {
|
||||
public static TextRenderer getInterRegular() {
|
||||
return Fonts.renderer("Inter", FontInfo.Type.Regular, TextRenderer.get());
|
||||
}
|
||||
|
||||
static TextRenderer getIosevkaRegular() {
|
||||
public static TextRenderer getIosevkaRegular() {
|
||||
return Fonts.renderer("Iosevka", FontInfo.Type.Regular, TextRenderer.get());
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +45,7 @@ final class BetterChatFontUtil {
|
|||
return Fonts.renderer("Iosevka", FontInfo.Type.BoldItalic, getIosevkaBold());
|
||||
}
|
||||
|
||||
static TextRenderer fontRenderer(String key) {
|
||||
public static TextRenderer fontRenderer(String key) {
|
||||
if (key == null) return getIosevkaRegular();
|
||||
return switch (key) {
|
||||
case "iosevka_bold" -> getIosevkaBold();
|
||||
|
|
@ -59,12 +61,12 @@ final class BetterChatFontUtil {
|
|||
return TextGlyphFallback.rendererForGlyph(preferred, codePoint);
|
||||
}
|
||||
|
||||
static String fontForGlyph(String baseFont, int codePoint) {
|
||||
public static String fontForGlyph(String baseFont, int codePoint) {
|
||||
TextRenderer preferred = fontRenderer(baseFont);
|
||||
return TextGlyphFallback.fontKeyForGlyph(baseFont, preferred, codePoint, "iosevka_medium");
|
||||
}
|
||||
|
||||
static String fontForStyle(Style style) {
|
||||
public static String fontForStyle(Style style) {
|
||||
boolean bold = style.isBold();
|
||||
boolean italic = style.isItalic();
|
||||
if (bold && italic) return "iosevka_bold_italic";
|
||||
|
|
@ -73,15 +75,15 @@ final class BetterChatFontUtil {
|
|||
return "iosevka_medium";
|
||||
}
|
||||
|
||||
static float scaleForSize(float size) {
|
||||
public static float scaleForSize(float size) {
|
||||
return size / 18.0f;
|
||||
}
|
||||
|
||||
static float svgGlyphSize(TextRenderer tr, float size, boolean shadow) {
|
||||
public static float svgGlyphSize(TextRenderer tr, float size, boolean shadow) {
|
||||
return Math.max(1.0f, textHeight(tr, size)) * 0.92f;
|
||||
}
|
||||
|
||||
static float textHeight(TextRenderer tr, float size) {
|
||||
public static float textHeight(TextRenderer tr, float size) {
|
||||
if (tr == null) return 0f;
|
||||
float scale = scaleForSize(size);
|
||||
tr.begin(scale, true, false);
|
||||
|
|
@ -92,7 +94,7 @@ final class BetterChatFontUtil {
|
|||
}
|
||||
}
|
||||
|
||||
static float textWidth(TextRenderer tr, String text, float size) {
|
||||
public static float textWidth(TextRenderer tr, String text, float size) {
|
||||
if (tr == null || text == null || text.isEmpty()) return 0f;
|
||||
float scale = scaleForSize(size);
|
||||
float svgAdvance = svgGlyphSize(tr, size, false);
|
||||
|
|
@ -126,7 +128,7 @@ final class BetterChatFontUtil {
|
|||
}
|
||||
}
|
||||
|
||||
static void drawText(TextRenderer tr, String text, float x, float y, float size, int argb, boolean shadow) {
|
||||
public static void drawText(TextRenderer tr, String text, float x, float y, float size, int argb, boolean shadow) {
|
||||
if (tr == null || text == null || text.isEmpty()) return;
|
||||
if (((argb >>> 24) & 0xFF) <= 0) return;
|
||||
float scale = scaleForSize(size);
|
||||
|
|
@ -167,7 +169,7 @@ final class BetterChatFontUtil {
|
|||
}
|
||||
}
|
||||
|
||||
static String fitText(TextRenderer font, String text, float size, float maxWidth) {
|
||||
public static String fitText(TextRenderer font, String text, float size, float maxWidth) {
|
||||
if (text == null) return "";
|
||||
if (textWidth(font, text, size) <= maxWidth) return text;
|
||||
String suffix = "...";
|
||||
|
|
@ -179,7 +181,7 @@ final class BetterChatFontUtil {
|
|||
return end <= 0 ? suffix : text.substring(0, end) + suffix;
|
||||
}
|
||||
|
||||
static String safeSub(String s, int start, int end) {
|
||||
public static String safeSub(String s, int start, int end) {
|
||||
if (s == null) return "";
|
||||
int len = s.length();
|
||||
start = Math.max(0, Math.min(len, start));
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.text.util;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.features.gui.chat;
|
||||
package dev.loki.lovisual.features.gui.chat.text.util;
|
||||
|
||||
import dev.loki.lovisual.util.item.TopEnchantUtil;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
|
@ -25,6 +25,9 @@ import java.util.ArrayList;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatStoreManager;
|
||||
import dev.loki.lovisual.features.gui.chat.text.util.BetterChatHoverCache;
|
||||
|
||||
/**
|
||||
* Hover payload helpers + cache fallbacks.
|
||||
|
|
@ -3,7 +3,7 @@ package dev.loki.lovisual.features.gui.clickgui;
|
|||
import dev.loki.lovisual.render.engine.animation.AnimationUtility;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
|
||||
final class ClickGuiDrawHelpers {
|
||||
public final class ClickGuiDrawHelpers {
|
||||
|
||||
private ClickGuiDrawHelpers() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ package dev.loki.lovisual.features.gui.clickgui;
|
|||
import dev.loki.lovisual.features.gui.clickgui.editor.ClickGuiTextEditorState;
|
||||
import dev.loki.lovisual.features.gui.clickgui.picker.ClickGuiPickerState;
|
||||
import dev.loki.lovisual.features.gui.clickgui.protocol.CombatProtocolHeuristicsEditorState;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.TextEditorOwner;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.TextSetting;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.owner.TextEditorOwner;
|
||||
import dev.loki.lovisual.features.gui.clickgui.settings.text.TextSetting;
|
||||
import dev.loki.lovisual.features.module.ModuleManager;
|
||||
import dev.loki.lovisual.util.text.ClipboardUtil;
|
||||
|
||||
final class ClickGuiEditorManager {
|
||||
public final class ClickGuiEditorManager {
|
||||
|
||||
private ClickGuiEditorManager() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import dev.loki.lovisual.render.engine.text.FontInfo;
|
|||
import dev.loki.lovisual.render.engine.text.Fonts;
|
||||
import dev.loki.lovisual.render.engine.text.TextRenderer;
|
||||
|
||||
final class ClickGuiFonts {
|
||||
public final class ClickGuiFonts {
|
||||
|
||||
private ClickGuiFonts() {
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue