Verity

Update log

Version update history for Verity-cn.

v3.5.3

2026-11-01Latest

Pixel music player in the inventory · Update check · Web page reading · DeepSeek search

Add

  • The inventory now has vertical tabs on its left edge (Inventory / Music): they reuse the vanilla recipe-book button texture from the top-left corner, sit flush against the panel's left edge, and show small pixel inventory and note icons. Clicking the note tab swaps the inventory screen in place for a pixel-art mini player (no extra window), and the inventory tab switches back. The player is drawn at 1:1 pixel scale and scales with the inventory, with Play, Comments and Lyrics pages. Long song, artist or album names scroll as a marquee, clicking a lyric line jumps to it, and while a song plays the bottom bar shows the platform's official icon and name.
  • Platform icons on Config → Account: the account selector button and the expanded account list now use each platform's official icon (NetEase Cloud / Kugou Concept Edition / QQ Music), so you can tell at a glance which platform is selected.
  • The NetEase Cloud account card shows account age and membership tier: below the account name it now shows "N years / N days" (derived from the registration date), and the tier label on the right distinguishes VIP from SVIP (SVIP is gold). The nickname colour follows the tier too: white for non-members, coral red for VIP, gold for SVIP.
  • Update checks are available on the About page: a "Check for updates" button sits to the right of the version number, shows "Checking…" and cannot be clicked again while checking. When a new version is found, a prompt appears over the current screen (the background is the blurred current UI, not plain black), where "Go to update" opens the official download page and "Cancel" or clicking outside leaves everything as it was. Config → About also gained a "Check for updates on startup" switch (on by default) that silently checks once each time you enter the main menu.
  • A user agreement is shown on first launch: nothing needs to be configured any more; read it and click "Agree" to reach the main menu, or "Disagree and quit" to close the game. Key points: the mod collects no private information and keeps configuration and login credentials locally; the only outbound data is music API call statistics (platform / endpoint, success or failure, duration), with no account, cookie, token, playlist or playback history.
  • Verity can now open web pages and read them: when search snippets are not enough (specific figures, dates, tables, documents), or when you send it a URL directly, it opens that page, reads the body and then answers. This works independently of web search — no key required, and it still works with web search off. For safety, local and intranet addresses are never opened.
  • DeepSeek was added as a web search channel: enter a DeepSeek API key under Config → AI settings → Web search to enable it; leave it empty to keep the channel off. As with the others, a missing key or a failed call reports the reason in game.

Change

  • The first-launch "Initial setup" screen is gone: no setup wizard appears after the main menu loads, and finishing or skipping the intro video returns straight to the main menu. Every setting can still be changed on the Config screen.
  • The Kugou Concept Edition account card no longer shows membership status: that platform's membership endpoint is unavailable, so after signing in it shows only the nickname and level instead of "VIP member / non-member". The account itself still works normally in the account selector.

Repair

  • Fixed "Disagree and quit" on the first-launch user agreement doing nothing; it now closes the game properly.
  • Both buttons on the TTS and STT cards of the self-check page now enter "Checking" together: while "Check" runs, "Playback test" and "Recording test" are greyed out, so checking never overlaps with playback or recording.
  • Multi-platform song requests no longer cross wires: every search, playlist, album, liked-songs and recommendation request gets its own result numbering, so platforms searched earlier can still be played by their original numbers (the last 5 sets are kept per player) without hitting the wrong song or an out-of-range error.
  • Kugou and QQ song IDs can be used directly: they are no longer treated as numeric NetEase Cloud IDs; song_id playback accepts an optional platform, and the platform is inferred from the ID shape when it is omitted.
  • Playable songs are no longer reported as unplayable: the pre-check walks standard → higher → very high quality and only fails when no URL can really be obtained; slow starts now report "still buffering" instead.
  • If one platform cannot play a song, another is tried automatically: when the original platform cannot provide a URL because of VIP or licensing, the same title is looked up on the other two by song name plus artist (switching only when both match), and the reply says the platform was switched.

v3.5.2

2026-10-25

Fish Audio speech · Per-platform song search · TTS playback test · Shiftcore Recalling v2

Add

  • Fish Audio was added to the built-in speech synthesis providers: the key, model name and voice ID are filled in separately, there is a "Model docs" button next to the model name and a "Search voice ID" button next to the voice ID that searches by keyword with paging and fills in the entry you click; if a direct connection does not work, turn on "Enable proxy" so every Fish Audio request goes through the proxy, and turn it off to go direct again
  • Characters now search songs per platform: NetEase Cloud, Kugou Concept Edition and QQ Music became three separate searches, so saying "look on NetEase Cloud..." searches that platform; when no platform is named it uses the one you are logged in to or have enabled, and asks a question when it is really unsure; every song still carries its platform tag and playback still uses the result number
  • "Play favourite song" is now explicitly labelled as a NetEase Cloud Music song ID, so it is no longer mistaken for a Kugou or QQ Music ID
  • The TTS card on the self-check page has a new "Playback test": one click really plays "Hello, I am Verity. This is a speech playback test." with the current configuration, and hearing it means the setup works; while playing, both "Playback test" and "Check" grey out and show "Playing...", then recover automatically
  • Shiftcore Recalling v2 was added to the built-in LLM providers: fill in the key and pick a model with "Get model list"; it uses the standard OpenAI-compatible interface

Repair

  • Fish Audio "check passes but no sound": playback and checking now fetch the complete audio in the same way, instead of sometimes getting only a truncated fragment
  • A failed playback test now states the specific reason: it shows the HTTP status, an empty response, an unavailable audio device and so on, instead of only "playback did not start within 30 seconds"
  • Fish Audio no longer returns 403 after enabling the proxy: synthesis, self-check and voice search all send a normal browser user agent, so it works with the proxy on or off
  • "Playback test" no longer only makes a sound the first time: the first successful synthesis is remembered, the same configuration replays it directly, and it re-synthesises after the key, endpoint or reference voice changes
  • The playback test now updates the check status: after the sound is heard the TTS card changes from "Not checked" to "✓ Playback test passed", and if nothing is heard within 30 seconds it shows "✗ Playback test made no sound"
  • Playback test made no sound in voice clone mode: the reference audio now uses the correct `data:audio/mpeg;base64,` prefix and the playback test runs in the background, so the interface no longer freezes
  • MiMo voice cloning works again: the request model and reference audio format sent to Xiaomi were corrected to the official interface, so voice cloning is usable again
  • Clone failures are no longer silent: a missing key, oversized reference audio, a network error or a response without audio are all reported in a notice with the specific reason
  • The notice for an unusable reference audio was corrected: "Switched to Base URL mode" is now "Switched to the default voice"
  • The voice clone "speed" option that never took effect was removed: old values in the save are cleared on the next save

v3.5.1

2026-10-19

Cloudflare Workers AI · NetEase Cloud QR login · Key-free web search · Play mode saved per world

Add

  • NetEase Cloud Music now supports QR login: click the NetEase Cloud card on the account page to get a code, scan it with the NetEase Cloud app on your phone and confirm the authorization there, and the nickname, avatar, VIP status and level are synced automatically; the old Cookies login is still available
  • A Bing channel was added to web search - built in, no API key, works out of the box, so that row only has an explanation and no input box
  • Three built-in speech recognition providers were added: AssemblyAI / Gladia / Speechmatics. Each only needs a Key, no Model row is shown, and the self-check page really runs one recognition request using each provider's own protocol
  • The built-in Xiaomi speech recognition model is now fixed to `mimo-v2.5-asr`; no Model row is shown and requests no longer carry an empty model
  • The self-check page now offers a "Recording check" for STT: record what you say and send it to STT, and the result area shows the recognized text, so a microphone or language problem is obvious at a glance
  • Deepgram was added as a built-in speech synthesis provider: fill in the Key and the voice ID (the "Get voice ID" button fetches it), no Model row is shown, and it uses the official streaming API
  • ElevenLabs was added as a built-in speech synthesis provider: the Model is fixed to `eleven_multilingual_v2`, the voice ID can be picked by clicking, and it uses the official streaming API
  • Cartesia was added as a built-in speech synthesis provider: the Model is fixed to `sonic-3.6`, the voice ID can be picked by clicking, and it uses the official streaming API
  • MiMo voices can now be picked by clicking: the new "Choose voice" button fills in Bingtang, Moli, Suda, Baihua, Mia, Chloe, Milo or Dean for you
  • Cloudflare Workers AI was added as a built-in provider: just fill in the Account ID and the API Token, click "Get model list", and replies stream out character by character
  • Play mode is now saved per world: the mode button on the create-world screen only affects the world being created, the world list shows each world's own mode, and an old save records the then-current default the first time it is entered

Change

  • The personal account page was rebuilt: a card-based layout, the account selector moved into a compact dropdown in the top-left, account info gathered into one profile card, NetEase Cloud's "service address" in its own small card, plus new "Liked music" and "My playlists" count cards; changes take effect immediately
  • Music account login was completed: QR login now has the full state flow (code shown -> waiting for scan -> scanned, waiting for authorization -> signing in), a failure stops the QR code right away, and phone-code login counts down after the code is received
  • Music playback and lookup are handier: playback uses the standard quality on every platform, VIP / paid songs pop up a notice, song lookups say which platform the track came from, "stop playback"-style notices show the song name, new lookups for song info / my playlists / liked music, and lyrics prefer word-by-word display
  • Model downloads are more reliable: downloads now go through a steadier relay channel, and model detail cards no longer show the extra relay prefix
  • Voice cloning became a Xiaomi-only switch: with MiMo selected as the built-in provider a "Voice cloning" switch appears in the panel, old configs migrate automatically, and other providers do not show it
  • The self-check page was redesigned: every check is its own card, a failure expands the complete error (including the cause chain) which can be clicked to copy, results reveal line by line, and the elapsed seconds are shown while checking
  • Config page layout in small windows: in 854x480 windows the title and subtitle, the label and grey hint, input heights, the left navigation and the about-page card were all fixed
  • All AI requests now use streaming: context compression and self-check go through the same channel, so "chat works but compression or self-check fails" no longer happens
  • The "Enable streaming response" switch was removed from speech synthesis: it always streams now, and the `ttsStream` entry was dropped from the config file
  • Long log lines are no longer truncated on the log page: they wrap and show in full, scrolling is calculated in pixels so rows are never mis-clicked or over-scrolled, and clicking a row still copies the whole original line

Repair

  • "Custom" speech was configured but made no sound: it now sends the request to the address you filled in, in OpenAI-compatible format (`/audio/speech`)
  • Cartesia speech passed the self-check but made no sound in game: WAVs of unknown streaming length are now played by the actual byte count received, and a parse or playback failure leaves a clear error on the log page
  • The Kugou concept edition screen did not react after QR authorization finished: it now fetches and shows the account info normally
  • Kugou QR login showed a "fake login" (a flash and then back to logged out): login is only confirmed after the authorization is really confirmed on the phone
  • NetEase Cloud QR login reported "invalid / expired session": the login state now keeps only the credential fields it really needs, so it survives restarting the game
  • The QQ Music avatar stayed a penguin after logging in through WeChat: the WeChat channel now uses the matching channel placeholder avatar and name, and the account type label follows the actual channel
  • The chat panel (CLI) did not refresh after leaving a save: leaving a world, disconnecting or entering a new world now clears the panel content, scroll position and context count, and the panel stays collapsed until new content arrives
  • Messages were popped up an extra time when "Take over the chat bar = off": off mode no longer takes over at all and only the vanilla chat bar is used; "Mixed" mode forwards notices only while the panel is actually shown
  • "Get model list" spun forever and the panel never appeared (Our Free Model): the list is now shown immediately and tested one by one in the background with a "testing 5/12" progress, and unusable models are removed or marked "region-restricted"
  • All requests to the built-in "Our Free Model" provider failed: the upstream changed its access method, this mod has been updated and it works again
  • Cloudflare credentials were filled in but it said `HTTP 401 · api key invalid`: requests were being sent to the wrong provider, now fixed
  • With the model download card open, the "Save / Reset" underneath could still be clicked and covered the card: the whole layer is now really blocked while the card is open
  • "HuggingFace real-time data" often returned `request timed out` and models could not be downloaded: the timeout was relaxed to 20 seconds with up to 3 automatic retries, and downloads retry too and still support resume
  • A save should be CN edition but replies were horror style and extreme darkness still applied: the root cause was the mode not following the save, fixed together with "Play mode saved per world"
  • Clicking "Reset" or "Exit without saving" on the config page changed the play mode back to vanilla: fixed, those two no longer touch the play mode
  • The main-menu "Black screen" option did nothing: fixed, it really goes black now
  • The create-world screen still said "(new)" and the hint text leaked `**` asterisks: the button is simplified to "Verity mode: CN", and the tooltip only explains the difference between the two modes

Known issues

  • Worlds that already exist cannot change mode in game yet; delete `saves/<world name>/verity/mode.txt` to let it record the default again. An in-game entry point will be added later
  • Not every model is available on the Cloudflare free plan; some return `403 not available on the Workers Free plan`, which is an upstream limit - just pick another model

v3.5

2026-09-13

Self-painted configuration interface · Zero pre-dependency · Perception upgrade · Built-in Penguin TTS

Add

  • `[[mods]]` `updateJSONURL` in `mods.toml`, pointing at the raw link of `update.json` in the repository root
  • `update.json`, containing `homepage` and `promos` (`1.20.1-latest` / `1.20.1-recommended` both `3.5`)
  • `VerityLog` `warn(String, Throwable)` / `warn(Throwable)` overloads
  • `logo.png` and `icon.png` added to the jar root (downscaled from `smiley_icon_transparent.png` to 512²)
  • Model "terms of use" dialog gained a ~170ms exit animation (reusing `mdlModalP`); the action only runs after it finishes
  • Confirm dialog gained enter/exit animations (`openConfirm` / `closeConfirm` / `confirmP`; 220ms in, 170ms out)
  • Reset success toast: `resetAllConfig()` shows "reset to default configuration" after writing to disk
  • "Reset" button `resetBtnBox` added to the left of "Save" in the top-right; clicking opens a hand-drawn confirmation (`confirmKind = 3`, Cancel / Reset, reset uses the danger red `C_DESTR`)
  • Reset semantics: non-immediate entries revert to `ConfigValue.getDefault()`, then `SPEC.save()` runs directly and the snapshot refreshes
  • Scrollbar added to the disclaimer body, supporting wheel and drag (reusing `drawBar`, geometry unified through `mdlDiscGeom`)
  • Two buttons: `Decline` (outlined) / `Agree and download` (primary color)
  • Save success toast "configuration saved", also effective on the "save and exit" and "save and open editor" paths of `doSaveAll()`
  • Clicking "lyric effect editing" with unsaved changes now opens a three-way confirmation: Cancel / Save and open editor / Open editor without saving
  • "Save" button added at the far right of the header bar; greyed out and disabled when there are no changes
  • Hand-drawn confirmation dialog for exiting without saving: Cancel / Save and exit / Exit (discard changes)
  • `PersonaEditorScreen` Ctrl+A/C/V/X plus Shift + arrow keys / Home / End extended selection (with selection highlight)
  • `VerityConfig.Mem`, exposing `get()` / `set()` with the same names as `ConfigValue` to hold runtime memory values
  • "Functional hints" toggle added to the General page (config key `functionalHint`, on by default), uniformly controlling toasts for disabled features and unmatched rules
  • `VerityEntity.bounceOffEntities` supports horizontal reflection off mobs (elasticity 0.6) and an impulse along the flight direction when hit by a projectile
  • 5-tick cooldown and a horizontal speed cap of 2.0, preventing repeated triggering during overlap or ever-faster bouncing
  • `VerityLlmSession` `actualContextChars`, `setActualContextChars()` and `actualContextTokens()`
  • `VerityLlmSession` `sessionChars` replaces `contextChars`; cleared on entering a world and accumulated from saved message lengths
  • "Extreme darkness" toggle added to the "Vanilla (horror) mode" section of the config page (noted as taking effect in vanilla mode only)
  • NCM `ncm_playlist_songs` action: fetches a playlist's song list by id and returns `song_id` values that can be fed straight to `ncm_play`
  • `ncm_playlist_songs` parameters `playlist_id` / `limit` (default 100, max 200); disabled in multiplayer, prompts for a cookie when not logged in
  • `get_player_health` return value extended to health/max, hunger (n/20) and all status effects (name, level, remaining seconds)
  • "Allow Verity to run commands" toggle under AI settings · Other (`VerityConfig.ALLOW_COMMANDS`, off by default)
  • Only after enabling it does `run_command` appear in the tool list; it runs with the player's own `CommandSourceStack`, granting no extra privileges
  • CLI panel supports mouse-wheel history scrolling (3 lines per notch, offset clamped to `[0, total-cap]`); `ChatScreenMixin` forwards `mouseScrolled`
  • The bottom-bar context ring now counts content still streaming out (new `liveTokens()`, estimated at about 4 characters per token)
  • Thinking content gained a leading ellipsis (three 1px dots); tool calls remain 3x3 small squares
  • CN version gained a "custom persona" toggle and the `PersonaEditorScreen` editor (hand-drawn multi-line input, auto-wrapping and growing with content)
  • CN version gained dedicated configs `CORES_CUSTOM_PERSONA` and `CORES_CUSTOM_PERSONA_ENABLED`, independent of vanilla `CUSTOM_PERSONA`
  • When a custom persona is enabled and non-empty it replaces the built-in neutral assistant identity, and horror content in its text is no longer filtered
  • The world creation screen gained a "Verity mode: Vanilla/CN" switch below "Allow cheats", writing to `VerityConfig.PLAY_MODE`
  • New "Other · in-game presentation" group under AI settings, housing the "fall scream" selector
  • `PersonaHelper.karmaDisposition()` public helper, shared by both persona prompt sets
  • Press Esc twice while thinking to stop: the first press shows a hint in the panel, a second press within 2 seconds takes effect and no longer opens the pause menu
  • "CLI panel" dropdown added to the General page: Off / Mixed (default) / Always
  • "Always" mode gained `ClientChatHook`, mirroring chat messages into the panel with the name prefix stripped and deduplicated
  • `Kind.SYSTEM` and `VerityLlmSession.proactive()`, marking conversations Verity starts on its own
  • Built-in provider Kilo added (gateway `https://api.kilo.ai/api/gateway`, default model `kilo-auto/free`, no key required)
  • Kilo uses the generic OpenAI request path (chat/completions plus three-level fallback); once selected, the config area can be left empty and it works directly
  • dsh-our-free-model repository link added to "Special thanks" on the About page
  • Built-in provider Our Free Model added (gateway `https://opencode.ai/zen/v1`, no key required, listing via `GET /models`)
  • `OurFreeModel` class, collecting that provider's constants, endpoint routing, fingerprint headers and both chat/responses SSE parsers
  • Tier type `ThinkKind.BUDGET` (concise/balanced/deep) and the single required config `LLM_BUILTIN_OURFREE_MODEL`
  • `checkLlmOurFree()` and `compressWithOurFree()`, self-check and context compression reusing `OurFreeModel.send()`
  • `send()` `quiet` overload, so side-channel requests no longer push streaming content into the chat panel
  • START/SYNC packets gained `timestampMs` (START also `durationMs`); position semantics changed to song progress at a given moment
  • `streamPlay` changed to a real HTTP Range seek: after probing the file length it converts proportionally to a byte offset and leaves a safety margin before it
  • Automatically falls back to full-stream playback when the Range response is not 206, avoiding crashes on CDNs that do not support Range
  • Full JIP chain logging (10 lines covering server re-send, Range start, frame calibration and more) mirrored into the in-game log page `VerityLog`
  • The client now logs one DEBUG line per sync packet received: `[ncm] sync drift Nms (local X / server Y)`
  • `NcmClientPlayer` gained three `VerityLog` lines: "START received", "skipping the client path" and "stream start failed"
  • The music block logs one INFO geometry line on first draw (`musicTop / blockH / ph / foldT / guiW / guiH`)
  • `EmojiGlyphProvider` / `EmojiFonts` / `FontManagerMixin`: appends a system emoji glyph provider to `minecraft:default` and `verity:terminal` (stb bakes the glyf outline, monochrome)
  • Build-time dependency fastutil-8.5.9.jar in `dev/tools/libs/forge/` (compile-time only, not packaged)
  • Empty lyric lines changed to three dots: horizontally arranged perfect circles (20-segment vertex fans), dot radius scaling with the lyric font size, anchored to the lyric line that triggers the gap
  • Four-stage millisecond animation: 180ms fade-in → dots pulse in sequence → 900ms breathing → 300ms fade-out; gaps shorter than 1600ms use a compressed timeline
  • Empty-line dot threshold config `ncmLyricGapDotThresholdMs`, default 1500ms
  • `LyricEditorScreen.drawPreviewLyric` reuses the same `drawGapDotsPublic`, so the editor matches what is seen in game
  • When the gap between two lyric lines is ≥ 4s, three vertically stacked dots are drawn in the centre, fading in one by one with wait progress (`GAP_DOT_THRESHOLD_MS=4000`)
  • The HUD (`LyricHudRenderer`) and the editor preview (`drawPreviewLyric`) share the same `drawGapDots` implementation
  • `LyricHudRenderer.prevNonEmpty/nextNonEmpty`, locating the real previous and next lines across empty text lines
  • `SkinManager.restoreBuiltinTextures()`, restoring the 16 entity texture RLs when switching back to a built-in skin
  • `probeCookieValid()` probes `/login/status`, distinguishing three states: cookie valid / explicitly invalid / undeterminable
  • When lyrics are unavailable during playback, a unified "(instrumental)" placeholder is shown (upcoming color, 0.75 opacity)
  • Wide/narrow screen switching changed to a `wideT` interpolated re-layout animation; geometry at rest matches before the change
  • `NcmHelper.parseYrc()` parses word-by-word lyrics, enabled automatically only for songs containing them, not exposed as a config option
  • `LyricLine.wordStart[]/wordChar[]` plus the compact sync-packet string `encodeWords`/`decodeWords`; above 32000 bytes the word-by-word data is dropped and the whole line is re-sent
  • Word-by-word rendering `drawLyricLineW/drawScaledTextW`; the editor preview goes through `drawLineWordPublic`, sharing its source with the HUD
  • `matchYrc()` attaches word-by-word data to lrc lines by exact text match, disambiguating identical lines with a 1500ms window
  • Active and upcoming colors each gained a "gradient toggle" and "gradient end color"; rendering gained `LyricHudRenderer.drawLyricLineG` for horizontal per-word gradients
  • A "wide/narrow" button added to the right of the panel title row; in wide mode the parameter rows re-layout into two columns, and column widths shrink overall when over-screen
  • Configs `ncmLyricActiveGradient`, `ncmLyricActiveColor2`, `ncmLyricPreviewGradient`, `ncmLyricPreviewColor2`
  • Configs `ncmLyricInfoNameColor` (A1A1AA) and `ncmLyricInfoArtistColor` (71717A); title/artist colors are now configurable
  • The editor row table changed from a fixed `ROWS` constant to dynamically generated `buildRowKinds()`; the color picker target expanded to 6
  • Collapsible bar at the bottom of the panel; when collapsed only the title row remains and the panel height goes from 330 to 66 (consolidated in `shownRows()`/`paramsBlockH()`)
  • New "show song info" option and "info font size" slider; title on top and artist below, and these two rows are excluded from the selection box
  • Configs `ncmLyricShowInfo` (bool, false) / `ncmLyricInfoFontSize` (int, 60)
  • ID input default value `570340213`, shown as a grey hint that disappears on input
  • `NcmHelper.active*()`, nine unified entry points for reading playback state
  • `SpeedHelper.setConfigValueNow(name, value)`: synchronous reflective write, bypassing the debounce queue
  • `LyricHudRenderer.advanceState()` and `sampleStatePublic()`, for the editor to call actively each frame
  • `SpeedHelper.configReadOk()` and the config snapshot `opened` taken when it opens
  • ID input switched to the vanilla `EditBox` for its backing, keeping the hand-drawn shadcn outer frame
  • `setFilter` restricts the ID to at most 20 digits; pasting is filtered the same way
  • ID field key routing: Enter plays the song, Tab/Esc drops focus, everything else is forwarded to `EditBox`
  • X/Y guide lines while dragging lyrics: the snap axis switches to a `C_OK` green solid line and is drawn thicker; snap threshold `SNAP_PX = 6` (through `s()`)
  • Simple music controls at the bottom of the panel: play/pause, `title · artist`, draggable progress bar with time, ID input and "play"
  • `NcmHelper.uiStatus() / uiTogglePause() / uiSeek(ms) / uiPlayById(id)` facade, sharing one implementation with in-game song requests
  • Toast when entering the lyric editor: single-player notes that playback was paused, multiplayer notes that it will not pause
  • `LyricHudRenderer.isAnimatingPublic() / animProgressPublic() / prevLineIndexPublic() / easeInOutPublic()`
  • Editable fields at the bottom of the color picker: HEX accepts only `0-9A-F` (max 6 digits), RGB only digits (0-255), refreshing HSV immediately per character
  • Backspace, Enter/Tab submit, Esc two-level retraction (first drops field focus, then closes the color picker)
  • `TOP_PAD = 6`; row height is divided evenly from `available height − TOP_PAD − PAD`, and all rows below shift up accordingly
  • `SpeedHelper.flushPendingConfig()`, draining the debounced write queue so parameters can be persisted immediately after a change
  • `SpeedHelper.currentNcmLyricParseColor(hex, fallback)`, unifying HEX parsing rules between the editor and in-game
  • `dev/tools/probe_download.py`, supporting `--apply` to actually test and write the measured size back into the entry
  • `/cnvtt` test command family (selector / compress / block / toast / center), producing UI only with no real effect
  • "Choose file…" button on the self-check page, opening the system native file picker via LWJGL tinyfd
  • "Special thanks" card on the About page, covering GeckoLib and NeteaseCloudMusicApiEnhanced
  • Local LLM streaming output, using `LlmOutput.streamMessage()` to extract only confirmed lines and restore escapes
  • TTS synthesizes and plays as it goes (`openPcmSink` / `writePcm` / `closePcm`), with the sample rate taken from `OfflineTts.getSampleRate()`
  • Streaming playback via an OpenAL buffer queue on Android, with 24-block backpressure, a 120-second watchdog and unqueue before cleanup
  • Added `varmite/jna/android-aarch64/` and `android-x86-64/` resources inside the jar and set `jnidispatch.path`
  • Diagnostic logging when GUI space dimensions change, directly reporting sidebar and content widths to help locate incomplete displays
  • Insufficient-context selection screen, where arrow keys, number keys or the mouse can choose to close this round or one of three compression sources
  • RuntimeSmoke assertion for "LLM prompt prefix reuse", verifying that enabling reuse produces byte-identical output
  • LocalInferRuntime.historyCharCapacity()/contextBudgetChars(), converting the character budget from n_ctx
  • LocalCompress, a pure-algorithm class for local-mode compression that does not depend on Minecraft
  • Model loading logs now print n_ctx / n_batch / reply limit
  • persona_sample.py and ProbePersona.java, measuring prompt token counts and headroom in one go
  • RuntimeSmoke static assertion, failing when prompt headroom drops below 5000 tokens
  • LlamaCpp.complete() decodeChunked(), submitting long prompts in chunks of at most n_batch
  • Three new smoke cases: chunked decoding of long LLM prompts, long prompts with a large context, and consecutive multi-turn dialogue
  • stage_runtime.py write_manifests(), uniformly rewriting each platform's FILES.txt at the end of staging
  • NativeRuntime.ownJar() fallback, inferring the jar from this class's own .class resource
  • run.sh --restricted mode, simulating the scenario where Forge cannot access its own jar
  • dev/tools/jna_repack.py, covering three notations and equal-length replacement across 8 platforms
  • dev/tools/pack_conflict_check.py, comparing package names between the deliverable and 462 jars under libraries
  • Section 8 of verify_shade.py, a static assertion on "module layer safety"
  • stage_runtime.py prune_stale cleanup before staging, avoiding leftover dead classes
  • A call to ggml_backend_load_all_from_path() to register the official package's plugin backends
  • All local inference native libraries are now packaged into the mod; LLM / TTS / STT cover desktop and Android arm64, while LLM for Windows x86 and Android x86_64 is still missing
  • NativeRuntime.java, which probes the platform, unpacks automatically and calls System.load in dependency order
  • VoskNative.java, a hand-written JNA binding to the vosk C API for Android compatibility
  • dev/tools/stage_runtime.py and native_check.py validate JNI symbol compatibility
  • dev/_smoke/ desktop-side smoke tests, using stub classes to exercise the real native libraries without Minecraft
  • CPU instruction set gate: CPUs without AVX2/FMA do not load the local LLM, and the UI explains why
  • Grid scrollbar on the model library page: reusing `drawBar` + `thumbGeom`, geometry stored in the `mdlSb*` fields, hit-testing and dragging reading the same data
  • A fixed 14px is reserved on the right of the grid for the scrollbar (`mdlGrid()`), preventing cards from re-laying out when it appears or disappears
  • Detail/download manager dialog close animation: 170ms reverse scale + sink + mask fade-out; clicks and hovers are swallowed during the exit
  • Self-check now really runs one minimal inference in "local inference" mode: LLM via `LocalInferRuntime.chat()` (max 32 tokens), TTS via `synthesize()`, STT via `transcribe()`
  • During self-check, a `[local inference] …` placeholder string returned by `chat()` is treated as a failure
  • Detail dialog gained "model introduction": each of the 30 models has a minimal Markdown description, sharing the scrollable content area with live HF data
  • "Mode selection" for LLM/TTS/STT renamed to "inference mode" with a new "local inference" mode
  • LLM local inference: model dropdown (only downloaded GGUF files), inference device, max reply length (256/512/1024/2048), temperature, repetition suppression, thread count (default 2)
  • TTS local inference includes model dropdown + inference device + inference threads; STT local inference has only the model dropdown
  • "Model download" TAB added on the left (above self-check): on first entry it shows the "local model download and usage notice", and does not appear again after agreeing
  • 30 built-in model cards (full Qwen2.5 line, Llama-3.2/3.1-8B, Mistral-Nemo-12B, DeepSeek-R1-Distill-7B, vits/matcha/zipvoice, vosk), filterable by type / config tier / downloaded state
  • Model detail card: basic information hardcoded; download counts/likes/update time/file list fetched live from HuggingFace (hf-mirror.com mirror)
  • "Download manager" in the top right: pause/resume/delete, live progress and speed; models are saved to `config/verity/models`, with resume support and recovery after restart
  • Local inference runtime layer: three JNI loaders for llama.cpp / sherpa-onnx / vosk; native libraries placed in the mod or in `config/verity/models/runtime/` are enough to enable it
  • Self-check and connectivity detection now use readiness checks for local inference mode (model downloaded + native library loaded) and no longer send network requests
  • A "fetch model list" button added to the left of the LLM built-in provider and custom Model input; it requests the OpenAI-compatible `GET /models` in the background and prompts for a key on 401
  • Model selection submenu: full-screen blur + dark overlay, a centred panel with a search box and a single-select list (hover highlight, green dot with bounce on selection, smooth scrolling + draggable scrollbar), Cancel/Confirm at the bottom
  • Confirming writes back to the corresponding Model config (the provider's Model entry for built-in providers, `LLM_MODEL` for custom); Enter = confirm, ESC / clicking outside = cancel
  • A "copy all" button added at the far right of the tier filter chip row; it copies the whole current filtered result and toasts the count
  • Log row backgrounds gained zebra striping (4.5% white every other row) and a 10% hover highlight, as visible feedback that rows are clickable
  • Matched substrings in search are highlighted in WARN amber, case-insensitively
  • Left-click a row to copy the whole line; right-click opens a small "copy whole line / copy message content" menu
  • Consecutive logs at the same level with the same message template collapse into `message ×N [expand]`, expandable/collapsible, with the group cache invalidated by filter/search/revision
  • The scrollbar is smooth and supports press-and-drag (clicking the track jumps); the target value reverse-computed from dragging is also eased
  • The "self-check" section of the config page was rebuilt with custom rendering (`LCard.selfPage` → `renderSelfPage` / `selfPageClick`): one 52px card per target, with "check all at once" on the right of the title row
  • Self-check now really sends a minimal real request: `VeritySelfCheck.probe` became `check(Target) → Result{int state; String text}`
  • LLM self-check goes through `POST {base}/chat/completions` and must call `AiAPI.applyThinking`, judging `content` or `reasoning_content` to be non-empty
  • TTS self-check: the proxy path uses `POST {base}/audio/speech`; preset/custom use the MiMo form `chat/completions + audio{format:"wav",voice}`; voice cloning uses the clone endpoint plus a real reference audio
  • STT self-check: the MiMo endpoint uses `chat/completions + input_audio`, the rest use multipart `POST /audio/transcriptions`; the test audio is a 1s/16kHz/mono/16-bit silent WAV
  • Self-check failure messages extract `error.message` / `message` / `detail` from the response body (e.g. `HTTP 401 · Invalid API key`); connect timeout 10s, request 60s
  • Log page level filter chips redesigned: ALL/INFO/WARN/ERROR changed to low-rounded rectangles (radius 5), the count area fixed-width and right-aligned, a hand-drawn check mark to the left of the selected item, and a 260ms damped sine bounce when switching
  • "Verbose logging" changed to a full "label + slide switch" control, with a border, hover brightening and a mini switch on the right
  • Log list transition animation: switching level/search term/clearing slides the list in from 5px below with a fade (200ms ease-out); newly arriving logs do not trigger it
  • Search supports log levels: typing info/warn/error/debug filters directly to that level, all = everything
  • Consecutive toasts changed to a "substitution" animation: the old toast slides up and fades out (240ms), the new one rises from 10px below
  • During connectivity detection on the account page the three result values show "checking…", with no stale values left over before completion
  • "Qwen open platform" added to the LLM built-in providers; the model name is required and has no default
  • TTS / STT built-in providers changed to selectable providers (Mimo / Qwen open platform)
  • "Local clone audio" dropdown, listing 5–30 second mp3 files under `config/verity/sample_audio`
  • The synthesis model for Qwen voice cloning is fixed to `qwen-audio-3.1-tts-next`
  • A scrollbar added to the left navigation bar of the config screen, appearing automatically when content does not fit, draggable, clickable and brightening on hover
  • `layout_check.py` scrollbar geometry regression assertions and the `--screen-h` parameter (default 360)
  • TTS mode "built-in (CQE)", using the built-in CQE Chinese voice model: ready out of the box, no network or key required
  • The built-in CQE model (CQE ONNX + jieba dictionary, about 158MB) ships inside the jar and is unpacked and loaded automatically on first use
  • "Built-in (CQE)" option added to "mode selection" in TTS config
  • Fully hand-drawn configuration interface (shadcn/ui design style, completely removing the Cloth Config dependency)
  • Six major sections: General / Vanilla (horror) mode / CN (CORES) mode / AI settings / Verity account / Feature config
  • AI settings group tree: LLM / TTS / STT (Verity site, built-in providers, custom — three states) plus connectivity detection for each
  • Account "personal centre" page: points balance card, server-side model, connection status, refresh / OAuth login / logout
  • Hand-drawn background image for the config interface (generated by agnes-image-2.5-flash)
  • Sub-groups collapsed by default, L-shaped hierarchy lines, skin dropdown selection (reading the skins directory)
  • Built-in GeckoLib animation library: no separate prerequisite install needed any more
  • Fall scream selectable: None / Follow TTS / Built-in voice / CQE
  • Verity perception upgrade: weather (rain/thunderstorm/biome/day-night/light level), deep item reading, and scanning of surrounding structures
  • Consecutive multi-turn tool calls, letting complex questions query several kinds of information within one conversation
  • Brand-new "About" page: version number, project introduction, maintenance status, related links, contributor thanks, and open-source licence notes
  • Debug mode toggle: when enabled it outputs all behaviour logs, making troubleshooting easier

Change

  • Both `mods.toml` files (`dev/patches/META-INF/mods.toml` is the effective one, `dev/work/jarroot/...` for reference) updated in sync
  • `updateJSONURL` written for the `main` branch; when the default branch is `master` it must be changed to `.../master/update.json`
  • log4j / slf4j in `MemoryHelper`, `NcmHelper`, `Verity`, `ModEvents` and elsewhere switched to the `VerityLog.getLogger(...)` adapter, no longer writing to `latest.log` or the console
  • Log level reassignment (400+ call sites across the codebase checked one by one, 56 changed): 10 ERROR→WARN, 21 INFO→WARN, 25 INFO→DEBUG
  • The Forge "Config" entry now shows `config_help_1.png` / `config_help_2.png` side by side (854×480, adapting to screen width)
  • `issueTrackerURL` changed to the GitHub repository address; `[[mods]] displayURL` and `[properties] homepage`/`github` changed to the official site address
  • Top-level `[properties]` `veritycn` (the Verity official site), which must be written before `[[mods]]`
  • The `mods.toml` version and the config page's `ABOUT_VER` badge unified from `3.5-beta` / `v3.5-beta` to `v3.5`
  • Exit, enter lyric editor and reset — the three confirmation flows now share one set of animations and geometry
  • The confirmation dialog's panel height and button positions now adapt to `confirmLabels().length`, removing the hardcoded 3 rows
  • The disclaimer dialog's size now uses full `scaleI()` (following `rowScale`) and reuses the unified shell `mdlModalChrome`
  • The text changed to the full "Model download and usage statement", with `Article X` and `Special notes` automatically recognised as bold subsection headings
  • "Verity JE" on the title card (frames 166–211) changed to "Verity-CN", replicated pixel by pixel at a 13.82px cell size and 6×8 glyphs
  • The original frames were backed up to `intro_video/_orig_title/`, and the 52 corrected frames injected into `dev/patches/assets/verity/textures/intro/`
  • The save button is now vertically centred in the header bar (`by = (headerH()-bh)/2`) instead of hanging on the "Settings" row's centre line
  • The save button's appearance now reuses the unified drawing routine `drawBtn`: primary color `V_PRIMARY` when changed, greyed outline `V_OUTLINE` when unchanged
  • Save button size `76×26` → `64×24` (scaling with the GUI), dropping the 1.25× label
  • Save button `52×18` → `76×26` (scaling with the GUI), with correspondingly larger corner radius and a 1.25× label
  • "Open editor without saving" now calls `discardAll()` first and then opens the editor; the dialog text gained "(unsaved changes will be discarded)"
  • The 16 `NCM_LYRIC_*` entries for lyric effect editing are excluded from the delayed-save snapshot: they neither light up the save button nor get reverted on discard
  • The account page's `verityBridgeKey` is excluded from delayed saving and is written to disk immediately on input or change
  • The config interface switched to delayed saving: changes only go to memory, `dirty()` compares the snapshot entry by entry, and `SPEC.save()` only runs when "Save" is clicked
  • `PersonaEditorScreen` no longer writes to disk on exit, saving uniformly with the config page instead
  • The input box no longer draws `...`; overflow is clipped on whole characters — shown from the head when unfocused, while focused the window only slides when the cursor passes the edge
  • A mouse click now only places the cursor on the clicked character and no longer changes the window
  • The input box's window origin is now stored in `visS`, pushed only by the cursor and moved in minimal steps once it passes the clipping edge
  • Overlong display when unfocused changed to `clipMid` middle truncation, showing one `...` on each hidden side of the window
  • `["AI settings"."LLM"]` rearranged around the four UI panels, with "select provider" immediately followed by its own pending entries
  • LLM / music / CN version key names unified to their UI label names, dropping the `llm`, `ncm` and `cores` dot-prefixes at call sites
  • `config/verity-client.toml` rearranged by the new key names and groupings, key values preserved, with the old file backed up as `verity-client.toml.pre_llm_reorg.bak`
  • `LlmMode.PRESET_FREE`, `TtsMode.MIMO`, `SttMode.MIMO` all renamed to `BUILTIN`
  • `llmFree*Key` merged into `llmBuiltin*Key`, and `LLM_FREE_PROVIDER` changed to `LLM_BUILTIN_PROVIDER`
  • The existing `config/verity-client.toml` has been renamed/deleted in sync, with key values preserved
  • Defaults in each `VerityConfig` `.define(...)` changed to the currently effective values (except Key, `MUSIC_U` and lyric effect values)
  • Config groupings rearranged to match the in-game UI pages; the existing `verity-client.toml` was rearranged in sync and backed up as `verity-client.toml.pre_reorg.bak`
  • On the account page, apart from `verityBridgeKey`, username / email / points / server model / connectivity are now in-memory values and no longer persisted to config
  • When this toggle is off, the reminders for LLM/TTS/STT not being enabled and for unmatched reply rules are silenced, while the toggle and the rules themselves keep working as before
  • The tick scan in `bounceOffEntities` now only handles `LivingEntity`; projectiles no longer take part in the per-tick point scan
  • The Allowed Faces in `PersonaHelper.buildCoresPrompt` expanded from neutral/happy/serious to the full emoji set
  • In the CN version, `VerityEntity` being hit by a falling block now gives friendly feedback via `playCallSound("ouch")`, while vanilla keeps the original furious line
  • The horizontal landing bounce factor went from `1.35 → 1.6` and the cap from `0.8 → 1.0` (`VerityEntity.lambda$causeFallDamage$2`)
  • Horizontal velocity on landing now also receives the bounce (factor 1.35, cap 0.8); nothing is applied when the horizontal speed is at most 0.02
  • `VerityEntity`'s horizontal wall bounce factor went from `0.6 → 0.8` (one place each for X/Z)
  • Landing bounce strength changed from `min(sqrt(fall)*0.22, 0.7)` to `min(sqrt(fall)*0.32, 1.0)`
  • The ring's track color reverted to `FAINT`, the active segment stays pure white, and the minimum visible arc is still 26°
  • The ring's track color changed from `FAINT` to `DIM`, and the minimum visible arc `RING_MIN_SWEEP` from 14° → 26°
  • The real context is shown as soon as you enter a world: `onPlayerJoinWorld` calls `MemoryHelper.primeContextEstimate()` to write `actualContextChars`
  • Landing, sneak-right-click throwing, lava, cactus and explosions now all go through `VerityEntity.playCallSound`, sounding according to the "fall scream" config
  • The three "destroyed" lines changed to neutral Chinese and are no longer broadcast to chat; they now write the player action and final reply as two lines in the CLI panel
  • The bottom-bar ring and number now uniformly use `actualContextTokens()` / `contextCapacity()`, representing the share of the full context actually sent to the LLM
  • The number still uses `contextTokens()` (added this session) while the ring switched to `actualContextTokens() / contextCapacity()`; `begin()` no longer clears `contextCapacity`
  • The "reply conditions" check mark moved right by another 1px (offset `+1` → `+2`)
  • The "reply conditions" check mark moved right by 1px (`ckX = cx + (17 - tw("✓"))/2` plus 1)
  • `VeritySpawnScheduler`'s chest easter egg now also records the player action and the final reply "hehe, you can't lock me up~" in the CLI panel
  • Reverted the CN guards in `VeritySpawnScheduler` and `ChestCloseHandler`: the chest easter egg triggers again in the CN version, with the line changed to "hehe, you can't lock me up~"
  • The note at the top of the CN version's config page was condensed to one sentence: follows vanilla config, no horror transformation or nightfall, and a neutral Minecraft assistant persona
  • In the CN version, `!isCores()` guards were added one by one for horror behaviours such as transformation, clearing the field, extreme darkness, the demon outside the window, killing villagers, furious lines and the chest easter egg
  • `PersonaHelper.buildToolRules` removes the `transform_following_day`, `forgive` and `transform_back` actions in the CN version
  • The AI settings group order changed from "memory → reply rules → other → skin" to "memory → reply rules → skin → other"
  • `ClientChatHook` no longer mirrors chat into the panel; achievements, command feedback, system messages and other players' speech all go to `VerityToast` instead
  • The three free-of-charge notices on first entering a world were condensed into one line: "This mod is free forever · anyone charging you is a scammer! · Bilibili @xzy4260"
  • The bottom bar was condensed from "time + speed + cache + context" to "time + context" (`RING_SEG=1`)
  • The context ring's progress segment changed to pure white, with a new minimum visible arc `RING_MIN_SWEEP=14°`
  • The player message prefix changed from `>` to a pixel-art solid right triangle `▸`
  • The "system override" prompt for proactive replies in the CN version changed to neutral wording (the `PersonaHelper.isCores()` branch)
  • The CN persona prompt dropped reverse statements such as "won't transform into a monster, won't darken the world", keeping only the neutral identity and relationship tendencies
  • "Must say the name" is no longer a separate line but the 4th checkbox in the "reply conditions" group (`REQUIRE_VERITY`)
  • The CN version's persona prompt was rebuilt as a neutral Minecraft assistant (`PersonaHelper.buildCoresPrompt()`), with the horror narrative branch no longer involved
  • The CN persona no longer prescribes a tone; the karma value still affects relationship tendencies as before
  • The CN version section of the config interface changed to a note: everything currently inherits the vanilla (horror) mode config, with dedicated configs to follow separately
  • The vanilla mode section was reduced to four items: `LLM/TTS/STT enabled`, corruption days, killing villagers, and the legacy item mechanic
  • "Must say the name" moved from vanilla mode to AI settings · reply rules, becoming the `ckG` checkbox (`REQUIRE_VERITY`)
  • In local inference the model name no longer carries the "local inference ·" prefix; the local model name itself is shown
  • The "always" mode panel opacity now uses the vanilla chat bar's 200-tick rhythm: fully lit for 180 ticks, then a linear 20-tick fade-out
  • Panel display timing is now driven by `VerityLlmSession.contentSerial` together with `ModClientEvents.onClientTick`
  • Proactive replies now `begin()` the session as soon as they trigger, with thinking, tool calls and the reply scrolling throughout, and the final sentence still recorded as SYSTEM
  • New chat messages arriving while thinking are dropped with a toast: "Verity is thinking, please wait", independent of the CLI panel toggle
  • "Always" mode follows the vanilla chat bar's lighting triggers (chat bar open / selector open / conversation in progress / content within the last 10 seconds)
  • In "always" mode `begin()` no longer clears the log, continuing to show existing content when it lights up again
  • Verity's replies are no longer broadcast to the vanilla chat bar in "always" mode (`VerityLlmSession.alwaysHooked`)
  • "Always" only takes effect in pure single-player; in multiplayer it automatically degrades to "mixed" without rewriting the configured display value
  • The four enable switches for LLM/TTS/STT/music changed to the vanilla-specific keys `vanilla*`, with the CN version to use `cores*` later so they do not interfere
  • After the four switches were renamed, upgrading returns them to their defaults (LLM/TTS/STT on, music off); just flip them again
  • The TTS/STT groups under AI settings dropped the overall "enable" switch, keeping the real TTS option `TTS_STREAM`
  • Self-check for keyless providers shows "no key needed" in `keyState` and goes through the generic `checkLlm`
  • The free list filters out models whose id contains `-free` and excludes `union-alpha`, which goes through the Anthropic line
  • Model probing rules: explicitly refused or 404 removed, 403 marked "(region restricted)", 429/5xx/timeout kept, and everything kept when an entire round is refused
  • The keyless lane no longer prompts for a key and hides the key field; the region-restricted marker is stripped automatically when writing back to config
  • The `AiAPI` request path gained only one `OurFreeModel.isCurrent()` branch; the other nine providers' code is untouched
  • SYNC correction changed from only adjusting `seekOffsetMs` to a real stream restart via `seekTo()` when the drift exceeds 800ms and it has been more than 3s since the last realignment
  • `streamPlay` no longer writes back to the clock; the local clock maps directly onto the server timeline
  • Server broadcasts, JIP re-sends and SYNC ticks now uniformly carry the anchor timestamp
  • The audio line now `open`s first and only calls `start()` after calibration completes
  • `resumeSong` changed from a local `line.start()` to fetching the server position and restarting the stream through `doSeek`, so everyone rejoins the stream at the same position
  • The log page's list displacement animation key now includes `logDebugShown()`, so toggling "verbose logging" and switching ALL/INFO/WARN/ERROR use the same animation
  • The `/cnv mplay` (without id) hint changed from "start playing" to "resume playing"
  • `CnvCommand.musicMsg` now uniformly wraps song titles in book-title marks, e.g. "《XXX》 now playing"
  • "Favourite song" changed from a built-in mp3 to NetEase Cloud 2115551659, with `playFavouriteSongEntry` delegating to `playSongById`
  • `/cnv mplay mygal | favorite | fav` map to the same id, and the persona prompt now guides it to use `ncm_play`
  • The local decoding path for the favourite song was normalised onto the server streaming path, so the dedicated server side no longer contains any code that opens audio devices
  • `VerityLog.debug()` changed from "the switch sits before the write" to "always record, the switch only controls display", and buffer eviction drops the oldest DEBUG entries first
  • `VerityToast.show` strips a leading `[TAG]` of at most 12 characters and removes the literal `[NetEase Cloud]` / `[web search]` tags
  • In multiplayer `LyricEditorScreen` no longer draws the music control block: `paramsBlockH()` returns 0 and mouse hit-testing skips the whole block
  • The lyric editor's "Save" was split out of `close(true)` into `saveNow()`, which only writes the config, persists it and toasts, without leaving the screen
  • The multiplayer notice text changed from "game paused" to "multiplayer · game time keeps running (the world will not pause)"
  • All `/cnv` subcommands plus `/recoververity` and `/changekarma` feedback now go through `CnvCommand.feedback()`: players get a toast, while command blocks and the console still reply in chat
  • `VerityToastRenderer` reworked: scales with GUI height (no scaling at ≥480, 0.88 at ≤240), and wraps overlong text by code point with a line limit
  • Toast animation: single-line ↔ single-line slides over the common prefix; if either side wraps it degrades to a whole-box cross-fade
  • Lyric emoji vertical offset `SHIFT_Y` changed from 3.0 to 2.2
  • Color picker dragging and input now write live into the snapshot `p` (`pkLiveApply`); neither clicking blank space to close nor clicking confirm writes config — only "save and exit" persists
  • The exit confirmation dialog's card opacity changed to `C_CARD 0.88f`, with a `drawWarnIcon` triangle warning icon added to the left of the title row
  • All of this mod's status and error notices switched to `VerityToast.show`; character lines, command output and announcements with links stay in chat
  • Emoji rendering parameters: `SIZE` 9.0 → 7.0, `SHIFT_Y` 1.0 → 4.5 → 3.0, `SHIFT_X` 0.0 → -0.9
  • Emoji glyphs are limited to a whitelist of code points (`231A-23FA` / `2600-27BF` / `2B00-2BFF` / `1F000-1FAFF` and so on); non-emoji returns null
  • Lyric rendering went back to "the previous line moves down, the next line stops below", keeping `nextNonEmpty` for non-empty lines across gaps and the "(instrumental)" placeholder
  • Dot appearance: color `0xFFFFFF` → `0xF9E9C5`, the 1px black backing removed, spacing factor 1.8 → 1.3, breathing period 900 → 1500ms
  • Dot position changed to vertical centring on the line below the previous one, nudged towards it by `max(1, round(textH*0.2))`
  • `hasUnsavedChanges()` dropped the leftover `dragLive` condition, keeping only `pLoaded && !sameAsOpened()`
  • The dots changed from 2px grey `0xAAAAAA` at 0.6 to 3px white `0xFFFFFF` at 0.85, with wider spacing
  • Config text input limits now differ per row: 8192 for the cookie row, 255 for the rest (`textCap()`), including the Ctrl+V path
  • `/song/url/v1` now sends the `Cookie: MUSIC_U=…` request header, keeping the query parameters
  • The fade-out of the previous line in word-by-word colouring now passes the real playback position instead of `Long.MAX_VALUE`
  • Word-by-word lyrics changed to continuous karaoke-style per-character colouring; `LyricLine` gained `wordEnd[]`, and each word in the sync packet went from 2 numbers to 3
  • Toast duration `VerityToast.SHOW_MS` changed from 1800 to 3000, with the config interface toasts updated in sync
  • Translation lines keep exact timestamp pairing, reverting the "nearest time + 1500ms window" approach
  • The lyric endpoint changed from `/lyric` to preferring `/lyric/new`, falling back to `/lyric` on failure
  • The YRC line header regex now accepts both `[start,dur]` and `[start,dur,0]`; word times are parsed as absolute milliseconds while also accepting relative notation
  • `parseLrc` now supports the JSON metadata rows of `/lyric/new` (lyricist / composer / arranger)
  • Panel collapse/expand changed from a hard jump to `foldT` exponential easing plus clipping, with geometry always computed from the expanded state
  • Song info toggle brightness is now driven by the `onT` state (`drawSwitch`) instead of hover, staying near-white once on
  • `dev/tools/editor_geom_check.py` rewritten to cover 9 resolutions × wide/narrow × 4 parameter row counts, 72 combinations in total
  • "Font size" renamed to "lyric font size" and the row structure rearranged (still 8 rows, panel height unchanged)
  • Vertical/horizontal spacing is now adjusted by dragging the selection box's edges and corners; dragging inside the box still moves it
  • The fading previous line now fades while keeping the active color, with the squared falloff kept for the tail
  • The playback URL pre-check was moved before `stopCurrentPlayback()`; when no URL is available it returns `vip_or_unavailable` and leaves the old song alone
  • Duration now uses `PlaybackTask.realStreamDurationMs` (preview endpoint or size÷br), refreshed by the panel on every clock sync
  • The panel's play / pause / seek / song request all moved to a background thread, with replies showing toasts via `mc.execute`
  • The toast texts for an invalid ID, preview-only playback and VIP preview start were shortened and distinguished
  • The HUD, editor panel and preview now read playback state from `active*()`
  • Duration now prefers the local playback task's millisecond value, falling back to the status endpoint when unavailable
  • Alignment guide colors changed to cyan `0xFF22D3EE`: a 2px solid line when snapped, a same-colored 35% dashed line when not
  • Sliders, lyric dragging and color picking now only change the in-memory snapshot `p`, no longer writing config on release
  • Writing is consolidated to close time: `applyToConfig()` writes the 8 entries and immediately calls `SPEC.save()`
  • The unsaved check returned to the `p != opened` rule (the snapshot taken on open)
  • "Exit (without saving!)" now closes the screen directly — the config was never modified, so no rollback is needed
  • The play button's triangle icon changed to a complete ▶ with the longest line in the middle and symmetric decrease above and below
  • The SV cursor shrank from "radius-5 filled circle + 2px black ring" to "radius `s(3)` + 1px outer ring"
  • The panel now compresses row height by the overflow amount in very small GUI spaces, with a floor of `s(11)`
  • The color picker width `pkW()` is now derived from `pkPad()*2 + pkSv()`, naturally matching the SV square's width
  • The bottom field row is allocated proportionally to available width: 13% color swatch, 26% HEX, and the three RGB fields splitting the rest
  • The panel's active/upcoming color layout changed to `R,G,B #RRGGBB`, prioritising HEX on narrow screens
  • The editor now takes lyrics from `currentLineIndexPublic()`, sharing its source with the HUD and replicating the slide-up fade animation
  • The "sample (not playing)" check changed to `realLineIndex() >= 0`
  • Song request failure notices now reuse the `CnvCommand.musicMsg` convention, with separate texts for preview/no VIP and for a song that does not exist
  • Card `C_CARD` alpha reduced from opaque to `0x8C`, with the border switched to semi-transparent `C_BORDER_H(0.55)`
  • The full-screen overlay was lowered from `0x50101014` to `0x30101014`
  • The color picker was rebuilt from three horizontal bands into a standard HSV picker: hue bar + SV square + vertical value bar
  • The exit confirmation's three options were settled as "Cancel / Save and exit / Exit (without saving!)"
  • The confirmation dialog's button geometry was extracted into `cfBtnW/H/Gap/X/Y`, shared by drawing and hit-testing
  • `isPauseScreen()` changed from always false to returning `Minecraft.isSingleplayer()`: paused in single-player, not in multiplayer
  • Lyric progress is based on `System.currentTimeMillis()`, so it still advances with real playback while single-player is paused
  • Editor geometry changed from hardcoded pixels to uniformly passing through `rowScale()`, covering the panel, row height, padding, sliders, color picker, confirmation box and corner handles
  • The slider fill's right end and the thumb's centre are now both positioned by `sx + sw * t`, strictly inverse to `applySlider()`
  • The preview's left/right offset changed from hardcoding row 0 to the left, to reading `LyricHudRenderer.currentLineIndexPublic()` for the real row number
  • Editor colors and control shapes were aligned with `VerityConfigScreen`'s shadcn style, including card corner radius, three-tier buttons, sliders and the color picker
  • All hover effects changed to `ease(cur,target,dt,speed)` exponential easing, independent of frame rate
  • The selection box changed from showing only while dragging and estimating from the widest line in the song, to always showing the union of the current two lines every frame, including translation lines and the real line spacing
  • Download integrity checking changed from declaration ×0.98 to trusting the server's `Content-Length`; the declared value is only a fallback with the tolerance relaxed to less than half
  • The total progress amount is corrected to the actual value, so the bar no longer sticks at 95% and then jumps to complete
  • Context compression changed from silent to showing the selector both locally and in the cloud when the threshold is reached; choosing "not now" defers the next prompt until the history grows another 8%
  • Sending messages during compression is refused with a toast, and a persistent "compressing context" banner sits at the top of the panel
  • The compression UI no longer shows the text to be compressed or the request sent to the LLM, keeping only the threshold notice, options and chunk progress
  • The compression selector is now bottom-anchored with reserved height; the bottom timer freezes while waiting and the panel stays visible
  • In-game toasts moved up to `height-78` (a fixed value in GUI space), clear of the hotbar, experience bar and chat input
  • Toast drawing was extracted into `gui/VerityToastRenderer`, shared by in-game and the config interface, with low-alpha drawing skipped
  • The compression option text changed to "not now" and was promoted to `MemoryHelper.compressOptions()`, sharing its source with `/cnvtt selector`
  • Android native library extraction moved from external storage to preferring internal storage (`org.lwjgl.librarypath` / tmpdir / user.home)
  • The `shortMsg` truncation limit changed from 220 to 480, so the critical half-sentence in a dlopen error is no longer cut off
  • Narrow-screen layout: the sidebar's hardcoded 132 floor changed to at most 34% of screen width, card width `max(240,…)` to `max(80,…)`, and dialog padding `sw-80` to `sw-32`
  • Header height, padding and bottom padding now shrink with screen height; at 600×270 the content area height went from 144 to 195
  • Each native library load failure is now logged on its own line (deduplicated via REPORTED), no longer masked by the group rule that "at least one success means ready"
  • `stage_runtime.py` writes Android jnidispatch into both `verity-runtime/<plat>/` and `varmite/jna/android-<arch>/`
  • Local inference output changed from after-the-fact LlmOutput.salvage rescue to loading a GBNF grammar constraint before sampling
  • LlmParams.jsonMode stays at its default false and is only explicitly enabled on the AiAPI chat path
  • Known cost: with forced grammar escaping, lines containing quotes may be truncated, but the format is always valid
  • JSON parse failure now treats the plain text as the message with empty actions, so the conversation keeps working
  • PersonaHelper's `[PlayerName]` marker is now read-only and the word is not output
  • The compression trigger changed to +85% of the converted value locally, and 131072 + 80% in the cloud
  • Compression output changed to the least.md body format, with recent conversation kept verbatim rather than paraphrased by a model
  • Context and reply length are now hardcoded: CTX_SIZE changed from 8192 to 12288, MAX_REPLY_TOKENS = 1024
  • Each conversation now starts with llama_memory_clear to empty the KV cache
  • When over-long, the oldest non-system messages are dropped one by one, preserving the persona settings
  • Decode failure messages now include the real prompt token count plus n_batch / n_ctx
  • The primary action in the model detail dialog's bottom right now has three states: not downloaded / in progress / ready
  • The dialog's primary action button width now adapts to the text as `max(96, tw(label) + 22)`
  • The hardcoded fallback list now includes the ggml-cpu-* variant names
  • The whole JNA package was relocated to varmite/jna and varmite.jna, with jnidispatch replaced byte-for-byte at equal length
  • The desktop side no longer sets a global jna.boot.library.path; jnidispatch now lives in the jar's private resource path
  • Local LLM changed from a third-party JNI binding to the official llama.cpp CPU package plus a hand-written LlamaCpp.java
  • Chat now uses the conversation template bundled with the GGUF, llama_chat_apply_template
  • The CPU instruction set gate is now only used for the UI tier display and no longer disables the local LLM on older CPUs
  • stage_runtime.py now uses the official packages under `dev/_vendor/lc/` and trims CPU variants per platform
  • LocalInferRuntime's three entry points now really call each library's Java API, with model instances cached per model
  • The grouping animation and re-layout in `m_88315_` are wrapped in try/catch; exceptions are only logged and no longer skip the frame's drawing
  • The detail/download manager dialog overlay `mdlModalChrome` was rewritten: it no longer reads the main framebuffer for blurring, instead stacking four pure `fill`/`rr` layers (dim → centre spotlight → drop shadow → raised surface + border + highlight)
  • The "select model" submenu `renderModelModal` still keeps framebuffer blurring (the same three defects are still unaddressed)
  • Removed the buffer-reuse retention logic in `renderModelModal`; the buffer is now released directly when `!mmOpen`
  • The "model detail" dialog was rebuilt as a whole: type/tier pills + status pill, model name scaled 1.25×, four spec mini-cards, HF data as three metric cards, and the file list as auto-wrapping chips
  • The "download manager" dialog was rebuilt as a whole: title row → overview bar → task card list (88px per card, with status bar, rounded progress bar and pause/resume/delete), and an empty state of a centred icon plus two lines of text
  • Both dialogs' geometry was consolidated into `mdlDetailGeom` / `mdlMgrGeom`, with hit-testing reading the button fields filled in during rendering
  • The detail dialog's content items changed from string prefixes to a structured `DLn` (kind + fixed height table), with measurement and drawing sharing the same table
  • Clicking outside the panel closes it, the same behaviour as the "select model" dialog
  • Click ripples changed to a circular wave spreading from the actual press point to the four corners, clipped inside the button and applied to all buttons uniformly
  • The detail/download manager dialog's entrance now reuses background blur + dark overlay + panel centre scaling/slide-up (220ms easeOutCubic)
  • The left TABs were rearranged: "feature config" and "Verity account" swapped places, and "model download" was added above "self-check"
  • Self-check and connectivity detection gained three layers of fallback: a render-side watchdog forces "✗ check timed out" after 90s (150s for connectivity) and resets the button
  • Background detection now catches Throwable throughout, the account page's `connBusy` reset uses finally semantics, and exceptions no longer pin the button at "checking…"
  • Each self-check now writes a log line on completion (e.g. `[verity-config] self-check LLM -> …`), making it easy to trace from latest.log
  • `VeritySelfCheck` reuses a single HttpClient (a class-level singleton), avoiding selector thread pile-up after repeated checks
  • The catch blocks of `VerityAccountBridge`'s three test* methods were widened from Exception to Throwable
  • The Mimo voice cloning model name was corrected from `mimo-v2.5-tts-voiceclone` to `mimo-v2.5-tts` (unified across AiAPI / VoiceCloneHelper / self-check / config interface)
  • While a dialog is open the underlying hover/press highlight no longer shows through; the selection dot changed from green to white, and confirming auto-fills the Model input
  • Removed the staggered per-row fade-in (the root cause of rows fading further and further until they vanished when scrolling long lists)
  • The dialog scrollbar now supports grabbing and dragging the whole track, with no displacement on press
  • Collapsible groups now show "click to expand" when collapsed and "click to collapse" when expanded, instead of always showing "click to expand"
  • The log list scrollbar reuses the page's implementation (`thumbGeom` with a 24px minimum thumb, `drawBar` visuals, whole track grabbable); the old custom thin-thumb version was discarded
  • The right-click "copy message content" menu was removed; left-clicking anywhere on a row copies the whole `[level] time message` line
  • The log list moved to a two-stage transition: stage 1 (220ms) fading departing rows in place while surviving rows stay put; stage 2 (430ms) surviving rows shake then fall (about 6.6% overshoot rebound) as new rows slide up and fade in
  • On the self-check page the y offsets of the LLM/TTS/STT names, mode·detail and right-hand results were unified to by+10, with the microphone row aligned in sync
  • Chip width is now computed from the check mark/text/count in the same frame from one source, with a fixed 5px gap between text and number
  • Log list displacement now uses ease-out-back (convergence factor 1.2), with opacity still linear
  • The easing speed of the level filter chip's check mark expansion and width re-layout was increased by about 1.7×
  • The log list transition changed to fully linear: FLIP for surviving rows, slide-up fade-in for new rows, in-place fade-out for departing rows
  • The toast's old differing text now "shrinks out character by character along the box edge": characters swept by the right edge as the box narrows are not drawn, and characters at the edge fade first
  • Level filter chips changed to dynamic width + horizontal re-layout (the check mark is the first child, collapsing to width/opacity 0 when unselected, with later chips shifting by the preceding widths), with zero vertical displacement
  • Departing log rows now move down 0.7 row heights while fading out (260ms ease-out), drawn first with surviving rows painted on top
  • Toasts changed to a shared-element/FLIP transition: tweening over the longest common prefix of the old and new texts, cross-fading the difference (ease-out 260ms), and staying completely still when retriggered with the same value
  • The level chip's check mark changed from a solid rectangle in `m_280509_` to a per-pixel drawn line, relocated into the 8x8 check slot on the left
  • The log list changed to row-level FLIP: keyed on `entry.time`, surviving rows tween to their new position, new rows slide in from below and departing rows fade out in place (260ms ease-out)
  • The self-check page's `checkResult` changed from `Map<String,String>` to `ConcurrentHashMap<String, VeritySelfCheck.Result>`; `runCheck` now does `put(name, Result.running())` and then `check()` each in the background
  • The account page now logs out automatically when the session expires: `doFetch` only calls `logout()` when the server explicitly confirms invalidity, based on HTTP 401/403/404 or a response body indicating invalid/expired/unauthorised
  • Entering the config page (`VerityConfigScreen.init`) and switching to the account tab now verify the login state automatically (4s throttled `refreshSync`), refreshing after a successful login poll
  • `doFetch` now uses `TRUST_ALL_HTTP` (consistent with testLLM/TTS/STT), and `autoLogoutOnServerExpired` gained a re-entry guard
  • Button components gained a Flutter InkWell-style effect: 5% press scaling and a 420ms white ripple on hit, with `m_6375_`/`m_6348_` recording the timestamp and coordinates
  • The log page's top "this mod only · N in memory · N after filtering" text row was removed and the chip row moved up as a whole
  • The duplicate "self-check" and "log" titles inside the cards on those pages were removed
  • The log page's status notice changed to a toast (`logStatusSet` degraded to `showToast`, fields `logStatus/logStatusAt`)
  • The "verbose logging" toggle now really controls Bridge refresh logging: four high-frequency account verification flow logs were downgraded to `VerityLog.debug`
  • The mode selection in the TTS built-in provider panel moved to the first row, determining what Model and Key display
  • The Model row is a required input for presets, but for voice cloning it shows a fixed value with no input box
  • Dropdowns now fade in with a slight slide-up when a new value is selected
  • Reference voice changed from click-to-cycle to a dropdown (Verity / Ashen's / old pastor / custom)
  • "Local clone audio" renamed to "custom audio", shown only when the reference voice is "custom", and "(not used)" was removed
  • The TTS / STT built-in provider layout is aligned with the LLM one, removing the extra titled grouping layer
  • The voice row's display name was corrected from `ttsVoiceUI` to "mode selection", and the "Bingtang" option changed to "preset"
  • The mod version changed to `v3.5-beta`, updating both `VerityConfigScreen.ABOUT_VER` and `META-INF/mods.toml`'s `version=`
  • All 271 classes of the bundled GeckoLib and mclib were renamed into a private namespace, taking the count of classes clashing with external ones from 271 to 0
  • The network channel changed from `geckolib:main` to `verity_geckolib:main`
  • `AnimatableIdCache`'s save keys changed from `geckolib_id_cache` / `geckolib_ids` to `verity_geckolib_*`
  • The NBT key for item animation IDs changed from `GeckoLibID` to `VerityGeckoLibID`
  • The Mixin config and access transformer were renamed in sync (`geckolib.mixins.json` → `verity_geckolib.mixins.json`)
  • The config interface entry moved from the mod manager to the game settings (Mixin injection)
  • The config interface changed from a static preview to real config reads and writes, saving on change

Repair

  • Game reported "not a valid mod file" on startup: the `mods.toml` version `v3.5` did not match Forge's `^\d+.*` validation, changed to `3.5`
  • The left TAB scrollbar's bottom could not reach the back button: `navBot` now uses `h - scaleI(50) - 10`, and the track `navViewH` takes the whole segment
  • After agreeing to the "model usage statement" the save button lit up and exiting prompted "unsaved": folded into `isImmediateKey` for immediate persistence
  • The disclaimer scrollbar and the page scrollbar's grab areas overlapped and scrolled the page by mistake: `m_6375_` now intercepts before `scGrabArea`
  • Agreeing without persisting caused the prompt to reappear on the next launch: `MODELS_DISCLAIMER_OK` now calls `SPEC.save()` plus a snapshot refresh
  • After choosing "open editor without saving", changes were still written to disk by the lyric editor's `SPEC.save()`
  • `discardAll()` did not clear the inline input box's buffered text `this.txt`, so the old value still showed after reverting
  • After the account License Key was persisted the snapshot was not refreshed, so the save button showed "unsaved changes"
  • The input box's truncation `...` disappeared: shown from the head with right-side truncation when unfocused, and one `...` drawn on each side when focused
  • After refocusing or a row being rebuilt by `buildCards()` the window jumped back to the start: each input box now records its own window origin in `inputWin`
  • After releasing a slider on the config page the fill replayed its animation from the old position; during dragging it now calls the `av` easing with the instantaneous speed
  • Projectile bounce detection mistakenly used `m_7639_` (getEntity) and picked up the shooter; changed to `m_7640_` (getDirectEntity) with `m_7639_` as fallback
  • `VerityEntity`'s collision box was misaligned with its visible sphere; `m_142242_()` now returns a 0.5 cube with its centre at `pos.y + 0.25`
  • `SphereRenderHelper`'s visible sphere centre was 0.125 above the collision box centre; the first segment's displacement went `0.5 → 0.375` to bring the centre back to `pos.y + 0.25`
  • Arrows hit the sphere without triggering a bounce; projectiles now attach to `VerityEntity.m_6469_` as a hit callback and bounce along the flight direction
  • Local feedback for lava/explosions/cactus and falling-block hits only wrote to the CLI panel; `addMessageToHistory` now also records the user/assistant entries
  • The context ring's white arc was not drawn because the denominator `contextCapacity` was 0; `ModEvents.onPlayerJoinWorld` now sets the denominator as well
  • Explosion and cactus sounds were silent; `playCallSound` now plays from the server-side world `m_5594_`, preferring to attach to a nearby Verity entity in TTS mode
  • The player's own chat echo triggered a duplicate toast; `ClientChatHook`'s `isOwnEcho()` now skips it
  • The context ring was measured before the messages group was assembled; `setContextChars()` moved to after `messages.add(userMsg)`
  • The context ring reset to zero on re-entering a world; it is now estimated from the summed character counts of the messages assembled by `AiAPI.askLLM`
  • When scrolling the CLI panel with the wheel, extra lines were drawn above the log area (now only `cap` lines are drawn, bottom-aligned)
  • The "reply conditions" check mark was not centred in its box; the vertical offset changed from `scaleI(2)` to `scaleI(4)`
  • The bottom bar's model name was empty on first showing the CLI: with no session it falls back to the currently configured model (`AiAPI.currentModelLabel()`, cached for 1 second)
  • "Stop thinking" did not take effect immediately: `requestCancel()` now closes the registered active stream directly, and truncating thinking switched to `discardLive()`
  • `builtinKey()` / `builtinModel()` lacked `case OUR_FREE_MODEL`, so the model name was written into the NVIDIA field and caused HTTP 401
  • The self-check page wrongly reported "Model not configured" for Our Free Model (the keyless lane is no longer blocked on an empty Key)
  • Players joining during a pause would start playing on their own: the server now sends a bare PAUSE to individual players while paused
  • Joining during a pause and then resuming skipped the whole pause duration; frame calibration now targets `NcmClientPlayer.getCurrentPositionMs()`
  • A stable drift of about 7400ms when joining mid-song: `handleStart`'s time anchor now uses the client's local clock at packet receipt
  • `handleSyncPosition` subtracted wall clocks across machines; SYNC now only takes the received authoritative position `positionMs()`
  • The server-sent `timestampMs` is kept in the packet but no longer participates in local time estimation
  • A PlaybackTask created by `doSeek` had no server set, so `signalStarted()` returned immediately and seeks and resume broadcasts never went out
  • Added `pausedDurationMs` / `pauseAtMs` bookkeeping on the broadcast side, fixing mid-join players running ahead by counting the pause duration
  • `NcmNetwork.sendToAll` skipped hostPlayerUUID, so the client did not receive its own START when initiating playback; now sent to everyone
  • Whether to emit sound is now decided by the client (`NcmClientPlayer.handleStart` skips when it is itself the server host)
  • Opening an audio device on the dedicated server threw and blocked the START broadcast; `streamUrlToSpeaker` now checks `DedicatedServer` first
  • The host JVM played through both the server and client paths, doubling the sound; `handleStart` now skips the client path when it is itself the server host
  • `m_7043_()` mistakenly used `m_91091_()` (`hasSingleplayerServer`), so LAN rooms were treated as pause screens; changed to `m_257720_()`
  • `drawMusicBlock` is now wrapped in try/catch with a `VerityLog.error` write, so an exception no longer stops the whole music block being drawn in multiplayer
  • After the expand animation the clipping region's bottom edge extended to the music block's bottom, covering the silent clipping when the panel height was a few pixels short of the content
  • `queryDurationMs()` sent a synchronous HTTP request on the render thread every clock-sync cycle; a 15s rate limit was added
  • With a pure interlude (an empty text line) the previous line vanished and the next did not show; it now shows the next line as usual and draws white dots in the gap
  • Switching skins under AI settings needed a restart to take effect; `setSel()` now calls `setSkin()` immediately when `VerityConfig.SKIN` is hit
  • Translated lyrics showed through translucent screens such as config / inventory / chat; the whole HUD layer is now drawn only when no Screen is open
  • MUSIC_U was silently truncated to 255 characters, so the server only returned a preview clip
  • Song title / artist showed through other interfaces when a Screen was open
  • The vertical spacing `vs` could be dragged to 0, overlapping the two lines; both reading and dragging now clamp to ≥ 26 (`NCM_LYRIC_V_SPACING_MIN`)
  • The selection box height bulged with the animation; `fitBox()` now takes the union of the current line and the next at their resting position only
  • The ID input's default value (hint) was displayed in white; textColor is now switched per frame: `0xFF71717A` while showing the hint, `0xFFF4F4F5` otherwise
  • The previous line's animation started with a horizontal jump (the editor preview's offset sign was inverted relative to the HUD) and jumped from active to upcoming color during fade-out
  • `NcmHelper.getCurrentPlaybackMs()` accumulated without bound on the local clock, so the progress bar time ran forever; now clamped to the real stream length
  • Animations that should move up flickered at the start; `alpha` changed from `f2` to `0.6 + 0.4 × f2`
  • The fade-out tail flickered bright because colors with alpha < 64 were forced opaque; now nothing is drawn when `f*255 < 64`
  • During local playback the panel showed not playing, the play button was always ▶, clicking restarted, and the progress bar did not move
  • The editor's lyric stayed at "sample lyric" — `advanceState()`'s playing check now uses `activePlaying()`
  • The ID input text was not centred — after disabling the vanilla border, a 4px left padding and vertical centring were restored
  • `musicToggle()` branch: pause/resume when playing, only request by ID when not playing, and give a clear hint for an empty ID
  • The whole color picker showed as a missing texture — `getTexture()` never returned null, so registration became an unconditional call
  • The editor's lyrics did not advance — row advancing was split out of the render path into `advanceState()`
  • Config was reset after dragging — the editor now reads once on open and no longer reflects back every frame
  • Save and "exit (without saving!)" stopped working — switched to a `diskDirty` check: the former persists and clears the flag, the latter rolls back to the snapshot taken on open
  • `flushPendingConfig()` race: the gap between clearing the queue and setting entries one by one let the following `save()` write stale values
  • Toasts were drawn twice in multiplayer — the HUD side now skips toasts while `LyricEditorScreen.isOpen()`
  • Changing the window or GUI scale re-triggered the toast — the entry notice gained a one-shot `entryToastShown` guard
  • The ID input's cursor followed the grey placeholder hint
  • The ID input did not support Ctrl+A/C/V/X, Home/End, Delete or the left/right arrow keys
  • Other controls the mouse swept over still showed hover feedback while dragging or holding an item — hover coordinates are now frozen while `hovLock` is set
  • The right side of the SV square appeared black — per-column resampling left gaps between rows; changed to `drawSvSquare` generating row by row
  • The color picker ignored GUI scaling — all design values now pass through `s()`, and `pkSv()` is double-clamped by available height and width (floor `s(88)`)
  • Content overflowed on extremely narrow screens — `layout()` now compares panel height with available height at the end, shifting everything up and compressing padding when over
  • Dragging a lyric flicked back to its original position on release — config re-reads are skipped within the `commitAt` and `COMMIT_GRACE_MS(120ms)` grace window
  • The world appeared to reload after adjusting config — `ConfigEventHandler.onConfigReload` dropped `levelRenderer.allChanged()`
  • Blank space accumulated at the top of the panel while content below was cramped
  • `isPauseScreen()` was always true on 1.20.1, freezing the world as soon as the editor opened; changed to pausing only in single-player
  • Calling `sample()` every frame while dragging read back config over the in-memory values, making dragging impossible; sampling is now skipped during dragging
  • `LyricEditorScreen.active` was never assigned, so lyrics were drawn twice by the HUD and the editor; now registered in init and unregistered in removed
  • The color picker geometry was copied as absolute pixels in `drawPicker` / `pickerClick` / `pickerDrag`, so on scaled screens it drew in one place and hit another bar
  • Clicking "Save" ran `SPEC.save()` before the debounced write, so exiting and re-entering reverted to old values; now flushes before saving
  • Opening the editor while not playing showed a blank page; it now decides which two lines to draw itself and reuses `drawLinePublic`, showing a sample when there are no lyrics
  • The color picker drew and hit in different places on scaled screens, and the dialog height was hardcoded at 150, squeezing out the preview block and "Confirm"
  • Row 8 "upcoming color" was cut off in narrow windows; row height now shrinks to a floor of 16 based on available height and the panel width is clamped
  • Declared sizes did not match the actual ones, so complete files were judged incomplete; 10 entries changed to measured values, and all 26 entries and 57 files are now within 2%
  • The TTS manifest was missing `tokens.txt` and 4 FSTs, breaking Chinese voice synthesis; about 2.4 MB × 6 entries were added
  • Engine load failure only returned null; `LocalInferRuntime.lastTtsError()` now names the missing file, including `espeak-ng-data/`
  • On small windows the compression selector's options 4/5 and the action hint overflowed the status bar; the panel now grows taller and sacrifices the hint row then the description row in order according to actual height
  • The HUD overlay still rendered while a Screen was open, ghosting the log; the HUD is now suspended via `askBegin/askEnd` during the selector
  • `java.awt.FileDialog` threw HeadlessException because `java.awt.headless=true`; changed to a reflective tinyfd call
  • The tinyfd reflective call must match the five-parameter `tinyfd_openFileDialog` signature exactly, otherwise it falls back to AWT and throws HeadlessException again
  • Android native libraries were extracted to noexec external storage, so dlopen failed for llama / sherpa / vosk across the board
  • JNA on Android picked up an unrenamed jnidispatch, so `FindClass` failed and LLM / STT initialisation reported UnsatisfiedLinkError
  • `download` did not wake the waiting thread on enqueue, so progress stayed stuck at queued
  • `remove` deleted the directory while the download thread was still writing, leaving a `.part` file and reporting "file incomplete"
  • A queued id absent from the task table made the download thread spin and burn a whole core
  • Silently dropping the oldest messages when local inference ran short of context caused sudden amnesia
  • Minecraft capturing the mouse meant the HUD overlay received no clicks; changed to a Screen that does not pause the world, redrawing the CLI panel
  • When a 0.5B model did not output JSON the whole round was judged failed and the error text was used as the reply
  • The model copied `[PlayerName]` from the prompt literally into its output
  • The compression threshold kept the fixed 131072 characters, so compression never triggered under local inference
  • `requestCompression` always went through `SpeedHelper.resolveLlmConfig`, sending local compression to the cloud
  • Submitting more tokens in one go than n_batch triggered an assertion and aborted the whole JVM
  • The KV cache was not cleared between conversations, so once n_ctx filled up every sentence failed
  • The prompt plus generation length exceeded n_ctx, making llama_decode return -1
  • The chat bar still showed the cloud model name under local inference; the model name and stage are now taken per mode
  • FILES.txt was only half written, so ggml-cpu-*.dll was not unpacked and 0 backends were registered
  • Model load failure only reported vague text; it now also reports the backend count and names the missing dll
  • The bundled JNA and the jna in Forge libraries exported the same package, causing module resolution failure
  • `prepare()` judged success by "were new files extracted", so local inference broke after the second launch
  • Linux / macOS native libraries were filtered by `endswith('.so')`, losing all the real files
  • The TTS sample rate was hardcoded at 22050, causing pitch and speed shifts; now played at the model's real sample rate
  • Missing libraries gave a uniform message; now the specific file and the failing step are reported
  • `isDownloaded()` treated "only a .part left" as downloaded, flipping the card early
  • The local inference panel's entry animation did not advance, leaving the whole block invisible
  • The detail dialog's "repository" row sat against the metric card's bottom edge; a 7px gap was added
  • Dropdowns showed only "none" when unset; changed to "not selected · click to select"
  • Self-check lumped unselected, not-fully-downloaded and missing-native-library into one "usable" message
  • When the local inference native library was missing it falsely claimed "will use the built-in fallback"; now it states plainly that it is unavailable
  • The model introduction showed twice: all `## ` lines in the body are now skipped when rendering
  • Text after bold was squeezed upward: the bold segment in `mdlMdLine` now advances by `tw("§l" + segment)`
  • The subsection heading's vertical bar hung 2px lower than the text; it is now exactly the same height, with line height 20 → 16 and group gap 9 → 12
  • The grid scrollbar jumped notch by notch: a new `mdlScrollAnim` easing was added, with rendering and hit-testing both using the rounded `mdlSc()` value
  • The model download page's detail / download manager / usage statement dialogs were cut off on the left by the content area's clipping frame; all three now draw outside `renderAll`'s clipping frame
  • The usage notice dialog was cut the same way on first entering the config page; moved outside the clipping frame as well
  • `renderModelsPage` now keeps only the "underlying layer does not respond to hover while a dialog is open" suppression logic
  • The picture showing through the overlay did not match the page: the frame's GUI had not flushed when grabbing the framebuffer; now it relies only on integer-coordinate drawing primitives, sharing the page's batch and matrix
  • The blur radius was computed in framebuffer pixels but used as GUI units and divided out by GUI scaling: the new implementation no longer uses blur
  • The overlay used a bare Tesselator global modelview while GuiGraphics used another matrix, misaligning everything: now unified through GuiGraphics
  • Task card buttons overlapped the content and the speed text fought the buttons: the content width now explicitly subtracts the button area (`MDL_MGR_RIGHT = 148`), always leaving a 4px gap
  • Labels overlapped in narrow panels: the status pill, overview pill and title/subtitle now compute the remaining width first, and are omitted or clipped when there is not enough room
  • Text overlapped inside dialogs in short windows or at GUI scale 4: panel heights gained a floor (170 for detail / 140 for download), and the spec mini-cards are omitted when the detail dialog is under 256
  • The download page's dialog triggered the "select model" panel by mistake: the model list dialog now renders only while it is itself open
  • Underlying cards' highlight showed through while a dialog was open: the layer below no longer responds to hover while the detail/download manager dialog is open
  • The download manager button could not be clicked: the hit-test geometry was missing the title row's height; opening works normally after the fix
  • Model card name and size overlapped: the download status is now right-aligned and the model name is clipped to its left
  • The log scrollbar's direction was mirrored overall: `logTop` and `thumbGeom` used opposite coordinate systems, now converted uniformly to document coordinates (`cur = (maxTop − logTop) × row height`)
  • `MicrophoneManager.scanForMicrophones`'s Skipping mic log level was lowered from ERROR to WARN, with the exception class name attached
  • Toasts/list fade flickered at the end: MC's font renderer forces opaque rendering below alpha 64; the unified text entry point now skips drawing below alpha 0.25
  • Completed the alpha at one bare `0xFFFFFF` call site
  • Toast end flickering: reverted to rolling back and fading out the old difference as a whole (alpha decays with the transition progress), with the common prefix at the same opacity as the whole
  • The account page's "⟳ refresh" icon was about 12px too far left: drawing now uses the hit area's centre `(rfb.x + 12, avY + 17)` as its reference
  • When `user/info` returned HTTP 200 but `success=false`, it is now treated directly as an expired session and logs out automatically, no longer guessing wording from keywords
  • `isAuthExpiredSignal` extended: HTTP 404 is now a direct verdict, and keywords gained "deactivated/cancelled/revoked/disabled/deleted/not found" plus NOT FOUND / REVOKED / SUSPENDED / DISABLED and so on
  • `loggedIn()` again requires both `VERITY_BRIDGE_KEY` and `VERITY_USERNAME` to be non-empty, fixing the dirty state where the key was cleared but the username remained
  • The account UI did not refresh after data was fetched: `doFetch` now calls `VerityClient.refreshScreen()` after writing the data
  • The account page showed `-` for everything: `findRow` compared the raw key against the "card index|" prefix added by `assignRowIds()`; it now also compares `idBase`
  • Login-state verification is now fully visible: each `doFetch` writes in-memory logs such as `[Bridge] verifying login state …`, `user/info HTTP …` and `verification passed`
  • `layout_check.py`'s `NAV_N` corrected from 8 to 9
  • Changing the TTS mode replayed the expand animation of an already expanded STT mode selection row: the id changed to "card index + group index chain|original label"
  • The interface was pushed upward when switching mode or provider: newly switched-in groups no longer compress `layoutGroup()`'s return value `iy`
  • Switching modes made the panel jump without animation: all groups now go through `gKey()` expansion progress
  • The Key / Model row names were not vertically centred: an empty `desc` string now counts as no subtitle and centres as a single line
  • Clicking the voice "preset" jumped to the "Verity official site": `TtsMode` / `SttMode` `CUSTOM` value, with the UI counter removed
  • The About page's version number was not aligned with the "Verity-cn" baseline on the left (it sat 4px lower); now both use the title's visual centre line
  • The large blank gap under the thanks section: card height changed from a hardcoded 540 to being computed from the content
  • The right scrollbar only triggered when the pointer was placed to the left of the thumb: the hit area changed to `cx ± 7` around the centre line, shared by three places via `scGrabArea()`
  • The hit area's left edge covered the content area's right 8px, stealing clicks from rows and the scrollbar near the right edge
  • Clicking "Back" did nothing or switched TABs by mistake: the back button now has top priority, with hit-testing and rendering sharing the same clipping boundary
  • Rows that were invisible after scrolling could still be clicked; hit-testing now uniformly requires visibility
  • The scrollbar thumb did not sit inside the track groove: track and thumb are now both drawn from the centre line `barCx()`, with the thumb widened to 6px
  • `ClassCastException` / `NoSuchMethodError` caused by the bundled GeckoLib clashing with identically named classes in an external geckolib-forge.jar
  • A potential null pointer issue
  • A crash when entering the config interface
  • Wrongly jumping back to the "General" page after refreshing or a connectivity check
  • The account page's overall layout being magnified
  • Sub-group entries not indented with their heading, and overlapping the content below when expanded
  • Wheel scroll direction, scrolling past the top divider, and the left navigation being too crowded to scroll
  • Occasionally getting no response and no hint at all when talking to Verity (when blocked, the reason is now stated clearly)
  • The speaking animation timing for the welcome message and AI replies was wrong (now it only starts once the voice actually begins)
  • Being unable to pick Verity up while she is speaking or thinking (she can now be picked up at any time without affecting the voice)
  • A crash on game startup caused by a missing prerequisite component
  • Xiaomi speech synthesis being completely silent in non-streaming mode
  • Mouse scrolling being too slow in the config interface
  • The back button and version text not being vertically centred, and the display of the input cursor, selection and keyboard editing

Optimize

  • The 248-frame PNG opening animation was downsampled from `1920×1080` to `1280×720` (71.3 MB → 32.9 MB, no code change needed)
  • Debug information stripped from 25 Android/Linux native libraries (saving 57.2 MB; `libllama.so` 31.8 → 3.2 MB), Windows natives untouched
  • `BuildV3714` packaging compression level changed from the default 6 to `Deflater.BEST_COMPRESSION` (9)
  • Icon and logo changed from 512² to 256²
  • Jar size `216,504,021 B` (206.5 MB) → `161,210,665 B` (153.7 MB), saving 55.3 MB (−25.5%)
  • Cost: the opening animation is slightly soft from 1.5× upsampling at 1080p, and crashes in the stripped native libraries can no longer be symbolicated
  • The color picker texture was re-uploaded unconditionally every second; an idempotent `TextureUtil.prepareImage()` was added before `upload`
  • The hue bar and SV square are baked into one `DynamicTexture` (128 wide, top 6 rows for the hue bar), cutting roughly 8500 `fill` calls per frame to 2 `blit` calls
  • Pixel format uses ABGR (`setPixelRGBA` convention), and the texture path `dynamic/verity_lyric_picker` skips resource pack reloads
  • Hue is quantised into 128 steps, re-baking only when the step changes and reusing the same `NativeImage` object
  • Falls back to the old per-row `fill` path if texture registration or upload fails
  • The SV square's per-frame fill changed from "96 columns × 48 segments with holes" to "176 rows × 48 segments, continuous", removing the 1px gaps between columns
  • The hue bar's drawing result is cached and reused; the SV square changed to a 96×96 grid cache, rebuilt only when the hue changes
  • `currentLines()` caches the converted result by the source list's `identityHashCode + size`, plus a per-frame snapshot `frameLines`, with the sample lyric promoted to a static constant
  • Introduced a parameter snapshot `P`, sampling only once per frame instead of 20+ reflective config reads per frame
  • During dragging only the in-memory snapshot changes; on release `m_6348_()` calls `commitAll()`, writing only the entries that differ from the saved values
  • The hue bar changed from 208 fill calls per frame to `ensureHueRow()` computing once into an `int[]` that is reused
  • The status bar's drop order changed so the stage and model name are never dropped; when space runs out, tokens/cache/speed are dropped first, then the model name is shortened
  • The model name is no longer cleared when a new round begins, so it stays visible
  • Leftover `.part` files are cleared before a fresh download, and only used as a resume point when "Resume" is clicked
  • On compression failure the specific reason is shown as-is and the user is asked again; on success it is persisted immediately
  • The prompt prefix changed to token-level KV cache reuse, cutting a single round from 101s to about 3s
  • When seq_rm or the reuse path fails to decode, that round falls back to submitting everything
  • When the summary is under 4% shorter than the original, the model is judged incapable of summarising and it falls back to zero-hallucination extractive compression
  • When the requested context exceeds the model's native limit a clear warning is given without blocking the run
  • Generation now wraps up normally at pos >= n_ctx - 2, no longer letting the last decode fail
  • [LLM] proxy / model debug logs are now printed separately for local and cloud
  • Download progress accounting now computes a baseline once before starting and then accumulates purely in memory, no longer scanning the directory every 64KB
  • The panel entry animation gained a 2.5s fallback that lands it directly when it expires
  • The microphone level bar changed to inline hand-drawing
  • AI replies no longer contain Markdown formatting symbols, and the raw query data is no longer dumped straight to the player
  • The speaking animation's amplitude now varies with the voice's emotion (loudness, pitch), making it livelier

Removal

  • The main menu's "Mod by Varmite" button and the 24px downward shift logic in `TitleScreenMixin`
  • The `[properties]` `veritycn` entry (the Verity official site) in `mods.toml`
  • The uncalled `NCM_USE_AUTHOR_SERVICE` config key and two dead methods in `SpeedHelper`
  • `TtsMode.BUILT_IN` (the old "built-in TTS config", duplicated by the built-in providers)
  • Six uncalled `getCurrentFree*` methods in `SpeedHelper`
  • The "loneliness" performance in the CN version (a `!isCores()` guard added to the `ModEvents` loneliness block), no longer broadcasting lines or deducting affinity
  • Leftover fixed sounds: `SoundEvents.f_11922_` in the explosion branch and `ModSounds.BONE_0` in the cactus branch
  • The "added this session" accounting implementation: `VerityLlmSession.sessionChars`, `addSessionChars()`, `contextTokens()` and the history accumulation
  • The statistics code in `AiAPI` that wrote `contextChars` from the messages volume
  • The overall "enable music" switch (`VANILLA_NCM_ENABLED`); music is on by default and `setCurrentNcmEnabled` became a no-op
  • `playFavouriteSongStream`, `onFavouriteSyncTick`, `pauseFavouriteSong`, `resumeFavouriteSong`, `loadFavouriteLyrics` (about 260 lines)
  • The bundled `assets/verity/sounds/favorite_song.mp3` and its accompanying lrc, cutting the jar size by about 5.6 MB
  • The entire dot subsection of `LyricHudRenderer` (the `GAP_*` constants, `drawGapDotsPublic` / `drawGapDots` / `fillDisc` and so on) and the editor's mirrored block
  • The dot threshold field `ncmLyricGapDotThresholdMs` in `VerityConfig` / `SpeedHelper`, its builder definition and its get/set accessors
  • The two parameter rows "vertical spacing / horizontal spacing"
  • The "sample (not playing)" label at the top right of the panel
  • The debounced queue and background write thread that wrote config live during editing
  • The `diskDirty` check and the write-only `saved` snapshot
  • The vertical value bar on the right of the color picker; value is now handled by the SV square itself
  • The grey small print "this mod bundles GeckoLib…" on the About page (constant, card height and rendering, three places), with the thanks folded into "special thanks"
  • Tasks are now deleted in the background: the task disappears from the list first, then the thread releases the directory
  • The temperature and repetition suppression settings in the UI, hardcoded to 0.7 / 1.1
  • The llmLocalTemperature / llmLocalRepeatPenalty config entries; old config is ignored and harmless
  • The "max reply length" dropdown and the llmLocalMaxTokens config entry
  • Leftovers cleaned up: de/kherud/llama, com/fasterxml/** and META-INF/versions/**
  • The "playback location" option under the personal account, the `musicPlayLocation` config entry, and four never-consumed read/write methods in SpeedHelper
  • The dead `K_METER` row type (constant declaration, `geom()`/`drawRow()` cases, `kbCollect()` filtering)
  • The four `K_RT` rows on the self-check page (Base URL / model / Key / connectivity) and each target's "check" button row
  • Dead code methods with zero references in the source tree: `VerityClient.createYACLScreen / originalParent / preserveConnTest`, `VerityAccountBridge.fetchValues / fetchFromConfig / checkNow / testAllModels`, `VerityConfigScreen.bt / selE / selS / sldD`
  • The "use custom clone audio" toggle, replaced by the new "custom" item under reference voice
  • The "AI auto-fill" entry was removed from the initial setup page (hidden for now for privacy reasons)
  • Xiaomi v1 backend TTS's special field concatenation rules, unified onto the OpenAI-compatible format
  • Friendly mode; vanilla horror mode is now the default
  • The external dependency declarations for Cloth Config and GeckoLib, greatly reducing the prerequisites
  • The built-in TTS voice model, greatly reducing the mod's size; voice now uses any of Xiaomi MiMo / Base URL / voice cloning / official proxy

v3.4

2026-08-21

NCM Music System + Networked Search + Multiplayer Performance Optimization

NCM Music System (New)

  • Song search and playback: ncm_search returns numbered results, AI can play them simply by returning the number; search results hide the original ID and prevent LLM from making up songs
  • Keep the song_id to play directly, it is still available when the user explicitly gives the ID
  • Multiplayer synchronization: console audio de-duplication, new players join autosync progress and lyrics in the middle, exit the world and stop playing automatically
  • Music volume Ducking: 300ms fade to 15% when TTS is playing, 800ms slow recovery after TTS

Networked search configuration (new)

  • Added "Network Search" configuration tab to enable/disable the switch independently
  • API key management for Tavily, Brave, AnySearch search engines

Android Platform Adaptation

  • NCM music walk OpenAL playback: stream buffer PCM back to WAV playback, dynamically select stereo/mono format
  • Fix playback failure due to alGenBuffers residual error
  • Android skips streaming TTS fallback non-streaming path; lyrics position precisely synchronized during OpenAL playback

Optimize

  • Fixed multiplayer mode carton root cause: the memory module scans the world's entities per tick, now changes to 40 tick throttling + caches recent player results, and the scanning frequency is reduced by 98.75%

Lyrics & Mouth Animation

  • Flashing lyrics fix: Empty lyrics no longer reset row indexes; row indexes only jump forward when progress fluctuates, don't jump back and forth
  • Mouth animation is only triggered by TTS, listening to songs no longer drives the mouth; it is only extended when the remaining time is not enough to avoid the mouth shape can not stop

Command system

  • /cnv locate tp: One-click teleport to Verity location (backpack→ player location, physical→ entity coordinates)
  • /cnv mplay<songid>: play the specified song ID directly

Stability repair

  • Automatically downgrade 44100Hz when microphone 16000Hz is not supported, fix some device crashes
  • OpenAL audio format dynamically selected by channel count; clean up OpenAL playback thoroughly when music is stopped

Technical improvements

  • Added NCM synchronization system: NcmNetwork/NcmBroadcastHelper/NcmClientPlayer/NcmSyncS2CPacket/NcmClientExit
  • New patches: PatchMicrophoneFallback/PatchVerityPlatformOpenAl (OpenAL error clearing)/PatchNcmClientPlayerAndroid (Android lyric position)
  • Refactoring core files such as NcmHelper (numbered search + index playback), VerityPlatform (full OpenAL playback), LyricHudRenderer, VerityTalkState, MemoryHelper, PersonaHelper, CnvCommand, etc.

v3.3

2026-08

Friendly mode + in-game tool call + configuration collation

Friendly mode (exclusive to friendly_mode · cn version)

  • Block all demonic transformation logic and horror content
  • Personality entirely driven by karma: < 7 cold, 7-14 neutral, 14-20 friendly, > 20 extremely friendly, ≥ 9000 angels
  • Forcibly prevent killing any entity (including villagers) and truly become a "game assistant"
  • Still in beta, welcome feedback

In-game tool calls

  • Recipe query get_recipe: LLM is forced to be called when the player asks for synthesis/burn, returning the real game recipe in real time
  • Installed Mods probe get_all_mods: lists all installed mods for the current game

Legacy item giving mechanism (drop_item_legacy_mode)

  • The ability to propose and vehemently request restoration by the grief buried in @
  • When the switch is ON, LLM can specify item_id to give various items; when the switch is off (default), only soil can be given

Custom Clone Audio Switch

  • Tone cloning supports custom reference audio
  • Added use_custom_clone_audio standalone switch
  • Custom audio directory: config/verity/sample_audio/

Stability repair

  • LLM request timed out for 60 seconds to prevent permanent blocking
  • Audio Channel Mutex Management: Play only one sound at a time
  • TTS Instant Interrupt: New request to immediately replace the TTS being played
  • Disconnecting automatically stops the sound

Technical improvements

  • Add VerityModFlags Standalone Flag Bit Management
  • Added PersonaHelper dynamic person setup auxiliary class
  • Added ModEventsMixin/VerityEntityMixin Mixin injection
  • remove YACL dependencies since v3.3

v3.2

2026-07-28

Speech Button + Streaming Response + Custom Tone + Black Screen Switch

TTS Speech Speed Button Control

  • Slider to Button Cycle Switch 7th Gear: Extremely → Slow Slow → Yourselves → Slightly Faster → Faster → Faster Faster → Faster
  • Fits MiMo models only, controls speech speed via audio tags
  • Added SpeedLevel enumeration and SpeedHelper bridging class

MiMo Streaming Response

  • Streaming while playing: Playback starts as soon as the first SSE chunk is received
  • Only MiMo endpoints take effect, configuration location: "Enable streaming response" at the bottom of the Base URL group

Custom Tone Clone

  • Put the mp3 sample audio in the config/verity/sample_audio/directory
  • Enter a filename (without suffix) to use, leave blank to fall back to the preset tone
  • Audio suggestion 8-30 seconds, single person single language speaking

Main Menu Black Screen Background Switch

  • Added the "Main Menu Black Screen Background" configuration, turned on the black screen background of the display module, and turned off the restoration of the original MC panorama
  • Fix Mixin Long Not Loading Issue

v3.1

2026-07-24

Repair + Initialize Configuration Wizard

Initialize Configuration Wizard for the first time

  • Full-screen initialization configuration screen pops up after the opening animation
  • Verity Mod Authorization Tips + Model Selection Tips
  • OAuth Authorization Process: 5 Seconds Full Screen Prompt → Auto Open Browser → 2 Minutes Timeout
  • Detect model connectivity after successful authorization

Repair

  • Fallout call switch repair: fall_sound_enabled now overwrites causeFallDamage
  • OAuth authorization status determination repair: check status = = complete instead
  • LLM/STT/Proxy enabled by default

v3.0

2026-07-24

Verity Mod website deep integration

Verity Mod site deep integration (core update)

  • Bridge service architecture: when enabled, all LLM/TTS/STT requests unify the Verity Bridge service
  • License Key Authorization System: The key is stored in config/verity-client.toml
  • Trust-all SSL Client: Compatible with self-signed certificates and various proxy environments
  • LLM Request 60 Seconds Timeout

TTS Voice System Enhancements

  • Instant interrupt mechanism: a new TTS request to immediately interrupt and replace the voice being played
  • OpenAL Playback Path Repair
  • Automatically stop playback when returning to the main menu/disconnecting

Chat Messages Localization

  • I'm alone... → I'm so alone... where have you been?
  • Ayo chat... → hey hey hey, why did I disappear like this
  • The → darkness... dissipated. Thank you.

v2.75

2026-07-21

Model Customization + Skin System

TTS/STT model name configurable

  • TTS model name: user input manually, default mimo-v2.5-tts
  • STT model name: user input manually, default mimo-v2.5-asr

Custom Skin System

  • Package 16 variant maps (1024 x 1024 PNG) as zip into config/verity/skins/
  • Enter the zip file name in the GUI to switch, the switch takes effect instantly
  • Smart Fallback: Use built-in default skin automatically when skin name is invalid

v2.5

2026-07-21

Sinicization Reconstruction

AI configuration system refactoring

  • Independent configuration of LLM/TTS/STT three sections
  • LLM: Supports any OpenAI compatible API (DeepSeek, Tongyi Qianwen, SiliconFlow, etc.)
  • TTS: Three Modes — Built-in Piper/Base URL/MiMo Tone Clone
  • STT: Two Modes — Local Whisper/Base URL

MiMo Deep Integration

  • mimo-v2.5-tts + mimo-v2.5-tts-voiceclone + mimo-v2.5-asr
  • Preset 3 groups of reference audio: Verity default, Ashen same style, old priest

Interface completely rewritten

  • YACL GUI full Chinese: group title, option name, description all localized
  • Chinese name displayed in TTS/STT mode drop-down menu

v2.0

2026-07

Android Adaptation

TTS speech synthesis

  • Android: Play MiMo TTS audio via LWJGL OpenAL for OpenSL backend
  • Desktop: Keep javax.sound.SourceDataLine the same

STT Speech Recognition

  • Android: Microphone acquisition directly via LWJGL JNI
  • Launcher support: ZalithLauncher 2.4.9+, PojavLauncher, FCL, HMCL-PE

v1.0

2026-07

First Chinese version

Configure system refactoring

  • New VerityConfig.java: Complete LLM/TTS/STT three-block configuration system
  • Remove original hard-coded Groq API Key and OpenRouter configurations
  • Added AiProvider enumeration: Added custom option to support any OpenAI compliant API

Interface sinicization

  • YACL configuration GUI full culture
  • zh_cn.json language file overwrites all visible text in the module
  • AI system prompt changed to Chinese

Repair

  • Chinese username microphone crash repair
  • Start TTS model decompression path repair for the first time