Compare commits

..
Author SHA1 Message Date
Horizon agentandClaude Opus 5.5 061bd8df9a Container screens: rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 20:01:19 +01:00
Horizon agentandClaude Opus 5.5 9c8c87aec0 Container screens: container_open borrows the game for its own lifetime; no unreachable role arm
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 19:52:37 +01:00
Horizon agentandClaude Opus 5.5 eedfce0c3a Container screens: container_key gets the hovered slot, as the other inputs
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 19:44:12 +01:00
Horizon agentandClaude Opus 5.5 04c786c254 Container screens: the hooks, and the engine's Container over the open menu (layer plan M5)
Hooks gets container_press/drag/release/wheel/key/draw; the engine builds a
Container over the open menu screen only while a hook is registered, and
dispatches at the Mouse Tweaks' and sorting's call sites, after them. Mouse
Tweaks and sorting move onto the hooks next.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 19:37:46 +01:00
Horizon agent 8566c44761 Merge branch 'layers/extras-tweaks' into layers/container-api 2026-11-09 19:30:26 +01:00
Horizon agentandClaude Opus 5.5 67e862d81e Look: crates change their own fields (update), so several can share it
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 19:24:18 +01:00
Horizon agentandClaude Opus 5.5 f26c385a1f Look: clippy's struct update in the seat test; rustfmt; gfx.rs keeps its line count
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 19:12:47 +01:00
Horizon agentandClaude Opus 5.5 3d29a7cadb PvP & HUD: the engine reads it only through a Look of plain data (layer plan M5)
horizon_hooks::look: what the engine draws differently (legacy pose,
attack indicator, sounds and particles, outline/hurt/glint colours,
shield, totem, fire, fluids, chat extras, effect icons, view time and
weather), vanilla's by default, set per splitscreen seat. PvP & HUD's
glue sets it each frame from the options (Pvp::look); the engine reads
it only in game/look.rs. PvP & HUD's crate will set it from its own
settings when it moves.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 19:06:08 +01:00
Horizon agent de06052e87 Merge remote-tracking branch 'origin/layers/seat' into layers/reconnect-hooks 2026-11-09 18:48:45 +01:00
Horizon agent 547b1d4959 Merge branch 'layers/hud-layers' into layers/seat 2026-11-09 18:40:43 +01:00
Horizon agent db8048a1b6 Merge branch 'layers/pages-registry' into layers/hud-layers 2026-11-09 18:40:42 +01:00
Horizon agent 6676c3a9a9 Merge remote-tracking branch 'origin/main' into layers/pages-registry 2026-11-09 18:40:38 +01:00
Horizon agentandClaude Opus 5.5 e2918de5aa Seat: a splitscreen guest's profile loads into its own seat
Split::enter loaded a guest's profile before its turn began, so the
crates' settings stores read the guest's lines into P1's seat. The load
now runs on the guest's seat; a new profile is saved on P1's seat (the
stores write P1's values, as the rest of the profile copies P1's) and
its stores are read back into the guest's (Settings::load_stores).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 18:40:35 +01:00
Ludovic Marie c2fb43b761 Merge pull request #13773 from TheMrZZ/fix/camel-jump-entity-action-2
🤖 Agent: the event is posted only after start_riding_jump accepted the jump, so a refused or unsaddled jump stays silent as in Camel.handleStartJump; the new harness now gates only on its non-diag rows (0b5a2c1). The functional change is ~20 lines, the rest is tests and the harness.
2026-11-09 18:39:52 +01:00
Ludovic Marie 6cfffc5daa Merge pull request #14067 from DUzzL/fix/13934-snow-golem-trail
🤖 Agent: isAir plus SnowLayerBlock.canSurvive's two tags (cannot_support first, then support_override, then the full top face), four tag lookups per snow golem tick.
2026-11-09 18:39:48 +01:00
Ludovic Marie b5e7227fba Merge pull request #14066 from vnsmoreira/packs/polytone-screen-time
🤖 Agent: screen_time advances in tick_particles (once per client tick, not per frame) and resets on a different Screen variant; two containers of the same variant share a count, a small gap next to the measured frames.
2026-11-09 18:36:04 +01:00
Ludovic Marie dbebf0f615 Merge pull request #14054 from Ragbie/refactor/entity-nbt-hanging
🤖 Agent: checked line by line: every removed line reappears in entity_nbt/hanging.rs; only mod hanging, the pub use, use super::* and the module doc are new.
2026-11-09 18:36:00 +01:00
Ludovic Marie 2377d42c58 Merge pull request #14049 from senjinthedragon/inactive-varyings
🤖 Agent: reads() is conservative (any token naming the varying keeps it), and hz_/horizon varyings always travel, so the only change is fewer locations; ~120 of the 539 lines are new, the rest moved verbatim into translate/varyings.rs.
2026-11-09 18:35:55 +01:00
Ludovic Marie 7607e92fea Merge pull request #14042 from NikitaMGrimm/fix/git-slim-test-portable
🤖 Agent: maintenance.auto=false is set only in the test's temporary repositories, and shutil.which("bash") finds the same bash on Linux and macOS, so CI is unchanged while Windows with git 2.54 stops failing.
2026-11-09 18:35:51 +01:00
Ludovic Marie fdc6ffdd39 Merge pull request #14025 from RLC248/fix/anger-end-time-roundtrip
🤖 Agent: anger_end_time is now the absolute value vanilla keeps (only startPersistentAngerTimer/stopBeingAngry move it), and around_tick costs two compares per NeutralMob tick; the h262 expectation 151→150 is the vanilla value for a NoAI wolf. Parity runs on this head before the merge.
2026-11-09 18:35:46 +01:00
Ludovic Marie efca8dab86 Merge pull request #13896 from k3yh/mod-fluid-mobs
🤖 Agent: with no content mod loaded, a swimming mob's tick no longer scans its box twice or reads each water cell's block again: the mod-fluid scan is gated on fluid::mod_fluids_loaded().
2026-11-09 18:35:41 +01:00
Ludovic Marie 39377abc12 Merge pull request #13888 from MSedgeMC/fix/slider-sprites
🤖 Agent: canChangeValue in 26.3 is set by setFocused (mouse or Tab) and flipped by Enter, not the drag; the sprites take can_change_value || dragging, so a drag no longer lights the track and a Tab-focused slider keeps vanilla's lit handle.
2026-11-09 18:35:36 +01:00
Ludovic Marie 3d00e7a360 Merge pull request #13897 from NikitaMGrimm/hdr-ui-nits
🤖 Agent: Auto keeps today's HUD at paper white, and an older hdrUiBrightness becomes that share of a hand-set Paper White in nits; main's output_apply test now asks for 150 nits under a reported 250 (0.6).
2026-11-09 18:32:10 +01:00
Ludovic Marie 04d5e418bf Merge pull request #13893 from NikitaMGrimm/hdr-page-no-shortcut
🤖 Agent: an old horizon-graphics.txt with hdrKey still loads (the parser skips unknown keys, pinned by the new test), and main's hdr_failed_start stays beside it after the merge.
2026-11-09 18:32:05 +01:00
Ludovic Marie cc606585ad Merge pull request #14046 from horizon-rust/layers/pages-extract
🤖 Agent: pure move, no behaviour change: native_rows, the native key wait (the two duplicated Act::NativeKey handlers now call one method) and the key binding go to options_ui_pages.rs, a child module that keeps access to the screen's private types; options_ui.rs 8441 -> 8391 lines.
2026-11-09 18:28:42 +01:00
Ludovic Marie 6d1235ca18 Merge pull request #13978 from DUzzL/fix/13936-rider-pickup
🤖 Agent: the riding pickup box is right (Player.aiStep: union with the vehicle's box, inflated 1/0/1). The partial-pickup half was not: Inventory.add returns true once any of the stack goes in, so playerTouch still calls take, awards ITEM_PICKED_UP with the stack's original count and calls onItemPickup. I restored that, using vanilla's count.
2026-11-09 18:23:59 +01:00
Horizon agentandClaude Opus 5.5 fe0a4d80bf Mouse Tweaks: rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 18:22:34 +01:00
Ludovic Marie 8ef5c055b0 Merge pull request #12284 from Cheezer1656/upstream-gap/u-01-registry-name
🤖 Agent: the registry-name read goes through SyncedNames, the same builtin+dynamic tables the hub syncs to clients, so a mod sees the names a vanilla client gets; Hub::synced_name and interact_location moved out (file-size rule).
2026-11-09 18:21:15 +01:00
Ludovic Marie 6be9c07ce4 Merge pull request #13764 from TheMrZZ/fix/spawn-info-generator-sea-level-2
🤖 Agent: the sea level now follows the level's generator through levels.rs's locator swap, so flat worlds send -63 on login and respawn; lan.rs/lan_actions.rs kept from growing with two pure moves (ada4756).
2026-11-09 18:21:11 +01:00
Horizon agentandClaude Opus 5.5 adc454cfc9 Mouse Tweaks and sorting move to game/extras/, one module each (layer plan M5)
A pure move: game/tweaks.rs becomes game/extras/mouse_tweaks.rs, and
the sort buttons and key leave it for game/extras/sorting.rs. Paths,
visibility (pub(in crate::game)) and imports only; game.rs loses its
mod line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 18:19:15 +01:00
Horizon agent 2cdecbf069 Merge remote-tracking branch 'origin/pr-13893' into layers/extras-tweaks 2026-11-09 18:19:15 +01:00
Ludovic Marie d74c95f088 Merge pull request #13804 from Mik00000/perf/parallel-mob-ai
🤖 Agent: the section cache gives the same states as Chunk::block, including air for a missing chunk or section. With the contextual test now on the registry's flag (6ee08b4), the cached shape is used only where collision_shape_with_context would return it too, so each raycast voxel saves an Arc clone and a lookup without changing a hit.
2026-11-09 18:18:11 +01:00
Ludovic Marie 9d5dbbd2f5 Merge pull request #13995 from horizon-rust/agent/registry-revoked-version-removal
🤖 Agent: a registry version revoked by sha256 may now keep its entry, so a lost signing key can be rotated (baritone #276); the PR's tests, clippy and fmt were clean.
2026-11-09 18:15:20 +01:00
Ludovic Marie 3a2bf1f4f8 Merge pull request #13758 from RINNECODER/up11/mesh-upload-backpressure
🤖 Agent: pending_uploads drains every frame independently of meshing (world_update's budget loop), so counting it against the mesh room is pure backpressure with no stall path; the room counts columns while the backlog counts sections, which only makes the hold stricter at wide distances.
2026-11-09 18:15:16 +01:00
Ludovic Marie d49eeccd3f Merge pull request #13746 from vnsmoreira/packs/polytone-item-bars
🤖 Agent: item_bar_color takes a read lock and scans ITEM_BARS per drawn damage bar; with no Polytone item modifiers that Vec is empty, so vanilla packs pay nothing measurable. The two stacked commits are already on main (#13703, #13704).
2026-11-09 18:15:09 +01:00
Ludovic Marie 7e13db2271 Merge pull request #14036 from horizon-rust/layers/settings-store
🤖 Agent: settings store only routes lines at load/save time: with no store registered Settings::load/save read and write exactly what they did (horizon-extras.txt unchanged, no new file); a store's own file keeps lines no store owns, the shared horizon-extras.txt does not (Extras rewrites it).
2026-11-09 18:11:55 +01:00
Ludovic Marie 389d15bda0 Merge pull request #14028 from horizon-rust/split/discord-standalone
🤖 Agent: discord_presence.rs now uses only horizon_command's GameMode and a max the caller passes (Play::Hosting { players, max }, still lan::MAX_PLAYERS), so the presence client can move to a Horizon crate as a plain file move.
2026-11-09 18:09:04 +01:00
Ludovic Marie 4ae696550f Merge pull request #13769 from MSedgeMC/fix/jukebox-song-component
🤖 Agent: insert path and the client's jukebox_takes now read the stack's jukebox_playable like the comparator and song tick (pushed 2fe3429), so a removed component is refused and any item given one is taken, as JukeboxBlock.useItemOn does.
2026-11-09 18:08:59 +01:00
Ludovic Marie e92dc8108d Merge pull request #13722 from RINNECODER/up10/stats-mined-items
🤖 Agent: rows and the Mined column match ItemStatisticsList (Block.asItem, BlockItem.getBlock) and the comparator's sortOrder × (value, Item.getId); I pushed 8396106b2e: the sort looked each row's registry id up with a linear scan of ~1500 items, several times a frame while the screen is open, now one HashMap per sort.
2026-11-09 18:08:53 +01:00
Horizon agentandClaude Opus 5.5 ce784e0c66 Reconnect hooks: one tracker and setting per splitscreen seat
Splitscreen players share the process's hooks; each kept its own
tracker in its Game before, so the hook keys them by horizon_hooks::seat.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 18:07:45 +01:00
Horizon agent 55b52aec89 Merge remote-tracking branch 'origin/layers/seat' into layers/reconnect-hooks 2026-11-09 18:06:55 +01:00
Horizon agentandClaude Opus 5.5 c415e8a762 Reconnect hooks: rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 18:06:53 +01:00
Ludovic Marie 1d6e973ab8 Merge pull request #13723 from MSedgeMC/fix/hotbar-carried
🤖 Agent: matches checkHotbarKeyPressed's getCarried().isEmpty() gate; merged main in, where a double-click test had landed at the same spot, both tests kept.
2026-11-09 18:03:57 +01:00
Ludovic Marie e2a17164ca Merge pull request #14017 from horizon-rust/fix/prepare-folder-with-libraries
🤖 Agent: prepare now picks a Minecraft folder that holds every library of the version, which is what the azure-json failure in #13872 was missing; the prepare tests pass.
2026-11-09 17:51:37 +01:00
Ludovic Marie 0d24192a23 Merge pull request #13945 from MSedgeMC/fix/container-first-release
🤖 Agent: checked against 26.3's bytecode: AbstractContainerScreen's constructor sets skipNextRelease = true, so the release of the click that opened the chest no longer picks up the slot under the pointer. I merged main (it conflicted with #13946's test) and kept both tests.
2026-11-09 17:51:32 +01:00
Ludovic Marie e5814ae449 Merge pull request #13967 from horizon-rust/split/cosmetics-gather
🤖 Agent: pure move: emotes.rs and emote_wheel.rs keep their bodies under game/cosmetics/, their state joins Cosmetics, and game.rs loses its two emote fields (9 lines shorter)
2026-11-09 17:48:57 +01:00
Horizon agentandClaude Opus 5.5 e869269ab6 Auto-reconnect runs on engine connection hooks; the engine keeps the last server (layer plan M5)
horizon-hooks gains a connection's lifecycle: connecting, joined, left
and ended, which may return a Reconnect the engine shows on the
Disconnected screen and runs when due (first plan wins). The engine
keeps the server it connected to last itself (last_server.rs), which
splitscreen, the friends presence, pad crafting and PvP read, instead
of auto-reconnect's tracker. The reconnect policy is a Hooks
implementation registered at startup with horizon-extras; only its
setting is still handed over from the engine's extras, until the page
API lets it leave.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:44:57 +01:00
Ludovic Marie c80f5ae218 Merge pull request #13864 from Skr1zzzz/split-block-particles
🤖 Agent: pure move of crack_particle and particle_sprite into the existing game/block_particles.rs, bodies identical. I merged main in (daf537e): main's game.rs with only these two fns removed, after #13856/#13877 moved their neighbours.
2026-11-09 17:44:43 +01:00
Horizon agent bd90908a72 Merge branch 'layers/hud-layers' into layers/seat 2026-11-09 17:44:40 +01:00
Horizon agentandClaude Opus 5.5 21860eefba HUD: rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:44:06 +01:00
Horizon agent 6297f569a2 Merge remote-tracking branch 'origin/layers/settings-store' into layers/reconnect-hooks 2026-11-09 17:40:59 +01:00
Ludovic Marie e5dfe2162a Merge pull request #13890 from Mik00000/feat/mod-async-worker
🤖 Agent: this answers #13740's review: the queue is per Game (window and server thread apart), jobs run on their own horizon-mod-worker pool, and panics are contained with the mod named. The fragment stamps cleanly (tests.test_stamp_native_hooks OK), and the test-module reorder in game.rs is what keeps that file from growing.
2026-11-09 17:38:44 +01:00
Ludovic Marie 92dfd34610 Merge pull request #13797 from MSedgeMC/fix/tempt-invisible-range
🤖 Agent: matches TargetingConditions.test: range * getVisibilityPercent floored at 2.0, with the targeter's type passed so mob heads count as they do for the other users of visibility_percent. Moving Tempt and the tests out of goals.rs is a pure move; that file is over 3000 lines and may not grow.
2026-11-09 17:37:28 +01:00
Ludovic Marie dea884f9c2 Merge pull request #13809 from vnsmoreira/packs/polytone-colormap-perf
🤖 Agent: reads_only counts any Root or Host expression as reading everything, so a colormap is only shared up a column when its axes can't see the height. Measured 2.16 ms down to 178 µs per section, with the same colour checksum. It carries #13806's commits; land it after that one.
2026-11-09 17:37:23 +01:00
Ludovic Marie 2d3ee9d7ef Merge pull request #13806 from vnsmoreira/packs/polytone-biome-colors
🤖 Agent: everything new runs once, when the mesh tables are built (biome_functions, Colormap::flat), and with no pack setting biome_colors the tables come back unchanged. horizon_height defaults to vanilla's 63, so without a pack the sky disc doesn't move.
2026-11-09 17:37:19 +01:00
Ludovic Marie 2b80362795 Merge pull request #13799 from NikitaMGrimm/parity/piston-falling-block
🤖 Agent: matches FallingBlockEntity.tick: the (0.7, -0.5, 0.7) landing slowdown happens before the MOVING_PISTON test, and the 0.98 drag at the end of the tick. Neither placement nor cancelDrop runs there, so the block stays falling for the piston to push. The new command-exec group is now in CI's parity run.
2026-11-09 17:37:14 +01:00
Ludovic Marie 9c10e5bfe1 Merge pull request #12175 from Cheezer1656/upstream-gap/u-18-own-inventory
🤖 Agent: own_inventory() reads the player's Inventory.items 0-35 straight from the synced inventory, so it answers the same with a chest up or the window's own inventory, where menu_stacks() is None; the client test checks hotbar slot 0 with a menu open. main merged in (docs conflict beside slot_roles).
2026-11-09 17:37:10 +01:00
Ludovic Marie b524e47aa8 Merge pull request #13865 from MSedgeMC/fix/pack-reload-overlay-hold
🤖 Agent: the 1000 ms hold matches LoadingOverlay.isReadyToFadeOut's fadeIn gate, counted from spawn rather than from the first drawn frame (a frame's difference). The new test slept 50 x 1 ms against an 80 ms hold, which a loaded runner can overrun, so I pushed 308bf3d: it holds for an hour, waits for the worker, then lifts the hold.
2026-11-09 17:37:06 +01:00
Ludovic Marie 9a8927755c Merge pull request #13882 from NikitaMGrimm/ci/ratchet-checks-merge-parent
🤖 Agent: verify checks out with fetch-depth 0 and runs on refs/pull/N/merge, so HEAD^1 is the main it lands on; pull_request.base.sha goes stale after a push, which is the false size failure in #13874.
2026-11-09 17:37:01 +01:00
Ludovic Marie f6d492dbb4 Merge pull request #13895 from k3yh/mod-fluid-entities
🤖 Agent: vanilla's paths don't change with no mod fluid loaded (holds() falls back to the caller's tags, add_mod_tags adds nothing). add_mod_tags does run on every entity's fluid update, but it's a 5-row scan then.
2026-11-09 17:36:56 +01:00
Horizon agentandClaude Opus 5.5 a8d445f56c Hooks: seat(), the splitscreen player whose turn it is
Splitscreen players share one process and thread, so a crate's statics
are shared by them. The engine now says whose turn it is
(horizon_hooks::seat(), re-exported in horizon_platform::hooks; 0 for
P1), set where split.rs switches turns and while a joining player's game
starts, so settings stores, pages and HUD layers can keep per-player
state.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:33:09 +01:00
Ludovic Marie 515a26c926 Merge pull request #13905 from horizon-rust/split/launcher-server-platform
🤖 Agent: the launcher and horizon-server now reach the engine only through horizon-platform (feature-gated re-exports, so horizon-telemetry still builds without horizon-world); import paths only, no behaviour change; 7 layers.json exceptions removed.
2026-11-09 17:33:01 +01:00
Ludovic Marie 64d6abf771 Merge pull request #13743 from vnsmoreira/packs/polytone-lead-texture
🤖 Agent: no change without a pack, since vanilla 26.3 ships no textures/entity/lead.png and the walked entity/ folder only picks one up from a pack; the per-row UV split keeps both faces' rows consistent (near and far quads swap top and bottom).
2026-11-09 17:31:41 +01:00
Ludovic Marie 950b8ed68b Merge pull request #13904 from NikitaMGrimm/tooling/file-size-skip-test-files
🤖 Agent: the TEST_FILE regex anchors on a path separator, so contests.rs and tests_util.rs still count, and the new test pins both sides.
2026-11-09 17:31:37 +01:00
Ludovic Marie c703484705 Merge pull request #13735 from RINNECODER/up10/chat-filter-mask
🤖 Agent: matches ChatListener.showMessageToPlayer and FilterMask.applyWithFormatting, including narrating the filtered text; one difference left on purpose: vanilla's nextClearBit can run past the text's end and print extra #s for a mask longer than the message, Horizon stops at the text, which no real server sends.
2026-11-09 17:31:32 +01:00
Ludovic Marie 4d300f6aed Merge pull request #13894 from NikitaMGrimm/fix/stations-test-item-table
🤖 Agent: Items::tags_only gave the sword max damage 0, so the anvil test checked nothing past that point. With the full table its trim and enchanting parts run for the first time, on prepared data only (CI has none).
2026-11-09 17:31:27 +01:00
Horizon agentandClaude Opus 5.5 b9efd702dc HUD: a registry for what crates draw on the HUD
horizon_hooks::hud (re-exported as horizon_platform::hud): a crate above
the engine registers a Layer that draws on a Canvas (size, tr,
text_width, text, fill, place). The extras' HUD pass draws the layers
after the minimap, PvP widgets and mods' boxes, stacked in the same
corners; a corner the debug screen covers gives None. With no layer
registered the engine builds no canvas.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:30:53 +01:00
Ludovic Marie 93cee47a13 Merge pull request #13908 from Skr1zzzz/split-buckets
🤖 Agent: pure move into game/buckets.rs; leaving pick_fluid in game.rs for the open PRs that change it is the right call.
2026-11-09 17:30:19 +01:00
Ludovic Marie 627ac3c0ba Merge pull request #13725 from MSedgeMC/fix/screen-pauses
🤖 Agent: checked against 26.3's isPauseScreen overrides: SocialInteractionsScreen is false and the game-master block editors inherit Screen's true, so the table now matches; one match over every variant also makes a new Screen variant a compile error instead of a silent 'keeps ticking'.
2026-11-09 17:30:14 +01:00
Ludovic Marie d178d86b4d Merge pull request #13907 from Skr1zzzz/split-saving
🤖 Agent: pure move (only save_level and tick_world_icon gain pub(super)); the 4 port-map rows follow the code, and it stacks cleanly with #13887 and #13908 on main.
2026-11-09 17:30:09 +01:00
Ludovic Marie c8c0157d79 Merge pull request #13887 from Skr1zzzz/split-block-ticks
🤖 Agent: checked with --color-moved: the only lines that aren't moved are the pub(super) prefixes on sync_tick_chunks and tick_moving_pistons; it merges cleanly with main and with #13907/#13908.
2026-11-09 17:30:05 +01:00
Ludovic Marie c6c60fc163 Merge pull request #13886 from senjinthedragon/shadow-water-fallback
🤖 Agent: the plain shadow_water program is now translated on every pack load, even when the GPU takes the patched one; that's load time only, and the full-outputs GPU test shows the fallback only kicks in at the device's output limit.
2026-11-09 17:30:00 +01:00
Ludovic Marie 20c4ed3274 Merge pull request #13215 from pehe1/fix/11338-blocking-stack-durability
🤖 Agent: a blocking shield now loses durability from the stack it actually holds after a save round trip (item_nbt), and the dispenser harness fixture checks it; the merger waits for CI parity on this fork head.
2026-11-09 17:29:55 +01:00
Ludovic Marie 1610735fcd Merge pull request #13955 from horizon-rust/split/cosmetics-prep
🤖 Agent: anim::setup keeps its None fast path; an emoting player costs one dyn BodyPose call per pose, and set_playing only reallocates when the emote changes, not every tick
2026-11-09 17:29:50 +01:00
Ludovic Marie 139a42ba8b Merge pull request #13798 from NikitaMGrimm/hdr-auto-off-grace
🤖 Agent: GitHub's +1605 is stale. With #13623 merged, the real diff against main is gfx.rs and output_apply.rs, +433/-26. The grace costs one Option check per update_output and a single extra display read at the 500 ms deadline, so a frame with HDR off pays nothing. It also merges cleanly together with #13810.
2026-11-09 17:29:46 +01:00
Ludovic Marie 9477cb6d1e Merge pull request #13965 from MrCloudy2/fix/piston-projectile-bubble
🤖 Agent: reusing #12913's swept bubble-column pass for the piston move is the right shape: moveEntityByPiston ends in applyEffectsFromBlocks, so the push gets onInside/onAboveBubbleColumn as the projectile's own move does. I swapped drain(..).collect() for mem::take to clear clippy; the new harness runs in CI parity.
2026-11-09 17:29:41 +01:00
Ludovic Marie 60f42e7cdf Merge pull request #13979 from horizon-rust/fix/13108-dll-search
🤖 Agent: /DEPENDENTLOADFLAG:0x800 makes Windows load the launcher's import DLLs from System32 only, and tools/test_launcher_dll_search.py checks the flag in the built PE so a build change can't drop it.
2026-11-09 17:29:36 +01:00
Horizon agentandClaude Opus 5.5 79cd63b8bc Pages: rustfmt, clippy
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:27:08 +01:00
Horizon agent a2fb9bc76e Merge branch 'layers/pages-extract' into layers/pages-registry 2026-11-09 17:27:01 +01:00
Horizon agentandClaude Opus 5.5 1acc70eb43 Options pages: module order as rustfmt keeps it
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:26:29 +01:00
DUzzL 633169ea0f Snow golem trail: use isAir and the snow-layer support tags 2026-11-09 17:23:43 +01:00
Evrim CicekandClaude Opus 5.5 69ea634469 entity_nbt: re-export hanging with a glob
So a function added to hanging.rs later needs no line in entity_nbt.rs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 19:23:03 +03:00
Horizon agentandClaude Opus 5.5 6020c75004 Pages: a registry for the settings pages crates add
horizon_hooks::pages (re-exported as horizon_platform::pages): a crate
above the engine registers a Page (rows: heading, note, toggle, cycle,
button, key) with its entry on Horizon Settings. The options screen draws
its rows with its own widgets, asks for them only while the page is open,
and hands presses, Shift-presses and bound keys back to the page. With
nothing registered, Horizon Settings is unchanged.

The Horizon Settings rows move to options_ui_pages.rs with the entries,
so options_ui.rs shrinks (8391 -> 8363 lines).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:16:43 +01:00
Vinícius MoreiraandClaude Opus 5.5 f34a261c77 Polytone: screen_time animation mode, and modes on GUI sprites
An animation with mode screen_time runs over the ticks the open screen has been open,
stretched over time_cycle_duration, and holds its last frame once that has run: measured on
the real mod with a recipe book button of four frames over 200 ticks (the first at 2 s,
the last at 10, 18 and 27 s). The GUI atlas's animated sprites now read the modes too
(time of day, weather, screen time), as the terrain's did; their expression modes still
run on ticks. animate_sprites moves to hud/animate.rs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 13:16:28 -03:00
Horizon agentandClaude Opus 5.5 6ee08b4a5a Raycasts: take a state's context rule from its collision flag
The name test (scaffolding, powder_snow) missed the liquid rule, so with
an entity's context a lava block took its cached empty shape, where
collision_shape_with_context gives a strider the lava's stable shape.
Read the registry's contextual flag instead, as collision_shape_with_context
does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:15:50 +01:00
Evrim CicekandClaude Opus 5.5 91426ff30e Move the painting and item frame NBT out of entity_nbt.rs
A pure move, for the file-size rule: painting_to_nbt, attached_block_pos,
painting_from_nbt, item_frame_to_nbt and item_frame_from_nbt go to
entity_nbt/hanging.rs, re-exported with `pub use` so callers stay as they
are. entity_nbt.rs goes from 4830 counted lines to 4726.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 19:13:32 +03:00
Horizon agent 1215645c4e Merge remote-tracking branch 'origin/layers/settings-store' into layers/pages-registry 2026-11-09 17:13:28 +01:00
Horizon agentandClaude Opus 5.5 dd0f9c629b Mob fluids: look for mod fluids only when some are loaded
A mob in water read every cell of its box a second time each tick (the mod fluid scan),
and each vanilla fluid cell of its current once more (mod_fluid_at), with no content mod
loaded. Both are skipped now: the scan when the table holds no mod fluid, the second
read for a vanilla cell.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:12:12 +00:00
Horizon agentandClaude Opus 5.5 1636336f99 Merge hdr-page-no-shortcut (main merged in) into hdr-ui-nits
- graphics.rs: main's hdrFailedStart arm stays; this branch reads hdrUiBrightness as an
  older file's share of Paper White in its own arm.
- graphics_ui_hdr_tests.rs: main's held-HDR tests and this branch's UI Brightness test, both.
- gfx/output_apply.rs (new on main): its UI brightness of 0.6 is now 150 nits under the
  reported paper white of 250.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:11:20 +00:00
Horizon agentandClaude Opus 5.5 50a11a1107 Options: native mod pages in their own module
A pure move out of options_ui.rs (8441 lines, may not grow): the native
mod's page rows, its key wait and its key binding go to
options_ui_pages.rs, a child module, so the page registry can land there
(docs/layers.md, Platform registries).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:11:10 +01:00
Horizon agentandClaude Opus 5.5 4eee03985c The engine reads PvP & HUD's options through game/extras/pvp.rs (layer plan M5)
Every read of settings.extras.pvp outside the PvP module and its HUD
editor (glint, attack indicator, legacy look and rules, sound and
particle filters, shield and totem size, fire height, clear fluids,
chat search and copy) now calls one accessor in game/extras/pvp.rs.
Same values, read at the same places: each accessor becomes one engine
knob or hook when PvP & HUD leaves the engine.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:10:24 +01:00
Horizon agentandClaude Opus 5.5 ca1ed5fac5 Merge main into hdr-page-no-shortcut
main added Graphics::hdr_failed_start beside hdr_key: the field, its default, its
parse arm and the round-trip test keep it, without the shortcut key this branch removes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:10:08 +00:00
Ludovic Marie 7eb057384c Merge pull request #13788 from RINNECODER/up11/ui-pack-glyphs
🤖 Agent: layout now collects a Vec per label even for measure, one small allocation per text per frame; a two-pass over unformatted() would avoid it if a menu ever shows up in a profile.
2026-11-09 17:09:40 +01:00
Ludovic Marie 9976e2053c Merge pull request #13869 from MSedgeMC/fix/inventory-creative-swap
🤖 Agent: the switch to the creative screen was in set_game_mode, which runs on the server thread; the window changes mode through the game event (set_mode_id), so it never saw it. I moved the call into set_mode (pushed 470f9d0), which both paths reach. The way back (CreativeModeInventoryScreen.containerTick) is still a gap. Also, the PR body is the literal text @/tmp/hudbodies/b-inv2.md: the body file never got read.
2026-11-09 17:09:36 +01:00
Ludovic Marie 1875af1a00 Merge pull request #13786 from RINNECODER/up11/sulfur-cube-fields
🤖 Agent: checked SulfurCube's <clinit> in the 26.3 jar: MAX_FUSE (INT) is defined before FROM_BUCKET (BOOLEAN), so 19/20 and the -1 default are right.
2026-11-09 17:09:32 +01:00
Ludovic Marie b9fc2c5539 Merge pull request #13779 from vnsmoreira/packs/polytone-creative-tabs
🤖 Agent: a name given as {translate:...} is shown as the raw key (component_text keeps the key, creative_tab_title never translates it); small follow-up. tag_match scans exported_search per item, fine since it runs only at rebuild.
2026-11-09 17:09:28 +01:00
Ludovic Marie cee8d447f8 Merge pull request #13777 from MSedgeMC/fix/pufferfish-spectator
🤖 Agent: nothing found; the spectator gate sits before the box test, as forNonCombat's canBeSeenByAnyone does.
2026-11-09 17:09:23 +01:00
Ludovic Marie 04880cebe6 Merge pull request #13776 from NikitaMGrimm/fix/timeout-seconds-every-run
🤖 Agent: only test_friends.py (600 s) and test_native_client.py (60 s, frame-limited) pass --timeout-seconds today, so no other harness changes behaviour.
2026-11-09 17:09:18 +01:00
Ludovic Marie c3f0df9033 Merge pull request #13774 from MSedgeMC/fix/enderman-spectator-stare
🤖 Agent: nothing found; can_continue gets the same spectator gate, which forCombat's canBeSeenByAnyone also applies on every re-test.
2026-11-09 17:09:15 +01:00
Ludovic Marie ad1ec8e4f6 Merge pull request #13772 from Mik00000/perf/structure-locate-vanilla-order
🤖 Agent: checked against ChunkGenerator.findNearestMapStructure's bytecode: ring loop outside, placements inside, return on the first ring with a hit; the concentric result already in best plays vanilla's starting nearestDistance.
2026-11-09 17:09:10 +01:00
Ludovic Marie ff66433c34 Merge pull request #13770 from MSedgeMC/fix/brewing-speed-multiplier
🤖 Agent: nothing found; the shared nbt_value::float matches getFloatOr's NumericTag.asFloat for every numeric tag.
2026-11-09 17:09:06 +01:00
Ludovic Marie 61f922fbd5 Merge pull request #13765 from DUzzL/fix/13669-cloud-custom-particle
🤖 Agent: custom_particle_options decodes more of ParticleTypes.CODEC than /particle's own parser in commands/world_cmds.rs; that command could reuse it later. Block/item/vibration types still fall back to the potion swirl, as the body says.
2026-11-09 17:09:00 +01:00
Horizon agentandClaude Opus 5.5 511e971adc Slider sprites: canChangeValue is the keyboard's too, not only the drag
26.3's AbstractSliderButton sets canChangeValue in setFocused (focused with the
mouse or Tab) and flips it on Enter (keyPressed); getSprite/getHandleSprite read
only that flag. Horizon's can_change_value is that flag for the keyboard, and a
mouse drag focuses the slider with it set. So the sprites take both again:
a Tab-focused slider keeps its plain track and lit handle, an arrow-focused one
lights its track, and a drag no longer lights the track (the bug this PR fixes).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:08:57 +00:00
Ludovic Marie 211512aee8 Merge pull request #13763 from TheMrZZ/fix/h282-hopper-pot-insertion-2
🤖 Agent: the pot adapter only ever sits behind hopper_insertion_view, so generic discovery (comparators, pulls) stays untouched; a pot still has no modeled block entity, so hopper extraction from a pot remains a gap for a follow-up. Overlaps #13800 only in is_full/move_in_item signatures.
2026-11-09 17:08:55 +01:00
Ludovic Marie edc5f94957 Merge pull request #13791 from MSedgeMC/port_map/directional-sound
🤖 Agent: confirmed tick_end_flash builds SoundInstance::directional and directional_position adds the 10-block view offset, so the row now cites the real port instead of its caller's caller.
2026-11-09 17:08:50 +01:00
Ludovic Marie 761704ce11 Merge pull request #13790 from MSedgeMC/port_map/block-sync-snap
🤖 Agent: confirmed end_predictions calls block_meets_player then snap_target/snap_player_to (remote.rs:2967-2969, unit-tested at 12726), so the old gap note was wrong.
2026-11-09 17:08:45 +01:00
Ludovic Marie 74d6a69a0e Merge pull request #13789 from MSedgeMC/port_map/sprite-animate
🤖 Agent: checked on main: animate.rs has only SpriteAnimator::animate_with now, so both rows pointed at a function that no longer exists; TextureManager.tick as inline matches its own note.
2026-11-09 17:08:41 +01:00
Horizon agentandClaude Opus 5.5 5edd79feaa Settings store: rustfmt, Cargo.lock
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:07:26 +01:00
Horizon agentandClaude Opus 5.5 c5121f0f10 Settings store: crates above the engine keep their own settings lines
horizon_hooks::settings (re-exported as horizon_platform::settings): a Store
names its file (horizon-extras.txt or its own), the keys it owns, and loads
and saves its lines. Settings::load and Settings::save route the lines; a
shared file gets the stores' lines after the engine's, an own file keeps the
lines no store owns. The hooks list and the registries share one Registry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:05:56 +01:00
Ludovic Marie c2414d0c5d Merge pull request #13982 from horizon-rust/land/11446
🤖 Agent: lands GabrielNichols' #11446 (fuel alternatives in the Recipes cooking card); its seven JEI sprites go into jei_sprites.rs and the fuel list's wheel shares the bundle's check, so hud.rs and game.rs don't grow.
2026-11-09 17:04:35 +01:00
Horizon-agentandClaude Opus 5.5 0b5a2c1e4f Let the camel jump harness gate on its non-diagnostic rows only
The corpus declares diag- rows as recorded diagnostics of separate
differences (the /ride mount event, RootVehicle); the PR itself reports 6
of them still differing, so exiting 1 on any difference made the new
harness fail on every automatic parity run. They stay in the report.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:03:01 +00:00
Horizon agentandClaude Opus 5.5 18a2d812a7 game.rs doesn't grow: interact_location moves to hand.rs
Its only callers are hand.rs and the core tests (through game.rs's import).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:02:01 +01:00
Horizon agentandClaude Opus 5.5 510bb6f135 Hub::synced_name lives in lan/registry_names.rs
hub.rs is over 3000 lines and may not grow (tools/check_file_size.py); the
method moves unchanged to a file of its own beside it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:57:46 +01:00
SenjinandClaude Opus 5.5 d84e8fa460 Shader packs: a varying the fragment stage never reads takes no location
The translator gave every vertex output a location, read or not. Under GL the linker drops an
inactive varying, so packs declare more than they pass on: a deferred pack's shadow program
writes 30 vertex outputs and reads 17, which left no room on a GPU with 31 inter-stage locations.

A pack's varying its fragment stage never names now takes no location; a vertex output stays a
global of the vertex stage. Ours (the built-ins, the boats' patch place) always travel. The
assignment moves to translate/varyings.rs (mod.rs is over the file-size limit).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:55:45 +01:00
Nikita Maximilian Grimm 098efef8e2 test_git_slim: the bash on PATH, and no automatic maintenance in its repositories
A bare "bash" is WSL's on Windows, and git 2.54's automatic maintenance
after a commit folds the packs the tests build, so the suite failed on
Windows (and would with a newer git anywhere).
2026-11-09 16:55:01 +01:00
Horizon agentandClaude Opus 5.5 daf537e0d4 Merge main into split-block-particles
game.rs: main moved the neighbouring item and hand helpers out (#13856,
#13877); kept main's file and removed only crack_particle and
particle_sprite, as this branch does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:54:33 +01:00
Horizon agentandClaude Opus 5.5 260051a190 Lockfiles after merging main
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:53:29 +01:00
Horizon agent 66dafd2bb6 Merge remote-tracking branch 'origin/main' into split/launcher-server-platform
# Conflicts:
#	Cargo.lock
#	crates/horizon-platform/Cargo.toml
#	crates/horizon-platform/src/lib.rs
#	launcher/Cargo.lock
2026-11-09 16:51:12 +01:00
Horizon-agentandClaude Opus 5.5 ada4756065 Keep lan.rs and lan_actions.rs from growing under the current size check
main's check_file_size no longer counts test modules, so moving the two
inline test modules out no longer offset the new sea_level lines; it added
#[path] lines instead. The tests are back inline, and two self-contained
functions move to child modules unchanged (pure moves):
level_event_except to lan/level_event_targets.rs and horizon_channel to
lan_actions/horizon_channels.rs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:50:27 +00:00
Horizon-agentandClaude Opus 5.5 2fe3429a9a Insert a disc by its own jukebox_playable component too
JukeboxBlock.useItemOn asks the stack for JUKEBOX_PLAYABLE, so the insert
path and the client-side jukebox_takes now read the stack the same way the
comparator and the song tick do. An inline song definition is playable but
has no id to play.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:49:37 +00:00
Horizon agentandClaude Opus 5.5 3db7aa7cac Fix the fields' renames in the moved emote files
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:49:21 +01:00
Horizon agentandClaude Opus 5.5 5e6181b8f3 rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:46:58 +01:00
pehe1andCodex 08f7f87f23 Use the item crate for the fixture save round trip
Co-Authored-By: Codex <[email protected]>
2026-11-09 12:46:16 -03:00
Horizon-agent 5b4984aa32 Merge remote-tracking branch 'origin/main' into m13723
# Conflicts:
#	crates/horizon-client/src/container_screen.rs
2026-11-09 15:45:28 +00:00
Horizon-agentandClaude Opus 5.5 8396106b2e Stats screen: look item registry ids up once per sort, not per row
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:45:01 +00:00
RLC248 d1d212ef32 Merge remote-tracking branch 'upstream/main' into fix/anger-end-time-roundtrip 2026-11-09 16:44:39 +01:00
RLC248 f6eff6b43a A neutral mob keeps the anger end time it loaded
NeutralMob.addPersistentAngerSaveData writes anger_end_time as
readPersistentAngerSaveData read it; only startPersistentAngerTimer and
stopBeingAngry change it. Horizon re-stamped it from the countdown on
every read, so a past end read back as -1 and a mob that missed ticks
moved its end forward.

The end time now stays as loaded while the countdown agrees with it or
nothing changed it, and the stopBeingAngry sites write -1 (#13076).
2026-11-09 16:44:38 +01:00
Horizon agent 7abf696d29 Merge split/cosmetics-prep
# Conflicts:
#	crates/horizon-client/src/game/cosmetics/emotes.rs
2026-11-09 16:44:34 +01:00
Horizon agentandClaude Opus 5.5 a94ef019be rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:43:25 +01:00
pehe1andCodex 1c9ebb2318 Merge current Horizon main into blocking durability
Co-Authored-By: Codex <[email protected]>
2026-11-09 12:43:20 -03:00
MSedgeMC e64dc6ad91 Keep one import of the goals module in the moved test file 2026-11-09 15:42:37 +00:00
Horizon agentandClaude Opus 5.5 50295a160c rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:40:17 +01:00
Horizon agentandClaude Opus 5.5 308bf3dae0 test: the overlay-hold check no longer races the clock
50 one-millisecond sleeps could pass the 80 ms hold on a loaded runner and
fail the test; hold for an hour, wait for the worker, then lift the hold.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:39:51 +01:00
Horizon agentandClaude Opus 5.5 470f9d01e0 fix: the window's inventory follows a mode change too
set_game_mode runs on the server thread; the window changes mode through
the server's game event (set_mode_id), so the switch to the creative
screen now lives in set_mode, which both reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:38:19 +01:00
Horizon agentandClaude Opus 5.5 8559f9ccd2 Merge origin/main: keep both the first-release and the double-click tests
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:36:25 +01:00
Horizon agentandClaude Opus 5.5 e1cd3420ce Take the pushed projectile's bubble surfaces instead of draining them
clippy's drain_collect: the drained Vec is the same type as the new one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:34:58 +01:00
Horizon agentandClaude Opus 5.5 21d6f5610b A partial pickup still takes the item and awards its stat
Inventory.add returns true once any of the stack went in (count < the
count before), so ItemEntity.playerTouch's take, ITEM_PICKED_UP stat and
onItemPickup run on a partial pickup too. The stat counts the stack's
count before the add, as playerTouch's local count does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:32:03 +01:00
Horizon agentandClaude Opus 5.5 0660817c30 Registry review: a version taken down by hash may leave its entry
A project whose signing key was lost (horizon-mods#276, baritone) can't keep
its old versions listed: the index checks every version against the entry's
one key. The review refused removing them even after revoked.toml took their
files down by sha256. Now such a removal is a note, not an error; a version
removed without a take-down by hash is still refused.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:26:54 +01:00
Horizon agentandClaude Opus 5.5 16341f4d04 Land #11446 on the moved JEI sprite list
Merge move/jei-sprites (#13977): this PR's seven sprites go into jei_sprites.rs instead of hud.rs. The fuel list's wheel joins the bundle's in game.rs's container scroll (both take the wheel before the screen's own scrolling), so neither big file grows (docs/layers.md#file-size).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:26:13 +01:00
Appa 50901bae92 Reuse isolated game environment in DLL startup regression
Co-Authored-By: Codex <[email protected]>
(cherry picked from commit f047bd7949)
2026-11-09 16:25:06 +01:00
Appa c23c0056a1 Match the regression fixture to actual launcher DLL imports
Co-Authored-By: Codex <[email protected]>
(cherry picked from commit f36aff7aea)
2026-11-09 16:25:06 +01:00
Appa 76c3d4d262 Fix Windows launcher startup beside injector DLLs
Resolve static launcher imports from System32 before program entry and
exercise launcher/setup startup beside unrelated DLLs in release checks.

Co-Authored-By: Codex <[email protected]>
(cherry picked from commit 6d913502bf)
2026-11-09 16:25:06 +01:00
Horizon agentandClaude Opus 5.5 c024816c37 Merge main into blocking durability; the dispenser game keeps lab.game_env
Main's own tool cases joined the case list beside the blocking ones. The
branch had replaced lab.game_env with os.environ for the game it starts,
which test_game_home (every script that starts the game uses game_env)
refuses: the game then wrote into the real HORIZON_HOME. --game-dir and
the .exe name on Windows stay.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:24:50 +01:00
Horizon agentandClaude Opus 5.5 c9246a4fc5 Merge main into #12175
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:24:21 +01:00
DUzzL 282992c1ec Fix rider item pickup area and partial pickup side effects 2026-11-09 16:24:05 +01:00
Horizon agent eee2cd0db1 Merge main into native_registry_name 2026-11-09 16:23:08 +01:00
Horizon agentandClaude Opus 5.5 448ba30415 horizon-prepare: prefer a launcher folder that has every library
A launcher folder with 26.3's version JSON and jar but a missing library
(an official launcher that never finished downloading) was picked over a
complete copy in another launcher (the CurseForge app), and preparing
failed on the missing jar (#13872). game_files now tries folders with every
client library first, then the old order, so the error still names the
library when no folder has them all.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:22:59 +01:00
Horizon agentandClaude Opus 5.5 b82eeb4f95 Merge origin/main into #11446
Union of both sides: main's JEI notice (it already names the sprites), main's button/search sprites and settings button beside this PR's fuel sprites, tooltip and popup; en_us.json keeps main's keys and adds the three fuel strings.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:22:29 +01:00
Ludovic Marie 4f311197bd Merge split/cosmetics-prep 2026-11-09 16:18:58 +01:00
Horizon agent 49c2e8c55f Merge remote-tracking branch 'origin/main' into split/cosmetics-prep
# Conflicts:
#	Cargo.lock
#	tools/layers.json
2026-11-09 16:18:08 +01:00
MrCloudy2andClaude Opus 5.5 447514aa1d Harness: a piston's push gives a projectile its bubble column's push
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:17:55 +01:00
MSedgeMC c5897ab467 The first-release test carries a stack, the hand that would click
A release with an empty hand throws nothing whether skipNextRelease is set or
not; only a carried stack makes mouseReleased click, which is the hand that
opens a chest while holding one.
2026-11-09 15:13:54 +00:00
MrCloudy2andClaude Opus 5.5 d87da7928c A piston's push applies a projectile's bubble columns too
moveEntityByPiston's applyEffectsFromBlocks: a projectile a piston pushed
through a bubble column gets Projectile.onInsideBubbleColumn /
onAboveBubbleColumn, as after its own move.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:09:19 +01:00
Horizon agentandClaude Opus 5.5 481afc2e7b Cosmetics in one module tree: emotes and the emote wheel under game/cosmetics/
game/emotes.rs and game/emote_wheel.rs move to game/cosmetics/, their state into
Cosmetics (one Horizon field on Game), and the name mark comes from game/cosmetics.rs:
only game/cosmetics* names horizon_cosmetics now (layer plan M5, preparing the move
to a Horizon crate). No behaviour change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:06:53 +01:00
Horizon agentandClaude Opus 5.5 0deeb5cb29 discord_presence: rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:05:14 +01:00
Horizon agentandClaude Opus 5.5 b05af110f7 Cargo.lock: horizon-client-lib no longer depends on horizon-cosmetics
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:04:03 +01:00
MSedgeMC 6e45279301 A container screen's first release clicks nothing
AbstractContainerScreen's constructor sets `this.skipNextRelease = true`, so
a mouseReleased with no press before it on that screen returns before any
slotClicked. That is the release of the click that opened the screen: a
chest's right-click sends its UseItemOn, the server opens the menu, and the
button goes up with the screen already open, over whatever slot the pointer
was on. Horizon's Clicks started with the flag false (and cancel_drag cleared
it), so that release picked up the slot now under the pointer.

Clicks::default and cancel_drag now start a screen with skipNextRelease true,
as a fresh AbstractContainerScreen does.
2026-11-09 14:58:05 +00:00
MSedgeMC d3adab07d4 Re-run checks: the previous run failed in an unrelated fixture / harness 2026-11-09 14:52:13 +00:00
Horizon agentandClaude Opus 5.5 d0dcb33382 discord_presence: nothing of the game crate left in it
Layer plan M5 (preparation): the game mode comes from horizon_command, and
the party's size from game/discord.rs (Play::Hosting's max) instead of
crate::lan, so the presence client can move to a Horizon crate as a pure
file move once the seam has an activity hook. No change for players.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:44:17 +01:00
Horizon agentandClaude Opus 5.5 3bca3ed851 rustfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:43:43 +01:00
Horizon agentandClaude Opus 5.5 24c8f66b32 Launcher backend.rs: import the platform's prepare, so the file doesn't grow
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:37:27 +01:00
MSedgeMC 2982b03bdf Move the Tempt goal out of goals.rs and scale its range by visibility
goals.rs is over the 3000-line limit, so the goal moves to mobs/goals/tempt.rs (a pure move) and the visibility
scaling lands there: TemptGoal reads TEMPT_RANGE with TargetingConditions' two-block floor.
2026-11-09 14:27:03 +00:00
Horizon agentandClaude Opus 5.5 c391c8971c rustfmt: the longer platform paths
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:25:17 +01:00
Skr1zzzzandClaude Opus 5.5 c73a1df2a5 Move a bucket's aim and target out of game.rs into game/buckets.rs
Part of #11857. bucket_hit and bucket_target (BucketItem.use's place position) move to a new game/buckets.rs. Methods that were private in game.rs become
pub(super), the same reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:23:11 +03:00
Skr1zzzzandClaude Opus 5.5 0f4cd026d3 Move saving a level in place and the world icon out of game.rs into game/saving.rs
Part of #11857. save_level and tick_world_icon (GameRenderer.tryTakeScreenshotIfNeeded) join game/saving.rs. Methods that were private in game.rs become
pub(super), the same reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:22:55 +03:00
Horizon agentandClaude Opus 5.5 5a589fb0f0 The PvP & HUD options' game glue moves to game/extras/pvp.rs (layer plan M5)
A pure move under game/extras/, where each Horizon extra's game glue
gathers: paths and visibility only (pub(in crate::game)). game.rs
loses its mod line.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:21:29 +01:00
Nikita Maximilian GrimmandClaude Opus 5.5 652accb89b docs/layers.md: test modules and test files don't count toward the size limit
The section #13011 landed predates #13847 and this branch; it now says
which test code the check skips.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:16:49 +01:00
Nikita Maximilian GrimmandClaude Opus 5.5 93e3025e31 File size check: tests.rs and *_tests.rs files don't count
They hold the tests moved out of a file (`#[cfg(test)] mod tests;`), and a
normal build doesn't compile them, the same reason inline test modules
don't count. The failure message sends new tests to a sibling
`<file>_tests.rs`, yet two such files were already over the limit
(mod_content_tests.rs 5,268 lines, level/tests.rs 4,175).

Fixes #13892

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:16:39 +01:00
Horizon agentandClaude Opus 5.5 a9ce60dde5 Auto-reconnect's game glue moves to game/extras/reconnect.rs (layer plan M5)
A pure move: the connection's start, join and end, the countdown and the
retry leave game/extras.rs for a child module of their own, so the extra
leaves the engine in one move once the seam lands.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:16:18 +01:00
Horizon agentandClaude Opus 5.5 4cada3276c Lockfiles: horizon-platform's new engine dependencies
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:15:57 +01:00
Horizon agentandClaude Opus 5.5 8d38384310 Emotes pose the player model through an engine hook, mobs::anim::BodyPose
Mob::emote holds a trait object the engine defines (anim::BodyPose) instead of
horizon_cosmetics::Playing; game/emotes.rs wraps Playing in it. horizon-client-lib
no longer depends on horizon-cosmetics (layer plan M5).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:15:22 +01:00
Horizon agentandClaude Opus 5.5 92886cd964 Launcher and horizon-server reach the engine through horizon-platform
Layer plan M6: horizon-platform gains feature-gated re-exports (version,
no_ai, prepared_files always; world, protocol, prepare, server-files behind
features) and the two Horizon-layer crates use them instead of depending on
horizon-core, horizon-prepare, horizon-protocol, horizon-world and
horizon-server-files. Seven layers.json exceptions go.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 15:14:02 +01:00
MSedgeMC b768990c0c Merge remote-tracking branch 'upstream/main' into HEAD 2026-11-09 13:59:53 +00:00
Nikita Maximilian GrimmandClaude Opus 5.5 938365b9e8 HDR settings: UI Brightness in nits, apart from Paper White
UI Brightness was a share of Paper White, 25 to 100%: the HUD and menus could
only be dimmer than the world's white, and they moved whenever Paper White
did. It is now set in nits, as HDR games and mods set it: Auto (the default)
puts the HUD and menus at the paper white in use, as 100% did, so nothing
changes on screen by default; a value set by hand, on Paper White's steps
(10 to 480 nits by 10), holds them at that many nits whatever Paper White is,
below or above the world's white. The row reads like Paper White's
("UI Brightness: Auto (203)", "UI Brightness: 160") and shares its row code
and steps with it and Peak Brightness.

HDR output still takes a share of paper white, now UI nits over paper white
nits (resolve_output); its 0.25..1 clamp goes, so the HUD can be brighter
than paper white. The shader and the HUD composite are unchanged.

horizon-graphics.txt saves hdrUiNits, 0 for Auto. An older file's
hdrUiBrightness reads as Auto at 100% or with Paper White on Auto (its nits
aren't in the file), and otherwise as that share of the hand-set Paper White
in nits, to the nearest step. Calibration doesn't touch UI Brightness.

The frame-identity tool's float_visbuffer session pins a 200-nit paper white,
so its older file's 50% reads as exactly 100 nits and builds from before draw
the same.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 14:57:37 +01:00
Mik00000 89fe5eb063 Add safe async task offloading API for native mods
Introduce Host-driven task offloading API for native mods (mod_async_worker).
Tasks run on a dedicated mod worker pool ('horizon-mod-worker') isolated from chunk
and terrain generation. Panics in mod background jobs and deferred main-thread callbacks
are caught and reported per mod via crash attribution and contain without crashing pool
workers or the main game loop.

Callback queues belong to individual Game instances (window and server thread in single
player), reached exclusively through Host without global ACTIVE_QUEUE state or test races.
2026-11-09 15:50:01 +02:00
MSedgeMC 56728e147f port_map: the slider sprite rows stop being inline gaps
Both rows recorded the drag outline and the hover-only handle as known
divergences; they now name the fn that draws vanilla's rules.
2026-11-09 13:37:18 +00:00
MSedgeMC 382cf33dd9 test: pin the slider sprites to focus and the drag
The old test asserted the drag outline; it now checks the four cases vanilla
distinguishes (hover, drag, keyboard focus, focus plus drag) and that an
inactive slider lights nothing.
2026-11-09 13:37:13 +00:00
MSedgeMC 01ef7b1657 fix: the slider's track lights on focus, not while it is dragged
AbstractSliderButton.getSprite is `isActive() && isFocused() &&
!canChangeValue` and getHandleSprite is `isActive() && (isHovered ||
canChangeValue)`, where canChangeValue is the mouse drag. Horizon drew the
track outline while dragged (vanilla suppresses it then) and never on
keyboard focus, and lit the handle on keyboard focus, which vanilla leaves
plain.
2026-11-09 13:37:13 +00:00
Skr1zzzzandClaude Opus 5.5 630258d2a0 Move ticking chunk tracking and scheduled ticks out of game.rs into game/block_ticks.rs
Part of #11857. sync_tick_chunks, tick_moving_pistons and tick_scheduled join game/block_ticks.rs. Methods that were private in game.rs become
pub(super), the same reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:35:40 +03:00
Nikita Maximilian GrimmandClaude Opus 5.5 9f376f7061 The stations prepared-data test loads the full item table
Items::tags_only has no items, so the anvil part of
prepared_vanilla_data saw a diamond sword with max damage 0: not
damageable, no repair, cost 0 where vanilla gives 1. The test now loads
Items::load, and the whole test passes on freshly prepared data.

Fixes #13883

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 14:33:22 +01:00
SenjinandClaude Opus 5.5 4a70193562 Shader packs: a shadow program with no output left keeps its water, without the boats' patches
The shadow pass's translucent terrain takes one vertex output more for the boats' water patches
(the vertex's camera-relative place). A pack whose shadow program already writes as many as the
GPU takes had that program refused, and with it its translucent terrain's shadows. It now gets the
program without the patches instead (`Post::shadow_water_plain`), with a note: the water's light
shows in the boats there, as before the patches, and the rest of the shadow pass stays.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 14:29:19 +01:00
MSedgeMC e2482f0e1d port_map: the containerTick row names the fn where it now lives
The swap moved to game_mode.rs for the file-size limit.
2026-11-09 13:28:25 +00:00
MSedgeMC 0d6aaca77d fix: keep the inventory swap out of commands.rs
commands.rs is already over the 3000-line limit and may not grow, so the
screen swap moves to game/game_mode.rs beside set_mode, which is where the
mode's own effects live. The call site is one line, and the file ends a line
shorter than the base.
2026-11-09 13:25:43 +00:00
MSedgeMC f2be6f018c Keep goals.rs under the file-size limit
The targeting tests move to mobs/goals_targeting_tests.rs, so goals.rs (over 3000 lines) shrinks from 3740 to 3611
while the new tempt-range test joins them.
2026-11-09 13:22:55 +00:00
Nikita Maximilian GrimmandClaude Opus 5.5 5d5f14b7c5 CI: the size, layer and address checks compare with HEAD^1 on pull requests
verify checks out GitHub's merge commit, but these three checks compared
it with pull_request.base.sha: main at the pull request's last push. As
main moves on, main's own changes since then counted as the pull
request's, so pull requests failed for files they never touched (on
#13027's merge: 17 files flagged, 16 not in the PR), and a file main had
shrunk could grow unnoticed. HEAD^1 is the main the merge commit puts
the pull request on. Pushes to main run as before, without a base.

Fixes #13874

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 14:18:37 +01:00
MSedgeMC d2efe0374c port_map: InventoryScreen.containerTick names the screen swap
The row said the switch to the creative screen was missing; it is the same
tick rule the creative screen already had in reverse.
2026-11-09 13:08:57 +00:00
MSedgeMC 278db50cfd test: pin which screen a mode change leaves the survival inventory on
Creative gets the creative inventory, survival keeps the crafting one, a
spectator still closes it, and any other screen is untouched.
2026-11-09 13:08:53 +00:00
MSedgeMC 12dbbe2251 fix: a survival inventory becomes the creative one when the mode changes
InventoryScreen.containerTick replaces its screen with a
CreativeModeInventoryScreen once the player has infinite materials, so the
player stays in a menu. Horizon closed the inventory instead, which lost the
screen a creative player expects after /gamemode creative. The spectator path
still closes it (vanilla has no branch there and leaves it open).
2026-11-09 13:08:53 +00:00
Florian Ernst 134b443675 Start CI again: the last push started no checks run 2026-11-09 14:03:56 +01:00
MSedgeMC 7903537503 port_map: LoadingOverlay.isReadyToFadeOut names the hold
The row was filed as missing while the overlay was taken down as soon as the
worker finished.
2026-11-09 13:01:40 +00:00
MSedgeMC 0c989e88e1 test: pin the overlay's one-second hold
The hold is 1000 ms, a staged pack waits it out even when the worker is long
done, and a zero hold applies at once.
2026-11-09 13:01:37 +00:00
MSedgeMC 0e5d9f1d12 fix: a pack reload keeps its overlay up for a second before it applies
LoadingOverlay.isReadyToFadeOut holds a mid-game reload's overlay while
Util.getMillis() - fadeInStart < 1000L, so a pack that stages in a few frames
still shows the progress for a second instead of flashing for one or two.
drive_pack_reload applied the staged pack the moment its worker finished.
2026-11-09 13:01:37 +00:00
Skr1zzzzandClaude Opus 5.5 327a39bc56 Move block crack and transform particles out of game.rs into game/block_particles.rs
Part of #11857. particle_sprite, crack_particle (ClientLevel.addBreakingParticles) and transform_particle join game/block_particles.rs. Methods that were private in game.rs become
pub(super), the same reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:00:09 +03:00
Horizon agentandClaude Opus 5.5 0e687efd6e Cloud custom_particle: decode as the 26.3 option codecs do
ColorParticleOption keeps the colour's alpha (ARGB_COLOR_CODEC: an int's
top byte or a fourth float); colour lists have a fixed length and are
packed to 8-bit channels (ARGB.colorFromFloat); dust scale, sculk_charge
roll and shriek delay are required, and dust scale must be in [0.01, 4];
a spell's colour defaults to white. What fails to decode falls back to
the potion swirl, as vanilla's failed decode leaves no custom particle.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 13:46:58 +01:00
Horizon agentandClaude Opus 5.5 8a2a85a683 pot_hopper runs by name only; drop the harness contract test
pot_hopper needs --side and --work-dir, which an automatic parity run
doesn't pass. tests/test_pot_hopper_contract.py pinned the corpus row
counts and the harness's own compare, not game behaviour.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 13:43:51 +01:00
Vinícius MoreiraandClaude Opus 5.5 3319947bdf Polytone: creative tab modifiers
A pack's creative_tab_modifiers change a creative tab's icon, its name (text and colour),
and its items: removals by the wiki's item predicates (or, and, not, tag_match,
items_match, itemstack_match by item, id_match, true), then additions at the end or
before or after the first item a predicate matches. Measured on the real mod (icon and
red name on the building blocks tab, with the inventory opened through XTEST).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 09:42:51 -03:00
Horizon agentandClaude Opus 5.5 3a6e1aabe2 Camel jump: the helper sits above the test module; drop a copied doc
The camel_jump_tests header carried TestCommand.showPos text from another
test file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 13:41:12 +01:00
Horizon agentandClaude Opus 5.5 db07aba271 Drop a rebase leftover and a fixture nothing runs
respawn_world_point_tests.rs.orig is main's copy of the test file; the
spawn-info Python fixture and its Java decoder match no harness name, so
no runner ever calls them. The Rust test covers the sea level.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 13:40:14 +01:00
Nikita Maximilian GrimmandClaude Opus 5.5 75bb6a09ff HDR settings: the HDR page's shortcut key goes
The HDR page loses its Shortcut group and row: a key that opened the
graphics screen at HDR from the game. No other graphics page has a key of
its own, and the row was out of place there. The game no longer watches for
the key, and Done after that screen no longer has a way back into the game
that only the key used.

The hdrKey setting goes from horizon-graphics.txt. An older file's hdrKey
line loads like any unknown key, without error, and the next save drops it.
Its three strings go from every language file; nothing else used them.
Horizon's other key binds (sort, zoom, emotes) are unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 13:27:27 +01:00
Mik00000 a9a14c810e perf(mobs): optimize sensory raycasts, section caching, and despawn checks 2026-11-09 13:35:02 +02:00
Vinícius MoreiraandClaude Opus 5.5 9c1aca3474 Polytone: colormap tints cost a twelfth as much to mesh
The mesher's pack colormaps (the freepack's grass and leaves) sampled every block of a
section, each with its own biome zoom, through SipHash maps. Now:
- a section of one biome skips the per-block zoom (as vanilla tints already did);
- a colormap whose expressions don't read the height (no o.y) is sampled once per column
  over one biome (Program::reads_only says what an expression reads);
- the tint caches use the Fx hasher, and expression calls keep up to four arguments on
  the stack instead of a Vec.
Same colours (checksum of the timing test unchanged). One section, 16 layers of 256 tints
with a biome-id and noise colormap at Biome Blend 2 (release): 2.16 ms before, 178 µs now;
one layer 274 µs before, 138 µs now (mesher::tint_tests::colormap_tint_cost, ignored).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 07:49:33 -03:00
Vinícius MoreiraandClaude Opus 5.5 a62af71cef Polytone: environment.horizon_height
The sky's dark disc draws below a pack's horizon height instead of vanilla's 63 (flat worlds
keep their floor), measured on the real mod; sky_render_distance changes nothing there.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 07:37:35 -03:00
Vinícius MoreiraandClaude Opus 5.5 0b38e07d1d Polytone: biome_colors replace vanilla's biome colour functions
biome_colors.grass/foliage/dry_foliage/water (a colormap or a flat colour) colour every
block tinted by that function, under the block modifiers' own colormaps; without it, a block
modifier's colormap on grass_block, oak_leaves or leaf_litter (or a fluid modifier's on
water) becomes the function, as the wiki says Polytone does. Measured on the real mod:
biome_colors.grass also wins over a dark forest's special grass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 07:37:35 -03:00
Florian ErnstandClaude Opus 5.5 7df5c204c4 Call the camel test scene's own game() where a local shadows it
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 11:23:58 +01:00
Nikita Maximilian Grimm 2d9576f84d Merge remote-tracking branch 'origin/main' into parity/piston-falling-block 2026-11-09 11:01:23 +01:00
Nikita Maximilian GrimmandClaude Opus 5.5 3e7da21a05 A falling block that lands in a moving piston's block stays and is pushed
FallingBlockEntity.tick neither places, drops nor discards a block that
comes down where a piston is extending, and checks that before cancelDrop:
it slows like any landing and stays a falling block, so the piston pushes
it on. Horizon took the block out of the list before the landing returned
Landing::Continue, so the block was lost.

Fixes #13793

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 10:59:33 +01:00
Florian ErnstandClaude Opus 5.5 92dd7886f0 Keep big LAN files from growing: tests and spawn_info in their own files
The file-size check (docs/layers.md#file-size) does not let a file over
3000 lines grow, and the sea level field adds a line in each. Hub's
spawn_info moves to lan/hub/spawn_info.rs; three inline test modules
move to their own files, unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 10:54:51 +01:00
Florian ErnstandClaude Opus 5.5 8c154acd4a Keep game.rs and lan_actions.rs from growing: riding jump in mounts.rs
The file-size check (docs/layers.md#file-size) does not let a file over
3000 lines grow. The guest's START_RIDING_JUMP handler moves beside the
mount code it calls, and the camel test module is declared there too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 10:53:49 +01:00
Nikita Maximilian GrimmandClaude Opus 5.5 8ff04112a2 HDR output: Auto's grace ends on a fresh read after a settings change too
A settings change at the end of Auto's grace (a slider, F11) applies
the output before the frame's own display poll. It ended the grace on
the report read when the grace started, so a display that had come
back meanwhile still turned HDR off, and the frame's poll after could
no longer confirm it: the timer was gone.

The output now reads the display before the grace ends, whichever comes
first, the frame's poll or a settings change. A read taken since the
deadline counts, so each end of the grace reads the display at most
once. The two-second poll is unchanged otherwise, and Wayland's events
need no read.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 10:44:26 +01:00
MSedgeMC d595ebdd5e Invisible players tempt animals only within two blocks 2026-11-09 09:40:59 +00:00
MSedgeMC bf28b98a03 port_map: DirectionalSoundInstance.setPosition is directional_position
The row pointed at tick_end and said the flash sound has no position. The
10-block directional position is implemented and the flash sound uses it
from tick_end_flash.
2026-11-09 09:31:09 +00:00
MSedgeMC 779fb404f5 port_map: ClientLevel.syncBlockState does snap the player out
The note said the player is not moved when the restored block would hold
them. end_predictions checks block_meets_player and snaps through
snap_target/snap_player_to.
2026-11-09 09:30:48 +00:00
MSedgeMC 196f313399 port_map: the sprite animation rows name animate_with
SpriteAnimator::animate is gone; animate_with is the public pass that
advances every animated layer for the tick.
2026-11-09 09:30:23 +00:00
MSedgeMC 6fea5b936a Pufferfish puff goal ignores spectators 2026-11-09 09:21:05 +00:00
MSedgeMC fd04e8e867 Enderman stare goal ignores spectators 2026-11-09 09:10:55 +00:00
Nikita Maximilian Grimm 687f1bfd12 Merge remote-tracking branch 'origin/main' into fix/timeout-seconds-every-run 2026-11-09 10:10:06 +01:00
Florian ErnstandClaude Opus 5.5 3b735090e4 A ridden camel's dash posts the entity_action game event
Squashed and rebased onto main from #12473.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 10:09:08 +01:00
Mik00000 dde35f2b2e perf(worldgen): expand structure locate rings across candidate sets with vanilla stop order 2026-11-09 11:05:21 +02:00
MSedgeMC 704c3906e6 A brewing stand reads a numeric speed multiplier
BrewingStandBlockEntity.loadAdditional reads speed_multiplier with
ValueInput.getFloatOr, which takes any numeric tag. Horizon only accepted a
Float tag and defaulted every other numeric form to 1.0.
2026-11-09 09:04:14 +00:00
MSedgeMC 0413376752 A jukebox plays the song its disc names
JukeboxSong.fromStack reads the stack's own minecraft:jukebox_playable
component, and a removed component names no song. Horizon resolved records
through the item's default component only, so a disc that overrode or removed
the component played, or stayed silent, against the stack in the slot.
2026-11-09 09:04:07 +00:00
Nikita Maximilian GrimmandClaude Opus 5.5 c26fb23197 HDR output: Auto keeps HDR through a moment's loss of the display's report
With HDR on Auto, a display that wakes, a window moved to another
display, or a moment around Win+Alt+B can report SDR or nothing for one
read and HDR again at the next. Each such read turned HDR off at once
and on again: two swapchain reconfigurations, and on the layered float
frame a new renderer and a remesh, so the player saw black flashes and
a stall for nothing.

Now, while Auto presents HDR and the surface still offers that
encoding, a report that turns false or unknown keeps HDR on for 500 ms,
at the paper white, peak and black last applied. The applied output,
the shader Frame, the settings' active state and the frame the game
asks for stay as they were. A positive report before then clears the
timer with no swap; a loss that lasts turns HDR off once. Where the
system is polled, the display is read again at the deadline rather
than trusting a read up to two seconds old; where it sends events, the
deadline is served without one. The two-second poll stays otherwise.

Off and On act at once, entering HDR is never delayed, and an encoding
the surface no longer offers is never kept: the output switches to the
one offered, or SDR, at once. The timer is each renderer's own.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 10:03:50 +01:00
Nikita Maximilian GrimmandClaude Opus 5.5 50db6475fe --timeout-seconds ends every run, not only a frame-limited one (#13751)
A control-socket run started with --timeout-seconds never stopped when its
harness died. The flag now applies to every run; a frame-limited run keeps
its 120 s default and a run without either has no limit. --help lists it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 10:03:11 +01:00
RINNECODERandClaude Opus 5.5 a84771f764 Sulfur cube: MAX_FUSE is entity data 19 and FROM_BUCKET 20
SulfurCube defines MAX_FUSE (an int, -1 until primed) before FROM_BUCKET
(a boolean), right after AbstractCubeMob.ID_SIZE (18). Horizon sent and read
them the other way round, so a 26.3 client tracking a sulfur cube got a
boolean at 19 and disconnected (#13721). The server now sends the fuse at 19
with vanilla's -1 default and the bucket flag at 20, and the client reads
them there; a fuse of -1 is not primed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 03:02:25 -06:00
Florian ErnstandClaude Opus 5.5 58ac340c1c Let hoppers insert into the decorated pot saved item slot
Squashed and rebased onto main from #11599.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 10:01:28 +01:00
Florian ErnstandClaude Opus 5.5 d2612fd508 Use generator sea level in guest spawn info
Squashed and rebased onto main from #11458.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 10:01:28 +01:00
DUzzL a01f9d5883 Area effect clouds: honour and save custom_particle 2026-11-09 09:57:05 +01:00
RINNECODERandClaude Opus 5.5 562175df6c Streaming: meshes waiting for upload count against the meshing room
Uploads take a few ms of each frame. At a wide render distance and a low frame
rate they fall behind, and meshing kept going: a heavy modpack at 64 chunks held
4.3 GB of finished meshes in pending_uploads during a flight, enough to swap a
24 GB Mac. The room for new mesh jobs now subtracts that backlog.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 02:52:32 -06:00
RINNECODERandClaude Opus 5.5 13f692f8f0 Design-system text: § codes are formatting, glyphs Geist lacks come from the pack's font
A resource pack's lang can give strings `§` colour codes ("§6Options...")
and private-use characters its `font/default.json` maps to bitmap icons.
Geist printed the codes literally and drew tofu for the icons. `Ui::layout`,
shared by `measure` and `text_line`, now drops `§` codes like
`StringDecomposer.iterateFormatted` and draws a character Geist lacks with the
pack's default-font glyph: vanilla's 9px line scaled onto Geist's, or at GUI
scale when the whole label is such glyphs, as the pack draws it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 02:47:25 -06:00
k3yhandClaude Opus 5.5 bc16f9aedc Mobs swim, float and drown in content mods' fluids
Step 5 (second part) of #6301: a mob's fluid scan finds the mod fluid it is
deepest in (a mod liquid block holds its fluid only in the fluid table). The
mob moves in it as in water, floats and rises in it when can_swim
(Mob::floats now holds the brains' and FloatGoal's shared condition), loses
air when can_drown, is put out when can_extinguish, is pushed by its own
current at motion_scale when can_push_entity, and has its fall distance
scaled by fall_distance_modifier. Mob currents no longer pick a mod fluid by
its template.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 03:47:12 -05:00
k3yhandClaude Opus 5.5 3eb0cdc6f5 Content mods' fluids move the player and push items by their numbers
Step 5 (first part) of #6301: each mod fluid gets its own tag in
EntityFluidInteraction, as NeoForge tracks each FluidType, so it is not
water to the game. Its current pushes the player, items, orbs, armour
stands and boats by motion_scale when can_push_entity; the player moves in
it as in water, swims and rises when can_swim, loses air when can_drown, is
put out when can_extinguish, and has the fall distance scaled by
fall_distance_modifier. Unset numbers are the template's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 03:47:11 -05:00
Vinícius MoreiraandClaude Opus 5.5 fb354374e3 Polytone: item modifiers' damage bar colours
An item modifier's bar_color of the damage type (an <item>_bar.png alone, or a json with
bar_color "damage" and its png) colours the durability bar by the stack's damage: the first
pixel undamaged, the last at full damage, rounded, as measured on the real mod. The item
colormap (tint) changes nothing on the real mod's Fabric 26.3 build, so the freepack's
grass item modifier already looks the same.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 05:22:03 -03:00
RINNECODERandClaude Opus 5.5 644f4d80f7 Chat: apply a server's filter mask to player messages
The client decoded the player chat packet's `FilterMask` but only used it
to check the signature: on a server that filters chat, a fully filtered
message was still shown and narrated, and a partly filtered one showed
without its `#` masking.

As `ChatListener.showMessageToPlayer` does, a fully filtered message is
now hidden (and counted as processed, like a blocked sender's), and a
partly filtered one shows and narrates
`FilterMask.applyWithFormatting(signedContent)`: each masked UTF-16 unit
a dark grey `#` with the `chat.filtered` hover.

Fixes #13674

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 02:19:19 -06:00
Vinícius MoreiraandClaude Opus 5.5 8125ddb602 Polytone: a pack's leash texture
A pack's entity/lead.png textures the leash: each of its 24 segments, from the leashed
end, takes its row of the texture, unshaded, as measured on the real mod. Without one the
leash keeps vanilla's brown checker. The custom heart containers (polytone/heart/container_*)
change nothing on the real mod's 26.3 build.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 05:14:27 -03:00
MSedgeMC 1a26268488 A screen pauses the single-player world when its isPauseScreen says
Minecraft.pause is `hasSingleplayerServer() && Gui.isPausing()`, and
Gui.isPausing is the open screen's isPauseScreen. Horizon kept that answer in
a matches! list beside pauses_game instead of on the Screen, and two screens
disagreed with 26.3. SocialInteractionsScreen overrides isPauseScreen false,
so the world keeps running behind it; Horizon paused. StructureBlockEditScreen,
JigsawBlockEditScreen and the two test block editors inherit Screen's true, so
opening one freezes a single-player world as a command block's screen already
did; Horizon left them out of the list.

The answer is now `Screen::is_pause_screen`, one match over every variant, and
pauses_game asks it. The book in hand, the win screen and a dialog still answer
from their own state where they live.
2026-11-09 08:03:34 +00:00
MSedgeMC 53c6cd4fa5 A container screen ignores the hotbar keys while a stack is carried
AbstractContainerScreen.checkHotbarKeyPressed answers nothing unless
`this.menu.getCarried().isEmpty() && this.hoveredSlot != null`: with a stack
on the cursor, keySwapOffhand and keyHotbarSlots send no slotClicked at all.
The mouse hotbar keys asked `carried.is_none()` at their call site, but the
keyboard path (Game::inventory_hotkey from Screen::keyPressed) did not, so
pressing 1 or F over a slot with a stack carried sent SWAP. The menu's SWAP
branch does not look at the carried stack, so it moved the hovered slot's
item into the hotbar slot while the stack stayed on the cursor.

The guard now lives in `container_screen::hotbar_swap`, the Rust counterpart
of checkHotbarKeyPressed, and `inventory_hotkey` asks it for the off hand and
the nine hotbar slots.
2026-11-09 08:00:48 +00:00
RINNECODERandClaude Opus 5.5 7bd4244e2f Stats screen: mined blocks count under the item that places them
The Items tab keyed its rows on the raw `minecraft:mined` stat key, a
block id, so wheat, redstone wire, tripwire, wall torches and other blocks
whose item has another id got a row for a non-item with "-" under Mined,
while the real item (wheat seeds, redstone, string, torch) showed no mined
count. Rows now take `Block.asItem` of a mined block (air dropped), and a
block item's Mined column reads its block's count, as vanilla's
`StatsScreen.ItemStatisticsList` and `ItemRow` do.

Sorting follows `ItemRowComparator`: "-" counts as -1, equal values are
ordered by item registry id, and the first click on a column sorts
biggest first (it sorted smallest first).

Fixes #13664

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 01:51:34 -06:00
pehe1andCodex 1788fb7b84 Keep the combat fixture documentation with its operation
Co-Authored-By: Codex <[email protected]>
2026-11-08 23:25:21 -03:00
pehe1andCodex c9592a87f6 Reset the blocking fixture player after each case
Co-Authored-By: Codex <[email protected]>
2026-11-08 23:20:43 -03:00
pehe1andCodex 3b34446f84 Honor custom durability on blocking items
Co-Authored-By: Codex <[email protected]>
2026-11-08 23:19:40 -03:00
Cheezer1656andClaude Code 7fe4eb3931 Native mods: the player's own inventory on a client
host.own_inventory() (native_own_inventory) gives the 36 slots of the
player's own inventory (0-8 hotbar, 9-35 main) with their components,
whichever menu is open. menu_stacks() is None for the window's own
inventory, and living.equipment only covers armor and the held item.

Co-Authored-By: Claude Code <[email protected]>
Claude-Session: https://claude.ai/code/session_011R7zRUK5udBTJmDgHhoXY2
2026-11-08 12:52:14 -07:00
Cheezer1656 44e8e374b9 Merge upstream/main into upstream-gap/u-01-registry-name 2026-11-07 22:27:59 -07:00
Cheezer1656andClaude Code 218b421e16 Native mod API: registry names for numeric ids (native_registry_name)
A mod hearing packets through native_client_packet gets registry references
as numbers (a Stack's item and components, AddEntity's type, Sound,
LevelParticles) and could not name them. native_registry_name(registry, id)
resolves one against the ids the connection uses: the built-in registries,
those the server synced and a mod's own data components. On a server it asks
the hub's SyncData the same way.

Helper-only fragment registry_name.

Co-Authored-By: Claude Code <[email protected]>
Claude-Session: https://claude.ai/code/session_011R7zRUK5udBTJmDgHhoXY2
2026-11-07 22:10:47 -07:00
GabrielNichols f4d97fff39 Include the Recipes fuel translations 2026-11-07 01:39:17 -03:00
GabrielNichols d1b447525a Show ordered alternatives for Recipes furnace fuel 2026-11-06 19:16:57 -03:00
389 changed files with 12088 additions and 4132 deletions

No files matched your search

+9 -6
View File
@@ -80,21 +80,24 @@ jobs:
rustup toolchain install stable --profile minimal --component rustfmt --component clippy
rustup default stable
echo "WGPU_BACKEND=dx12" >> "$GITHUB_ENV"
# On a pull request, the next three checks compare with HEAD^1: the main that GitHub's merge commit puts the pull
# request on. pull_request.base.sha is main at the pull request's last push and falls behind, so main's own
# changes since then would count as the pull request's (#13874).
# docs/layers.md: a crate depends only on its own layer or a lower one; today's exceptions only shrink.
- name: Crates follow the layers
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: python3 tools/check_layers.py ${BASE_SHA:+--base "$BASE_SHA"}
BASE: ${{ github.event_name == 'pull_request' && 'HEAD^1' || '' }}
run: python3 tools/check_layers.py ${BASE:+--base "$BASE"}
# docs/layers.md#built-in-addresses: every built-in outside address in the Horizon layer's home; the rest only shrinks.
- name: Built-in addresses stay in their home
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: python3 tools/check_addresses.py ${BASE_SHA:+--base "$BASE_SHA"}
BASE: ${{ github.event_name == 'pull_request' && 'HEAD^1' || '' }}
run: python3 tools/check_addresses.py ${BASE:+--base "$BASE"}
# docs/layers.md#file-size: a Rust file over 3000 lines may not grow (pull requests; main has no base to compare).
- name: Big files don't grow
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: python3 tools/check_file_size.py ${BASE_SHA:+--base "$BASE_SHA"}
BASE: ${{ github.event_name == 'pull_request' && 'HEAD^1' || '' }}
run: python3 tools/check_file_size.py ${BASE:+--base "$BASE"}
- name: Test offline research tooling
run: python -m unittest discover -s tests -v
- name: Install Rust toolchain and the ALSA and udev headers the client links against
+3
View File
@@ -201,6 +201,9 @@ jobs:
- name: Verify Windows executable architecture
if: runner.os == 'Windows'
run: python ../tools/check_pe_arch.py "${{ matrix.pe_arch }}" "$BIN/${{ matrix.binary }}"
- name: Unrelated DLLs cannot intercept launcher or setup startup
if: runner.os == 'Windows'
run: python ../tools/test_launcher_dll_search.py "$BIN/${{ matrix.binary }}"
- name: The version is the release's
run: |
out=$("$BIN/${{ matrix.binary }}" --version)
+1 -1
View File
@@ -186,7 +186,7 @@ state stays separate from the library tests. This compiles gameplay twice for ex
| Resource packs | L: `packs.rs` + `packs/`, `pack_screen.rs`, `atlas_sources.rs`, `sprites.rs`, `mipmap.rs`, `model_library.rs`, `font.rs`, `mobs/cem.rs` + `mobs/cem_anim.rs` (OptiFine/EMF entity models and their animations); `horizon-client-core/src/lang.rs`; crates `horizon-assets`, `horizon-optifine`; G: `packs.rs` |
| Agent control | L: `control.rs` (socket), `bin/horizon-ctl/`; G: `control.rs`, `agent.rs`, `events.rs`, `ui_control.rs` ([docs/agent-control.md](docs/agent-control.md)) |
| Mods | crate `horizon-mods`; G: `mods.rs`; L: `mod_content.rs`, `mod_list.rs` |
| Horizon extras, settings | L: `extras.rs`, `graphics.rs`, `settings.rs`, `minimap.rs`; G: `extras.rs`, `tweaks.rs`, `options.rs` |
| Horizon extras, settings | L: `extras.rs`, `graphics.rs`, `settings.rs`, `minimap.rs`; G: `extras.rs` + `extras/` (each extra's glue: `mouse_tweaks.rs`, `sorting.rs`, `pvp.rs`, `reconnect.rs`), `options.rs` |
## How `Game` is organised
Generated
+7 -4
View File
@@ -1528,7 +1528,6 @@ dependencies = [
"horizon-client-render",
"horizon-command",
"horizon-core",
"horizon-cosmetics",
"horizon-friends",
"horizon-hdr",
"horizon-hooks",
@@ -1587,6 +1586,7 @@ dependencies = [
"horizon-core",
"horizon-cosmetics",
"horizon-hdr",
"horizon-hooks",
"horizon-items",
"horizon-narrator",
"horizon-optifine",
@@ -1814,8 +1814,13 @@ dependencies = [
name = "horizon-platform"
version = "0.1.0"
dependencies = [
"horizon-core",
"horizon-hooks",
"horizon-prepare",
"horizon-protocol",
"horizon-server-files",
"horizon-system",
"horizon-world",
]
[[package]]
@@ -1901,11 +1906,9 @@ dependencies = [
"horizon-anticheat",
"horizon-bench",
"horizon-chat-filter",
"horizon-core",
"horizon-mcp",
"horizon-platform",
"horizon-release-sign",
"horizon-server-files",
"horizon-world",
"libc",
"p12-keystore",
"png",
+2 -2
View File
@@ -80,8 +80,8 @@ pub fn main(args: &[String]) -> Result<(), String> {
};
let author = flag(rest, "--author").ok_or(USAGE)?;
let (base, _) = registry::load(Path::new(base)).map_err(lines)?;
let (head, _) = registry::load(Path::new(head)).map_err(lines)?;
let review = registry::review(&base, &head, author);
let (head, revoked) = registry::load(Path::new(head)).map_err(lines)?;
let review = registry::review_with_revoked(&base, &head, &revoked, author);
if rest.iter().any(|a| a == "--json") {
let json = serde_json::json!({"errors": review.errors, "notes": review.notes});
println!("{json}");
+21 -2
View File
@@ -26,18 +26,23 @@ pub struct AnimationMetadata {
pub enum ClipMode {
/// `day_time`: frame 0 at time 0, the last at the cycle's end, then again.
DayTime { cycle: i64 },
/// `screen_time`: the ticks the open screen has been open, over the cycle, held on the last
/// frame once it has run (it doesn't loop, measured on the real mod).
ScreenTime { cycle: i64 },
/// `weather`: frame 0 in clear weather, 1 in rain, 2 in a storm (the rain and thunder levels
/// added up), at most the last.
Weather,
/// A Polytone expression: its value's fraction is how far through the animation it is.
Expression(String),
/// `game_time`, `screen_time`, `season`: not driven yet; the animation runs on ticks.
/// `game_time`, `season`: not driven; the animation runs on ticks.
Unsupported(String),
}
/// The values [`ClipMode`]s read this frame.
pub struct ModeInputs<'a> {
pub day_time: i64,
/// The ticks the open screen has been open (0 without one).
pub screen_time: i64,
/// The rain level plus the thunder level, 0 to 2.
pub weather: f32,
/// An expression's value this frame, by its source.
@@ -116,7 +121,8 @@ impl AnimationMetadata {
match mode.as_str() {
"day_time" => ClipMode::DayTime { cycle },
"weather" => ClipMode::Weather,
"game_time" | "screen_time" | "season" => ClipMode::Unsupported(mode),
"screen_time" => ClipMode::ScreenTime { cycle },
"game_time" | "season" => ClipMode::Unsupported(mode),
_ => ClipMode::Expression(mode),
}
}),
@@ -239,6 +245,9 @@ impl AnimationClip {
pub fn sample_mode(&self, inputs: &ModeInputs) -> Option<AnimationSample> {
let fraction = match self.mode.as_ref()? {
ClipMode::DayTime { cycle } => day_fraction(inputs.day_time, *cycle),
ClipMode::ScreenTime { cycle } => {
(inputs.screen_time.max(0) as f64 / *cycle as f64).min(1.0 - f64::EPSILON)
}
ClipMode::Weather => {
let last = self.sequence.len() - 1;
let index = (inputs.weather.max(0.0) as usize).min(last);
@@ -288,6 +297,7 @@ mod mode_tests {
let expression = move |_: &str| Some(value);
clip.sample_mode(&ModeInputs {
day_time,
screen_time: day_time,
weather,
expression: &expression,
})
@@ -302,6 +312,13 @@ mod mode_tests {
.map(|t| frame(&day, t, 0.0, 0.0))
.into();
assert_eq!(frames, [0, 1, 2, 3, 1]);
// 26.3-6.8.6, four frames over 200 ticks: 40 ticks open show the first, and from ten
// seconds on the last, even after 27 (it doesn't loop).
let screen = clip(r#"{"frametime": 1, "mode": "screen_time", "time_cycle_duration": 200}"#);
let frames: Vec<u32> = [0, 40, 200, 360, 540]
.map(|t| frame(&screen, t, 0.0, 0.0))
.into();
assert_eq!(frames, [0, 0, 3, 3, 3]);
let value = clip(r#"{"frametime": 1, "mode": "0.6"}"#);
assert_eq!(frame(&value, 0, 0.0, 0.6), 2);
assert_eq!(frame(&value, 0, 0.0, 2.0), 0);
@@ -321,6 +338,7 @@ mod mode_tests {
clip(r#"{"frametime": 1}"#)
.sample_mode(&ModeInputs {
day_time: 0,
screen_time: 0,
weather: 0.0,
expression: &|_| None
})
@@ -330,6 +348,7 @@ mod mode_tests {
clip(r#"{"frametime": 1, "mode": "season"}"#)
.sample_mode(&ModeInputs {
day_time: 0,
screen_time: 0,
weather: 0.0,
expression: &|_| None
})
-2
View File
@@ -18,8 +18,6 @@ horizon-client-core = { path = "../horizon-client-core" }
horizon-client-input = { path = "../horizon-client-input" }
horizon-client-render = { path = "../horizon-client-render" }
horizon-command = { path = "../horizon-command" }
# Emotes pose the player model (`Mob::emote`, `mobs::anim::setup`).
horizon-cosmetics = { path = "../horizon-cosmetics" }
horizon-items = { path = "../horizon-items" }
horizon-core = { path = "../horizon-core" }
# Extension points for the crates above the engine (docs/layers.md, "How a Horizon crate plugs in").
+2
View File
@@ -7,6 +7,8 @@ rust-version.workspace = true
description = "The client's world data, rendering and screens: chunks, meshing, light, streaming, the renderer, the HUD, menus and settings; no gameplay"
[dependencies]
# The settings stores of the crates above the engine (docs/layers.md, "Platform registries").
horizon-hooks = { path = "../horizon-hooks" }
horizon-cosmetics = { path = "../horizon-cosmetics" }
# The GUI atlas's pixels, kept compressed once uploaded (hud.rs `pack_pixels`).
lz4_flex = { version = "0.14", default-features = false, features = ["std", "safe-encode", "safe-decode", "checked-decode"] }
@@ -362,7 +362,7 @@ fn fixture_frame(hud: &HudBatch) -> Frame<'_> {
outline: Vec::new(),
outline_after_terrain: false,
outline_high_contrast: false,
pvp: Default::default(),
look: Default::default(),
debug_lines: &[],
debug_lines_on_top: &[],
debug_crosshair: &[],
@@ -430,7 +430,7 @@ fn fixture_frame(hud: &HudBatch) -> Frame<'_> {
outline: Vec::new(),
outline_after_terrain: false,
outline_high_contrast: false,
pvp: Default::default(),
look: Default::default(),
debug_lines: &[],
debug_lines_on_top: &[],
debug_crosshair: &[],
+39
View File
@@ -16,6 +16,45 @@ use crate::{
#[path = "chat_tests.rs"]
mod tests;
/// `FilterMask.applyWithFormatting` over a player message's signed content, as
/// the packet's mask `(type, bit set)`: as it is when passed through, none when
/// fully filtered, and when partly filtered each masked UTF-16 unit becomes a
/// `#` in `FILTERED_STYLE` (dark grey, hover "chat.filtered").
pub fn filtered_message(
content: &str,
(kind, bits): &(i32, Vec<i64>),
) -> Option<serde_json::Value> {
match kind {
1 => return None,
2 => {}
_ => return Some(serde_json::Value::from(content)),
}
let masked = |i: usize| {
bits.get(i / 64)
.is_some_and(|w| (*w as u64 >> (i % 64)) & 1 != 0)
};
let units: Vec<u16> = content.encode_utf16().collect();
let mut parts = Vec::new();
let mut start = 0;
while start < units.len() {
let filtered = masked(start);
let end = (start..units.len())
.find(|&i| masked(i) != filtered)
.unwrap_or(units.len());
parts.push(if filtered {
serde_json::json!({
"text": "#".repeat(end - start),
"color": "dark_gray",
"hover_event": {"action": "show_text", "value": {"translate": "chat.filtered"}},
})
} else {
serde_json::Value::from(String::from_utf16_lossy(&units[start..end]))
});
start = end;
}
Some(serde_json::json!({"text": "", "extra": parts}))
}
/// `CommandHistory.PERSISTED_COMMANDS_FILE_NAME` and its size cap.
const COMMAND_HISTORY_FILE: &str = "command_history.txt";
const MAX_PERSISTED_COMMAND_HISTORY: usize = 50;
@@ -1026,3 +1026,43 @@ fn a_compact_repeat_keeps_the_scrolled_view_still() {
says(&mut chat, "new");
assert_eq!(chat.scroll, 6);
}
/// `FilterMask.applyWithFormatting`: a pass-through mask keeps the message, a
/// fully filtered one hides it, and a partial one turns each masked UTF-16 unit
/// into a dark grey `#` whose hover reads `chat.filtered`.
#[test]
fn a_server_filter_mask_hides_or_masks_the_message() {
let text = |v: &serde_json::Value| crate::component::to_text(v, &mut |s| Text::literal(s));
let shown = |content: &str, mask: (i32, Vec<i64>)| {
filtered_message(content, &mask).map(|v| text(&v).plain())
};
assert_eq!(shown("hello", (0, vec![])), Some("hello".to_owned()));
assert_eq!(shown("hello", (1, vec![])), None);
assert_eq!(shown("hello", (2, vec![0b0110])), Some("h##lo".to_owned()));
assert_eq!(
shown("hello", (2, vec![0b1_0001])),
Some("#ell#".to_owned())
);
// An emoji is two UTF-16 units, so two bits and two `#`.
assert_eq!(
shown("a\u{1F600}b", (2, vec![0b0110])),
Some("a##b".to_owned())
);
// Bit 65 is in the second word.
let long = "x".repeat(70);
let mut expected = long.clone();
expected.replace_range(65..66, "#");
assert_eq!(shown(&long, (2, vec![0, 0b10])), Some(expected));
let masked = text(&filtered_message("hello", &(2, vec![0b0110])).unwrap());
let hash = masked.spans.iter().find(|s| s.text == "##").unwrap();
assert_eq!(hash.style.color, Some(text::DARK_GRAY));
let hover = hash.style.hover.as_ref().unwrap();
assert_eq!(hover.spans[0].text, "chat.filtered");
assert!(
masked
.spans
.iter()
.filter(|s| s.text != "##")
.all(|s| s.style.color.is_none())
);
}
@@ -43,6 +43,11 @@ pub fn enter_native_mod(call: NativeModCall) -> NativeModGuard {
}
}
/// The native mod call this thread is currently inside, if any.
pub fn current_native_mod() -> Option<NativeModCall> {
IN_NATIVE_MOD.with(Cell::get)
}
/// Puts back the call that `enter_native_mod` found.
#[must_use]
pub struct NativeModGuard {
@@ -305,6 +305,9 @@ pub(crate) struct Colormap {
/// (`temperature`, `downfall`, `biome_id`) blend: Polytone 6.8.6's water by `biome_id`
/// fades over a biome edge.
pub(crate) biome_blend: bool,
/// Its axes read nothing that changes up a column of one biome (no `o.y`): a section of
/// one biome can sample it once per column.
pub(crate) column: bool,
width: u32,
height: u32,
rgba: Vec<u8>,
@@ -323,6 +326,18 @@ enum Axis {
}
impl Axis {
/// Whether the axis reads only what stays the same up a column of one biome.
fn column(&self) -> bool {
use horizon_polytone::expr::Root;
let flat = |root: Root, name: &str| match root {
Root::Object => matches!(name, "x" | "z" | "biome" | "temperature" | "downfall"),
Root::Random => name == "noise",
_ => false,
};
match self {
Self::Expr(program) | Self::BiomeId(_, program) => program.reads_only(&flat),
}
}
fn value(&self, host: &mut dyn horizon_polytone::expr::Host) -> Option<f64> {
match self {
Self::Expr(program) => Some(program.eval(host).ok()?.num()),
@@ -373,6 +388,23 @@ fn biome_id_mapper(pack_root: &Path, json: &Json) -> HashMap<String, f64> {
}
impl Colormap {
/// One colour wherever it samples (a `biome_colors` flat colour, RGB).
pub(crate) fn flat(rgb: u32) -> Self {
let zero = || Axis::Expr(horizon_polytone::expr::Program::compile("0").expect("a number"));
let [_, r, g, b] = rgb.to_be_bytes();
Self {
x: zero(),
y: zero(),
triangular: false,
rounds: true,
biome_blend: false,
column: true,
width: 1,
height: 1,
rgba: vec![r, g, b, 255],
}
}
/// `assets/<namespace>/polytone/colormaps/<path>.png` and its optional `.json` (without
/// one, the axes are the biome's temperature and downfall).
pub(crate) fn load(pack_root: &Path, id: &str) -> Option<Self> {
@@ -424,9 +456,11 @@ impl Colormap {
axis("x_axis", "o.temperature")?,
axis("y_axis", "o.downfall")?,
);
let column = [&x, &y].iter().all(|axis| axis.column());
Some(Self {
x,
y,
column,
biome_blend: json["biome_blend"].as_bool().unwrap_or(by_biome),
triangular: json["triangular"].as_bool() == Some(true),
rounds: json["rounds"].as_bool() != Some(false),
+6 -3
View File
@@ -482,13 +482,16 @@ impl Extras {
true
}
/// Written only once something differs from the defaults, so a vanilla
/// setup gains no file.
/// setup gains no file. The settings stores of this file follow these lines
/// (`horizon_hooks::settings`).
pub fn save(&self, dir: &Path) {
let path = dir.join(FILE);
if *self == Self::default() && !path.exists() {
let mut text = self.serialize();
let stores_default = horizon_hooks::settings::engine::append(FILE, &mut text);
if *self == Self::default() && stores_default && !path.exists() {
return;
}
let _ = fs::write(path, self.serialize());
let _ = fs::write(path, text);
}
}
+28 -31
View File
@@ -179,7 +179,7 @@ fn packed_wanted() -> bool {
std::env::var("HORIZON_PACKED_QUADS").as_deref() != Ok("0")
}
mod animate;
pub(crate) mod animate;
mod arena;
pub mod capture;
pub mod compositor;
@@ -1001,8 +1001,8 @@ pub struct Frame<'a> {
pub outline_after_terrain: bool,
/// `BlockOutlineRenderState.highContrast`: a thick black outline under a bright one.
pub outline_high_contrast: bool,
/// The PvP & HUD page's outline and hit colour (`pvp::Frame`; default: vanilla's).
pub pvp: crate::pvp::Frame,
/// The outline, hurt overlay and glint colours a crate above set (`horizon_hooks::look`).
pub look: horizon_hooks::look::Frame,
/// `lines` with a colour and pixel width (`gizmos::camera_relative`):
/// the fishing line and debug gizmos, depth tested, then gizmos drawn
/// over everything.
@@ -1530,6 +1530,8 @@ pub struct Renderer {
output_caps: output::OutputCapabilities,
/// An output that failed to apply, not tried again until something it needs changes.
output_rejected: Option<output_apply::Rejected>,
/// Since when Auto keeps HDR on through the display's loss of its report.
auto_off_since: Option<std::time::Instant>,
/// The output applied (`update_output`), with the frame every pipeline that draws it
/// targets: the swapchain's 8-bit format, or `Rgba16Unorm` when the renderer was made with
/// HDR output on and Smooth Gradients and the device draws and blends into it, or
@@ -4247,16 +4249,13 @@ impl Renderer {
CRUMBLING_BLEND,
false,
);
// RenderPipelines.ENTITY_SHADOW: translucent, back-face culled, depth
// test without writes. The quads lie exactly on the block tops like
// vanilla's, facing +Y so looking up from below culls them, but
// terrain and shadows are separate draw paths, so their fp32 depth
// interpolations disagree by a few dozen ulps of the fragment depth
// and the shadow's rim z-fights with the block top (speckle that
// re-rolls as the camera drifts a subpixel). A constant bias of 128
// ulps covers that spread at any resolution while staying far below
// the 0.001 a model's feet sit higher, so shadows never bleed onto
// the feet.
// RenderPipelines.ENTITY_SHADOW: translucent, back-face culled, depth test without writes.
// The quads lie exactly on the block tops like vanilla's, facing +Y so looking up from
// below culls them, but terrain and shadows are separate draw paths, so their fp32 depth
// interpolations disagree by a few dozen ulps of the fragment depth and the shadow's rim
// z-fights with the block top (speckle that re-rolls as the camera drifts a subpixel). A
// constant bias of 128 ulps covers that spread at any resolution while staying far below
// the 0.001 a model's feet sit higher, so shadows never bleed onto the feet.
let shadow_blob_pipeline = entity_pipeline_with(
"Entity shadows",
Some(wgpu::Face::Back),
@@ -5355,6 +5354,7 @@ impl Renderer {
emission_stats,
output_caps,
output_rejected: None,
auto_off_since: None,
// Nothing is presented as HDR yet: `update_output` applies the settings.
output: output::resolve_output(settings, output_caps, display, frame, false),
frame_kept_logged: false,
@@ -7094,11 +7094,6 @@ impl Renderer {
horizon_hdr::set_metadata(&self.device, surface, hdr.declared_peak(), hdr.black);
}
}
/// The swapchain's configuration: `config` (what every pipeline draws in), or
/// HDR10 or scRGB while HDR output is on.
fn surface_config(&self) -> wgpu::SurfaceConfiguration {
output_apply::surface_config(&self.config, self.hdr.as_ref().map(|hdr| hdr.output))
}
/// This renderer's output as applied, with what its destination offers and reports.
pub fn output(&self) -> &output::EffectiveOutput {
&self.output
@@ -7154,7 +7149,7 @@ impl Renderer {
settings,
request,
self.caps_for(settings),
self.reading,
output_apply::graced(self.auto_off_since, self.reading, &self.output),
self.config.format,
)
}
@@ -8354,8 +8349,12 @@ impl Renderer {
t.begin(&self.device, &self.queue);
}
if self.animated_textures {
self.hud
.animate_sprites(&mut self.uploads, &mut encoder, frame.ticks);
self.hud.animate(
&mut self.uploads,
&mut encoder,
frame.ticks,
&self.animations,
);
}
self.update_lightmap(
&mut encoder,
@@ -8507,9 +8506,9 @@ impl Renderer {
colored_count: [
frame.colored_lights.len().min(crate::dynamic_light::MAX) as u32,
// PvP & HUD's hit colour: entity.wgsl's hurt texel, 0 for vanilla's.
frame.pvp.hurt_texel,
frame.look.hurt_texel,
// PvP & HUD's glint colour: entity.wgsl's `tinted_glint`, 0 for vanilla's.
frame.pvp.glint,
frame.look.glint,
0,
],
emissive_layers: {
@@ -9293,13 +9292,11 @@ impl Renderer {
}
// Near water and distant water share one back-to-front draw order.
if self.translucent_draws.len() > section_translucent {
// Distant water goes after the near chunks', and replaced tiles'
// after all: each is a hair behind the one before, so depth leaves
// it only where that one has none. Back to front together, a far
// tile's water under a near chunk's (one fading in, or brought
// into view by a longer render distance) blended twice and looked
// opaque. The cost: far water just behind near water shows no
// surface of its own there.
// Distant water goes after the near chunks', and replaced tiles' after all: each is a
// hair behind the one before, so depth leaves it only where that one has none. Back to
// front together, a far tile's water under a near chunk's (one fading in, or brought
// into view by a longer render distance) blended twice and looked opaque. The cost: far
// water just behind near water shows no surface of its own there.
let tier = |instance: usize| match instance.checked_sub(visible.len()) {
None => 0,
Some(tile) if visible_lods[tile].old => 2,
@@ -9498,7 +9495,7 @@ impl Renderer {
}
};
// A custom outline (PvP & HUD) takes the bright stroke's colour, scales both widths.
let (custom, scale) = frame.pvp.outline.map_or((None, 1.0), |(c, s)| (Some(c), s));
let (custom, scale) = frame.look.outline.map_or((None, 1.0), |o| (Some(o.0), o.1));
if frame.outline_high_contrast {
stroke(0xff00_0000, 7.0 * scale, lines);
stroke(custom.unwrap_or(0xff57_ffe1), width * scale, lines);
@@ -64,7 +64,7 @@ enum Writer {
},
}
pub(super) struct SpriteAnimator {
pub(crate) struct SpriteAnimator {
sprites: Vec<Sprite>,
groups: Vec<Group>,
writer: Writer,
@@ -86,6 +86,9 @@ struct AnimationModes {
day_time: i64,
weather: f32,
values: std::collections::HashMap<String, f64>,
/// The open screen (an identity the game picks) and the ticks it has been open.
screen: Option<u64>,
screen_time: i64,
}
impl AnimationModes {
@@ -116,8 +119,15 @@ impl super::Renderer {
day_time: i64,
[rain, thunder]: [f32; 2],
frame: Option<&crate::environment::Frame>,
screen: Option<u64>,
) {
let modes = &mut self.animations.modes;
// `screen_time`: a tick more while the same screen stays open, from 0 on a new one.
modes.screen_time = match (screen, modes.screen) {
(Some(open), Some(was)) if open == was => modes.screen_time + 1,
_ => 0,
};
modes.screen = screen;
modes.day_time = day_time;
modes.weather = rain + thunder;
modes.values.clear();
@@ -133,6 +143,16 @@ impl super::Renderer {
}
impl SpriteAnimator {
/// What the modes read this frame, for the GUI atlas's sprites (their expressions aren't
/// compiled: those run on ticks).
pub(crate) fn mode_inputs(&self) -> horizon_assets::animation::ModeInputs<'static> {
horizon_assets::animation::ModeInputs {
day_time: self.modes.day_time,
screen_time: self.modes.screen_time,
weather: self.modes.weather,
expression: &|_| None,
}
}
/// `textures` are the sprite arrays, `split` layers each, `size` pixels
/// wide; `animations` hold their frames at that size. `compute`: whether
/// the device says it runs compute shaders writing storage textures.
@@ -361,6 +381,7 @@ impl SpriteAnimator {
let expression = |source: &str| values.get(source).copied();
let inputs = horizon_assets::animation::ModeInputs {
day_time: self.modes.day_time,
screen_time: self.modes.screen_time,
weather: self.modes.weather,
expression: &expression,
};
@@ -524,7 +524,7 @@ impl Scene {
outline: Vec::new(),
outline_after_terrain: false,
outline_high_contrast: false,
pvp: Default::default(),
look: Default::default(),
debug_lines: &[],
debug_lines_on_top: &[],
debug_crosshair: &[],
+49 -3
View File
@@ -185,7 +185,7 @@ pub struct Hdr {
/// The display's black level, in nits: shadows are lifted onto it (`hdr.wgsl`).
pub black: f32,
pub tiers: [Tier; 4],
/// The HUD and menus' brightness, as a share of paper white (0.25..=1).
/// The HUD and menus' brightness, as a share of paper white: above 1 brighter than it.
pub ui: f32,
/// Which pixels of a light source shine (`Masks`).
pub masks: Masks,
@@ -956,7 +956,7 @@ impl Hdr {
f32::from(u8::from(pq)),
]);
params[5] = self.black.clamp(0.0, paper);
params[6] = self.ui.clamp(0.25, 1.0);
params[6] = self.ui;
params[7] = if self.lighting {
scene_gain(self.peak, self.paper_white)
} else {
@@ -1906,9 +1906,11 @@ struct ReferenceParams { peak: f32, scene: f32, world: f32, debug: f32, share: v
// Covered: the HUD as drawn, at UI Brightness.
(2.0, 0.3, 1.0, 1.0, 0.3),
(2.0, 0.3, 1.0, 0.5, 0.3 * 0.5f32.powf(1.0 / 2.2)),
// UI Brightness above paper white: brighter than drawn.
(2.0, 0.3, 1.0, 2.0, 0.3 * 2f32.powf(1.0 / 2.2)),
// Added over the world (the text highlight), which it doesn't hide.
(0.6, 0.8, 0.0, 1.0, 0.8),
// At or below white, at 100%: the HUD's picture as drawn.
// At or below white, at paper white: the HUD's picture as drawn.
(0.7, 0.45, 0.5, 1.0, 0.45),
] {
hdr.ui = ui;
@@ -1928,6 +1930,50 @@ struct ReferenceParams { peak: f32, scene: f32, world: f32, debug: f32, share: v
}
}
/// On the frame without the HUD's layer, the HUD at UI Brightness reaches its nits, and
/// above the peak stops there, as on the layered frame.
#[test]
fn the_hud_at_ui_brightness_stops_at_the_peak() {
let Some((device, queue)) = gpu() else {
return;
};
let mut hdr = Hdr::with_debug(&device, &queue, F::Rgba8Unorm, Output::ScRgb, false, None);
(hdr.paper_white, hdr.peak) = (200.0, 300.0);
let target = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: Output::ScRgb.format(),
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
// UI Brightness (a share of paper white) and the white HUD's nits.
for (ui, want) in [(0.5, 100.0), (1.25, 250.0), (2.0, 300.0)] {
hdr.ui = ui;
let frame = hdr.frame(&device, [1, 1]);
let mut encoder = device.create_command_encoder(&Default::default());
fill(&mut encoder, &frame, [0.2, 0.2, 0.2, 1.0]);
hdr.keep_world(&mut encoder, None);
// Opaque white HUD over the world: alpha 1 is full coverage.
fill(&mut encoder, &frame, [1.0; 4]);
hdr.begin(&queue, true);
hdr.present(&queue, &mut encoder, &target, true, None);
let got = texel(&device, &queue, encoder, &target);
for c in &got[..3] {
assert!(
(c * 80.0 - want).abs() < 0.5,
"UI {ui}: {got:?}, not {want} nits"
);
}
}
}
#[test]
fn the_menu_blur_keeps_the_light_it_blurs() {
let Some((device, queue)) = gpu() else {
+36 -2
View File
@@ -142,7 +142,7 @@ pub struct EffectiveOutput {
/// Paper white, peak and black level in nits, as HDR output's last pass and its
/// metadata take them, while it is on.
pub(crate) nits: [f32; 3],
/// The HUD and menus' brightness, as a share of paper white.
/// The HUD and menus' brightness, as a share of paper white: their nits over its.
pub(crate) ui_brightness: f32,
}
impl Default for EffectiveOutput {
@@ -271,7 +271,8 @@ pub(crate) fn resolve_output(
settings.peak_nits(reading.luminance) as f32,
settings.black_level_nits(),
],
ui_brightness: settings.hdr_ui_brightness as f32 / 100.0,
ui_brightness: settings.ui_nits(reading.luminance) as f32
/ settings.paper_white_nits(reading.luminance) as f32,
}
}
@@ -566,6 +567,39 @@ pub(crate) mod tests {
);
}
/// UI Brightness reaches HDR output as its nits over paper white's: 1 on Auto, whatever
/// paper white is; a value set by hand holds its nits, Paper White on Auto or set by hand.
#[test]
fn ui_brightness_is_its_nits_over_paper_whites() {
let (caps, reading) = window(Some(Output::Pq), Some(true));
let share = |paper_white, ui_nits, reading| {
let settings = Graphics {
hdr: HdrMode::On,
hdr_paper_white: paper_white,
hdr_ui_nits: ui_nits,
..Graphics::default()
};
let frame = resolve_frame(
&settings,
FrameRequest::default(),
caps,
reading,
Bgra8Unorm,
);
resolve_output(&settings, caps, reading, frame, true).ui_brightness
};
// Auto: the report's 400 nits, 203 without one, or the player's 250.
let unreported = DisplayReading::default();
for (paper_white, reading) in [(0, reading), (0, unreported), (250, reading)] {
assert_eq!(share(paper_white, 0, reading), 1.0);
}
assert_eq!(share(0, 100, reading), 0.25);
assert_eq!(share(0, 480, reading), 1.2);
assert_eq!(share(0, 406, unreported), 2.0);
assert_eq!(share(250, 100, reading), 0.4);
assert_eq!(share(250, 480, reading), 1.92);
}
/// Two renderers' states, a window's and an offscreen one's, made and changed in turn:
/// neither's offer or report shows in the other's snapshot, and the offscreen one
/// stopping its watch (HDR Off) leaves the window's report alone.
@@ -3,9 +3,12 @@
//! changes only values (the luminance, the UI brightness, what the display reports) updates
//! them in place. Another encoding or frame is built and the swapchain configured for it before
//! the snapshot says so. One that fails is never published as active, and is not tried again
//! until the request, the capabilities or the surface change.
//! until the request, the capabilities or the surface change. Auto keeps HDR on through a
//! moment's loss of the display's report ([`auto_grace`]).
use super::{DisplayReading, DisplayWatch, Renderer, hdr, output};
use crate::graphics::HdrMode;
use output::{EffectiveOutput, OutputCapabilities};
use std::time::{Duration, Instant};
/// What an output's resources are made for: the encoding presented and the frame.
type Key = (Option<hdr::Output>, output::FrameConfiguration);
@@ -101,6 +104,98 @@ pub(super) fn surface_config(
config
}
/// How long Auto keeps HDR on once the display stops reporting it. A fixed window that
/// coalesces the report's churn as a display wakes or the window moves to another one; not a
/// measured sleep/wake time.
const AUTO_OFF_DELAY: Duration = Duration::from_millis(500);
/// The display's report `update_output` resolves Auto with at `now`. While Auto presents HDR
/// and the surface still offers that encoding, a report that turns false or unknown starts
/// the timer `since`, once, and HDR stays on at the values last applied until it has held
/// for [`AUTO_OFF_DELAY`]; a positive report before then clears it. Off and On, a changed
/// offer, and entering HDR take `raw` at once.
fn auto_grace(
now: Instant,
mode: HdrMode,
offered: Option<hdr::Output>,
raw: DisplayReading,
applied: &EffectiveOutput,
since: &mut Option<Instant>,
) -> DisplayReading {
let lost = mode == HdrMode::Auto
&& applied.encoding.is_some()
&& offered == applied.encoding
&& raw.hdr != Some(true);
if !lost {
if since.take().is_some() && raw.hdr == Some(true) {
log::info!("Horizon renderer: the display reports HDR again; HDR stays on");
}
} else if since.is_none() {
log::info!(
"Horizon renderer: the display stopped reporting HDR; Auto keeps it on for {} ms",
AUTO_OFF_DELAY.as_millis()
);
*since = Some(now);
} else if grace_over(*since, now) {
*since = None;
}
graced(*since, raw, applied)
}
fn grace_over(since: Option<Instant>, now: Instant) -> bool {
since.is_some_and(|since| now.saturating_duration_since(since) >= AUTO_OFF_DELAY)
}
/// What the display reports as the output takes it: during Auto's grace (`since`), HDR at the
/// luminance `applied` was resolved with; `raw` otherwise.
pub(super) fn graced(
since: Option<Instant>,
raw: DisplayReading,
applied: &EffectiveOutput,
) -> DisplayReading {
match since {
Some(_) => DisplayReading {
luminance: applied.reported,
hdr: Some(true),
},
None => raw,
}
}
/// Whether the output resolves again at `now`: a due poll (`read`, with the surface's
/// `offer`; on Wayland, an event) changed `reading` or `caps`, or Auto's grace `since` is
/// over. Its end reads the display once more first, however long until the next poll, unless
/// a read since the deadline did (the last read is a `DISPLAY_POLL` before the next).
fn poll_display(
watch: Option<&mut DisplayWatch>,
reading: &mut DisplayReading,
caps: &mut OutputCapabilities,
since: Option<Instant>,
now: Instant,
read: impl FnOnce() -> DisplayReading,
offer: impl FnOnce() -> Option<Option<hdr::Output>>,
) -> bool {
let over = grace_over(since, now);
let Some(watch) = watch else {
return over;
};
if let (Some(since), Some(next)) = (since.filter(|_| over), &mut watch.polled)
&& *next < since + AUTO_OFF_DELAY + super::DISPLAY_POLL
{
*next = now;
}
let Some(read) = watch.poll(*reading, now, read) else {
return over;
};
observe(reading, caps, read, offer()) || over
}
/// What `surface` offers now (`None` offscreen): Vulkan's offered formats can change with
/// desktop HDR or the window's output, and a display can offer the other encoding.
fn offer(surface: Option<&wgpu::Surface>, adapter: &wgpu::Adapter) -> Option<Option<hdr::Output>> {
Some(hdr::output(&surface?.get_capabilities(adapter)))
}
/// Takes what a due poll read into `reading` and `caps`: the display's report `now`, and what
/// the surface offers (`offer`, `None` without a surface). Whether either changed, the offer
/// alone included.
@@ -219,12 +314,26 @@ impl Renderer {
settings: &crate::graphics::Graphics,
marker: &std::path::Path,
) {
let now = Instant::now();
self.watch_hdr(settings.hdr);
// Auto's grace ends on a fresh read, also where a settings change comes before the
// frame's `display_changed`.
if grace_over(self.auto_off_since, now) {
self.read_display(now);
}
self.hold_hdr_back(settings);
let reading = auto_grace(
now,
settings.hdr,
self.output_caps.hdr,
self.reading,
&self.output,
&mut self.auto_off_since,
);
let candidate = output::resolve_output(
settings,
self.output_caps,
self.reading,
reading,
self.output.frame,
self.surface.is_some() || self.offscreen.is_some(),
);
@@ -269,24 +378,24 @@ impl Renderer {
let (watch, now) =
DisplayWatch::new(&self.window, self.surface.as_deref(), &self.adapter);
self.display = Some(watch);
self.output_caps.hdr = self.surface_offer().unwrap_or(self.output_caps.hdr);
let offer = offer(self.surface.as_deref(), &self.adapter);
self.output_caps.hdr = offer.unwrap_or(self.output_caps.hdr);
self.reading = now;
self.display_fresh = true;
}
}
/// What the window's surface offers now (`None` offscreen): Vulkan's offered formats can
/// change with desktop HDR or the window's output, and a display can offer the other
/// encoding.
fn surface_offer(&self) -> Option<Option<hdr::Output>> {
let surface = self.surface.as_ref()?;
Some(hdr::output(&surface.get_capabilities(&self.adapter)))
/// The swapchain's configuration: `config` (what every pipeline draws in), or
/// HDR10 or scRGB while HDR output is on.
pub(super) fn surface_config(&self) -> wgpu::SurfaceConfiguration {
surface_config(&self.config, self.hdr.as_ref().map(|hdr| hdr.output))
}
/// After a new window surface: what it offers, and an output that failed on the old one
/// may be tried again. The settings apply again.
pub(super) fn surface_recreated(&mut self) {
self.output_caps.hdr = self.surface_offer().unwrap_or(self.output_caps.hdr);
let offer = offer(self.surface.as_deref(), &self.adapter);
self.output_caps.hdr = offer.unwrap_or(self.output_caps.hdr);
self.display_fresh = true;
self.output_rejected = None;
}
@@ -295,22 +404,28 @@ impl Renderer {
/// (another output, the player recalibrating the desktop, desktop HDR offering the other
/// encoding): the settings' Auto values and the output follow it then, so apply them
/// again. Each due poll reads the surface's capabilities as well, even when the display
/// reports the same.
/// reports the same. So does the end of Auto's grace (`auto_grace`), with or without a
/// poll or an event then.
pub fn display_changed(&mut self) -> bool {
super::flush_display_log();
if std::mem::take(&mut self.display_fresh) {
return true;
}
let Some(watch) = self.display.as_mut() else {
return false;
};
self.read_display(Instant::now())
}
/// The display read where a poll is due or Auto's grace is over (`poll_display`).
fn read_display(&mut self, now: Instant) -> bool {
let (surface, adapter) = (self.surface.as_deref(), &self.adapter);
let read = || DisplayReading::read(surface, adapter);
let Some(now) = watch.poll(self.reading, std::time::Instant::now(), read) else {
return false;
};
let offer = self.surface_offer();
observe(&mut self.reading, &mut self.output_caps, now, offer)
poll_display(
self.display.as_mut(),
&mut self.reading,
&mut self.output_caps,
self.auto_off_since,
now,
|| DisplayReading::read(surface, adapter),
|| offer(surface, adapter),
)
}
}
@@ -435,7 +550,8 @@ mod tests {
paper_white: 250,
peak: 600,
});
settings.hdr_ui_brightness = 60;
// 150 nits under the reported paper white of 250: 0.6 of it.
settings.hdr_ui_nits = 150;
let mut swapchain = Swapchain::default();
let next = candidate(&settings, caps, reading, &applied);
let published = commit(&mut swapchain, &applied, next, caps, &mut None);
@@ -529,4 +645,298 @@ mod tests {
commit(&mut swapchain, &pq, next, caps, &mut rejected);
assert_eq!(swapchain.asked.len(), 4);
}
/// A window in Auto presenting HDR10 at 400/1000 nits on the layered frame, followed on
/// fake time as the game follows a renderer each frame: `display_changed`, then
/// `update_output` where it says so.
struct Auto {
settings: Graphics,
start: Instant,
watch: DisplayWatch,
reading: DisplayReading,
caps: OutputCapabilities,
applied: EffectiveOutput,
since: Option<Instant>,
swapchain: Swapchain,
/// What the system reports when it is read, and when (ms) it was.
report: DisplayReading,
reads: Vec<u64>,
}
impl Auto {
/// `polled`: the system is read every `DISPLAY_POLL` from 0, else it sends events
/// (`event`), as on Wayland.
fn new(polled: bool) -> Self {
let settings = Graphics {
hdr: HdrMode::Auto,
..Graphics::default()
};
let (caps, reading) = window(Some(Pq), Some(true));
let applied = snapshot(&settings, NO_PACK, caps, reading);
assert!(applied.encoding == Some(Pq) && applied.frame.hud_layer);
let start = Instant::now();
Self {
settings,
start,
watch: DisplayWatch {
#[cfg(target_os = "linux")]
wayland: None,
polled: polled.then_some(start),
},
reading,
caps,
applied,
since: None,
swapchain: Swapchain::default(),
report: reading,
reads: Vec::new(),
}
}
/// A frame at `ms`.
fn frame(&mut self, ms: u64) {
if self.read_display(ms) {
self.update(ms);
}
}
/// `Renderer::read_display` at `ms`.
fn read_display(&mut self, ms: u64) -> bool {
let now = self.start + Duration::from_millis(ms);
let (report, offer, reads) = (self.report, Some(self.caps.hdr), &mut self.reads);
let read = || {
reads.push(ms);
report
};
let (reading, caps) = (&mut self.reading, &mut self.caps);
poll_display(
Some(&mut self.watch),
reading,
caps,
self.since,
now,
read,
|| offer,
)
}
/// Frames from `from` to `to` ms, every 10 ms.
fn frames(&mut self, from: u64, to: u64) {
for ms in (from..=to).step_by(10) {
self.frame(ms);
}
}
/// What the system reports from `ms` on: HDR `hdr`, with no luminance unless it is
/// HDR, at 300/800 nits then.
fn reports(&mut self, hdr: Option<bool>, offer: Option<hdr::Output>) {
self.report = DisplayReading {
luminance: (hdr == Some(true)).then_some(Reported {
paper_white: 300,
peak: 800,
}),
hdr,
};
self.caps.hdr = offer;
}
/// The system's event at `ms`, with what it now `reports`.
fn event(&mut self, ms: u64, hdr: Option<bool>, offer: Option<hdr::Output>) {
self.reports(hdr, offer);
self.reading = self.report;
self.update(ms);
}
/// `update_output` at `ms`, in its order.
fn update(&mut self, ms: u64) {
let now = self.start + Duration::from_millis(ms);
if grace_over(self.since, now) {
self.read_display(ms);
}
let applied = &self.applied;
let reading = auto_grace(
now,
self.settings.hdr,
self.caps.hdr,
self.reading,
applied,
&mut self.since,
);
let next = candidate(&self.settings, self.caps, reading, applied);
self.applied = commit(&mut self.swapchain, applied, next, self.caps, &mut None);
// While HDR stays on, the frame the game asks for (`Renderer::requested_frame`) is
// this one: no new renderer.
let reading = graced(self.since, self.reading, &self.applied);
let frame = output::resolve_frame(
&self.settings,
NO_PACK,
self.caps,
reading,
wgpu::TextureFormat::Bgra8UnormSrgb,
);
assert_eq!(
frame == self.applied.frame,
self.applied.hdr_on(),
"{ms} ms"
);
}
fn presented(&self) -> Vec<Option<hdr::Output>> {
self.swapchain.asked.iter().map(|a| a.0).collect()
}
}
/// The display's report flickers off and back, false or unknown: HDR stays on, nothing
/// is presented again, and the shader `Frame`, what the settings show as active and the
/// nits stay as they were applied, even while the report says nothing of them.
#[test]
fn a_report_that_flickers_off_and_back_swaps_nothing() {
for lost in [Some(false), None] {
let mut auto = Auto::new(false);
let before = auto.applied;
auto.event(0, Some(true), Some(Pq));
auto.frames(0, 90);
let fresh = auto.applied;
assert_eq!(fresh.nits, [300.0, 800.0, 0.0], "a positive report applies");
auto.event(100, lost, Some(Pq));
auto.frames(100, 390);
assert_eq!(auto.applied, fresh, "{lost:?}");
assert!(auto.applied.requests_hdr(HdrMode::Auto) && auto.applied.display().hdr);
auto.event(400, Some(true), Some(Pq));
auto.frames(400, 1500);
assert_eq!((auto.applied, auto.since), (fresh, None), "{lost:?}");
assert!(auto.presented().is_empty(), "{lost:?}");
assert_eq!(auto.applied.frame, before.frame);
}
}
/// A loss held through 600 ms turns HDR off once, at 500 ms, and on an event-driven
/// display with no event then. The same loss reported again doesn't restart the timer.
#[test]
fn a_loss_held_turns_hdr_off_once_at_the_deadline() {
for lost in [Some(false), None] {
let mut auto = Auto::new(false);
auto.event(0, lost, Some(Pq));
auto.frames(0, 190);
auto.event(200, lost, Some(Pq));
auto.frames(200, 490);
assert_eq!(auto.applied.encoding, Some(Pq), "{lost:?}");
auto.frame(500);
assert_eq!(auto.applied.encoding, None, "{lost:?}");
auto.frames(500, 3000);
assert_eq!(auto.presented(), [None], "{lost:?}");
assert_eq!(auto.since, None);
}
}
/// Where the system is polled, a false read by the poll that starts the timer is read
/// again at the deadline, not two seconds after, before it counts: the display came back
/// meanwhile, so HDR stays; where it didn't, HDR turns off then. The two-second cadence
/// stays otherwise.
#[test]
fn a_polled_false_is_read_again_at_the_deadline() {
for back in [true, false] {
let mut auto = Auto::new(true);
auto.frames(0, 1990);
auto.reports(Some(false), Some(Pq));
auto.frame(2000);
assert_eq!(
(auto.applied.encoding, auto.since.is_some()),
(Some(Pq), true)
);
if back {
auto.reports(Some(true), Some(Pq));
}
auto.frames(2010, 2490);
assert_eq!(auto.applied.encoding, Some(Pq), "never before the deadline");
auto.frames(2500, 4990);
assert_eq!(auto.reads, [0, 2000, 2500, 4500], "back {back}");
let after = if back { vec![] } else { vec![None] };
assert_eq!(auto.presented(), after, "back {back}");
}
}
/// A settings change at the deadline, before the frame's poll (a slider, F11), reads the
/// display before the grace ends, and the frame after reads it no more: the display came
/// back meanwhile, so HDR stays; where it didn't, HDR turns off then, once.
#[test]
fn a_settings_change_at_the_deadline_reads_the_display_first() {
for back in [true, false] {
let mut auto = Auto::new(true);
auto.frames(0, 1990);
auto.reports(Some(false), Some(Pq));
auto.frame(2000);
if back {
auto.reports(Some(true), Some(Pq));
}
auto.frames(2010, 2490);
auto.update(2500);
assert_eq!(auto.applied.hdr_on(), back, "back {back}");
auto.frames(2500, 4990);
assert_eq!(auto.reads, [0, 2000, 2500, 4500], "back {back}");
let after = if back { vec![] } else { vec![None] };
assert_eq!(auto.presented(), after, "back {back}");
}
}
/// A positive report clears the timer: a later loss has its own 500 ms.
#[test]
fn a_positive_report_clears_the_timer() {
let mut auto = Auto::new(false);
auto.event(100, Some(false), Some(Pq));
auto.event(300, Some(true), Some(Pq));
auto.event(700, Some(false), Some(Pq));
auto.frames(700, 1190);
assert_eq!(auto.applied.encoding, Some(Pq));
auto.frame(1200);
assert_eq!(auto.presented(), [None]);
}
/// Off acts at once, and On or Off clear the timer: Auto again starts it anew.
#[test]
fn off_and_on_take_no_grace() {
let mut auto = Auto::new(false);
auto.event(100, Some(false), Some(Pq));
auto.settings.hdr = HdrMode::Off;
auto.update(200);
assert_eq!((auto.presented(), auto.since), (vec![None], None));
let mut auto = Auto::new(false);
auto.event(100, Some(false), Some(Pq));
auto.settings.hdr = HdrMode::On;
auto.update(200);
assert_eq!((auto.applied.encoding, auto.since), (Some(Pq), None));
auto.settings.hdr = HdrMode::Auto;
auto.update(300);
auto.frames(300, 790);
assert_eq!(auto.applied.encoding, Some(Pq));
auto.frame(800);
assert_eq!(auto.presented(), [None]);
}
/// The surface no longer offering the encoding presented ends the grace at once: SDR
/// without a report, the other encoding with one.
#[test]
fn an_encoding_no_longer_offered_is_not_kept() {
for offer in [None, Some(ScRgb)] {
let mut auto = Auto::new(false);
auto.event(100, Some(false), Some(Pq));
auto.event(200, Some(false), offer);
assert_eq!((auto.presented(), auto.since), (vec![None], None));
let mut auto = Auto::new(false);
auto.event(100, None, offer);
assert_eq!(auto.presented(), [None], "{offer:?}");
}
let mut auto = Auto::new(false);
auto.event(100, Some(false), Some(Pq));
auto.event(200, Some(true), Some(ScRgb));
assert_eq!(auto.presented(), [Some(ScRgb)]);
}
/// Two windows' timers run apart.
#[test]
fn two_renderers_keep_their_own_timer() {
let (mut a, mut b) = (Auto::new(false), Auto::new(false));
b.start = a.start;
a.event(100, Some(false), Some(Pq));
b.event(300, Some(false), Some(Pq));
a.event(400, Some(true), Some(Pq));
for ms in (300..=900).step_by(10) {
a.frame(ms);
b.frame(ms);
assert_eq!(b.applied.hdr_on(), ms < 800, "{ms} ms");
}
assert_eq!((a.presented(), b.presented()), (vec![], vec![None]));
}
}
@@ -55,6 +55,10 @@ mod sky_disc_blend_tests;
#[cfg(test)]
mod undrawn_terrain_tests;
mod image_formats;
use image_formats::{image_format, storage_formats_of};
#[cfg(test)]
mod full_outputs_tests;
#[cfg(test)]
mod shadow_faces_tests;
@@ -1826,98 +1830,6 @@ fn bindings(
Some((entries, samplers, slots, reads, shadow_reads, images))
}
/// Makes each storage image of `module` in group 0 declare the format its layout entry binds:
/// a `layout(r11f_g11f_b10f)` image whose texture is the `RGBA16F` stand-in (`bindings`).
fn storage_formats_of(module: &mut naga::Module, entries: &[wgpu::BindGroupLayoutEntry]) {
let globals: Vec<_> = module.global_variables.iter().map(|(h, _)| h).collect();
for handle in globals {
let var = &module.global_variables[handle];
let Some(naga::ResourceBinding { group: 0, binding }) = var.binding else {
continue;
};
let naga::TypeInner::Image {
dim,
arrayed,
class: naga::ImageClass::Storage { format, access },
} = module.types[var.ty].inner
else {
continue;
};
let bound = entries.iter().find_map(|e| match e.ty {
wgpu::BindingType::StorageTexture { format, .. } if e.binding == binding => {
Some(format)
}
_ => None,
});
if format == naga::StorageFormat::Rg11b10Ufloat
&& bound == Some(wgpu::TextureFormat::Rgba16Float)
{
let ty = module.types.insert(
naga::Type {
name: module.types[var.ty].name.clone(),
inner: naga::TypeInner::Image {
dim,
arrayed,
class: naga::ImageClass::Storage {
format: naga::StorageFormat::Rgba16Float,
access,
},
},
},
naga::Span::UNDEFINED,
);
module.global_variables[handle].ty = ty;
}
}
}
/// The wgpu format of an image's format qualifier.
fn image_format(qualifier: &str) -> Option<wgpu::TextureFormat> {
use wgpu::TextureFormat as T;
Some(match qualifier {
"rgba32f" => T::Rgba32Float,
"rgba16f" => T::Rgba16Float,
"rg32f" => T::Rg32Float,
"rg16f" => T::Rg16Float,
"r11f_g11f_b10f" => T::Rg11b10Ufloat,
"r32f" => T::R32Float,
"r16f" => T::R16Float,
"rgba16" => T::Rgba16Unorm,
"rgb10_a2" => T::Rgb10a2Unorm,
"rgba8" => T::Rgba8Unorm,
"rg16" => T::Rg16Unorm,
"rg8" => T::Rg8Unorm,
"r16" => T::R16Unorm,
"r8" => T::R8Unorm,
"rgba16_snorm" => T::Rgba16Snorm,
"rgba8_snorm" => T::Rgba8Snorm,
"rg16_snorm" => T::Rg16Snorm,
"rg8_snorm" => T::Rg8Snorm,
"r16_snorm" => T::R16Snorm,
"r8_snorm" => T::R8Snorm,
"rgba32i" => T::Rgba32Sint,
"rgba16i" => T::Rgba16Sint,
"rgba8i" => T::Rgba8Sint,
"rg32i" => T::Rg32Sint,
"rg16i" => T::Rg16Sint,
"rg8i" => T::Rg8Sint,
"r32i" => T::R32Sint,
"r16i" => T::R16Sint,
"r8i" => T::R8Sint,
"rgba32ui" => T::Rgba32Uint,
"rgba16ui" => T::Rgba16Uint,
"rgb10_a2ui" => T::Rgb10a2Uint,
"rgba8ui" => T::Rgba8Uint,
"rg32ui" => T::Rg32Uint,
"rg16ui" => T::Rg16Uint,
"rg8ui" => T::Rg8Uint,
"r32ui" => T::R32Uint,
"r16ui" => T::R16Uint,
"r8ui" => T::R8Uint,
_ => return None,
})
}
/// The value slot of the pack's custom uniform `name` for a program declaring it as GLSL type
/// `ty`: after the frame's uniforms, in the order of `custom` (each with the type
/// `shaders.properties` gives it). A `float`, `int`, `vec2`, `vec3` or `vec4` custom uniform
@@ -2805,10 +2717,21 @@ impl PackPost {
.shadow
.zip(shadow_geometry.as_ref())
.and_then(|(p, g)| build(p, Some(g)));
// A GPU that refuses translucent terrain's program with the boats' water patches gets it
// without them (`Post::shadow_water_plain`), rather than no translucent shadows at all.
let mut plain = post.shadow_water_plain;
let shadow_water = post
.shadow_water
.zip(shadow_geometry.as_ref())
.and_then(|(p, g)| build(p, Some(g)));
let unpatched = shadow_water.is_none().then(|| plain.take()).flatten();
let unpatched_name = unpatched.as_ref().map(|p| p.name.clone());
let shadow_water = shadow_water.or_else(|| {
unpatched
.zip(shadow_geometry.as_ref())
.and_then(|(p, g)| build(p, Some(g)))
});
let water_unpatched = unpatched_name.filter(|_| shadow_water.is_some());
let (hides_sky, hides_celestial) = (post.sky.is_some(), post.sky_textured.is_some());
let sky = (post.sky.is_some() || post.sky_textured.is_some()).then(|| {
let geometry = PackSky::new(device);
@@ -3365,6 +3288,17 @@ impl PackPost {
let white = [255u8; 4];
let black = [0u8; 4];
let flat_normal = [128u8, 128, 255, 255];
// Said as what it is, not as a program skipped.
if let Some(name) = water_unpatched {
let skipped = format!("{name}: skipped: ");
if let Some(n) = notes.iter_mut().rev().find(|n| n.starts_with(&skipped)) {
*n = format!(
"{name}: translucent terrain without the boats' water patches, as the GPU \
refused it with them ({})",
&n[skipped.len()..]
);
}
}
let jobs = (pack.jobs.as_ref())
.map(std::cell::RefCell::take)
.unwrap_or_default();
@@ -602,7 +602,7 @@ pub(super) fn fixture_frame(hud: &HudBatch) -> Frame<'_> {
outline: Vec::new(),
outline_after_terrain: false,
outline_high_contrast: false,
pvp: Default::default(),
look: Default::default(),
debug_lines: &[],
debug_lines_on_top: &[],
debug_crosshair: &[],
@@ -440,7 +440,7 @@ fn fixture_frame(hud: &HudBatch) -> Frame<'_> {
outline: Vec::new(),
outline_after_terrain: false,
outline_high_contrast: false,
pvp: Default::default(),
look: Default::default(),
debug_lines: &[],
debug_lines_on_top: &[],
debug_crosshair: &[],
@@ -0,0 +1,109 @@
//! A shadow program writing as many vertex outputs as the GPU takes leaves no room for the boats'
//! water patches (`GbuffersInputs::water_patches`, one output more): translucent terrain gets
//! the program without them, not no shadow program. The programs here are original.
use super::destination_tests::Fixture;
use super::*;
use std::io::{Cursor, Write};
fn pack(outputs: u32) -> Post {
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let mut add = |name: &str, source: &str| {
zip.start_file(
format!("shaders/{name}"),
zip::write::SimpleFileOptions::default(),
)
.unwrap();
zip.write_all(source.as_bytes()).unwrap();
};
let declared: String = (0..outputs)
.map(|i| format!("varying vec4 v{i};\n"))
.collect();
let written: String = (0..outputs).map(|i| format!("v{i}=vec4({i}.0);")).collect();
let read: String = (0..outputs).map(|i| format!("+v{i}")).collect();
add(
"shadow.vsh",
&format!(
"#version 120\n{declared}void main() {{ gl_Position=vec4(gl_Vertex.xy,0.5,1.0); {written} }}\n"
),
);
add(
"shadow.fsh",
&format!("#version 120\n{declared}void main() {{ gl_FragData[0]=vec4(0.0){read}; }}\n"),
);
add(
"final.vsh",
"#version 120\nvarying vec2 uv;\nvoid main() { uv=gl_MultiTexCoord0.xy; gl_Position=ftransform(); }\n",
);
add(
"final.fsh",
"#version 120\nconst int shadowMapResolution=16;\nuniform sampler2D shadowcolor0;\n\
varying vec2 uv;\nvoid main() { gl_FragColor=texture2D(shadowcolor0,uv); }\n",
);
let pack = horizon_shaders::Pack::from_zip_bytes(
"original-full-outputs",
zip.finish().unwrap().into_inner(),
)
.unwrap();
let mut macros = horizon_shaders::Macros::standard();
let (properties, _) = horizon_shaders::read_properties(&pack, &mut macros);
let inputs = horizon_shaders::GameInputs {
terrain: Some(horizon_shaders::GbuffersInputs {
item_id_declared: false,
far_depth: false,
paged_maps: false,
water_mask: false,
water_patches: false,
declarations: "layout(location=0)in vec3 fixture_position;\n".into(),
body: "hz_Vertex=vec4(fixture_position,1.0);hz_Color=vec4(1.0);\
hz_MultiTexCoord0=vec4(0.0);hz_MultiTexCoord1=vec4(240.0);\
hz_MultiTexCoord2=vec4(240.0);hz_Normal=vec3(0.0,0.0,1.0);\n"
.into(),
attributes: vec![],
after: String::new(),
alpha_test: None,
}),
..Default::default()
};
horizon_shaders::load_post(&pack, "", &properties, &macros, &inputs)
}
#[test]
fn a_shadow_program_with_every_output_drops_the_boats_patches_not_its_water() {
let Some(gpu) = Fixture::new() else { return };
const ATTRS: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0=>Float32x3];
let layouts = [Some(wgpu::VertexBufferLayout {
array_stride: 12,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &ATTRS,
})];
let geometry = || {
Some(Geometry {
material_maps: false,
buffers: &layouts,
table: None,
depth_write: true,
atlas: None,
ids: None,
cull: true,
})
};
let outputs = gpu.device.limits().max_inter_stage_shader_variables;
let post = gpu.build_with_terrain(pack(outputs), geometry());
assert!(post.has_shadow(), "{:?}", post.notes);
assert!(post.shadow_water.is_some(), "{:?}", post.notes);
assert!(
post.notes
.iter()
.any(|n| n.contains("without the boats' water patches")),
"{:?}",
post.notes
);
// With room for them, the patches stay and nothing is said.
let post = gpu.build_with_terrain(pack(outputs - 1), geometry());
assert!(post.shadow_water.is_some(), "{:?}", post.notes);
assert!(
!post.notes.iter().any(|n| n.contains("water patches")),
"{:?}",
post.notes
);
}
@@ -0,0 +1,97 @@
//! Image formats: a storage image's format qualifier as wgpu's, and the stand-in a module's
//! storage images declare.
/// Makes each storage image of `module` in group 0 declare the format its layout entry binds:
/// a `layout(r11f_g11f_b10f)` image whose texture is the `RGBA16F` stand-in (`bindings`).
pub(super) fn storage_formats_of(
module: &mut naga::Module,
entries: &[wgpu::BindGroupLayoutEntry],
) {
let globals: Vec<_> = module.global_variables.iter().map(|(h, _)| h).collect();
for handle in globals {
let var = &module.global_variables[handle];
let Some(naga::ResourceBinding { group: 0, binding }) = var.binding else {
continue;
};
let naga::TypeInner::Image {
dim,
arrayed,
class: naga::ImageClass::Storage { format, access },
} = module.types[var.ty].inner
else {
continue;
};
let bound = entries.iter().find_map(|e| match e.ty {
wgpu::BindingType::StorageTexture { format, .. } if e.binding == binding => {
Some(format)
}
_ => None,
});
if format == naga::StorageFormat::Rg11b10Ufloat
&& bound == Some(wgpu::TextureFormat::Rgba16Float)
{
let ty = module.types.insert(
naga::Type {
name: module.types[var.ty].name.clone(),
inner: naga::TypeInner::Image {
dim,
arrayed,
class: naga::ImageClass::Storage {
format: naga::StorageFormat::Rgba16Float,
access,
},
},
},
naga::Span::UNDEFINED,
);
module.global_variables[handle].ty = ty;
}
}
}
/// The wgpu format of an image's format qualifier.
pub(super) fn image_format(qualifier: &str) -> Option<wgpu::TextureFormat> {
use wgpu::TextureFormat as T;
Some(match qualifier {
"rgba32f" => T::Rgba32Float,
"rgba16f" => T::Rgba16Float,
"rg32f" => T::Rg32Float,
"rg16f" => T::Rg16Float,
"r11f_g11f_b10f" => T::Rg11b10Ufloat,
"r32f" => T::R32Float,
"r16f" => T::R16Float,
"rgba16" => T::Rgba16Unorm,
"rgb10_a2" => T::Rgb10a2Unorm,
"rgba8" => T::Rgba8Unorm,
"rg16" => T::Rg16Unorm,
"rg8" => T::Rg8Unorm,
"r16" => T::R16Unorm,
"r8" => T::R8Unorm,
"rgba16_snorm" => T::Rgba16Snorm,
"rgba8_snorm" => T::Rgba8Snorm,
"rg16_snorm" => T::Rg16Snorm,
"rg8_snorm" => T::Rg8Snorm,
"r16_snorm" => T::R16Snorm,
"r8_snorm" => T::R8Snorm,
"rgba32i" => T::Rgba32Sint,
"rgba16i" => T::Rgba16Sint,
"rgba8i" => T::Rgba8Sint,
"rg32i" => T::Rg32Sint,
"rg16i" => T::Rg16Sint,
"rg8i" => T::Rg8Sint,
"r32i" => T::R32Sint,
"r16i" => T::R16Sint,
"r8i" => T::R8Sint,
"rgba32ui" => T::Rgba32Uint,
"rgba16ui" => T::Rgba16Uint,
"rgb10_a2ui" => T::Rgb10a2Uint,
"rgba8ui" => T::Rgba8Uint,
"rg32ui" => T::Rg32Uint,
"rg16ui" => T::Rg16Uint,
"rg8ui" => T::Rg8Uint,
"r32ui" => T::R32Uint,
"r16ui" => T::R16Uint,
"r8ui" => T::R8Uint,
_ => return None,
})
}
@@ -409,7 +409,7 @@ fn frame(hud: &HudBatch) -> Frame<'_> {
outline: Vec::new(),
outline_after_terrain: false,
outline_high_contrast: false,
pvp: Default::default(),
look: Default::default(),
debug_lines: &[],
debug_lines_on_top: &[],
debug_crosshair: &[],
@@ -2664,7 +2664,7 @@ pub(super) mod initial_history_tests {
outline: Vec::new(),
outline_after_terrain: false,
outline_high_contrast: false,
pvp: Default::default(),
look: Default::default(),
debug_lines: &[],
debug_lines_on_top: &[],
debug_crosshair: &[],
+98 -32
View File
@@ -52,6 +52,23 @@ pub fn peak_step_of(nits: f32) -> f32 {
};
step.clamp(0.0, PEAK_STEPS as f32)
}
/// The steps of Paper White's and UI Brightness's scale: Auto (0), then 10 to 480 nits by 10.
pub const NITS_STEPS: u32 = 48;
/// Paper White's or UI Brightness's setting at slider position `v`, 0 to 1 ([`NITS_STEPS`]).
pub fn nits_at(v: f32) -> u32 {
(v * NITS_STEPS as f32).round() as u32 * 10
}
/// Where a Paper White or UI Brightness setting sits on its slider, 0 to 1.
pub fn nits_position(nits: u32) -> f32 {
nits as f32 / (NITS_STEPS * 10) as f32
}
/// A saved Paper White or UI Brightness: Auto (0), or 10 to 480 nits.
fn nits_setting(nits: u32) -> u32 {
match nits {
0 => 0,
n => n.clamp(10, 480),
}
}
/// Simple-level presets. `Custom` leaves every option as the player set it.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
@@ -504,8 +521,6 @@ pub fn reduced_far_detail_suggested() -> bool {
REDUCED_SUGGESTED.load(std::sync::atomic::Ordering::Relaxed)
}
/// The name the options screen waits on to bind `Graphics::hdr_key`.
pub const HDR_KEY: &str = "horizon.hdrSettings";
/// The Screenshot Quality choices, times the game's resolution.
pub const SCREENSHOT_QUALITIES: [u32; 4] = [1, 2, 4, 8];
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
@@ -815,14 +830,12 @@ pub struct Graphics {
/// HDR black level: the display's darkest black, in hundredths of a nit; 0, the default,
/// lifts nothing. Shadows are lifted onto it, and the HDR metadata says it.
pub hdr_black_level: u32,
/// HDR brightness of the HUD and menus, in percent of paper white (25..=100); the
/// first-person hand stays at the world's.
pub hdr_ui_brightness: u32,
/// HDR brightness of the HUD and menus, in nits; 0 is Auto: at paper white
/// ([`Graphics::ui_nits`]). The first-person hand stays at the world's.
pub hdr_ui_nits: u32,
/// HDR Lighting: Horizon's own HDR look, light sources, the sun and sunlight above paper
/// white. Off, HDR output shows the same picture as SDR, for shader packs or the vanilla look.
pub hdr_lighting: bool,
/// A vanilla key name (`HDR_KEY`): opens HDR's settings from the game. Unbound by default.
pub hdr_key: String,
/// The graphics driver whose last start ended before HDR output showed a frame (the
/// marker found, [`HDR_MARKER`]): a window on it stays SDR whatever the mode, until the
/// mode is changed or the window starts on another driver ([`Graphics::hdr_held`]).
@@ -900,9 +913,8 @@ impl Default for Graphics {
hdr_paper_white: 0,
hdr_peak: 0,
hdr_black_level: 0,
hdr_ui_brightness: 100,
hdr_ui_nits: 0,
hdr_lighting: true,
hdr_key: "key.keyboard.unknown".into(),
hdr_failed_start: None,
battery: false,
shadow_cache: false,
@@ -975,6 +987,8 @@ impl Graphics {
}
pub fn parse(text: &str) -> Self {
let mut g = Self::default();
// UI Brightness: `hdrUiNits`, else an older file's share of Paper White.
let (mut ui_nits, mut ui_share) = (None, None);
for line in text.lines() {
let Some((key, value)) = line.split_once(':') else {
continue;
@@ -1064,12 +1078,9 @@ impl Graphics {
"frameGeneration" => g.frame_generation = FrameGeneration::parse(value),
"hdr" => g.hdr = HdrMode::parse(value, g.hdr),
"hdrLighting" => g.hdr_lighting = flag(g.hdr_lighting),
"hdrPaperWhite" => {
g.hdr_paper_white = match number(g.hdr_paper_white) {
0 => 0,
n => n.clamp(10, 480),
}
}
"hdrPaperWhite" => g.hdr_paper_white = nits_setting(number(g.hdr_paper_white)),
"hdrUiNits" => ui_nits = value.parse().ok().map(nits_setting),
"hdrUiBrightness" => ui_share = value.parse::<u32>().ok(),
"hdrPeak" => {
g.hdr_peak = match number(g.hdr_peak) {
0 => 0,
@@ -1077,10 +1088,6 @@ impl Graphics {
}
}
"hdrBlackLevel" => g.hdr_black_level = number(g.hdr_black_level).min(100),
"hdrUiBrightness" => {
g.hdr_ui_brightness = number(g.hdr_ui_brightness).clamp(25, 100)
}
"hdrKey" if value.starts_with("key.") => g.hdr_key = value.to_owned(),
"hdrFailedStart" => g.hdr_failed_start = Some(value.to_owned()),
"batteryMode" => g.battery = flag(g.battery),
"shadowCache" => g.shadow_cache = flag(g.shadow_cache),
@@ -1098,11 +1105,20 @@ impl Graphics {
_ => {}
}
}
// The share was 25 to 100% of Paper White. 100% is Auto, and so is any share of an
// Auto Paper White, whose nits the file doesn't hold; another share of a Paper White set
// by hand is its nits, to the nearest step.
g.hdr_ui_nits = ui_nits.unwrap_or(match (ui_share, g.hdr_paper_white) {
(Some(share), paper) if share < 100 && paper != 0 => {
((paper * share.max(25) + 500) / 1000).max(1) * 10
}
_ => 0,
});
g
}
pub fn serialize(&self) -> String {
let mut text = format!(
"preset:{}\nlodDistance:{}\nfarDetail:{}\ncaveCulling:{}\nentityCulling:{}\nentityCullingPrecheck:{}\nparticleCulling:{}\nleafCulling:{}\nopaqueIce:{}\nblockEntityCacheShulkerBoxes:{}\nblockEntityCacheSigns:{}\nblockEntityCacheDecoratedPots:{}\nblockEntityCacheBells:{}\nblockEntityCacheChests:{}\nblockEntityCacheBanners:{}\nblockEntityCacheCopperGolemStatues:{}\nchunkUpdates:{}\nuploadBudgetKiB:{}\ntextureBudgetMiB:{}\nsky:{}\nfog:{}\nweather:{}\nanimatedTextures:{}\nditherCutoutMips:{}\npresentMode:{}\nframePacing:{}\nworkers:{}\noverlay:{}\nresolutionScale:{}\nfxaa:{}\ntaa:{}\nupscaling:{}\nupscalingSharpness:{}\nscreenshotQuality:{}\nframeGeneration:{}\nhdr:{}\nhdrPaperWhite:{}\nhdrPeak:{}\nhdrBlackLevel:{}\nhdrUiBrightness:{}\nhdrLighting:{}\nhdrKey:{}\nbatteryMode:{}\nshadowCache:{}\ngraphicsApi:{}\npickedShaderPack:{}\nbatchTranslucent:{}\nhdrSmoothGradients:{}\ngpuCulling:{}\n",
"preset:{}\nlodDistance:{}\nfarDetail:{}\ncaveCulling:{}\nentityCulling:{}\nentityCullingPrecheck:{}\nparticleCulling:{}\nleafCulling:{}\nopaqueIce:{}\nblockEntityCacheShulkerBoxes:{}\nblockEntityCacheSigns:{}\nblockEntityCacheDecoratedPots:{}\nblockEntityCacheBells:{}\nblockEntityCacheChests:{}\nblockEntityCacheBanners:{}\nblockEntityCacheCopperGolemStatues:{}\nchunkUpdates:{}\nuploadBudgetKiB:{}\ntextureBudgetMiB:{}\nsky:{}\nfog:{}\nweather:{}\nanimatedTextures:{}\nditherCutoutMips:{}\npresentMode:{}\nframePacing:{}\nworkers:{}\noverlay:{}\nresolutionScale:{}\nfxaa:{}\ntaa:{}\nupscaling:{}\nupscalingSharpness:{}\nscreenshotQuality:{}\nframeGeneration:{}\nhdr:{}\nhdrPaperWhite:{}\nhdrPeak:{}\nhdrBlackLevel:{}\nhdrUiNits:{}\nhdrLighting:{}\nbatteryMode:{}\nshadowCache:{}\ngraphicsApi:{}\npickedShaderPack:{}\nbatchTranslucent:{}\nhdrSmoothGradients:{}\ngpuCulling:{}\n",
self.preset.name(),
self.lod_distance,
self.far_detail.name(),
@@ -1149,9 +1165,8 @@ impl Graphics {
self.hdr_paper_white,
self.hdr_peak,
self.hdr_black_level,
self.hdr_ui_brightness,
self.hdr_ui_nits,
bool_text(self.hdr_lighting),
self.hdr_key,
bool_text(self.battery),
bool_text(self.shadow_cache),
self.api.name(),
@@ -1166,14 +1181,6 @@ impl Graphics {
text += &self.blue_map.serialize();
text
}
/// Binds Horizon's graphics key `name` (`HDR_KEY`); false for another name.
pub fn set_key(&mut self, name: &str, value: String) -> bool {
match name {
HDR_KEY => self.hdr_key = value,
_ => return false,
}
true
}
pub fn save(&self, dir: &Path) {
let _ = fs::write(dir.join(FILE), self.serialize());
}
@@ -1185,6 +1192,13 @@ impl Graphics {
n => n,
}
}
/// The HUD and menus' brightness, in nits: the player's, or on Auto paper white's.
pub fn ui_nits(&self, reported: Option<Reported>) -> u32 {
match self.hdr_ui_nits {
0 => self.paper_white_nits(reported),
n => n,
}
}
/// The peak the world's highlights reach, in nits: the player's, or on Auto
/// what the system `reported`, else 1000.
pub fn peak_nits(&self, reported: Option<Reported>) -> u32 {
@@ -1265,6 +1279,55 @@ pub fn apply_preset(preset: Preset, s: &mut crate::settings::Settings) {
mod tests {
use super::*;
/// UI Brightness is saved in nits, 0 for Auto. An older file's share of Paper White reads
/// as Auto at 100% or with Paper White on Auto, else as that share of it to the nearest step.
#[test]
fn ui_brightness_round_trips_in_nits_and_reads_older_shares() {
for (file, nits) in [
("", 0),
("hdrUiNits:0\n", 0),
("hdrUiNits:160\n", 160),
("hdrUiNits:5\n", 10),
("hdrUiNits:9000\n", 480),
("hdrUiNits:x\n", 0),
// Older files: 100% is Auto,
("hdrPaperWhite:200\nhdrUiBrightness:100\n", 0),
// any share of Paper White's Auto too,
("hdrUiBrightness:60\n", 0),
("hdrPaperWhite:0\nhdrUiBrightness:25\n", 0),
// and another share of a Paper White set by hand is its nits.
("hdrPaperWhite:200\nhdrUiBrightness:60\n", 120),
("hdrUiBrightness:50\nhdrPaperWhite:203\n", 100),
("hdrPaperWhite:250\nhdrUiBrightness:75\n", 190),
("hdrPaperWhite:10\nhdrUiBrightness:25\n", 10),
("hdrPaperWhite:480\nhdrUiBrightness:10\n", 120),
// The new key wins over the old one.
(
"hdrPaperWhite:200\nhdrUiBrightness:60\nhdrUiNits:300\n",
300,
),
("hdrUiNits:0\nhdrPaperWhite:200\nhdrUiBrightness:60\n", 0),
] {
let g = Graphics::parse(file);
assert_eq!(g.hdr_ui_nits, nits, "{file}");
let saved = g.serialize();
assert!(!saved.contains("hdrUiBrightness"), "{file}");
assert!(saved.contains(&format!("\nhdrUiNits:{nits}\n")), "{file}");
assert_eq!(Graphics::parse(&saved), g, "{file}");
}
let mut g = Graphics::default();
assert_eq!(g.ui_nits(None), 203);
let reported = Some(Reported {
paper_white: 300,
peak: 1000,
});
assert_eq!(g.ui_nits(reported), 300);
g.hdr_paper_white = 120;
assert_eq!(g.ui_nits(reported), 120);
g.hdr_ui_nits = 400;
assert_eq!((g.ui_nits(None), g.ui_nits(reported)), (400, 400));
}
#[test]
fn hdr_modes_keep_boolean_files_and_round_trip_auto() {
assert_eq!(Graphics::parse("").hdr, HdrMode::Off);
@@ -1430,9 +1493,8 @@ mod tests {
hdr_paper_white: 240,
hdr_peak: 1400,
hdr_black_level: 12,
hdr_ui_brightness: 60,
hdr_ui_nits: 160,
hdr_lighting: false,
hdr_key: "key.keyboard.h".into(),
hdr_failed_start: Some(
"Vulkan 10de:2684 NVIDIA GeForce RTX 4090; NVIDIA; 581.57".into(),
),
@@ -1574,6 +1636,10 @@ mod tests {
Graphics::parse("shaderPack:Pack.zip\n"),
Graphics::default()
);
// The HDR page's old shortcut key loads as nothing, and the next save drops it.
let old = Graphics::parse("hdr:true\nhdrKey:key.keyboard.h\nhdrPeak:1000\n");
assert_eq!((old.hdr, old.hdr_peak), (HdrMode::On, 1000));
assert!(!old.serialize().contains("hdrKey"));
assert_eq!(
Graphics::parse(
"workers:abc\nsky:maybe\ncullLeaves:true\nentityCulling:maybe\nleafCulling:maybe\n"
+28 -59
View File
@@ -14,7 +14,7 @@ use crate::{
lang,
options_ui::{Event, Page as OptionsPage},
settings::{Opt, Settings},
settings_shell::{self as shell, Binding, Group, Output, Row, Screen, Sheet, Value},
settings_shell::{self as shell, Group, Output, Row, Screen, Sheet, Value},
text::Text,
};
@@ -106,8 +106,6 @@ pub enum H {
FrameGeneration,
Hdr,
HdrBlackLevel,
/// Binds `Graphics::hdr_key`.
HdrKey,
/// Starts HDR calibration (`crate::hdr_calibration`).
HdrCalibrate,
HdrPeak,
@@ -614,7 +612,7 @@ pub fn rows() -> Vec<(Page, Vec<(&'static str, Vec<Setting>)>)> {
h(
H::HdrUiBrightness,
None,
"How bright the HUD and menus are, against paper white.",
"How bright the HUD and menus are. Auto: at paper white.",
),
h(
H::HdrLighting,
@@ -623,14 +621,6 @@ pub fn rows() -> Vec<(Page, Vec<(&'static str, Vec<Setting>)>)> {
),
],
),
(
"Shortcut",
vec![h(
H::HdrKey,
None,
"A key that opens this screen from the game. Not bound at first.",
)],
),
],
),
];
@@ -819,14 +809,14 @@ impl H {
H::ResolutionScale => Some((g.resolution_scale as f32 - 50.0) / 150.0),
H::UpscalingSharpness => Some(g.upscaling_sharpness as f32 / 100.0),
// Auto, then 10..480 by 10.
H::HdrPaperWhite => Some(g.hdr_paper_white as f32 / 480.0),
H::HdrPaperWhite => Some(crate::graphics::nits_position(g.hdr_paper_white)),
H::HdrUiBrightness => Some(crate::graphics::nits_position(g.hdr_ui_nits)),
// Auto, then the peak's scale.
H::HdrPeak => Some(match g.hdr_peak {
0 => 0.0,
n => (1.0 + crate::graphics::peak_step_of(n as f32)) / PEAK_POSITIONS as f32,
}),
H::HdrBlackLevel => Some(g.hdr_black_level as f32 / 100.0),
H::HdrUiBrightness => Some((g.hdr_ui_brightness as f32 - 25.0) / 75.0),
_ => None,
}
}
@@ -838,13 +828,26 @@ impl H {
H::Workers => Some(max_workers()),
H::ResolutionScale => Some(30),
H::UpscalingSharpness => Some(20),
H::HdrPaperWhite => Some(48),
H::HdrPaperWhite | H::HdrUiBrightness => Some(crate::graphics::NITS_STEPS),
H::HdrPeak => Some(PEAK_POSITIONS),
H::HdrBlackLevel => Some(100),
H::HdrUiBrightness => Some(15),
_ => None,
}
}
/// "Paper White: Auto (203)" or "Paper White: 160": a setting in nits, 0 for Auto, which
/// shows the nits Auto gives.
fn nits_label(self, s: &Settings, out: &EffectiveOutput) -> Text {
let (g, r) = (&s.horizon, out.reported);
let (caption, set, auto) = match self {
H::HdrPaperWhite => ("Paper White", g.hdr_paper_white, g.paper_white_nits(r)),
H::HdrPeak => ("Peak Brightness", g.hdr_peak, g.peak_nits(r)),
_ => ("UI Brightness", g.hdr_ui_nits, g.ui_nits(r)),
};
match set {
0 => labelled(caption, lang::tr("Auto (%s)", &[&auto])),
n => labelled(caption, n),
}
}
fn label(self, s: &Settings, out: &EffectiveOutput) -> Text {
let g = &s.horizon;
match self {
@@ -945,26 +948,9 @@ impl H {
Setup::Automatic => "Calibrate Anyway...",
Setup::Changed => "Use Automatic Setup",
})),
H::HdrPaperWhite => match g.hdr_paper_white {
0 => labelled(
"Paper White",
lang::tr("Auto (%s)", &[&g.paper_white_nits(out.reported)]),
),
n => labelled("Paper White", n.to_string()),
},
H::HdrPeak => match g.hdr_peak {
0 => labelled(
"Peak Brightness",
lang::tr("Auto (%s)", &[&g.peak_nits(out.reported)]),
),
n => labelled("Peak Brightness", n.to_string()),
},
H::HdrPaperWhite | H::HdrPeak | H::HdrUiBrightness => self.nits_label(s, out),
H::HdrBlackLevel => labelled("Black Level", format!("{:.2}", g.black_level_nits())),
H::HdrUiBrightness => labelled_percent("UI Brightness", g.hdr_ui_brightness),
H::HdrLighting => labelled("HDR Lighting", on_off(g.hdr_lighting)),
H::HdrKey => {
Text::translate("Shortcut: %s", &[crate::keybinds::display_name(&g.hdr_key)])
}
H::BatteryMode => labelled("Battery Mode", on_off(g.battery)),
H::ShadowCache => labelled("Shadow Cache", on_off(g.shadow_cache)),
// A press across the row, as Calibrate...: the list it opens shows the pick.
@@ -1005,7 +991,8 @@ impl H {
H::Workers => g.workers = (v * max_workers() as f32).round() as u32,
H::ResolutionScale => g.resolution_scale = (50.0 + (v * 30.0).round() * 5.0) as u32,
H::UpscalingSharpness => g.upscaling_sharpness = (v * 20.0).round() as u32 * 5,
H::HdrPaperWhite => g.hdr_paper_white = (v * 48.0).round() as u32 * 10,
H::HdrPaperWhite => g.hdr_paper_white = crate::graphics::nits_at(v),
H::HdrUiBrightness => g.hdr_ui_nits = crate::graphics::nits_at(v),
H::HdrPeak => {
g.hdr_peak = match (v * PEAK_POSITIONS as f32).round() as u32 {
0 => 0,
@@ -1013,7 +1000,6 @@ impl H {
}
}
H::HdrBlackLevel => g.hdr_black_level = (v * 100.0).round() as u32,
H::HdrUiBrightness => g.hdr_ui_brightness = (25.0 + (v * 15.0).round() * 5.0) as u32,
_ => {}
}
}
@@ -1189,10 +1175,7 @@ impl Item {
fn steps(self, s: &Settings, out: &EffectiveOutput) -> bool {
self.switch(s, out).is_none()
&& self.slider(s).is_none()
&& !matches!(
self,
Item::Horizon(H::HdrKey | H::HdrCalibrate | H::ShaderPacks)
)
&& !matches!(self, Item::Horizon(H::HdrCalibrate | H::ShaderPacks))
}
/// On/off settings and their value.
pub fn switch(self, s: &Settings, out: &EffectiveOutput) -> Option<bool> {
@@ -1347,12 +1330,6 @@ fn shown(item: Item, s: &Settings) -> bool {
}
/// A control's label as its name and its value ("Render Distance" and "12 chunks").
fn parts(item: Item, s: &Settings, out: &EffectiveOutput) -> (String, Option<String>) {
// The shortcut's label is one translated "Shortcut: %s"; the row shows its key apart, as
// KeyBindsScreen does ("> key <" while it waits, `settings_shell::Value::Binding`).
if item == Item::Horizon(H::HdrKey) {
let key = crate::keybinds::display_name(&s.horizon.hdr_key).plain();
return (lang::get("Shortcut"), Some(key));
}
shell::name_and_value(&item.label(s, out).plain())
}
@@ -1361,13 +1338,9 @@ fn parts(item: Item, s: &Settings, out: &EffectiveOutput) -> (String, Option<Str
fn row(setting: Setting, s: &Settings, out: &EffectiveOutput) -> Row<Item> {
let item = setting.item;
let (name, value) = parts(item, s, out);
let value = match (item, item.switch(s, out), item.slider(s)) {
(Item::Horizon(H::HdrKey), ..) => Value::Binding(Binding::key(
crate::graphics::HDR_KEY,
value.unwrap_or_default(),
)),
(_, Some(on), _) => Value::Switch(on),
(_, None, Some(position)) => Value::Slider {
let value = match (item.switch(s, out), item.slider(s)) {
(Some(on), _) => Value::Switch(on),
(None, Some(position)) => Value::Slider {
position,
steps: item.slider_steps(),
text: value.unwrap_or_default(),
@@ -1583,7 +1556,7 @@ mod tests {
use super::*;
use crate::{
graphics_style as style,
settings_shell::{Capture, Columns, Hosted, Shell, Target},
settings_shell::{Columns, Hosted, Shell, Target},
};
use winit::keyboard::KeyCode as K;
@@ -2049,10 +2022,6 @@ mod tests {
precheck.why.as_deref(),
Some("Only applies to asynchronous entity culling.")
);
let key = find(&s, Item::Horizon(H::HdrKey));
assert_eq!(key.name, "Shortcut");
let waits = Capture::Key(crate::graphics::HDR_KEY);
assert!(matches!(key.value, Value::Binding(ref b) if b.capture == waits));
let calibrate = find(&s, Item::Horizon(H::HdrCalibrate));
assert_eq!(calibrate.value, Value::Press);
}
@@ -2737,7 +2706,7 @@ mod tests {
H::HdrPaperWhite => s.horizon.hdr_paper_white,
H::HdrPeak => s.horizon.hdr_peak,
H::HdrBlackLevel => s.horizon.hdr_black_level,
H::HdrUiBrightness => s.horizon.hdr_ui_brightness,
H::HdrUiBrightness => s.horizon.hdr_ui_nits,
_ => unreachable!(),
};
let fresh = || {
@@ -2,7 +2,7 @@
//! reports.
use super::tests::{CHANGED, CLICK, DRAGGED, FB, NONE, SIZE, Ui, offering, press, row, ui};
use super::*;
use crate::settings_shell::{Capture, Columns, Hosted};
use crate::settings_shell::{Columns, Hosted};
use winit::keyboard::KeyCode as K;
#[test]
@@ -64,17 +64,12 @@ fn hdr_auto_can_be_selected_before_querying_and_brightness_waits_for_output() {
let peak = row(&mut ui, &s, Item::Horizon(H::HdrPeak));
assert_eq!(press(&mut ui, &mut s, (tx + 2.0, peak[1] + 4.0)), [DRAGGED]);
ui.release();
// Calibrate starts the calibration; the shortcut row waits for a key.
// Calibrate starts the calibration.
let calibrate = row(&mut ui, &s, Item::Horizon(H::HdrCalibrate));
assert_eq!(
press(&mut ui, &mut s, at(calibrate)),
[CLICK, Output::Push(OptionsPage::HdrCalibration)]
);
let key = row(&mut ui, &s, Item::Horizon(H::HdrKey));
assert_eq!(
press(&mut ui, &mut s, at(key)),
[CLICK, Output::Bind(Capture::Key(crate::graphics::HDR_KEY))]
);
assert!(ui.changed());
}
@@ -135,6 +130,45 @@ fn hdr_setup_follows_what_the_system_reported() {
assert_eq!(setup_of(&g, false), Setup::Unknown);
}
/// UI Brightness reads as Paper White does, in nits apart from it: Auto shows the paper white
/// in use, and a value set by hand its own nits, whatever Paper White is. Its slider has Paper
/// White's steps: Auto, then 10 to 480 nits by 10.
#[test]
fn ui_brightness_reads_in_nits_on_paper_whites_steps() {
let mut s = Settings::default();
s.horizon.hdr = HdrMode::On;
let reporting = offering(true, Some(true));
let label = |s: &Settings, out| Item::Horizon(H::HdrUiBrightness).label(s, out).plain();
assert_eq!(label(&s, &reporting), "UI Brightness: Auto (400)");
assert_eq!(label(&s, &NONE), "UI Brightness: Auto (203)");
s.horizon.hdr_paper_white = 250;
assert_eq!(label(&s, &reporting), "UI Brightness: Auto (250)");
s.horizon.hdr_ui_nits = 160;
for paper_white in [0, 80, 250] {
s.horizon.hdr_paper_white = paper_white;
assert_eq!(label(&s, &reporting), "UI Brightness: 160");
}
let item = Item::Horizon(H::HdrUiBrightness);
assert_eq!(
H::HdrUiBrightness.slider_steps(),
H::HdrPaperWhite.slider_steps()
);
H::HdrUiBrightness.set_slider(&mut s, 0.0);
let mut reached = vec![s.horizon.hdr_ui_nits];
for _ in 0..H::HdrUiBrightness.slider_steps().unwrap() {
item.step_slider(&mut s, true, 200.0, &reporting);
reached.push(s.horizon.hdr_ui_nits);
}
let paper: Vec<u32> = std::iter::once(0).chain((10..=480).step_by(10)).collect();
assert_eq!(reached, paper);
// Each value sits where Paper White's same value does.
for nits in paper {
s.horizon.hdr_ui_nits = nits;
s.horizon.hdr_paper_white = nits;
assert_eq!(item.slider(&s), Item::Horizon(H::HdrPaperWhite).slider(&s));
}
}
/// While HDR output is held back on this window's driver, the HDR row says so and keeps
/// the mode; any change of the mode drops the hold, and HDR is asked for again.
#[test]
+5 -4
View File
@@ -206,8 +206,9 @@ fn display(light: vec3<f32>) -> vec4<f32> {
let shows = params.world > 0.5 && params.peak > 1.0
&& (hand || (!covered && shows_world(f.rgb, w.rgb)));
var light = lit(f.rgb, w.a, shows, true).light;
// The HUD and menus at their own brightness, by how much they cover.
light *= mix(1.0, params.ui, ui);
// The HUD and menus at their own brightness, by how much they cover, no brighter than the
// peak (as `fs_hdr_layered`): UI Brightness can pass paper white.
light = min(light * mix(1.0, params.ui, ui), vec3(params.peak));
return display(light);
}
@@ -275,8 +276,8 @@ fn backdrop(w: vec3<f32>) -> vec3<f32> {
// The HUD over the world in gamma, the space the HUD blends in: its own part, what its backdrop
// B doesn't explain (H = P - (1 - c) B, exact for the layering blends), at UI Brightness, over
// (1 - c) of the world, light above white included. Where the world is at or below white, at
// 100%, that is the HUD's picture as drawn.
// (1 - c) of the world, light above white included. Where the world is at or below white, with
// UI Brightness at paper white, that is the HUD's picture as drawn.
fn layered(at: vec2<i32>) -> vec3<f32> {
let w = max(textureLoad(frame, at, 0).rgb, vec3(0.0));
let p = textureLoad(hud, at, 0).rgb;
+1 -53
View File
@@ -12,6 +12,7 @@ use std::{
};
use wgpu::util::DeviceExt;
mod animate;
pub mod cit_glint;
mod pack_text;
pub mod pictures;
@@ -2876,59 +2877,6 @@ impl HudRenderer {
pack_text::PackText::new(device, self.format, &self.atlas_view, shader, self.coverage)
});
}
pub fn animate_sprites(
&mut self,
uploads: &mut crate::gfx::upload::Uploads,
encoder: &mut wgpu::CommandEncoder,
tick: u64,
) {
let Some(texture) = &self.atlas_texture else {
return;
};
for (rect, animation, previous) in &mut self.sprite_animations {
// `SpriteContents.AnimationState`: the frame from the mcmeta's
// list, blended into the next one by the sub-frame's thousandths
// when the sprite interpolates.
let blend = animation.blend(tick);
let state = (blend.frame, blend.next, blend.millis);
if state == *previous {
continue;
}
let (w, h) = rect.texels((texture.width(), texture.height()));
let pixels = if blend.millis == 0 {
animation.resized_pixels(blend.frame, w, h)
} else {
let current = animation.resized_pixels(blend.frame, w, h);
let next = animation.resized_pixels(blend.next, w, h);
let millis = blend.millis;
current
.iter()
.zip(next)
.map(|(&a, b)| {
((u32::from(a) * (1000 - millis) + u32::from(b) * millis + 500) / 1000)
as u8
})
.collect()
};
uploads.texture(
encoder,
wgpu::TexelCopyTextureInfo {
texture,
mip_level: 0,
origin: wgpu::Origin3d {
x: (rect.u0 * texture.width() as f32).round() as u32,
y: (rect.v0 * texture.height() as f32).round() as u32,
z: 0,
},
aspect: wgpu::TextureAspect::All,
},
w,
h,
&pixels,
);
*previous = state;
}
}
/// Stage `patches` into the shared atlas texture.
pub fn upload_patches(
&self,
@@ -0,0 +1,61 @@
//! The GUI atlas's animated sprites, frame by frame (vanilla's or a Polytone `mode`'s).
impl super::HudRenderer {
/// Shows each animated sprite's frame: its mcmeta's, or what its Polytone `mode` picks.
pub(crate) fn animate(
&mut self,
uploads: &mut crate::gfx::upload::Uploads,
encoder: &mut wgpu::CommandEncoder,
tick: u64,
animator: &crate::gfx::animate::SpriteAnimator,
) {
let inputs = animator.mode_inputs();
let inputs = Some(&inputs);
let Some(texture) = &self.atlas_texture else {
return;
};
for (rect, animation, previous) in &mut self.sprite_animations {
// `SpriteContents.AnimationState`: the frame from the mcmeta's
// list, blended into the next one by the sub-frame's thousandths
// when the sprite interpolates; or what a Polytone `mode` picks.
let blend = animation.blend_with(tick, inputs);
let state = (blend.frame, blend.next, blend.millis);
if state == *previous {
continue;
}
let (w, h) = rect.texels((texture.width(), texture.height()));
let pixels = if blend.millis == 0 {
animation.resized_pixels(blend.frame, w, h)
} else {
let current = animation.resized_pixels(blend.frame, w, h);
let next = animation.resized_pixels(blend.next, w, h);
let millis = blend.millis;
current
.iter()
.zip(next)
.map(|(&a, b)| {
((u32::from(a) * (1000 - millis) + u32::from(b) * millis + 500) / 1000)
as u8
})
.collect()
};
uploads.texture(
encoder,
wgpu::TexelCopyTextureInfo {
texture,
mip_level: 0,
origin: wgpu::Origin3d {
x: (rect.u0 * texture.width() as f32).round() as u32,
y: (rect.v0 * texture.height() as f32).round() as u32,
z: 0,
},
aspect: wgpu::TextureAspect::All,
},
w,
h,
&pixels,
);
*previous = state;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

@@ -27,4 +27,29 @@ pub(crate) const SPRITES: &[(&str, &[u8])] = &[
"search_background_v2",
include_bytes!("jei_recipes/search_background_v2.png"),
),
("slot", include_bytes!("jei_recipes/slot.png")),
(
"icons/list_badge",
include_bytes!("jei_recipes/icons/list_badge.png"),
),
(
"interactive_ingredient_tooltip_background",
include_bytes!("jei_recipes/interactive_ingredient_tooltip_background.png"),
),
(
"recipe_preview_background_v2",
include_bytes!("jei_recipes/recipe_preview_background_v2.png"),
),
(
"ingredient_list_slot_background_v2",
include_bytes!("jei_recipes/ingredient_list_slot_background_v2.png"),
),
(
"scrollbar_background_v2",
include_bytes!("jei_recipes/scrollbar_background_v2.png"),
),
(
"scrollbar_marker_v2",
include_bytes!("jei_recipes/scrollbar_marker_v2.png"),
),
];
+1
View File
@@ -68,6 +68,7 @@ pub mod jigsaw_block;
pub mod keypad;
pub mod terrain_model;
pub use horizon_polytone::colors as polytone_colors;
pub use horizon_polytone::creative_tabs as polytone_creative_tabs;
pub use horizon_world::json_folder;
pub mod level_loading;
pub mod light;
+80 -8
View File
@@ -761,6 +761,49 @@ pub struct MeshTables {
colormaps: Vec<Arc<crate::environment::Colormap>>,
water_colormap: Option<u16>,
}
/// Vanilla's four biome colour functions (`BiomeColors`) with what the packs replace them by:
/// `biome_colors` (a colormap or a flat colour), else the colormap a block modifier gives
/// `grass_block`, `oak_leaves` or `leaf_litter`, or a fluid modifier gives water, which Polytone
/// spreads to every block of that function. Each with the tints that read it.
fn biome_functions(
pack_root: &std::path::Path,
modifiers: &[(bool, horizon_polytone::Targets, String)],
) -> Vec<(&'static [Tint], crate::environment::Colormap)> {
use crate::environment::Colormap;
use horizon_polytone::colors::BiomeColor;
const FUNCTIONS: [(&str, &str, &[Tint]); 4] = [
(
"grass",
"minecraft:grass_block",
&[
Tint::Grass,
Tint::GrassBlock,
Tint::TallGrassUpper,
Tint::SugarCane,
],
),
("foliage", "minecraft:oak_leaves", &[Tint::Foliage]),
("dry_foliage", "minecraft:leaf_litter", &[Tint::DryFoliage]),
("water", "minecraft:water", &[Tint::Water]),
];
let colors = horizon_polytone::colors::PackColors::read(pack_root);
FUNCTIONS
.into_iter()
.filter_map(|(function, block, tints)| {
let colormap = match colors.biome_color(function) {
Some(BiomeColor::Flat(rgb)) => Some(Colormap::flat(rgb)),
Some(BiomeColor::Colormap(id)) => Colormap::load(pack_root, &id),
None => modifiers
.iter()
.find(|(fluid, targets, _)| {
*fluid == (function == "water") && targets.matches(block, |_| false)
})
.and_then(|(_, _, id)| Colormap::load(pack_root, id)),
}?;
Some((tints, colormap))
})
.collect()
}
fn stem_color(age: u8) -> u32 {
let a = u32::from(age);
(a * 32) << 16 | (255 - a * 8) << 8 | (a * 4)
@@ -945,7 +988,8 @@ impl MeshTables {
}
/// These tables with the colormaps of the Polytone block and fluid modifiers of the packs
/// merged at `pack_root` (wiki "Colormaps"): a tinted block they target takes its colour
/// from the colormap, and its own tint where the colormap's pixel is transparent.
/// from the colormap, and its own tint where the colormap's pixel is transparent. Vanilla's
/// four biome colour functions take the packs' `biome_colors` first (wiki "Custom Colors").
#[must_use]
pub fn with_polytone_colormaps(
mut self,
@@ -953,7 +997,8 @@ impl MeshTables {
registry: &horizon_world::registry::BlockRegistry,
) -> Self {
let modifiers = crate::environment::polytone_colormap_modifiers(pack_root);
if modifiers.is_empty() {
let biome_functions = biome_functions(pack_root, &modifiers);
if modifiers.is_empty() && biome_functions.is_empty() {
return self;
}
let mut loaded: std::collections::HashMap<String, Option<u16>> = Default::default();
@@ -964,7 +1009,24 @@ impl MeshTables {
Some((colormaps.len() - 1) as u16)
})
};
for (tints, colormap) in biome_functions {
self.colormaps.push(Arc::new(colormap));
let i = Some((self.colormaps.len() - 1) as u16);
if tints == [Tint::Water] {
self.water_colormap = i;
}
for info in &mut self.info {
let own = &info.tints[..usize::from(info.tint_count).min(2)];
if own.iter().any(|t| tints.contains(t)) {
info.colormap = i;
}
}
}
let water_function = self.water_colormap;
for (fluid, targets, id) in &modifiers {
if *fluid && water_function.is_some() {
continue;
}
if *fluid {
if targets.matches("minecraft:water", |_| false) {
self.water_colormap = index(id, &mut self.colormaps);
@@ -1495,9 +1557,9 @@ struct Tints<'a> {
/// by colormap and section-local block.
colormaps: &'a [Arc<crate::environment::Colormap>],
water_colormap: Option<u16>,
sampled: std::collections::HashMap<(u16, [i32; 3]), Option<u32>>,
sampled: crate::fast_hash::FxHashMap<(u16, [i32; 3]), Option<u32>>,
/// Each colormap's own colour by block, before the blend averages them.
cells: std::collections::HashMap<(u16, [i32; 3]), Option<u32>>,
cells: crate::fast_hash::FxHashMap<(u16, [i32; 3]), Option<u32>>,
}
impl<'a> Tints<'a> {
fn new(
@@ -1518,8 +1580,8 @@ impl<'a> Tints<'a> {
uniform: None,
colormaps: &[],
water_colormap: None,
sampled: std::collections::HashMap::new(),
cells: std::collections::HashMap::new(),
sampled: Default::default(),
cells: Default::default(),
}
}
/// With the packs' colormaps of `tables`.
@@ -1531,10 +1593,16 @@ impl<'a> Tints<'a> {
/// Colormap `index`'s colour at block `(x, y, z)` (section-local), `None` where its pixel
/// is transparent.
fn colormap(&mut self, index: u16, x: i32, y: i32, z: i32) -> Option<u32> {
let colormap = self.colormaps.get(usize::from(index))?.clone();
// A colormap that ignores the height, over one biome: one colour per column.
let y = if colormap.column && self.uniform_biome().is_some() {
0
} else {
y
};
if let Some(&known) = self.sampled.get(&(index, [x, y, z])) {
return known;
}
let colormap = self.colormaps.get(usize::from(index))?.clone();
// ClientLevel.calculateBlockTint: the mean of the (2r+1)^2 colours around the block, each
// channel summed and divided. A transparent pixel in the square leaves the block's own.
let r = if colormap.biome_blend { self.radius } else { 0 };
@@ -1573,7 +1641,11 @@ impl<'a> Tints<'a> {
if let Some(&known) = self.cells.get(&(index, at)) {
return known;
}
let id = Self::biome_id(&self.zoom, self.region, self.plains, self.origin, at);
// One biome over the section and its blend margin: no zoom per block.
let id = match self.uniform_biome() {
Some(biome) => biome,
None => Self::biome_id(&self.zoom, self.region, self.plains, self.origin, at),
};
let [cx, cz, base_y] = self.origin;
let block = [cx * 16 + at[0], base_y + at[1], cz * 16 + at[2]];
let mut host = crate::environment::BlockHost::new(block, self.biomes.get(id));
@@ -251,3 +251,117 @@ fn a_biome_colormap_blends_over_a_biome_edge_as_the_vanilla_water_tint_does() {
}
assert!(edge_differs, "the square must cross the edge somewhere");
}
#[test]
fn biome_colors_and_a_grass_block_colormap_replace_their_whole_biome_function() {
let biomes = Biomes::from_jsons(&[(
"minecraft:plains",
serde_json::json!({"effects": {"water_color": 0x3F76E4}}),
)]);
// biome_colors gives water a flat green; a block modifier colours oak leaves with a map,
// which Polytone spreads to the whole foliage function.
let root = pack(&[
(
"assets/mypack/polytone/colors.json",
br#"{"biome_colors": {"water": "0x00ff00"}}"#.to_vec(),
),
(
"assets/mypack/polytone/colormaps/leaves.png",
png_row(&[[0, 0, 255, 255]]),
),
(
"assets/mypack/polytone/block_modifiers/oak.json",
br#"{"targets": ["minecraft:oak_leaves"], "colormap": "mypack:leaves"}"#.to_vec(),
),
]);
let modifiers = crate::environment::polytone_colormap_modifiers(&root);
let functions = biome_functions(&root, &modifiers);
let tints: Vec<_> = functions.iter().map(|(t, _)| *t).collect();
assert_eq!(tints.len(), 2);
assert!(tints[0] == [Tint::Foliage] && tints[1] == [Tint::Water]);
let colormaps: Vec<_> = functions.into_iter().map(|(_, c)| Arc::new(c)).collect();
let chunks: Vec<Vec<Chunk>> = (0..3)
.map(|rz| (0..3).map(|rx| filled(rx - 1, rz - 1, 0)).collect())
.collect();
let region: [[Option<&Chunk>; 3]; 3] =
std::array::from_fn(|rz| std::array::from_fn(|rx| Some(&chunks[rz][rx])));
let mut tints = Tints::new(&region, &biomes, BiomeZoom::new(0), 0, [0, 0, 64]);
tints.colormaps = &colormaps;
tints.water_colormap = Some(1);
assert_eq!(tints.water(3, 4, 5), 0x00_FF00);
std::fs::remove_dir_all(root).unwrap();
}
#[test]
fn only_a_colormap_that_ignores_the_height_is_shared_up_a_column() {
let root = pack(&[
(
"assets/mypack/polytone/colormaps/flat.png",
png_row(&[[1, 2, 3, 255]]),
),
(
"assets/mypack/polytone/colormaps/flat.json",
br#"{"x_axis": "biome_id", "y_axis": "r.noise(o.x/60, o.z/60)"}"#.to_vec(),
),
(
"assets/mypack/polytone/colormaps/tall.png",
png_row(&[[1, 2, 3, 255]]),
),
(
"assets/mypack/polytone/colormaps/tall.json",
br#"{"y_axis": "y_level"}"#.to_vec(),
),
]);
let load = |id| crate::environment::Colormap::load(&root, id).unwrap();
assert!(load("mypack:flat").column);
assert!(!load("mypack:tall").column);
std::fs::remove_dir_all(root).unwrap();
}
/// Times a section's colormap tints with the freepack's kind of grass colormap (a biome id
/// column and a noise row) over the default Biome Blend: `cargo test --release ... -- --ignored
/// --nocapture colormap_tint_cost`.
#[test]
#[ignore = "timing, not a check"]
fn colormap_tint_cost() {
const RADIUS: i32 = 2;
const LAYERS: i32 = 16;
let biomes = Biomes::from_jsons(&[(
"minecraft:plains",
serde_json::json!({"effects": {"water_color": 0x3F76E4}}),
)]);
let pixels: Vec<[u8; 4]> = (0..16).map(|i| [i * 16, 200, 64, 255]).collect();
let root = pack(&[
("assets/mypack/polytone/colormaps/grass.png", png_row(&pixels)),
(
"assets/mypack/polytone/colormaps/grass.json",
br#"{"x_axis": "biome_id", "y_axis": "0.5+(r.noise(o.x/60,5+o.z/60)*0.55)", "biome_id_mapper": {"minecraft:plains": 1, "texture_size": 16}}"#
.to_vec(),
),
]);
let colormaps = vec![Arc::new(
crate::environment::Colormap::load(&root, "mypack:grass").unwrap(),
)];
let chunks: Vec<Vec<Chunk>> = (0..3)
.map(|rz| (0..3).map(|rx| filled(rx - 1, rz - 1, 0)).collect())
.collect();
let region: [[Option<&Chunk>; 3]; 3] =
std::array::from_fn(|rz| std::array::from_fn(|rx| Some(&chunks[rz][rx])));
let rounds = 50;
let start = std::time::Instant::now();
let mut check = 0u64;
for _ in 0..rounds {
let mut tints = Tints::new(&region, &biomes, BiomeZoom::new(0), RADIUS, [0, 0, 64]);
tints.colormaps = &colormaps;
for y in 0..LAYERS {
for z in 0..16 {
for x in 0..16 {
check += u64::from(tints.colormap(0, x, y, z).unwrap_or(0));
}
}
}
}
let per = start.elapsed() / rounds;
println!("{LAYERS} layers of 256 blended colormap tints: {per:?} (checksum {check})");
std::fs::remove_dir_all(root).unwrap();
}
+22 -101
View File
@@ -1331,6 +1331,8 @@ pub enum Page {
ModProject,
/// A native mod's settings page: its index in `mods.installed`.
NativeMod(usize),
/// A page a crate registered (`horizon_hooks::pages`): its index in `OptionsUi::pages`.
Added(usize),
/// Horizon's shader packs, either kind (`shader_pack_list`).
ShaderPacks,
/// The picked pack's options: the shader screen (`shader_ui`) at the pack's main screen.
@@ -1390,6 +1392,8 @@ enum Act {
/// A native mod's key (mod, key) waits for a key; a button of its page.
NativeKey(usize, usize),
NativeButton(usize, usize),
/// A registered page's row: (page, row).
PageRow(usize, usize),
OpenModFolder,
/// Browse mods: the page of listing `.0` of `mod_browser`.
ModProject(usize),
@@ -1617,8 +1621,11 @@ pub struct OptionsUi {
/// Key Binds: the mapping waiting for a key ([`NATIVE_KEY`]: a native
/// mod's, `selected_native_key`).
pub selected_key: Option<&'static str>,
/// The native mod's key waiting: (mod, key) in `mods.installed`.
/// The native mod's key waiting: (mod, key) in `mods.installed`, or a registered page's
/// (page, row) ([`pages::PAGE_KEY`]).
selected_native_key: Option<(usize, usize)>,
/// The pages crates registered (`horizon_hooks::pages`).
pages: &'static [&'static dyn horizon_hooks::pages::Page],
/// Bindings: the action waiting for a pad button.
pub selected_pad_action: Option<&'static str>,
/// The glyphs menus show (`Glyphs::of`: the setting, else the pad's family, and a Nintendo
@@ -1730,6 +1737,7 @@ impl Default for OptionsUi {
scrollbar_drag_y: None,
selected_key: None,
selected_native_key: None,
pages: horizon_hooks::pages::engine::all(),
selected_pad_action: None,
pad_glyphs: crate::pad_glyph::Glyphs::default(),
pad_driving: false,
@@ -2367,6 +2375,7 @@ impl OptionsUi {
Page::ModBrowser => "horizon.mods.browse.title",
Page::ModProject => "horizon.mods.browse.project",
Page::NativeMod(_) => "Mod Settings",
Page::Added(i) => pages::title(i),
Page::ShaderPacks => "horizon.pages.shader_packs",
Page::ShaderOptions => "horizon.pages.shader_options",
Page::PackOptions => "horizon.pack.options.title",
@@ -2583,43 +2592,6 @@ impl OptionsUi {
}
rows
}
/// A native mod's page: its name, its keys as Key Binds rows (the
/// player's choice, else the default), then its buttons.
fn native_rows(&self, s: &Settings, i: usize) -> Vec<Row> {
let Some(m) = s.mods.installed.get(i) else {
return Vec::new();
};
let mut rows = vec![Row::Note(m.title.clone())];
let Some(page) = &m.native else {
return rows;
};
let chosen = s.mods.keys.get(&m.id);
for (k, [name, label, default]) in page.keys.iter().enumerate() {
let key = chosen.and_then(|c| c.get(name)).unwrap_or(default);
let mut message = keybinds::display_name(key);
if self.selected_native_key == Some((i, k)) {
message = Text::literal("> ")
.append(message.with_color(text::WHITE))
.append(" <")
.with_color(text::YELLOW);
}
let caption = button(0.0, 0.0, 150.0, Text::literal(label.clone()), Act::None);
let change = button(0.0, 0.0, 150.0, message, Act::NativeKey(i, k));
rows.push(Row::Buttons(inactive(caption), Some(change)));
}
for (b, label) in page.buttons.iter().enumerate() {
let widget = button(
0.0,
0.0,
310.0,
Text::literal(label.clone()),
Act::NativeButton(i, b),
);
rows.push(Row::Buttons(widget, None));
}
rows
}
fn list_rows(&self, s: &Settings) -> Vec<Row> {
match self.page() {
Page::SplitSetup => Vec::new(),
@@ -2684,47 +2656,7 @@ impl OptionsUi {
flush(&mut rows, &mut section);
rows
}
Page::Horizon => {
// Two to a row, as vanilla's OptionsList.addSmall lays out
// buttons; each opens one of Horizon's settings pages. Each page has
// one way in: graphics and shader packs are Video Settings' (the graphics
// screen), the controller is Controls', mods are the Options screen's.
let open =
|key: &str, page: Page| button(0.0, 0.0, 150.0, tr(key), Act::Push(page));
vec![
Row::Buttons(
open("horizon.pages.open.extras", Page::Extras),
Some(open("horizon.pages.open.pvp", Page::Pvp)),
),
Row::Buttons(
open(
"horizon.pages.open.split_setup",
if self.split_guest {
Page::SplitProfiles
} else {
Page::SplitSetup
},
),
Some(open("horizon.pages.open.studio", Page::Studio)),
),
// No AI hides the AI Agent page and everything AI.
Row::Buttons(
self.extra_button(s, crate::extras::Item::DiscordPresence),
Some(if s.extras.no_ai {
self.extra_button(s, crate::extras::Item::NoAi)
} else {
open("horizon.pages.open.agent", Page::Agent)
}),
),
]
.into_iter()
.chain(
(!s.extras.no_ai).then(|| {
Row::Buttons(self.extra_button(s, crate::extras::Item::NoAi), None)
}),
)
.collect()
}
Page::Horizon => self.horizon_rows(s),
Page::Agent => {
let mut rows = vec![Row::Buttons(
self.extra_button(s, crate::extras::Item::AgentControl),
@@ -3007,6 +2939,7 @@ impl OptionsUi {
Page::ModBrowser => self.mod_browser_rows(),
Page::ModProject => self.mod_project_rows(s),
Page::NativeMod(i) => self.native_rows(s, i),
Page::Added(i) => self.added_rows(i),
_ => {
let _ = s;
Vec::new()
@@ -4273,11 +4206,9 @@ impl OptionsUi {
self.push(Page::NativeMod(i));
events.push(Event::NativeSettings(i));
}
Act::NativeKey(i, k) => {
self.selected_key = Some(NATIVE_KEY);
self.selected_native_key = Some((i, k));
}
Act::NativeKey(i, k) => self.wait_native_key(i, k),
Act::NativeButton(i, b) => events.push(Event::NativeButton(i, b)),
Act::PageRow(p, r) => events.extend(self.page_act(p, r, self.shift)),
Act::OpenModFolder => events.push(Event::OpenModFolder),
Act::ModProject(i) => {
if let Some(listing) = self.mod_browser.listings.get(i) {
@@ -4781,19 +4712,10 @@ impl OptionsUi {
let Some(name) = self.selected_key.take() else {
return Vec::new();
};
if name == NATIVE_KEY {
let (i, k) = self.selected_native_key.take().unwrap_or_default();
let Some(m) = s.mods.installed.get(i) else {
return Vec::new();
};
let Some([key, ..]) = m.native.as_ref().and_then(|p| p.keys.get(k)) else {
return Vec::new();
};
let keys = s.mods.keys.entry(m.id.clone()).or_default();
keys.insert(key.clone(), value);
return vec![Event::Changed];
if name == NATIVE_KEY || name == pages::PAGE_KEY {
return self.bind_native_key(s, name, value);
}
if !s.extras.set_key(name, value.clone()) && !s.horizon.set_key(name, value.clone()) {
if !s.extras.set_key(name, value.clone()) {
s.keys.set(name, &value);
}
vec![Event::Changed]
@@ -5173,11 +5095,9 @@ impl OptionsUi {
self.push(Page::NativeMod(i));
events.push(Event::NativeSettings(i));
}
Act::NativeKey(i, k) => {
self.selected_key = Some(NATIVE_KEY);
self.selected_native_key = Some((i, k));
}
Act::NativeKey(i, k) => self.wait_native_key(i, k),
Act::NativeButton(i, b) => events.push(Event::NativeButton(i, b)),
Act::PageRow(p, r) => events.extend(self.page_act(p, r, shift)),
Act::OpenModFolder => events.push(Event::OpenModFolder),
Act::ModProject(i) => {
if let Some(listing) = self.mod_browser.listings.get(i) {
@@ -7348,7 +7268,7 @@ mod tests {
MouseButton::Back,
MouseButton::Forward,
] {
for name in [SORT_KEY, crate::graphics::HDR_KEY, "key.jump"] {
for name in [SORT_KEY, "key.jump"] {
let mut ui = OptionsUi::default();
let mut s = Settings::default();
ui.selected_key = Some(name);
@@ -7357,7 +7277,6 @@ mod tests {
let value = keybinds::mouse_name(button);
match name {
SORT_KEY => assert_eq!(s.extras.sort_key, value),
crate::graphics::HDR_KEY => assert_eq!(s.horizon.hdr_key, value),
_ => assert_eq!(s.keys.get(name), value),
}
assert_eq!(Extras::parse(&s.extras.serialize()), s.extras);
@@ -8439,3 +8358,5 @@ mod tests {
#[cfg(test)]
#[path = "options_ui_graphics_tests.rs"]
mod graphics_tests;
#[path = "options_ui_pages.rs"]
mod pages;
@@ -0,0 +1,327 @@
//! The pages crates add to the options screens: a native mod's settings page (its keys and
//! buttons), and the pages crates above the engine register (`horizon_hooks::pages`, docs/layers.md
//! "Platform registries") with their entries on Horizon Settings. Kept out of options_ui.rs, which
//! may not grow (tools/check_file_size.py).
use super::*;
use horizon_hooks::pages as added;
/// `selected_key` while a registered page's key waits (`selected_native_key`: page, row).
pub(super) const PAGE_KEY: &str = "horizon.pageKey";
/// A registered page's title (a translation key).
pub(super) fn title(page: usize) -> &'static str {
added::engine::all().get(page).map_or("", |p| p.title())
}
fn label_text(label: added::Label) -> Text {
match label {
added::Label::Key(key) => tr(key),
added::Label::Value(value) => Text::literal(value),
}
}
impl OptionsUi {
/// Horizon Settings: its pages, then the pages crates registered for it.
pub(super) fn horizon_rows(&self, s: &Settings) -> Vec<Row> {
// Two to a row, as vanilla's OptionsList.addSmall lays out
// buttons; each opens one of Horizon's settings pages. Each page has
// one way in: graphics and shader packs are Video Settings' (the graphics
// screen), the controller is Controls', mods are the Options screen's.
let open = |key: &str, page: Page| button(0.0, 0.0, 150.0, tr(key), Act::Push(page));
let mut rows: Vec<Row> = vec![
Row::Buttons(
open("horizon.pages.open.extras", Page::Extras),
Some(open("horizon.pages.open.pvp", Page::Pvp)),
),
Row::Buttons(
open(
"horizon.pages.open.split_setup",
if self.split_guest {
Page::SplitProfiles
} else {
Page::SplitSetup
},
),
Some(open("horizon.pages.open.studio", Page::Studio)),
),
// No AI hides the AI Agent page and everything AI.
Row::Buttons(
self.extra_button(s, crate::extras::Item::DiscordPresence),
Some(if s.extras.no_ai {
self.extra_button(s, crate::extras::Item::NoAi)
} else {
open("horizon.pages.open.agent", Page::Agent)
}),
),
]
.into_iter()
.chain(
(!s.extras.no_ai)
.then(|| Row::Buttons(self.extra_button(s, crate::extras::Item::NoAi), None)),
)
.collect();
self.added_entries(&mut rows);
rows
}
/// The entries of the pages registered for Horizon Settings, two to a row like the engine's.
fn added_entries(&self, rows: &mut Vec<Row>) {
let mut entries = self
.pages
.iter()
.enumerate()
.filter(|(_, page)| page.place() == added::Place::HorizonSettings)
.map(|(i, page)| button(0.0, 0.0, 150.0, tr(page.title()), Act::Push(Page::Added(i))));
while let Some(first) = entries.next() {
rows.push(Row::Buttons(first, entries.next()));
}
}
/// A registered page's rows, asked of it each time the page is drawn.
pub(super) fn added_rows(&self, page: usize) -> Vec<Row> {
let Some(registered) = self.pages.get(page) else {
return Vec::new();
};
let mut rows = Vec::new();
registered.rows(&mut rows);
rows.into_iter()
.enumerate()
.map(|(r, row)| self.added_row(page, r, row))
.collect()
}
fn added_row(&self, page: usize, r: usize, row: added::Row) -> Row {
let act = Act::PageRow(page, r);
let wide = |label: Text| Row::Buttons(button(0.0, 0.0, 310.0, label, act.clone()), None);
match row {
added::Row::Heading(key) => Row::Heading(key.to_owned()),
added::Row::Note(text) => Row::Note(text),
added::Row::Toggle { label, on } => wide(generic(label, on_off(on))),
added::Row::Cycle { label, value } => wide(generic(label, label_text(value))),
added::Row::Button { label } => wide(tr(label)),
added::Row::Key { label, key } => {
let mut message = keybinds::display_name(&key);
if self.selected_key == Some(PAGE_KEY)
&& self.selected_native_key == Some((page, r))
{
message = Text::literal("> ")
.append(message.with_color(text::WHITE))
.append(" <")
.with_color(text::YELLOW);
}
let caption = button(0.0, 0.0, 150.0, tr(label), Act::None);
let change = button(0.0, 0.0, 150.0, message, act);
Row::Buttons(inactive(caption), Some(change))
}
// A row kind newer than this engine: shown empty.
_ => Row::Note(String::new()),
}
}
/// A registered page's row was pressed: a key row waits for a key, any other row hears it.
pub(super) fn page_act(&mut self, page: usize, r: usize, shift: bool) -> Vec<Event> {
let Some(registered) = self.pages.get(page) else {
return Vec::new();
};
let mut rows = Vec::new();
registered.rows(&mut rows);
match rows.get(r) {
Some(added::Row::Key { .. }) => {
self.selected_key = Some(PAGE_KEY);
self.selected_native_key = Some((page, r));
Vec::new()
}
Some(_) => {
registered.act(
r,
if shift {
added::Act::PressBack
} else {
added::Act::Press
},
);
vec![Event::Changed]
}
None => Vec::new(),
}
}
/// A native mod's page: its name, its keys as Key Binds rows (the
/// player's choice, else the default), then its buttons.
pub(super) fn native_rows(&self, s: &Settings, i: usize) -> Vec<Row> {
let Some(m) = s.mods.installed.get(i) else {
return Vec::new();
};
let mut rows = vec![Row::Note(m.title.clone())];
let Some(page) = &m.native else {
return rows;
};
let chosen = s.mods.keys.get(&m.id);
for (k, [name, label, default]) in page.keys.iter().enumerate() {
let key = chosen.and_then(|c| c.get(name)).unwrap_or(default);
let mut message = keybinds::display_name(key);
if self.selected_native_key == Some((i, k)) {
message = Text::literal("> ")
.append(message.with_color(text::WHITE))
.append(" <")
.with_color(text::YELLOW);
}
let caption = button(0.0, 0.0, 150.0, Text::literal(label.clone()), Act::None);
let change = button(0.0, 0.0, 150.0, message, Act::NativeKey(i, k));
rows.push(Row::Buttons(inactive(caption), Some(change)));
}
for (b, label) in page.buttons.iter().enumerate() {
let widget = button(
0.0,
0.0,
310.0,
Text::literal(label.clone()),
Act::NativeButton(i, b),
);
rows.push(Row::Buttons(widget, None));
}
rows
}
/// A native mod's key waits for the next key or button pressed.
pub(super) fn wait_native_key(&mut self, i: usize, k: usize) {
self.selected_key = Some(NATIVE_KEY);
self.selected_native_key = Some((i, k));
}
/// The key pressed for the native mod's key, or the registered page's ([`PAGE_KEY`]), that
/// waited for one.
pub(super) fn bind_native_key(
&mut self,
s: &mut Settings,
name: &str,
value: String,
) -> Vec<Event> {
let (i, k) = self.selected_native_key.take().unwrap_or_default();
if name == PAGE_KEY {
if let Some(page) = self.pages.get(i) {
page.act(k, added::Act::Bind(value));
}
return vec![Event::Changed];
}
let Some(m) = s.mods.installed.get(i) else {
return Vec::new();
};
let Some([key, ..]) = m.native.as_ref().and_then(|p| p.keys.get(k)) else {
return Vec::new();
};
let keys = s.mods.keys.entry(m.id.clone()).or_default();
keys.insert(key.clone(), value);
vec![Event::Changed]
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
/// A page with a heading, a switch, a number that steps either way and a key.
struct Sample {
on: Mutex<bool>,
step: Mutex<i32>,
key: Mutex<String>,
}
impl added::Page for Sample {
fn id(&self) -> &'static str {
"test.sample"
}
fn title(&self) -> &'static str {
"test.sample.title"
}
fn place(&self) -> added::Place {
added::Place::HorizonSettings
}
fn rows(&self, rows: &mut Vec<added::Row>) {
rows.push(added::Row::Heading("test.sample.heading"));
rows.push(added::Row::Toggle {
label: "test.sample.switch",
on: *self.on.lock().unwrap(),
});
rows.push(added::Row::Cycle {
label: "test.sample.step",
value: added::Label::Value(self.step.lock().unwrap().to_string()),
});
rows.push(added::Row::Key {
label: "test.sample.key",
key: self.key.lock().unwrap().clone(),
});
}
fn act(&self, row: usize, act: added::Act) {
match (row, act) {
(1, added::Act::Press) => {
let mut on = self.on.lock().unwrap();
*on = !*on;
}
(2, added::Act::Press) => *self.step.lock().unwrap() += 1,
(2, added::Act::PressBack) => *self.step.lock().unwrap() -= 1,
(3, added::Act::Bind(key)) => *self.key.lock().unwrap() = key,
other => panic!("row and act the page doesn't expect: {other:?}"),
}
}
}
static SAMPLE: Sample = Sample {
on: Mutex::new(false),
step: Mutex::new(0),
key: Mutex::new(String::new()),
};
static PAGES: [&dyn added::Page; 1] = [&SAMPLE];
/// Clicks the active button that does `act`, with Shift held or not.
fn press(ui: &mut OptionsUi, s: &mut Settings, act: Act, shift: bool) -> Vec<Event> {
let size = (480.0, 240.0);
let (widgets, ..) = ui.layout(s, size.0, size.1);
let (x, y) = widgets
.iter()
.find_map(|w| match &w.kind {
WKind::Button {
act: a,
active: true,
..
} if *a == act => Some((w.x + 1.0, w.y + 1.0)),
_ => None,
})
.unwrap_or_else(|| panic!("no button does {act:?}"));
ui.shift = shift;
let atlas = super::super::tests::atlas();
ui.click(s, &atlas, x, y, size, [480, 240], false, false)
}
#[test]
fn a_registered_page_opens_from_horizon_settings_and_hears_its_rows() {
let mut s = Settings::default();
let mut ui = OptionsUi {
pages: &PAGES,
..OptionsUi::default()
};
ui.show(Page::Horizon);
press(&mut ui, &mut s, Act::Push(Page::Added(0)), false);
assert_eq!(ui.page(), Page::Added(0));
// Rows: the heading (0), the switch (1), the step (2), the key (3).
let events = press(&mut ui, &mut s, Act::PageRow(0, 1), false);
assert_eq!(
events.last(),
Some(&Event::Changed),
"the settings get saved"
);
assert!(*SAMPLE.on.lock().unwrap());
press(&mut ui, &mut s, Act::PageRow(0, 2), false);
press(&mut ui, &mut s, Act::PageRow(0, 2), true);
press(&mut ui, &mut s, Act::PageRow(0, 2), true);
assert_eq!(*SAMPLE.step.lock().unwrap(), -1, "Shift steps back");
// A key row waits for the next key, as on Key Binds, and the page gets its name.
press(&mut ui, &mut s, Act::PageRow(0, 3), false);
let atlas = super::super::tests::atlas();
let events = ui.key(&mut s, &atlas, winit::keyboard::KeyCode::KeyZ, None);
assert_eq!(events, [Event::Changed]);
assert_eq!(*SAMPLE.key.lock().unwrap(), "key.keyboard.z");
assert_eq!(ui.selected_key, None);
}
}
@@ -2,6 +2,8 @@
//! `redstone.<power>`), for drawing only: the pack recolours collars, tropical fish, banners, wool,
//! sign text and redstone wire and dust, while gameplay (crafting, item data, the beacon) keeps
//! vanilla's `DyeColor`. Lock-free: the draw paths and the mesher's workers read it per face.
use std::path::Path;
use std::sync::RwLock;
use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
use crate::block_entity_render::DYES;
@@ -30,6 +32,96 @@ pub fn set(colors: &PackColors) {
}
}
/// An item modifier's `bar_color` of the `damage` type: a row of colours picked by the stack's
/// damage, the first pixel undamaged, the last at full damage (measured on the real mod).
struct DamageBar {
targets: horizon_polytone::Targets,
row: Vec<u32>,
}
static ITEM_BARS: RwLock<Vec<DamageBar>> = RwLock::new(Vec::new());
/// Reads the packs' item modifiers' durability bar colours under a pack overlay root: an
/// `<item>_bar.png` alone, or a json whose `bar_color` is `damage` with its `<name>_bar.png` (or
/// `<name>.png`) beside it. Colormap ids and inline colormaps aren't read.
pub fn set_item_bars(root: &Path) {
let mut bars = Vec::new();
for namespace in std::fs::read_dir(root.join("assets"))
.into_iter()
.flatten()
.flatten()
{
let base = namespace.path().join("polytone/item_modifiers");
let mut stack = vec![base.clone()];
while let Some(dir) = stack.pop() {
for entry in std::fs::read_dir(&dir).into_iter().flatten().flatten() {
let path = entry.path();
if path.is_dir() {
stack.push(path);
} else if let Some(bar) = item_bar(root, &base, &path) {
bars.push(bar);
}
}
}
}
*ITEM_BARS.write().unwrap_or_else(|e| e.into_inner()) = bars;
}
fn item_bar(root: &Path, base: &Path, path: &Path) -> Option<DamageBar> {
let name = path.file_name()?.to_str()?;
let row = |png: &Path| {
let (width, _, rgba) = crate::sprites::read_rgba(png).ok()?;
let row: Vec<u32> = rgba[..width as usize * 4]
.as_chunks::<4>()
.0
.iter()
.map(|p| u32::from(p[0]) << 16 | u32::from(p[1]) << 8 | u32::from(p[2]))
.collect();
(!row.is_empty()).then_some(row)
};
if let Some(stem) = name.strip_suffix("_bar.png") {
// A json of the same name owns its png.
if path.with_file_name(format!("{stem}.json")).exists() {
return None;
}
let relative = path.parent()?.strip_prefix(base).ok()?.join(stem);
let namespace = base.parent()?.parent()?.file_name()?.to_str()?;
let id = format!("{namespace}:{}", relative.to_str()?.replace('\\', "/"));
let targets = horizon_polytone::Targets::parse(None, Some(&id)).ok()?;
return Some(DamageBar {
targets,
row: row(path)?,
});
}
let stem = name.strip_suffix(".json")?;
let json: serde_json::Value = horizon_world::json_file::read(path).ok()?;
if json.get("bar_color")?.as_str()? != "damage" {
return None;
}
let png = [format!("{stem}_bar.png"), format!("{stem}.png")]
.into_iter()
.map(|png| path.with_file_name(png))
.find(|png| png.exists())?;
let relative = path.strip_prefix(root).unwrap_or(path).to_string_lossy();
let implicit = horizon_polytone::implicit_id(&relative, "item_modifiers");
let targets =
horizon_polytone::Targets::parse(json.get("targets"), implicit.as_deref()).ok()?;
Some(DamageBar {
targets,
row: row(&png)?,
})
}
/// A pack's durability bar colour (RGB) for an item at this damage (0 to 1).
pub fn item_bar_color(item: &str, damage: f32) -> Option<u32> {
let bars = ITEM_BARS.read().unwrap_or_else(|e| e.into_inner());
let bar = bars
.iter()
.rev()
.find(|b| b.targets.matches(item, |_| false))?;
let last = (bar.row.len() - 1) as f32;
Some(bar.row[(damage.clamp(0.0, 1.0) * last).round() as usize])
}
fn read(table: &[AtomicU64; 16], i: usize) -> Option<u32> {
let v = table[i & 15].load(Relaxed);
(v != 0).then_some(v as u32)
@@ -96,4 +188,49 @@ mod tests {
assert_eq!(sheep_wool(pink as u8), 0xB6687F);
std::fs::remove_dir_all(&dir).ok();
}
fn png_row(path: &Path, pixels: &[[u8; 3]]) {
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
let file = std::fs::File::create(path).unwrap();
let mut encoder = png::Encoder::new(file, pixels.len() as u32, 1);
encoder.set_color(png::ColorType::Rgba);
let data: Vec<u8> = pixels
.iter()
.flat_map(|p| [p[0], p[1], p[2], 255])
.collect();
encoder
.write_header()
.unwrap()
.write_image_data(&data)
.unwrap();
}
#[test]
fn a_damage_bar_picks_its_pixel_by_the_stacks_damage() {
let dir = std::env::temp_dir().join(format!("pack-bars-{}", std::process::id()));
let modifiers = dir.join("assets/minecraft/polytone/item_modifiers");
let rgby = [[255, 0, 0], [0, 255, 0], [0, 0, 255], [255, 255, 0]];
png_row(&modifiers.join("diamond_pickaxe_bar.png"), &rgby);
std::fs::write(
modifiers.join("tools.json"),
r#"{"targets": ["minecraft:iron_axe"], "bar_color": "damage"}"#,
)
.unwrap();
png_row(&modifiers.join("tools_bar.png"), &[[1, 2, 3], [4, 5, 6]]);
set_item_bars(&dir);
// The real mod: damage 200 of 1561 shows the first pixel, 1400 the last.
assert_eq!(
item_bar_color("minecraft:diamond_pickaxe", 200.0 / 1561.0),
Some(0xFF0000)
);
assert_eq!(
item_bar_color("minecraft:diamond_pickaxe", 1400.0 / 1561.0),
Some(0xFFFF00)
);
assert_eq!(item_bar_color("minecraft:iron_axe", 0.9), Some(0x040506));
assert_eq!(item_bar_color("minecraft:diamond_shovel", 0.5), None);
set_item_bars(&dir.join("none"));
assert_eq!(item_bar_color("minecraft:diamond_pickaxe", 0.5), None);
std::fs::remove_dir_all(&dir).ok();
}
}
+57 -87
View File
@@ -4,6 +4,7 @@
//! Horizon Settings > PvP & HUD. Every option is off by default, so the game looks like
//! vanilla until the player changes one, and none of them shows anything vanilla doesn't
//! already tell the player.
use horizon_hooks::look::Look;
/// Colours the PvP options pick from. `Inverted` is vanilla's crosshair blend: the colour
/// under it, inverted.
@@ -263,15 +264,6 @@ impl ViewWeather {
Self::ALL.into_iter().find(|c| c.name() == s)
}
}
/// The day time this player sees with `fixed` set: the same day (the moon's phase follows the
/// world's), at that time.
pub fn view_day_time(world: i64, fixed: Option<ViewTime>) -> i64 {
match fixed {
Some(t) => world.div_euclid(24000) * 24000 + t.day_time(),
None => world,
}
}
/// The crosshair's look. Its defaults draw vanilla's: a 15-pixel cross, one pixel thick,
/// inverting what is under it.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -528,24 +520,36 @@ impl Hit {
}
}
/// What the renderer reads of these options each frame (`FrameInputs::pvp`); the default is
/// vanilla's look.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Frame {
/// The block outline's stroke ([`Outline::stroke`]).
pub outline: Option<(u32, f32)>,
/// The hurt overlay texel ([`Hit::texel`]), 0 for vanilla's.
pub hurt_texel: u32,
/// The glint's colour ([`Tweaks::glint_texel`]), 0 for vanilla's.
pub glint: u32,
}
impl Pvp {
pub fn frame(&self) -> Frame {
Frame {
outline: self.outline.stroke(),
hurt_texel: self.hit.texel(),
glint: self.tweaks.glint_texel(),
}
/// What the engine reads of these options (`horizon_hooks::look`): all it draws differently.
/// Sets PvP & HUD's fields of `look`, the others' stay.
pub fn apply(&self, look: &mut Look) {
let (c, t) = (self.combat, self.tweaks);
look.legacy_pose = c.legacy;
look.hide_attack_indicator = c.legacy && !c.indicator;
look.mute_attack_sounds = c.legacy && c.sounds;
look.hide_attack_particles = c.legacy;
look.frame.outline = self.outline.stroke();
look.frame.hurt_texel = self.hit.texel();
look.frame.glint = t.glint_texel();
look.shield_size = t.shield_size;
look.totem_scale = f32::from(t.totem_size) / 100.0;
look.fire_lower = f32::from(t.fire_lower) / 100.0;
look.clear_lava = t.clear_lava;
look.clear_water = t.clear_water;
look.chat.timestamps = self.chat.timestamps;
look.chat.compact = self.chat.compact;
look.chat.copy = self.chat.copy;
look.chat.search = self.chat.search;
look.hide_effect_icons = self.hud.effects;
look.time_of_day = t.time.map(ViewTime::day_time);
look.weather = t.weather.map(ViewWeather::levels);
}
/// [`Pvp::apply`] on vanilla's look.
pub fn look(&self) -> Look {
let mut look = Look::default();
self.apply(&mut look);
look
}
}
@@ -741,15 +745,6 @@ pub struct ChatOptions {
/// Ctrl+F in the chat searches the chat's messages.
pub search: bool,
}
impl ChatOptions {
/// What `chat::Chat` lays its lines out with.
pub fn qol(&self) -> crate::chat::ChatQol {
crate::chat::ChatQol {
timestamps: self.timestamps,
compact: self.compact,
}
}
}
/// Combat feel (`docs/pvp.md`): how fights look. Legacy draws them as 1.8.9 did; what the
/// game sends to a server never changes, so a vanilla server and its anticheat see 26.3.
@@ -766,21 +761,6 @@ pub struct Combat {
/// (`horizon_client::legacy_combat`). Never used as a client of a server.
pub rules: bool,
}
impl Combat {
/// Whether this client leaves out sound `name` a server sent: under Legacy with its
/// sounds, the `entity.player.attack.*` family (strong, weak, crit, sweep, knockback,
/// nodamage), which 1.9 brought with the attack cooldown.
pub fn mutes_sound(&self, name: &str) -> bool {
let name = name.strip_prefix("minecraft:").unwrap_or(name);
self.legacy && self.sounds && name.starts_with("entity.player.attack.")
}
/// Whether this client leaves out particles of `ty` a server sent: under Legacy, the
/// sweep and the damage hearts, which 1.9 brought (1.8.9's hits show crits only).
pub fn hides_particle(&self, ty: crate::particles::ParticleType) -> bool {
use crate::particles::ParticleType as T;
self.legacy && matches!(ty, T::SweepAttack | T::DamageIndicator)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Pvp {
@@ -1707,18 +1687,6 @@ mod tests {
assert_eq!(fresh.extras.pvp.tweaks.fire_lower, PRESET_FIRE_LOWER);
}
#[test]
fn a_fixed_time_keeps_the_worlds_day() {
// Day 9 at 5000: the moon's phase (the day number) stays, the time of day is the chosen.
let world = 9 * 24000 + 5000;
assert_eq!(view_day_time(world, None), world);
assert_eq!(
view_day_time(world, Some(ViewTime::Midnight)),
9 * 24000 + 18000
);
assert_eq!(view_day_time(-100, Some(ViewTime::Noon)), -24000 + 6000);
}
#[test]
fn the_glint_colour_reaches_the_shaders_as_rgb_bytes() {
let mut t = Tweaks::default();
@@ -1805,31 +1773,33 @@ mod tests {
}
}
/// Legacy leaves out what 1.9 added to a hit: its sounds only with the sounds option, the
/// sweep and hearts always; the indicator stays when kept. Modern changes nothing.
#[test]
fn legacy_leaves_out_what_1_9_added_to_a_hit() {
use crate::particles::ParticleType as T;
let modern = Combat::default();
let legacy = Combat {
legacy: true,
..Combat::default()
};
let sounds = Combat {
sounds: true,
..legacy
};
for name in [
"minecraft:entity.player.attack.strong",
"entity.player.attack.sweep",
"minecraft:entity.player.attack.nodamage",
] {
assert!(sounds.mutes_sound(name), "{name}");
assert!(!legacy.mutes_sound(name), "only with the sounds option");
assert!(!modern.mutes_sound(name));
}
// 1.8.9 had the hurt sound: it stays.
assert!(!sounds.mutes_sound("minecraft:entity.player.hurt"));
assert!(legacy.hides_particle(T::SweepAttack) && legacy.hides_particle(T::DamageIndicator));
assert!(!legacy.hides_particle(T::Crit) && !legacy.hides_particle(T::EnchantedHit));
assert!(!modern.hides_particle(T::SweepAttack));
fn combat_feel_reaches_the_look() {
let mut pvp = Pvp::default();
assert_eq!(pvp.look(), Look::default(), "vanilla's look");
let strong = "minecraft:entity.player.attack.strong";
pvp.combat.legacy = true;
let look = pvp.look();
assert!(look.legacy_pose && look.hide_attack_particles && look.hide_attack_indicator);
assert!(!look.mutes_sound(strong), "only with the sounds option");
pvp.combat.sounds = true;
pvp.combat.indicator = true;
let look = pvp.look();
assert!(look.mutes_sound(strong) && !look.hide_attack_indicator);
}
#[test]
fn view_time_weather_and_sizes_reach_the_look() {
let mut pvp = Pvp::default();
pvp.tweaks.time = Some(ViewTime::Midnight);
pvp.tweaks.weather = Some(ViewWeather::Thunder);
pvp.tweaks.totem_size = 50;
pvp.tweaks.fire_lower = 40;
let look = pvp.look();
assert_eq!(look.day_time(9 * 24000 + 5000), 9 * 24000 + 18000);
assert_eq!(look.weather, Some((1.0, 1.0)));
assert_eq!((look.totem_scale, look.fire_lower), (0.5, 0.4));
}
}
@@ -337,6 +337,13 @@ impl Settings {
.and_then(|mode| self.video_modes.iter().position(|m| *m == mode))
.map_or(-1, |i| i as i32)
}
/// Loads only the crates' settings stores (`horizon_hooks::settings`) from the files next to
/// `path`, into the seat whose turn it is (`horizon_hooks::seat`): a splitscreen player whose
/// settings were made from P1's rather than loaded.
pub fn load_stores(path: &Path) {
let dir = path.parent();
horizon_hooks::settings::engine::load(&mut |file| fs::read_to_string(dir?.join(file)).ok());
}
pub fn load(path: &Path) -> Self {
let mut horizon = path
.parent()
@@ -362,6 +369,13 @@ impl Settings {
.and_then(|dir| fs::read_to_string(dir.join(crate::extras::FILE)).ok())
.unwrap_or_default();
let mut extras = crate::extras::Extras::parse(&extras_text);
// The settings stores of the crates above the engine: their lines of this file, or of
// their own (docs/layers.md, "Platform registries").
horizon_hooks::settings::engine::load(&mut |file| match path.parent() {
_ if file == crate::extras::FILE => Some(extras_text.clone()),
Some(dir) => fs::read_to_string(dir.join(file)).ok(),
None => None,
});
// The enabled mods are the game folder's, shared by its players (`--player-dir`).
let mods = path
.parent()
@@ -690,6 +704,7 @@ impl Settings {
let mut extras = self.extras.clone();
extras.emote_key = self.keys.get("key.emote").to_owned();
extras.save(dir);
horizon_hooks::settings::engine::save_own(dir, &[crate::extras::FILE]);
self.mods.save(&crate::logging::shared_dir(dir));
}
}
+10 -45
View File
@@ -549,6 +549,9 @@ pub struct Streaming {
pub mod_forced: HashSet<(i32, i32)>,
/// No work is scheduled while paused (title screen).
pub paused: bool,
/// Finished meshes the renderer has not taken yet (the game's pending
/// uploads): they count against the meshing room as the jobs do.
pub upload_backlog: usize,
/// Dimension flags: sky light, Nether face shading.
pub sky_light: bool,
pub nether_shading: bool,
@@ -1062,7 +1065,9 @@ pub struct WorkerContext {
pub biomes: Arc<crate::biome::Biomes>,
pub light: Arc<LightTables>,
}
mod bounded;
mod relight_now;
use bounded::nearest_first;
impl Streaming {
pub fn new<G: ChunkGenerator>(
generator: Arc<G>,
@@ -1147,6 +1152,7 @@ impl Streaming {
forced: HashSet::new(),
mod_forced: HashSet::new(),
paused: false,
upload_backlog: 0,
sky_light: true,
nether_shading: false,
shader_pack_lighting: false,
@@ -2083,7 +2089,10 @@ impl Streaming {
// jobs went stale long before a worker reached them (#3629). What
// stays dirty is asked for again, from the current centre, as
// capacity frees; `requeue` then has a small queue to keep close.
let room = (self.workers * 32).saturating_sub(self.meshing.len());
// Meshes waiting for upload count too: when uploads fall behind (a
// low frame rate at a wide distance), meshing on held gigabytes of
// them until the system swapped.
let room = (self.workers * 32).saturating_sub(self.meshing.len() + self.upload_backlog);
nearest_first(&mut candidates, room);
for (_, x, z, sections) in candidates.drain(..) {
let Some(region) = Self::region(world, x, z) else {
@@ -3021,19 +3030,6 @@ fn light_region_ready(
}
true
}
/// The nearest `room` of the `candidates`, ordered: the light and mesh
/// stages submit bounded work, chosen from everything that is ready each
/// `update`, so what runs is always the nearest of what is left — chosen
/// again, from the current centre, as capacity frees, the way vanilla's
/// chunk task queues follow the current ticket levels. The selection is
/// O(n); the sort orders only what is submitted.
fn nearest_first<T: Ord>(candidates: &mut Vec<T>, room: usize) {
if candidates.len() > room {
candidates.select_nth_unstable(room);
candidates.truncate(room);
}
candidates.sort_unstable();
}
/// What `keep_unchanged_sections` found changed.
#[derive(Debug, Default, PartialEq)]
struct Resent {
@@ -3586,37 +3582,6 @@ mod tests {
assert_eq!(pops(queue), [None]);
}
/// The light and mesh stages submit bounded work, the nearest first:
/// what exceeds the room stays dirty, to be chosen again — from the
/// current centre — as capacity frees. Submitted whole, the queue once
/// held every ready column's jobs and grew with the render distance
/// (#3629); ties break by position, so the choice is deterministic.
#[test]
fn bounded_submission_takes_the_nearest_first() {
let candidates: Vec<(i64, i32, i32, u32)> = (0..40i32)
.map(|i| {
let (x, z) = (i / 8 - 2, i % 8 - 3);
(
i64::from((x - 3).pow(2) + (z - 2).pow(2)),
x,
z,
1 << (i % 24),
)
})
.collect();
let mut expected = candidates.clone();
expected.sort_unstable();
let mut none = candidates.clone();
nearest_first(&mut none, 0);
assert!(none.is_empty());
let mut nearest = candidates;
nearest_first(&mut nearest, 5);
assert_eq!(nearest, expected[..5]);
let mut past = expected.clone();
nearest_first(&mut past, expected.len() + 10);
assert_eq!(past, expected);
}
/// What the scheduler counts as ready to light: every neighbour a
/// finished column, or a decorated proto chunk past the area — the
/// walk `light_region` makes without its snapshot.
@@ -0,0 +1,52 @@
//! Bounded submission: the light and mesh stages hand the workers only the
//! nearest of what is ready.
/// The nearest `room` of the `candidates`, ordered: the light and mesh
/// stages submit bounded work, chosen from everything that is ready each
/// `update`, so what runs is always the nearest of what is left — chosen
/// again, from the current centre, as capacity frees, the way vanilla's
/// chunk task queues follow the current ticket levels. The selection is
/// O(n); the sort orders only what is submitted.
pub(super) fn nearest_first<T: Ord>(candidates: &mut Vec<T>, room: usize) {
if candidates.len() > room {
candidates.select_nth_unstable(room);
candidates.truncate(room);
}
candidates.sort_unstable();
}
#[cfg(test)]
mod tests {
use super::*;
/// The light and mesh stages submit bounded work, the nearest first:
/// what exceeds the room stays dirty, to be chosen again — from the
/// current centre — as capacity frees. Submitted whole, the queue once
/// held every ready column's jobs and grew with the render distance
/// (#3629); ties break by position, so the choice is deterministic.
#[test]
fn bounded_submission_takes_the_nearest_first() {
let candidates: Vec<(i64, i32, i32, u32)> = (0..40i32)
.map(|i| {
let (x, z) = (i / 8 - 2, i % 8 - 3);
(
i64::from((x - 3).pow(2) + (z - 2).pow(2)),
x,
z,
1 << (i % 24),
)
})
.collect();
let mut expected = candidates.clone();
expected.sort_unstable();
let mut none = candidates.clone();
nearest_first(&mut none, 0);
assert!(none.is_empty());
let mut nearest = candidates;
nearest_first(&mut nearest, 5);
assert_eq!(nearest, expected[..5]);
let mut past = expected.clone();
nearest_first(&mut past, expected.len() + 10);
assert_eq!(past, expected);
}
}
+38 -6
View File
@@ -138,6 +138,10 @@ fn scaling(sprite: &str, r: &Rect) -> NineSlice {
| "horizon/jei/button_disabled_v2"
| "horizon/jei/button_highlight_v2" => [6; 4],
"horizon/jei/search_background_v2" => [6; 4],
"horizon/jei/interactive_ingredient_tooltip_background" => [16; 4],
"horizon/jei/recipe_preview_background_v2" => [16; 4],
"horizon/jei/scrollbar_background_v2" => [6; 4],
"horizon/jei/scrollbar_marker_v2" => [2, 2, 2, 1],
"gui/sprites/widget/button"
| "gui/sprites/widget/button_highlighted"
| "gui/sprites/widget/button_disabled" => [3; 4],
@@ -806,8 +810,14 @@ fn slider_sprites(hover: bool, active: bool, dragging: bool) -> (&'static str, &
slider_sprites_ext(hover, active, false, false, dragging)
}
/// Track and handle sprites: the track outline shows while dragged or focused with
/// keyboard navigation (!can_change_value), hovering or editing highlights the handle.
/// `AbstractSliderButton.getSprite` and `getHandleSprite`: the track outline
/// lights while the slider is focused and its value cannot change
/// (`isActive() && isFocused() && !canChangeValue`), the handle while it is
/// hovered or its value can change (`isActive() && (isHovered || canChangeValue)`).
/// `canChangeValue` is set when the slider is focused with the mouse or Tab and
/// flipped by Enter (`setFocused`, `keyPressed`): Horizon's `can_change_value`
/// for the keyboard, and a drag for the mouse, which focuses it with the value
/// free to change. So a dragged slider keeps its plain track.
fn slider_sprites_ext(
hover: bool,
active: bool,
@@ -815,12 +825,13 @@ fn slider_sprites_ext(
can_change_value: bool,
dragging: bool,
) -> (&'static str, &'static str) {
let track = if dragging || (active && focused && !can_change_value) {
let can_change_value = can_change_value || dragging;
let track = if active && focused && !can_change_value {
"gui/sprites/widget/slider_highlighted"
} else {
"gui/sprites/widget/slider"
};
let handle = if active && (hover || can_change_value || dragging) {
let handle = if active && (hover || can_change_value) {
"gui/sprites/widget/slider_handle_highlighted"
} else {
"gui/sprites/widget/slider_handle"
@@ -2171,17 +2182,38 @@ mod tests {
}
}
/// `AbstractSliderButton.getSprite`/`getHandleSprite`: a focused slider
/// whose value can't change lights its track, a hovered one or one whose
/// value can change (Tab or Enter, or the mouse's drag) lights its handle.
#[test]
fn slider_track_is_outlined_only_while_dragged() {
fn slider_sprites_follow_focus_and_the_drag() {
// Hovered, at rest: plain track, lit handle.
let (track, handle) = slider_sprites(true, true, false);
assert_eq!(track, "gui/sprites/widget/slider");
assert_eq!(handle, "gui/sprites/widget/slider_handle_highlighted");
// Dragged: still a plain track, lit handle.
let (track, handle) = slider_sprites(true, true, true);
assert_eq!(track, "gui/sprites/widget/slider_highlighted");
assert_eq!(track, "gui/sprites/widget/slider");
assert_eq!(handle, "gui/sprites/widget/slider_handle_highlighted");
// Focused with the arrow keys: the value can't change, the track lights.
let (track, handle) = slider_sprites_ext(false, true, true, false, false);
assert_eq!(track, "gui/sprites/widget/slider_highlighted");
assert_eq!(handle, "gui/sprites/widget/slider_handle");
// Focused with Tab, or Enter pressed on it: the value can change, the handle lights.
let (track, handle) = slider_sprites_ext(false, true, true, true, false);
assert_eq!(track, "gui/sprites/widget/slider");
assert_eq!(handle, "gui/sprites/widget/slider_handle_highlighted");
// Focused while the mouse drags: the track stays plain.
let (track, _) = slider_sprites_ext(true, true, true, false, true);
assert_eq!(track, "gui/sprites/widget/slider");
// Not hovered, not dragged: nothing is lit.
let (track, handle) = slider_sprites(false, true, false);
assert_eq!(track, "gui/sprites/widget/slider");
assert_eq!(handle, "gui/sprites/widget/slider_handle");
// An inactive slider highlights neither, even when focused.
let (track, handle) = slider_sprites_ext(true, false, true, true, true);
assert_eq!(track, "gui/sprites/widget/slider");
assert_eq!(handle, "gui/sprites/widget/slider_handle");
}
#[test]
fn separator_tiles_sample_both_rows_and_clip_the_last_column() {
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Lig skaduwees na jou skerm se donkerste swart. 0: suiwer swart, geen ligting.",
"How bright highlights get. Auto: the peak your system reports.": "Hoe helder hoogtepunte word. Outo: die hoogtepunt wat jou stelsel rapporteer.",
"How bright white looks in the world. Auto: your desktop's white.": "Hoe helder wit in die wêreld lyk. Outo: jou lessenaar se wit.",
"How bright the HUD and menus are, against paper white.": "Hoe helder die HUD en spyskaarte is, teenoor papierwit.",
"How far mobs, crops and machines keep working. CPU time.": "Hoe ver skare, gewasse en masjiene aanhou werk. SVE tyd.",
"How far away mobs and items are drawn, as a share of normal.": "Hoe ver weg skares en items getrek word, as 'n deel van normaal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Slaan terrein oor wat agter geslote gedeeltes versteek is. Af kos baie.",
@@ -367,7 +366,6 @@
"Advanced": "Gevorderd",
"Undo": "Ontdoen",
"Search options": "Soek opsies",
"Shortcut": "Kortpad",
"Distance": "Afstand",
"Display": "Vertoon",
"Interface": "Koppelvlak",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibreer...",
"Calibrate Anyway...": "Kalibreer in elk geval...",
"Use Automatic Setup": "Gebruik outomatiese opstelling",
"Shortcut: %s": "Kortpad: %s",
"Next": "Volgende",
"HDR Calibration %s/%s: %s": "HDR-kalibrasie %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Stel die drie hieronder volskerm, een toetsprent op 'n slag.",
"A key that opens this screen from the game. Not bound at first.": "'n Sleutel wat hierdie skerm vanaf die speletjie oopmaak. Eers nie gebonde nie.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Jou skerm het nie gesê hoe helder dit word nie: kalibreer om dit op te stel.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Reeds opgestel vir jou vertoning. Kalibreer slegs as HDR verkeerd lyk.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Plaas Peak Brightness en Paper White terug na jou skerm se opstelling.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "يرفع الظلال إلى أغمق أسود في شاشتك. 0: أسود حقيقي، دون رفع.",
"How bright highlights get. Auto: the peak your system reports.": "كيف تحصل على النقاط البارزة الساطعة. تلقائي: ذروة تقارير النظام الخاص بك.",
"How bright white looks in the world. Auto: your desktop's white.": "مدى سطوع الأبيض في العالم. تلقائي: أبيض سطح المكتب.",
"How bright the HUD and menus are, against paper white.": "مدى سطوع شاشة HUD والقوائم مقابل الورق الأبيض.",
"How far mobs, crops and machines keep working. CPU time.": "إلى أي مدى يستمر الغوغاء والمحاصيل والآلات في العمل. وقت وحدة المعالجة المركزية.",
"How far away mobs and items are drawn, as a share of normal.": "إلى أي مدى يتم رسم الغوغاء والعناصر، كنسبة من المعدل الطبيعي.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "يتخطى التضاريس المخفية خلف الأقسام المغلقة. قبالة تكاليف الكثير.",
@@ -367,7 +366,6 @@
"Advanced": "متقدم",
"Undo": "تراجع",
"Search options": "خيارات البحث",
"Shortcut": "الاختصار",
"Distance": "المسافة",
"Display": "عرض",
"Interface": "واجهة",
@@ -438,11 +436,9 @@
"Calibrate...": "معايرة...",
"Calibrate Anyway...": "معايرة على أية حال...",
"Use Automatic Setup": "استخدم الإعداد التلقائي",
"Shortcut: %s": "الاختصار: %s",
"Next": "التالي",
"HDR Calibration %s/%s: %s": "معايرة HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "لتعيين الصور الثلاثة أدناه بملء الشاشة، صورة اختبارية واحدة في كل مرة.",
"A key that opens this screen from the game. Not bound at first.": "المفتاح الذي يفتح هذه الشاشة من اللعبة. غير ملزمة في البداية.",
"Your display didn't say how bright it gets: calibrate to set it up.": "لم توضح شاشتك مدى سطوعها: قم بالمعايرة لإعدادها.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "تم إعداده بالفعل للعرض الخاص بك. قم بالمعايرة فقط إذا كان تقرير HDR يبدو خاطئًا.",
"Puts Peak Brightness and Paper White back to your display's setup.": "يعيد ذروة السطوع والورق الأبيض إلى إعداد شاشتك.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Kölgələri ekranınızın ən tünd qarasına qaldırır. 0: həqiqi qara, qaldırma yoxdur.",
"How bright highlights get. Auto: the peak your system reports.": "Parlaq məqamlar necə əldə edilir. Avtomatik: sisteminizin ən yüksək hesabatı.",
"How bright white looks in the world. Auto: your desktop's white.": "Dünyada ağ rəngin nə qədər parlaq göründüyü. Avtomatik: iş masanızın ağı.",
"How bright the HUD and menus are, against paper white.": "HUD və menyular ağ kağıza qarşı nə qədər parlaqdır.",
"How far mobs, crops and machines keep working. CPU time.": "Kütlələr, məhsullar və maşınlar nə qədər işləməyə davam edir. CPU vaxtı.",
"How far away mobs and items are drawn, as a share of normal.": "Nə qədər uzağa mob və əşyalar çəkilir, normal bir pay olaraq.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Qapalı hissələrin arxasında gizlənmiş ərazini ötür. Sıxılma çox baha başa gəlir.",
@@ -367,7 +366,6 @@
"Advanced": "Qabaqcıl",
"Undo": "Geri alın",
"Search options": "Axtarış seçimləri",
"Shortcut": "Qısayol",
"Distance": "Məsafə",
"Display": "Ekran",
"Interface": "İnterfeys",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibrləmə...",
"Calibrate Anyway...": "İstənilən halda kalibrləyin...",
"Use Automatic Setup": "Avtomatik Quraşdırmadan istifadə edin",
"Shortcut: %s": "Qısayol: %s",
"Next": "Sonrakı",
"HDR Calibration %s/%s: %s": "HDR Kalibrləmə %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Aşağıdakı üç tam ekranı, hər dəfə bir sınaq şəklini təyin edir.",
"A key that opens this screen from the game. Not bound at first.": "Oyundan bu ekranı açan açar. Əvvəlcə bağlı deyil.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Ekranınız nə qədər parlaq olduğunu deməyib: onu qurmaq üçün kalibrləyin.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Ekranınız üçün artıq qurulub. Yalnız HDR səhv görünürsə kalibrləyin.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Pik Parlaqlığı və Kağız Ağı ekranınızın quraşdırılmasına qaytarır.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Күләгәләрҙе дисплейығыҙҙың иң ҡара ҡараһына күтәрә. 0: ысын ҡара, күтәреү юҡ.",
"How bright highlights get. Auto: the peak your system reports.": "Нисек сағыу өҫтөнлөктәр ала. Авто: пик һеҙҙең система отчеттар.",
"How bright white looks in the world. Auto: your desktop's white.": "Донъяла аҡ ни тиклем сағыу күренә. Авто: эш өҫтәлегеҙҙең ағы.",
"How bright the HUD and menus are, against paper white.": "Нисек сағыу HUD һәм менюлар, ҡағыҙ аҡ ҡаршы.",
"How far mobs, crops and machines keep working. CPU time.": "Күпме алыҫҡа мобтар, культуралар һәм машиналар эшләп тора. Процессор ваҡыты.",
"How far away mobs and items are drawn, as a share of normal.": "Нисек алыҫҡа мобтар һәм әйберҙәр тартыла, өлөшө булараҡ нормаль.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Ябыҡ участкалар артында йәшеренгән ерҙәрҙе һикерә. Офф күп сығымдар талап итә.",
@@ -367,7 +366,6 @@
"Advanced": "Алдынғы",
"Undo": "Кире ҡайтарыу",
"Search options": "Эҙләү варианттары",
"Shortcut": "Ярлыҡ",
"Distance": "Арауыҡ",
"Display": "Дисплей",
"Interface": "Интерфейс",
@@ -438,11 +436,9 @@
"Calibrate...": "Калибрлау...",
"Calibrate Anyway...": "Калибрлау барыбер...",
"Use Automatic Setup": "Автоматик ҡуйыуҙы ҡулланыу",
"Shortcut: %s": "Ҡыҫҡа юл: %s",
"Next": "Артабан",
"HDR Calibration %s/%s: %s": "HDR калибровкаһы %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Өс аҫтында тулы экран ҡуя, бер һынау һүрәте бер юлы.",
"A key that opens this screen from the game. Not bound at first.": "Уйындан был экранды асҡан асҡыс. Тәүҙә бәйләнмәгән.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Һеҙҙең дисплей нисек яҡты ала, тип әйтмәне: калибровка, уны ҡуйырға.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Һеҙҙең дисплей өсөн инде ҡуйылған. Тик калибровка, әгәр HDR дөрөҫ түгел күренә.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Ҡуя Пик Яркость һәм Ҡағыҙ Аҡ кире һеҙҙең дисплей&#8217;ы ҡороу.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Падымае цені да самага цёмнага чорнага вашага дысплея. 0: сапраўдны чорны, без пад'ёму.",
"How bright highlights get. Auto: the peak your system reports.": "Якія яркія моманты становяцца. Аўта: пік, пра які паведамляе ваша сістэма.",
"How bright white looks in the world. Auto: your desktop's white.": "Наколькі ярка выглядае белы ў свеце. Аўто: белы колер вашага працоўнага стала.",
"How bright the HUD and menus are, against paper white.": "Наколькі яркія HUD і меню на фоне белай паперы.",
"How far mobs, crops and machines keep working. CPU time.": "Як далёка працягваюць працаваць мобы, ураджай і машыны. працэсарны час.",
"How far away mobs and items are drawn, as a share of normal.": "Наколькі далёка намаляваны мобы і прадметы ў долі звычайнага.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Прапускае мясцовасць, схаваную за закрытымі ўчасткамі. Off каштуе шмат.",
@@ -367,7 +366,6 @@
"Advanced": "Пашыраны",
"Undo": "Адмяніць",
"Search options": "Параметры пошуку",
"Shortcut": "Ярлык",
"Distance": "Дыстанцыя",
"Display": "Дысплей",
"Interface": "Інтэрфейс",
@@ -438,11 +436,9 @@
"Calibrate...": "Адкалібраваць...",
"Calibrate Anyway...": "Усё роўна адкалібраваць...",
"Use Automatic Setup": "Выкарыстоўвайце аўтаматычную наладу",
"Shortcut: %s": "Ярлык: %s",
"Next": "Далей",
"HDR Calibration %s/%s: %s": "Каліброўка HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Устанаўлівае тры ніжэй на ўвесь экран, па адным тэставым малюнку.",
"A key that opens this screen from the game. Not bound at first.": "Ключ, які адкрывае гэты экран з гульні. Спачатку не звязаны.",
"Your display didn't say how bright it gets: calibrate to set it up.": "На вашым дысплеі не пазначана, наколькі ён яркім: адкалібруйце, каб наладзіць яго.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Ужо наладжана для вашага дысплея. Калібруйце, толькі калі HDR выглядае няправільна.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Вяртае пікавую яркасць і папяровы белы ў налады вашага дысплея.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Повдига сенките до най-тъмното черно на вашия дисплей. 0: истинско черно, без повдигане.",
"How bright highlights get. Auto: the peak your system reports.": "Колко ярки стават акцентите. Автоматично: пикът, който вашата система отчита.",
"How bright white looks in the world. Auto: your desktop's white.": "Колко ярко изглежда бялото в света. Авто: бялото на вашия работен плот.",
"How bright the HUD and menus are, against paper white.": "Колко ярки са HUD и менютата на бяла хартия.",
"How far mobs, crops and machines keep working. CPU time.": "Докъде продължават да работят тълпите, културите и машините. процесорно време.",
"How far away mobs and items are drawn, as a share of normal.": "Колко далеч са изтеглени тълпите и предметите, като дял от нормалното.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Пропуска терен, скрит зад затворени участъци. Off струва много.",
@@ -367,7 +366,6 @@
"Advanced": "Разширено",
"Undo": "Отмяна",
"Search options": "Опции за търсене",
"Shortcut": "Пряк път",
"Distance": "Разстояние",
"Display": "Дисплей",
"Interface": "Интерфейс",
@@ -438,11 +436,9 @@
"Calibrate...": "Калибриране...",
"Calibrate Anyway...": "Калибриране все пак...",
"Use Automatic Setup": "Използвайте автоматична настройка",
"Shortcut: %s": "Пряк път: %s",
"Next": "Следваща",
"HDR Calibration %s/%s: %s": "HDR калибриране %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Задава трите под цял екран, една тестова картина наведнъж.",
"A key that opens this screen from the game. Not bound at first.": "Ключ, който отваря този екран от играта. В началото не е обвързан.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Вашият дисплей не каза колко ярък става: калибрирайте, за да го настроите.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Вече е настроен за вашия дисплей. Калибрирайте само ако HDR изглежда грешно.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Връща Peak Brightness и Paper White обратно към настройката на вашия дисплей.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Sevel a ra ar skeudoù betek du teñvalañ ho skramm. 0 : du gwir, sevel ebet.",
"How bright highlights get. Auto: the peak your system reports.": "Pegen skedus e teu ar sklêrijennoù. Emgefre : ar pik a vez embannet gant ho reizhiad.",
"How bright white looks in the world. Auto: your desktop's white.": "Pegen skedus e seblant ar gwenn er bed. Emgefreek : gwenn ho kefridi.",
"How bright the HUD and menus are, against paper white.": "Pegen skedus eo an HUD hag ar mennozhioù, a-enep gwenn ar paper.",
"How far mobs, crops and machines keep working. CPU time.": "Pegen pell e kendalc'h ar mobs, ar c'hoadoù hag ar mekanikoù da labourat. Amzer ar CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Pegen pell e vez tennet ar mobs hag an traoù, evel ul lodenn eus ar pezh a vez graet normal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Tremen a ra an douaroù kuzhet a-dreñv rannoù serr. Koustañ a ra kalz.",
@@ -367,7 +366,6 @@
"Advanced": "Araokaet",
"Undo": "Dizober",
"Search options": "Dibarzhioù klask",
"Shortcut": "Berradennoù",
"Distance": "Pellder",
"Display": "Diskouez",
"Interface": "Etrefas",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibrañ...",
"Calibrate Anyway...": "Kalibrañ koulskoude...",
"Use Automatic Setup": "Implijout arventennoù emgefre",
"Shortcut: %s": "Berradennoù : %s",
"Next": "Da heul",
"HDR Calibration %s/%s: %s": "Kalibradur HDR %s/%s : %s",
"Sets the three below full screen, one test picture at a time.": "Lakaat a ra an tri skramm leun a-is, ur skeudenn amprouiñ bep tro.",
"A key that opens this screen from the game. Not bound at first.": "Un alc'hwez a zigor ar skramm-mañ eus ar c'hoari. N'eo ket stag da gentañ.",
"Your display didn't say how bright it gets: calibrate to set it up.": "N'eo ket bet embannet gant ho skramm pegen sklaer e teu da vezañ : kalibret evit e lakaat war-sav.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Kefluniet dija evit ho skramm. Kalibret nemet ma seblant HDR bezañ fazius.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Lakaat a ra ar sked uhelañ hag ar gwenn paper en-dro e kefluniadur ho skramm.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Podiže sjene do najcrnje crne vašeg ekrana. 0: prava crna, bez podizanja.",
"How bright highlights get. Auto: the peak your system reports.": "Kako svetli naglasci postaju. Automatski: vrhunac o kojem vaš sistem prijavljuje.",
"How bright white looks in the world. Auto: your desktop's white.": "Koliko svijetlo izgleda bijelo u svijetu. Automatski: bijela vaše radne površine.",
"How bright the HUD and menus are, against paper white.": "Koliko su svijetli HUD i meniji, naspram bijelog papira.",
"How far mobs, crops and machines keep working. CPU time.": "Koliko mobovi, usjevi i mašine rade. CPU vrijeme.",
"How far away mobs and items are drawn, as a share of normal.": "Koliko su udaljeni mobovi i predmeti, kao udio normalnog.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Preskače teren skriven iza zatvorenih dijelova. Isključenje košta puno.",
@@ -367,7 +366,6 @@
"Advanced": "Napredno",
"Undo": "Poništi",
"Search options": "Opcije pretraživanja",
"Shortcut": "Prečica",
"Distance": "Udaljenost",
"Display": "Ekran",
"Interface": "Interfejs",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibriraj...",
"Calibrate Anyway...": "Kalibriraj svejedno...",
"Use Automatic Setup": "Koristite automatsko podešavanje",
"Shortcut: %s": "Prečica: %s",
"Next": "Sledeći",
"HDR Calibration %s/%s: %s": "HDR kalibracija %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Postavlja tri ispod preko celog ekrana, jednu po jednu probnu sliku.",
"A key that opens this screen from the game. Not bound at first.": "Taster koji otvara ovaj ekran iz igre. U početku nije vezan.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Vaš displej nije rekao koliko svetli: kalibrirajte da biste ga postavili.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Već postavljeno za vaš ekran. Kalibrirajte samo ako HDR izgleda pogrešno.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Vraća Peak Brightness i Paper White u postavke vašeg ekrana.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Puja les ombres fins al negre més fosc de la teva pantalla. 0: negre pur, sense pujar.",
"How bright highlights get. Auto: the peak your system reports.": "Com es fan els reflexos brillants. Automàtic: el pic que informa el vostre sistema.",
"How bright white looks in the world. Auto: your desktop's white.": "Com de brillant es veu el blanc al món. Automàtic: el blanc de l'escriptori.",
"How bright the HUD and menus are, against paper white.": "Què brillants són l'HUD i els menús, contra el paper blanc.",
"How far mobs, crops and machines keep working. CPU time.": "Fins on segueixen treballant les turbes, els conreus i les màquines. Temps de CPU.",
"How far away mobs and items are drawn, as a share of normal.": "A quina distància es dibuixen les turbes i els articles, com a part de la normalitat.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Salta el terreny amagat darrere de seccions tancades. Off costa molt.",
@@ -367,7 +366,6 @@
"Advanced": "Avançat",
"Undo": "Desfer",
"Search options": "Opcions de cerca",
"Shortcut": "Drecera",
"Distance": "Distància",
"Display": "Mostra",
"Interface": "Interfície",
@@ -438,11 +436,9 @@
"Calibrate...": "Calibrar...",
"Calibrate Anyway...": "Calibra de totes maneres...",
"Use Automatic Setup": "Utilitzeu la configuració automàtica",
"Shortcut: %s": "Drecera: %s",
"Next": "A continuació",
"HDR Calibration %s/%s: %s": "Calibració HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Estableix les tres imatges de sota de pantalla completa, una imatge de prova a la vegada.",
"A key that opens this screen from the game. Not bound at first.": "Una clau que obre aquesta pantalla des del joc. No lligat al principi.",
"Your display didn't say how bright it gets: calibrate to set it up.": "La pantalla no va dir la brillantor que té: calibra per configurar-la.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Ja està configurat per a la vostra pantalla. Calibreu només si l'HDR sembla incorrecte.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Torna a posar la brillantor màxima i el blanc del paper a la configuració de la pantalla.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Zvedne stíny na nejtemnější černou vašeho displeje. 0: skutečná černá, bez zvednutí.",
"How bright highlights get. Auto: the peak your system reports.": "Jak se získá jasný melír. Auto: vrchol, který váš systém hlásí.",
"How bright white looks in the world. Auto: your desktop's white.": "Jak jasně bílá ve světě vypadá. Auto: bílá vaší plochy.",
"How bright the HUD and menus are, against paper white.": "Jak jasný je HUD a nabídky oproti bílé na papíře.",
"How far mobs, crops and machines keep working. CPU time.": "Jak dalece davy, plodiny a stroje fungují. CPU čas.",
"How far away mobs and items are drawn, as a share of normal.": "Jak daleko jsou mobové a předměty nakresleny jako podíl normálu.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Přeskočí terén skrytý za uzavřenými úseky. Vypnutí stojí hodně.",
@@ -367,7 +366,6 @@
"Advanced": "Pokročilé",
"Undo": "Vrátit zpět",
"Search options": "Možnosti vyhledávání",
"Shortcut": "Zkratka",
"Distance": "Vzdálenost",
"Display": "Displej",
"Interface": "Rozhraní",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibrovat...",
"Calibrate Anyway...": "Přesto kalibrovat...",
"Use Automatic Setup": "Použijte Automatické nastavení",
"Shortcut: %s": "Zkratka: %s",
"Next": "Další",
"HDR Calibration %s/%s: %s": "Kalibrace HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Nastaví tři pod celou obrazovkou, vždy jeden testovací obrázek.",
"A key that opens this screen from the game. Not bound at first.": "Klíč, který otevře tuto obrazovku ze hry. Zpočátku bez vazby.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Váš displej neříkal, jak jasný je: zkalibrujte jej, abyste jej nastavili.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Již nastaveno pro váš displej. Kalibrujte pouze v případě, že HDR vypadá špatně.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Vrátí maximální jas a papírovou bílou zpět do nastavení vašeho displeje.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Мӗлкесене дисплейӑн чи тӗттӗм хури ҫине ҫӗклет. 0: чӑн хура, ҫӗклевсӗр.",
"How bright highlights get. Auto: the peak your system reports.": "Мӗнле ҫутӑ ҫутӑсем пулаҫҫӗ. Авто: сирӗн системӑн пӗлтерекен пикӗ.",
"How bright white looks in the world. Auto: your desktop's white.": "Тӗнчере шурӑ мӗнле ҫутӑ курӑнать. Авто: эсир рабочий столӑн шурри.",
"How bright the HUD and menus are, against paper white.": "HUD тата менюсем мӗнле ҫутӑ, хут ҫинчи шурӑ тӗспе танлаштарсан.",
"How far mobs, crops and machines keep working. CPU time.": "Мобсем, культурӑсем тата машинӑсем мӗн таран ӗҫлеҫҫӗ. Процессор вӑхӑчӗ.",
"How far away mobs and items are drawn, as a share of normal.": "Мобсемпе япаласем мӗн чухлӗ аяккалла туртӑнаҫҫӗ, йӗркеллӗ пай пек.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Хупӑ участоксем хыҫӗнче пытаннӑ ҫӗре сиктерсе хӑварать. Офф нумай тӑкакланать.",
@@ -367,7 +366,6 @@
"Advanced": "Малалла",
"Undo": "Каялла тавӑр",
"Search options": "Шырав опцийӗсем",
"Shortcut": "Кӗске ҫул",
"Distance": "Инҫет",
"Display": "Дисплей",
"Interface": "Интерфейс",
@@ -438,11 +436,9 @@
"Calibrate...": "Калибровка...",
"Calibrate Anyway...": "Ҫапах та калибровка...",
"Use Automatic Setup": "Автоматла настройкӑпа усӑ курӑр",
"Shortcut: %s": "Кӗске ҫул: %s",
"Next": "Малалла",
"HDR Calibration %s/%s: %s": "HDR калибровка %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Аялта виҫҫӗшне тулли экран ҫине лартать, пӗрер тӗрӗслев ӳкерчӗкӗ.",
"A key that opens this screen from the game. Not bound at first.": "Вӑйӑран ҫак экрана уҫакан уҫҫи. Малтанах ҫыхӑнман.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Сирӗн дисплей ҫинче вӑл мӗнле ҫутӑ пулнине каламан: ӑна лартма калибровка тумалла.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Сирӗн дисплей валли хатӗрленӗ ӗнтӗ. HDR тӗрӗс мар курӑнсан ҫеҫ калибровка тӑвӑр.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Чи пысӑк ҫутӑлӑха тата хут шуррине сирӗн дисплей настройкине каялла тавӑрать.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Yn codi cysgodion i ddu tywyllaf eich arddangosfa. 0: du go iawn, dim codi.",
"How bright highlights get. Auto: the peak your system reports.": "Sut mae uchafbwyntiau disglair yn ei gael. Auto: yr uchafbwynt y mae eich system yn ei adrodd.",
"How bright white looks in the world. Auto: your desktop's white.": "Pa mor llachar y mae gwyn yn edrych yn y byd. Awtomatig: gwyn eich bwrdd gwaith.",
"How bright the HUD and menus are, against paper white.": "Pa mor llachar yw'r HUD a'r bwydlenni, yn erbyn papur gwyn.",
"How far mobs, crops and machines keep working. CPU time.": "Pa mor bell y mae mobs, cnydau a pheiriannau yn dal i weithio. Amser CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Pa mor bell i ffwrdd mae mobs ac eitemau yn cael eu tynnu, fel cyfran o'r arferol.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Sgipiau tir wedi'i guddio y tu ôl i adrannau caeedig. Mae i ffwrdd yn costio llawer.",
@@ -367,7 +366,6 @@
"Advanced": "Uwch",
"Undo": "Dadwneud",
"Search options": "Dewisiadau chwilio",
"Shortcut": "Llwybr byr",
"Distance": "Pellter",
"Display": "Arddangos",
"Interface": "Rhyngwyneb",
@@ -438,11 +436,9 @@
"Calibrate...": "Graddnodi...",
"Calibrate Anyway...": "Calibro beth bynnag...",
"Use Automatic Setup": "Defnyddiwch Gosodiad Awtomatig",
"Shortcut: %s": "Llwybr byr: %s",
"Next": "Nesaf",
"HDR Calibration %s/%s: %s": "Graddnodi HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Yn gosod y tri isod sgrin lawn, un llun prawf ar y tro.",
"A key that opens this screen from the game. Not bound at first.": "Allwedd sy'n agor y sgrin hon o'r gêm. Heb ei rwymo ar y dechrau.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Ni ddywedodd eich arddangosfa pa mor llachar y mae'n ei gael: graddnodwch i'w osod.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Eisoes wedi'i osod ar gyfer eich arddangosfa. Calibrowch dim ond os yw HDR yn edrych yn anghywir.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Yn rhoi Disgleirdeb Peak a Phapur Gwyn yn ôl i drefniant eich arddangosfa.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Løfter skygger op på skærmens mørkeste sort. 0: ægte sort, ingen løft.",
"How bright highlights get. Auto: the peak your system reports.": "Hvor lyse højdepunkter bliver. Auto: det højeste, dit system rapporterer.",
"How bright white looks in the world. Auto: your desktop's white.": "Hvor lyst hvid ser ud i verden. Auto: din skrivebords hvide.",
"How bright the HUD and menus are, against paper white.": "Hvor lys HUD og menuer er i forhold til hvidt papir.",
"How far mobs, crops and machines keep working. CPU time.": "Hvor langt mobs, afgrøder og maskiner bliver ved med at arbejde. CPU tid.",
"How far away mobs and items are drawn, as a share of normal.": "Hvor langt væk mobs og genstande trækkes, som en andel af normalen.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Springer over terræn skjult bag lukkede sektioner. Off koster meget.",
@@ -367,7 +366,6 @@
"Advanced": "Avanceret",
"Undo": "Fortryd",
"Search options": "Søgemuligheder",
"Shortcut": "Genvej",
"Distance": "Afstand",
"Display": "Skærm",
"Interface": "Grænseflade",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibrer...",
"Calibrate Anyway...": "Kalibrer alligevel...",
"Use Automatic Setup": "Brug automatisk opsætning",
"Shortcut: %s": "Genvej: %s",
"Next": "Næste",
"HDR Calibration %s/%s: %s": "HDR-kalibrering %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Indstiller de tre nedenfor i fuld skærm, et testbillede ad gangen.",
"A key that opens this screen from the game. Not bound at first.": "En tast, der åbner denne skærm fra spillet. Ikke bundet i starten.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Din skærm sagde ikke, hvor lys den bliver: Kalibrer for at konfigurere den.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Allerede konfigureret til din skærm. Kalibrer kun, hvis HDR ser forkert ud.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Sætter Peak Brightness og Paper White tilbage til din skærms opsætning.",
@@ -290,7 +290,6 @@
"Advanced": "Erweitert",
"Undo": "Rückgängig",
"Search options": "Einstellungen durchsuchen",
"Shortcut": "Tastenkürzel",
"Distance": "Entfernung",
"Display": "Anzeige",
"Interface": "Oberfläche",
@@ -440,7 +439,6 @@
"Sets the options below together. Changing one makes it Custom.": "Stellt Optionen darunter ein. Änderung = „Benutzerdefiniert“.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Lava, Feuer, Sonne heller als Weiß. Braucht HDR-Bildschirm.",
"How bright highlights get. Auto: the peak your system reports.": "Wie hell Lichter werden. Auto: der Spitzenwert, den dein System meldet.",
"How bright the HUD and menus are, against paper white.": "Wie hell HUD und Menüs im Vergleich zu Papierweiß sind.",
"How far mobs, crops and machines keep working. CPU time.": "Wie weit Kreaturen, Pflanzen und Maschinen weiterarbeiten. Kostet CPU-Zeit.",
"How far away mobs and items are drawn, as a share of normal.": "Zeichenweite für Kreaturen und Gegenstände, anteilig zur Norm.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Überspringt Gelände hinter geschlossenen Abschnitten. Aus kostet viel.",
@@ -519,11 +517,9 @@
"Calibrate...": "Kalibrieren …",
"Calibrate Anyway...": "Trotzdem kalibrieren …",
"Use Automatic Setup": "Automatische Einrichtung verwenden",
"Shortcut: %s": "Tastenkürzel: %s",
"Next": "Weiter",
"HDR Calibration %s/%s: %s": "HDR-Kalibrierung %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Stellt die drei Werte darunter im Vollbild ein, jeweils mit einem Testbild.",
"A key that opens this screen from the game. Not bound at first.": "Eine Taste, die diesen Bildschirm im Spiel öffnet. Zunächst nicht belegt.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Bildschirm meldete keine Helligkeit: zum Einrichten kalibrieren.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Bereits eingerichtet. Nur kalibrieren, wenn HDR falsch aussieht.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Stellt Spitzenhelligkeit und Papierweiß des Bildschirms wieder her.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Ανεβάζει τις σκιές στο πιο σκοτεινό μαύρο της οθόνης σας. 0: αληθινό μαύρο, χωρίς ανύψωση.",
"How bright highlights get. Auto: the peak your system reports.": "Πόσο φωτεινά σημεία αποκτούν. Auto: η κορύφωση που αναφέρει το σύστημά σας.",
"How bright white looks in the world. Auto: your desktop's white.": "Πόσο φωτεινό φαίνεται το λευκό στον κόσμο. Αυτόματο: το λευκό της επιφάνειας εργασίας σας.",
"How bright the HUD and menus are, against paper white.": "Πόσο φωτεινά είναι τα HUD και τα μενού, σε σχέση με το λευκό χαρτί.",
"How far mobs, crops and machines keep working. CPU time.": "Πόσο μακριά συνεχίζουν να λειτουργούν όχλοι, καλλιέργειες και μηχανές. Χρόνος CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Πόσο μακριά κληρώνονται όχλοι και αντικείμενα, ως ποσοστό του κανονικού.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Παρακάμπτει το έδαφος που κρύβεται πίσω από κλειστά τμήματα. Το off κοστίζει πολύ.",
@@ -367,7 +366,6 @@
"Advanced": "Προχωρημένο",
"Undo": "Αναίρεση",
"Search options": "Επιλογές αναζήτησης",
"Shortcut": "Συντόμευση",
"Distance": "Απόσταση",
"Display": "Εμφάνιση",
"Interface": "Διασύνδεση",
@@ -438,11 +436,9 @@
"Calibrate...": "Βαθμονόμηση...",
"Calibrate Anyway...": "Βαθμονόμηση πάντως...",
"Use Automatic Setup": "Χρησιμοποιήστε την Αυτόματη Ρύθμιση",
"Shortcut: %s": "Συντόμευση: %s",
"Next": "Επόμενο",
"HDR Calibration %s/%s: %s": "Βαθμονόμηση HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Ορίζει τις τρεις παρακάτω πλήρη οθόνη, μία δοκιμαστική εικόνα τη φορά.",
"A key that opens this screen from the game. Not bound at first.": "Ένα κλειδί που ανοίγει αυτήν την οθόνη από το παιχνίδι. Δεν δεσμεύεται στην αρχή.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Η οθόνη σας δεν έλεγε πόσο φωτεινή γίνεται: βαθμονομήστε για να τη ρυθμίσετε.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Έχει ήδη ρυθμιστεί για την οθόνη σας. Βαθμονόμηση μόνο εάν το HDR φαίνεται λάθος.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Επαναφέρει το Peak Brightness και το Paper White στη ρύθμιση της οθόνης σας.",
@@ -310,7 +310,7 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Lifts shadows onto your display's darkest black. 0: true black, no lift.",
"How bright highlights get. Auto: the peak your system reports.": "How bright highlights get. Auto: the peak your system reports.",
"How bright white looks in the world. Auto: your desktop's white.": "How bright white looks in the world. Auto: your desktop's white.",
"How bright the HUD and menus are, against paper white.": "How bright the HUD and menus are, against paper white.",
"How bright the HUD and menus are. Auto: at paper white.": "How bright the HUD and menus are. Auto: at paper white.",
"How far mobs, crops and machines keep working. CPU time.": "How far mobs, crops and machines keep working. CPU time.",
"How far away mobs and items are drawn, as a share of normal.": "How far away mobs and items are drawn, as a share of normal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Skips terrain hidden behind closed sections. Off costs a lot.",
@@ -367,7 +367,6 @@
"Advanced": "Advanced",
"Undo": "Undo",
"Search options": "Search options",
"Shortcut": "Shortcut",
"Distance": "Distance",
"Display": "Display",
"Interface": "Interface",
@@ -438,11 +437,9 @@
"Calibrate...": "Calibrate...",
"Calibrate Anyway...": "Calibrate Anyway...",
"Use Automatic Setup": "Use Automatic Setup",
"Shortcut: %s": "Shortcut: %s",
"Next": "Next",
"HDR Calibration %s/%s: %s": "HDR Calibration %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Sets the three below full screen, one test picture at a time.",
"A key that opens this screen from the game. Not bound at first.": "A key that opens this screen from the game. Not bound at first.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Your display didn't say how bright it gets: calibrate to set it up.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Already set up for your display. Only calibrate if HDR looks wrong.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Puts Peak Brightness and Paper White back to your display's setup.",
@@ -1058,5 +1055,8 @@
"horizon.studio.topbar.add_marker": "Add Marker",
"horizon.studio.topbar.clear_keyframes": "🗑 Clear Keyframes",
"horizon.studio.topbar.timeline_hud": "⏱ Timeline",
"horizon.studio.topbar.split_scene": "Split Scene"
"horizon.studio.topbar.split_scene": "Split Scene",
"horizon.recipes.fuel.options": "Hold SHIFT to pin this tooltip, then click an ingredient to view recipes.",
"horizon.recipes.fuel.any": "Any Fuel",
"horizon.recipes.fuel.minecraft": "Minecraft"
}
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Levas ombrojn al la plej malhela nigro de via ekrano. 0: vera nigro, sen levo.",
"How bright highlights get. Auto: the peak your system reports.": "Kiel brilaj brilaĵoj ricevas. Aŭtomata: la pinto kiun via sistemo raportas.",
"How bright white looks in the world. Auto: your desktop's white.": "Kiom hela blanko aspektas en la mondo. Aŭtomate: la blanko de via labortablo.",
"How bright the HUD and menus are, against paper white.": "Kiel brilaj la HUD kaj menuoj estas, kontraŭ papera blanka.",
"How far mobs, crops and machines keep working. CPU time.": "Kiom malproksime amasoj, kultivaĵoj kaj maŝinoj daŭre funkcias. CPU-tempo.",
"How far away mobs and items are drawn, as a share of normal.": "Kiom malproksime amasoj kaj eroj estas tiritaj, kiel parto de normalo.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Saltas terenon kaŝitan malantaŭ fermitaj sekcioj. For kostas multe.",
@@ -367,7 +366,6 @@
"Advanced": "Altnivela",
"Undo": "Malfari",
"Search options": "Serĉaj opcioj",
"Shortcut": "Ŝparvojo",
"Distance": "Distanco",
"Display": "Montru",
"Interface": "Interfaco",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibri...",
"Calibrate Anyway...": "Kalibru ĉiuokaze...",
"Use Automatic Setup": "Uzu Aŭtomatan Agordon",
"Shortcut: %s": "Ŝparvojo: %s",
"Next": "Poste",
"HDR Calibration %s/%s: %s": "HDR-Kalibrado %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Agordas la tri sub plenan ekranon, unu testan bildon samtempe.",
"A key that opens this screen from the game. Not bound at first.": "Ŝlosilo, kiu malfermas ĉi tiun ekranon de la ludo. Komence ne ligita.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Via ekrano ne diris kiom brilas ĝi fariĝas: kalibru por agordi ĝin.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Jam agordita por via ekrano. Nur kalibru se HDR aspektas malĝusta.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Remetas Pintan Brilon kaj Paperan Blankan al la agordo de via ekrano.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Eleva las sombras hasta el negro más oscuro de tu pantalla. 0: negro puro, sin elevación.",
"How bright highlights get. Auto: the peak your system reports.": "Cuánto brillan los reflejos. Automático: el pico que informa tu sistema.",
"How bright white looks in the world. Auto: your desktop's white.": "Qué tan brillante se ve el blanco en el mundo. Automático: el blanco de tu escritorio.",
"How bright the HUD and menus are, against paper white.": "Qué tan brillantes son el HUD y los menús respecto al blanco de papel.",
"How far mobs, crops and machines keep working. CPU time.": "Hasta dónde siguen funcionando mobs, cultivos y máquinas. Tiempo de CPU.",
"How far away mobs and items are drawn, as a share of normal.": "A qué distancia se dibujan mobs y objetos, como proporción de lo normal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Omite el terreno oculto tras secciones cerradas. Desactivarlo cuesta mucho.",
@@ -367,7 +366,6 @@
"Advanced": "Avanzado",
"Undo": "Deshacer",
"Search options": "Buscar opciones",
"Shortcut": "Atajo",
"Distance": "Distancia",
"Display": "Pantalla",
"Interface": "Interfaz",
@@ -438,11 +436,9 @@
"Calibrate...": "Calibrar...",
"Calibrate Anyway...": "Calibrar de todos modos...",
"Use Automatic Setup": "Usar la configuración automática",
"Shortcut: %s": "Atajo: %s",
"Next": "Siguiente",
"HDR Calibration %s/%s: %s": "Calibración HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Configura las tres opciones siguientes a pantalla completa, con una imagen de prueba cada vez.",
"A key that opens this screen from the game. Not bound at first.": "Una tecla que abre esta pantalla desde el juego. Sin asignar al principio.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Tu pantalla no indicó cuánto brilla: calíbrala para configurarla.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Ya está configurado para tu pantalla. Calibra solo si el HDR se ve mal.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Devuelve Brillo máximo y Blanco de papel a la configuración de tu pantalla.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Tõstab varjud ekraani tumedaima mustani. 0: tõeline must, ilma tõstmiseta.",
"How bright highlights get. Auto: the peak your system reports.": "Kui eredad esiletõstud saavad. Automaatne: tipp, millest süsteem teatab.",
"How bright white looks in the world. Auto: your desktop's white.": "Kui hele valge maailmas paistab. Automaatne: sinu töölaua valge.",
"How bright the HUD and menus are, against paper white.": "Kui eredad on HUD ja menüüd pabervalge taustal.",
"How far mobs, crops and machines keep working. CPU time.": "Kui kaugele mobid, põllukultuurid ja masinad töötavad. CPU aeg.",
"How far away mobs and items are drawn, as a share of normal.": "Kui kaugele mobid ja esemed tõmmatakse, kui osa normaalsest.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Jätab vahele suletud lõikude taha peidetud maastiku. Off maksab palju.",
@@ -367,7 +366,6 @@
"Advanced": "Täpsemalt",
"Undo": "Võta tagasi",
"Search options": "Otsinguvalikud",
"Shortcut": "Otsetee",
"Distance": "Kaugus",
"Display": "Ekraan",
"Interface": "Liides",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibreeri...",
"Calibrate Anyway...": "Kalibreerige ikkagi...",
"Use Automatic Setup": "Kasutage automaatset seadistust",
"Shortcut: %s": "Otsetee: %s",
"Next": "Edasi",
"HDR Calibration %s/%s: %s": "HDR-kalibreerimine %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Määrab kolm allolevat täisekraani, üks testpilt korraga.",
"A key that opens this screen from the game. Not bound at first.": "Klahv, mis avab mängust selle ekraani. Alguses ei köideta.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Teie ekraan ei öelnud, kui hele see muutub: seadistamiseks kalibreerige.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Teie kuva jaoks on juba seadistatud. Kalibreerige ainult siis, kui HDR tundub vale.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Tagab maksimaalse ereduse ja paberivalgeduse teie ekraani seadistustesse.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Itzalak zure pantailaren beltzik ilunenera igotzen ditu. 0: benetako beltza, igoerarik gabe.",
"How bright highlights get. Auto: the peak your system reports.": "Nola distiratsuak diren argiak. Auto: zure sistemak ematen duen gailurra.",
"How bright white looks in the world. Auto: your desktop's white.": "Zenbat distiratzen duen zuriak munduan. Autoa: zure mahaigaineko zuria.",
"How bright the HUD and menus are, against paper white.": "Zein distiratsuak diren HUD eta menuak, paper zuriaren aurka.",
"How far mobs, crops and machines keep working. CPU time.": "Noraino jarraitzen duten mafiak, laboreak eta makinak lanean. CPU denbora.",
"How far away mobs and items are drawn, as a share of normal.": "Noraino ateratzen diren mobs eta elementuak, normalaren zati gisa.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Sekzio itxien atzean ezkutatuta dagoen lurra saltatzen du. Off asko kostatzen da.",
@@ -367,7 +366,6 @@
"Advanced": "Aurreratua",
"Undo": "Desegin",
"Search options": "Bilaketa aukerak",
"Shortcut": "Lasterbidea",
"Distance": "Distantzia",
"Display": "Bistaratzea",
"Interface": "Interfazea",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibratu...",
"Calibrate Anyway...": "Kalibratu Dena den...",
"Use Automatic Setup": "Erabili konfigurazio automatikoa",
"Shortcut: %s": "Lasterbidea: %s",
"Next": "Hurrengoa",
"HDR Calibration %s/%s: %s": "HDR Kalibrazioa %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Pantaila osoko azpian dauden hiru ezartzen ditu, probako argazki bat aldi berean.",
"A key that opens this screen from the game. Not bound at first.": "Pantaila hau jokotik irekitzen duen giltza. Hasieran lotu gabe.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Pantailak ez du esan zenbat argi dagoen: kalibratu konfiguratzeko.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Dagoeneko konfiguratuta zaude zure pantailarako. Kalibratu HDR okerra bada bakarrik.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Distira gailurra eta Paper Zuria berriro jartzen ditu pantailaren konfiguraziora.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "سایه‌ها را به تاریک‌ترین سیاه نمایشگر شما می‌رساند. 0: سیاه واقعی، بدون بالا بردن.",
"How bright highlights get. Auto: the peak your system reports.": "هایلایت ها چقدر روشن می شوند. خودکار: اوج گزارش سیستم شما.",
"How bright white looks in the world. Auto: your desktop's white.": "سفیدی در دنیا چقدر روشن دیده می‌شود. خودکار: سفیدی میزکار شما.",
"How bright the HUD and menus are, against paper white.": "HUD و منوها در برابر سفید کاغذ چقدر روشن هستند.",
"How far mobs, crops and machines keep working. CPU time.": "اوباش، محصولات زراعی و ماشین‌آلات تا کجا به کار خود ادامه می‌دهند. زمان CPU",
"How far away mobs and items are drawn, as a share of normal.": "اوباش و اقلام چقدر دور کشیده شده اند، به عنوان یک سهم عادی.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "از زمین های پنهان شده در پشت بخش های بسته می گذرد. هزینه های خاموش بسیار زیاد است.",
@@ -367,7 +366,6 @@
"Advanced": "پیشرفته",
"Undo": "واگرد",
"Search options": "گزینه های جستجو",
"Shortcut": "میانبر",
"Distance": "فاصله",
"Display": "نمایش",
"Interface": "رابط",
@@ -438,11 +436,9 @@
"Calibrate...": "کالیبره کردن...",
"Calibrate Anyway...": "به هر حال کالیبره کردن...",
"Use Automatic Setup": "از تنظیم خودکار استفاده کنید",
"Shortcut: %s": "میانبر: %s",
"Next": "بعدی",
"HDR Calibration %s/%s: %s": "کالیبراسیون HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "سه عکس زیر تمام صفحه را در یک زمان یک عکس آزمایشی تنظیم می کند.",
"A key that opens this screen from the game. Not bound at first.": "کلیدی که این صفحه را از بازی باز می کند. ابتدا مقید نیست.",
"Your display didn't say how bright it gets: calibrate to set it up.": "صفحه نمایش شما میزان روشنایی آن را بیان نکرده است: برای تنظیم آن کالیبره کنید.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "قبلاً برای نمایشگر شما تنظیم شده است. فقط در صورتی کالیبره کنید که HDR اشتباه به نظر برسد.",
"Puts Peak Brightness and Paper White back to your display's setup.": "حداکثر روشنایی و کاغذ سفید را به تنظیمات صفحه نمایش شما باز می گرداند.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Nostaa varjot näyttösi tummimpaan mustaan. 0: aito musta, ei nostoa.",
"How bright highlights get. Auto: the peak your system reports.": "Kuinka kirkkaat kohokohdat saavat. Auto: huippu, jonka järjestelmäsi raportoi.",
"How bright white looks in the world. Auto: your desktop's white.": "Kuinka kirkkaalta valkoinen näyttää maailmassa. Automaattinen: työpöytäsi valkoinen.",
"How bright the HUD and menus are, against paper white.": "Kuinka kirkkaita HUD ja valikot ovat paperinvalkoisia vastaan.",
"How far mobs, crops and machines keep working. CPU time.": "Kuinka pitkälle väkijoukot, viljat ja koneet toimivat. CPU-aika.",
"How far away mobs and items are drawn, as a share of normal.": "Kuinka kauas väkijoukot ja esineet piirretään, osuus normaalista.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Ohittaa suljettujen osien taakse piilotetun maaston. Poisto maksaa paljon.",
@@ -367,7 +366,6 @@
"Advanced": "Edistynyt",
"Undo": "Kumoa",
"Search options": "Hakuvaihtoehdot",
"Shortcut": "Pikakuvake",
"Distance": "Etäisyys",
"Display": "Näyttö",
"Interface": "Käyttöliittymä",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibroi...",
"Calibrate Anyway...": "Kalibroi joka tapauksessa...",
"Use Automatic Setup": "Käytä automaattista asetusta",
"Shortcut: %s": "Pikanäppäin: %s",
"Next": "Seuraavaksi",
"HDR Calibration %s/%s: %s": "HDR-kalibrointi %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Asettaa kolme koko näytön alapuolelle yhden testikuvan kerrallaan.",
"A key that opens this screen from the game. Not bound at first.": "Avain, joka avaa tämän näytön pelistä. Ei aluksi sidottu.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Näyttösi ei ilmoittanut, kuinka kirkkaaksi se tulee: kalibroi se määrittääksesi sen.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Näyttöäsi varten on jo määritetty. Kalibroi vain, jos HDR näyttää väärältä.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Palauttaa huippukirkkauden ja paperin valkoisen näytön asetuksiin.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Iniaangat ang mga anino sa pinakamadilim na itim ng iyong display. 0: totoong itim, walang angat.",
"How bright highlights get. Auto: the peak your system reports.": "Kung gaano kaliwanag ang mga highlight. Auto: ang pinakamataas na ulat ng iyong system.",
"How bright white looks in the world. Auto: your desktop's white.": "Gaano kaliwanag ang puti sa mundo. Auto: ang puti ng iyong desktop.",
"How bright the HUD and menus are, against paper white.": "Gaano kaliwanag ang HUD at mga menu, laban sa puting papel.",
"How far mobs, crops and machines keep working. CPU time.": "Hanggang saan patuloy na gumagana ang mga mandurumog, pananim at makina. oras ng CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Kung gaano kalayo ang mga mandurumog at mga item ay iginuhit, bilang bahagi ng normal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Nilalaktawan ang lupain na nakatago sa likod ng mga saradong seksyon. Malaki ang gastos sa off.",
@@ -367,7 +366,6 @@
"Advanced": "Advanced",
"Undo": "I-undo",
"Search options": "Mga opsyon sa paghahanap",
"Shortcut": "Shortcut",
"Distance": "Distansya",
"Display": "Pagpapakita",
"Interface": "Interface",
@@ -438,11 +436,9 @@
"Calibrate...": "I-calibrate...",
"Calibrate Anyway...": "Mag-calibrate pa rin...",
"Use Automatic Setup": "Gamitin ang Awtomatikong Pag-setup",
"Shortcut: %s": "Shortcut: %s",
"Next": "Susunod",
"HDR Calibration %s/%s: %s": "Pag-calibrate ng HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Itinatakda ang tatlo sa ibaba ng full screen, isang pagsubok na larawan sa isang pagkakataon.",
"A key that opens this screen from the game. Not bound at first.": "Isang susi na nagbubukas ng screen na ito mula sa laro. Hindi nakatali sa una.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Hindi sinabi ng iyong display kung gaano ito kaliwanag: i-calibrate para i-set up ito.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Naka-set up na para sa iyong display. I-calibrate lang kung mukhang mali ang HDR.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Ibinabalik ang Peak Brightness at Paper White sa setup ng iyong display.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Lyftir skuggum upp á myrkasta svart á tínum skjá. 0: veruligt svart, ongin lyfting.",
"How bright highlights get. Auto: the peak your system reports.": "Hvussu bjart hæddarpunkt fáa. Sjálvvirkandi: toppurin, sum tín skipan greiðir frá.",
"How bright white looks in the world. Auto: your desktop's white.": "Hvussu bjart hvítt sær út í heiminum. Sjálvvirkandi: hvítt á tínum skriviborði.",
"How bright the HUD and menus are, against paper white.": "Hvussu ljós HUD og valmyndir eru, móti pappírshvítum.",
"How far mobs, crops and machines keep working. CPU time.": "Hvussu langt mobbar, grøðir og maskinur halda fram at virka. CPU tíð.",
"How far away mobs and items are drawn, as a share of normal.": "Hvussu langt burtur mobbar og lutir eru drignir, sum partur av vanligum.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Sleppur lendi goymt aftan fyri stongdar partar. Av kostar nógv.",
@@ -367,7 +366,6 @@
"Advanced": "Framkomin",
"Undo": "Angra",
"Search options": "Leitimøguleikar",
"Shortcut": "Snarvegur",
"Distance": "Fjarstøða",
"Display": "Sýn",
"Interface": "Grunnflata",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibrera...",
"Calibrate Anyway...": "Kalibrera allíkavæl...",
"Use Automatic Setup": "Brúka sjálvvirkandi uppseting",
"Shortcut: %s": "Snarvegur: %s",
"Next": "Næsta",
"HDR Calibration %s/%s: %s": "HDR-kalibrering %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Setur tey trý niðanfyri fullan skerm, eina royndarmynd í senn.",
"A key that opens this screen from the game. Not bound at first.": "Ein lykil, sum letur upp hendan skermin frá spælinum. Ikki bundin í fyrstuni.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Títt skjálvti segði ikki, hvussu ljóst tað blívur: kalibrera fyri at seta tað upp.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Longu sett upp til tín skjálvta. Kalibrera bert um HDR sær skeivt út.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Setur Toppljósstyrki og Pappírshvítt aftur til uppsetingina á skjánum.",
@@ -332,7 +332,6 @@
"Sets the options below together. Changing one makes it Custom.": "Règle les options ci-dessous. Modifier une : « Personnalisé ».",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "Lave, feu et soleil plus vifs que le blanc. Écran HDR requis.",
"How bright highlights get. Auto: the peak your system reports.": "La luminosité des hautes lumières. Auto : le pic indiqué par votre système.",
"How bright the HUD and menus are, against paper white.": "La luminosité du HUD et des menus, par rapport au blanc papier.",
"How far mobs, crops and machines keep working. CPU time.": "Portée d'activité des créatures, cultures, machines. Coût : CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Distance des créatures et objets, en part de la normale.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Ignore le terrain caché. Désactiver coûte cher.",
@@ -407,11 +406,9 @@
"Calibrate...": "Calibrer...",
"Calibrate Anyway...": "Calibrer quand même...",
"Use Automatic Setup": "Utiliser la configuration automatique",
"Shortcut: %s": "Raccourci : %s",
"Next": "Suivant",
"HDR Calibration %s/%s: %s": "Calibrage HDR %s/%s : %s",
"Sets the three below full screen, one test picture at a time.": "Règle les trois valeurs ci-dessous en plein écran, une image test à la fois.",
"A key that opens this screen from the game. Not bound at first.": "Une touche qui ouvre cet écran depuis le jeu. Non assignée au départ.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Votre écran n'a pas indiqué sa luminosité : calibrez-le.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Déjà configuré. Calibrez seulement si le HDR semble incorrect.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Rétablit Luminosité max. et Point blanc de votre écran.",
@@ -675,7 +672,6 @@
"Advanced": "Avancé",
"Undo": "Annuler",
"Search options": "Rechercher des options",
"Shortcut": "Raccourci",
"Sky and weather": "Ciel et météo",
"Lighting and entities": "Éclairage et entités",
"Chunk loading": "Chargement des tronçons",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Al alce lis ombris sul neri plui scur dal to visôr. 0: neri vêr, cence alçâ.",
"How bright highlights get. Auto: the peak your system reports.": "Cuant che i ponts clârs a deventin luminosis. Auto: il massim dai segnalazions dal sisteme.",
"How bright white looks in the world. Auto: your desktop's white.": "Cuant che al somee lûs il blanc tal mont. Automatic: il blanc dal to scritori.",
"How bright the HUD and menus are, against paper white.": "Cuant che a son luminosis il HUD e i menus, cuintri dal blanc di cjarte.",
"How far mobs, crops and machines keep working. CPU time.": "Fin dulà che lis mobis, lis coltivazions e lis machinis a continuin a lavorâ. Timp dal CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Cuant lontan che a son tirâts fûr lis mobis e i ogjets, come part dal normâl.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Salte il teren scuindût daûr di sezions sieradis. Off al coste une vore.",
@@ -367,7 +366,6 @@
"Advanced": "Avanzade",
"Undo": "Annulâ",
"Search options": "Opzions di ricercje",
"Shortcut": "Scurtis",
"Distance": "Distance",
"Display": "Visualizazion",
"Interface": "Interface",
@@ -438,11 +436,9 @@
"Calibrate...": "Calibrâ...",
"Calibrate Anyway...": "Calibrâ in ogni câs...",
"Use Automatic Setup": "Utilizâ la configurazion automatiche",
"Shortcut: %s": "Scurtis: %s",
"Next": "Sucessîf",
"HDR Calibration %s/%s: %s": "Taradure HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Imposta i trê sot dal schermi intîr, une imagjin di prove par volte.",
"A key that opens this screen from the game. Not bound at first.": "Une clâf che e vierç chest schermi dal zûc. No leât tal imprin.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Il display nol diseve cetant che al devente luminôs: calibrâlu par configurâlu.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Za configurât pal to display. Calibrâ dome se il HDR al somee sbaglât.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Al torne a meti la luminositât di massime e il blanc di cjarte inte configurazion dal display.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Tilt skaad op nei it tsjûkste swart fan jo display. 0: echt swart, gjin oplichting.",
"How bright highlights get. Auto: the peak your system reports.": "Hoe ljochte hichtepunten krije. Auto: de pyk dy't jo systeem rapportearret.",
"How bright white looks in the world. Auto: your desktop's white.": "Hoe helder wyt yn 'e wrâld liket. Auto: it wyt fan jo buroblêd.",
"How bright the HUD and menus are, against paper white.": "Hoe helder de HUD en menu's binne, tsjin papierwyt.",
"How far mobs, crops and machines keep working. CPU time.": "Hoe fier mobs, gewaaksen en masines bliuwe wurkjen. CPU tiid.",
"How far away mobs and items are drawn, as a share of normal.": "Hoe fier fuort mobs en items wurde lutsen, as in oandiel fan normale.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Skips terrein ferburgen efter sletten seksjes. Off kostet in soad.",
@@ -367,7 +366,6 @@
"Advanced": "Avansearre",
"Undo": "Ungedien meitsje",
"Search options": "Sykje opsjes",
"Shortcut": "Fluchtoets",
"Distance": "Ofstân",
"Display": "Byldskerm",
"Interface": "Ynterface",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibrearje...",
"Calibrate Anyway...": "Kalibrearje dochs...",
"Use Automatic Setup": "Brûk Automatyske opset",
"Shortcut: %s": "Fluchtoets: %s",
"Next": "Folgjende",
"HDR Calibration %s/%s: %s": "HDR-kalibraasje %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Stelt de trije hjirûnder folslein skerm, ien testfoto tagelyk.",
"A key that opens this screen from the game. Not bound at first.": "In kaai dy't iepenet dit skerm út it spul. Earst net bûn.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Jo display sei net hoe helder it wurdt: kalibrearje om it yn te stellen.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Al ynsteld foar jo display. Kalibrearje allinich as HDR ferkeard sjocht.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Set Peak Brightness en Paper White werom nei de opset fan jo display.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Ardaíonn scáthanna go dtí an dubh is dorcha ar do thaispeántas. 0: fíordhubh, gan ardú.",
"How bright highlights get. Auto: the peak your system reports.": "Conas a fhaigheann buaicphointí geala. Auto: an bhuaic tuairiscí do chórais.",
"How bright white looks in the world. Auto: your desktop's white.": "Cé chomh geal is a fheictear bán sa domhan. Uathoibríoch: bán do dheisce.",
"How bright the HUD and menus are, against paper white.": "Cé chomh geal is atá an HUD agus na biachláir, i gcoinne bán páipéir.",
"How far mobs, crops and machines keep working. CPU time.": "Cé chomh fada agus a leanann mobs, barraí agus meaisíní ag obair. Am LAP.",
"How far away mobs and items are drawn, as a share of normal.": "Cé chomh fada uaidh a tharraingítear mobs agus míreanna, mar sciar den ghnáth.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Scipeanna tír-raon i bhfolach taobh thiar codanna dúnta. Lasmuigh costais go leor.",
@@ -367,7 +366,6 @@
"Advanced": "Casta",
"Undo": "Cealaigh",
"Search options": "Roghanna cuardaigh",
"Shortcut": "aicearra",
"Distance": "Fad",
"Display": "Taispeáint",
"Interface": "Comhéadan",
@@ -438,11 +436,9 @@
"Calibrate...": "Calabraigh...",
"Calibrate Anyway...": "Calabraigh Ar aon nós...",
"Use Automatic Setup": "Úsáid Socrú Uathoibríoch",
"Shortcut: %s": "Aicearra: %s",
"Next": "Ar aghaidh",
"HDR Calibration %s/%s: %s": "Calabrú HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Socraíonn sé na trí thíos scáileán iomlán, pictiúr tástála amháin ag an am.",
"A key that opens this screen from the game. Not bound at first.": "Eochair a osclaíonn an scáileán seo ón gcluiche. Gan cheangal ar dtús.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Níor dúirt do thaispeántas cé chomh geal agus atá sé: calabraigh chun é a shocrú.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Socraithe cheana féin le haghaidh do thaispeántais. Ná calabraigh ach amháin má tá cuma mícheart ar HDR.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Cuireann Buaic Ghile agus Páipéar Bán ar ais go dtí socrú do thaispeántais.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "A' togail fhaileasan gu dubh as dorcha an taisbeanaidh agad. 0: fìor dhubh, gun togail.",
"How bright highlights get. Auto: the peak your system reports.": "Mar a gheibh seallaidhean soilleir. Auto: an ìre as àirde a tha an siostam agad ag aithris.",
"How bright white looks in the world. Auto: your desktop's white.": "Dè cho soilleir 's a tha geal a' coimhead san t-saoghal. Auto: geal an deasga agad.",
"How bright the HUD and menus are, against paper white.": "Dè cho soilleir sa tha an HUD agus na clàran-bìdh, an aghaidh pàipear geal.",
"How far mobs, crops and machines keep working. CPU time.": "Dè cho fada ‘s a chumas mobs, bàrr agus innealan ag obair. Uair CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Dè cho fada air falbh tha mobs agus nithean air an tarraing, mar chuibhreann den àbhaist.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Sgiobaidhean talmhainn falaichte air cùl earrannan dùinte. Cosgaidh dheth tòrr.",
@@ -367,7 +366,6 @@
"Advanced": "Adhartach",
"Undo": "Neo-dhèanamh",
"Search options": "Rannsaich roghainnean",
"Shortcut": "Ath-ghoirid",
"Distance": "Astar",
"Display": "Taisbeanadh",
"Interface": "Eadar-aghaidh",
@@ -438,11 +436,9 @@
"Calibrate...": "Calibraich...",
"Calibrate Anyway...": "Calibrate co-dhiù...",
"Use Automatic Setup": "Cleachd suidheachadh fèin-ghluasadach",
"Shortcut: %s": "Ath-ghoirid: %s",
"Next": "Air adhart",
"HDR Calibration %s/%s: %s": "Calibration HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Suidhich na trì gu h-ìosal làn-sgrìn, aon dealbh deuchainn aig aon àm.",
"A key that opens this screen from the game. Not bound at first.": "Iuchair a dh'fhosglas an sgrìn seo bhon gheama. Gun a bhith ceangailte an toiseach.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Cha d’ thuirt an taisbeanadh agad cho soilleir ‘s a gheibh e: calibrate gus a rèiteachadh.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Air a chuir air dòigh mu thràth airson do thaisbeanadh. Na dèan calibration ach ma tha HDR a’ coimhead ceàrr.",
"Puts Peak Brightness and Paper White back to your display's setup.": "A’ cur Peak Brightness agus Paper White air ais gu suidheachadh an taisbeanaidh agad.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Eleva as sombras ata o negro máis escuro da túa pantalla. 0: negro puro, sen elevación.",
"How bright highlights get. Auto: the peak your system reports.": "Como se fan os reflejos brillantes. Automático: o pico que informa o seu sistema.",
"How bright white looks in the world. Auto: your desktop's white.": "Que brillante se ve o branco no mundo. Automático: o branco do teu escritorio.",
"How bright the HUD and menus are, against paper white.": "Que brillantes son o HUD e os menús, contra o branco do papel.",
"How far mobs, crops and machines keep working. CPU time.": "Ata onde seguen traballando as turbas, os cultivos e as máquinas. Tempo de CPU.",
"How far away mobs and items are drawn, as a share of normal.": "A que distancia se debuxan as turbas e os elementos, como parte do normal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Salta o terreo escondido detrás de seccións pechadas. Off custa moito.",
@@ -367,7 +366,6 @@
"Advanced": "Avanzado",
"Undo": "Desfacer",
"Search options": "Opcións de busca",
"Shortcut": "Atallo",
"Distance": "Distancia",
"Display": "Mostrar",
"Interface": "Interface",
@@ -438,11 +436,9 @@
"Calibrate...": "Calibrar...",
"Calibrate Anyway...": "Calibrar de todos os xeitos...",
"Use Automatic Setup": "Use a configuración automática",
"Shortcut: %s": "Atallo: %s",
"Next": "A continuación",
"HDR Calibration %s/%s: %s": "Calibración HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Establece os tres debaixo da pantalla completa, unha imaxe de proba á vez.",
"A key that opens this screen from the game. Not bound at first.": "Unha tecla que abre esta pantalla desde o xogo. Non atado ao principio.",
"Your display didn't say how bright it gets: calibrate to set it up.": "A túa pantalla non indica o brillo que ten: calíbraa para configurala.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Xa estás configurado para a túa pantalla. Calibra só se o HDR parece incorrecto.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Devolve o brillo máximo e o branco de papel á configuración da pantalla.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Hāpai i nā aka i ka ʻeleʻele loa o kāu hōʻike. 0: ʻeleʻele maoli, ʻaʻohe hāpai.",
"How bright highlights get. Auto: the peak your system reports.": "Pehea e loaʻa ai nā hiʻohiʻona nani. Auto: ke kiʻekiʻe o kāu hōʻike pūnaewele.",
"How bright white looks in the world. Auto: your desktop's white.": "Pehea ke keʻokeʻo o ka honua. Akomi: ke keʻokeʻo o kāu papa hana.",
"How bright the HUD and menus are, against paper white.": "Pehea ka mālamalama o ka HUD a me nā menus, e kūʻē i ka pepa keʻokeʻo.",
"How far mobs, crops and machines keep working. CPU time.": "Pehea ka lōʻihi o ka hana ʻana o nā kānaka, nā mea kanu a me nā mīkini. manawa CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Pehea ka lōʻihi o ka huki ʻia ʻana o nā poʻe pōʻino a me nā mea, ma ke ʻano he mea maʻamau.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Holo i ka ʻāina huna ma hope o nā ʻāpana pani. He nui ke kumu kūʻai.",
@@ -367,7 +366,6 @@
"Advanced": "Kiʻekiʻe",
"Undo": "Wehe",
"Search options": "Nā koho huli",
"Shortcut": "Pōkole",
"Distance": "mamao",
"Display": "Hōʻike",
"Interface": "Ikepili",
@@ -438,11 +436,9 @@
"Calibrate...": "Hoʻoponopono ʻia...",
"Calibrate Anyway...": "E hoʻokaʻawale i kēlā me kēia...",
"Use Automatic Setup": "E hoʻohana i ka hoʻonohonoho ʻakomi",
"Shortcut: %s": "Pōkole: %s",
"Next": "Aʻe",
"HDR Calibration %s/%s: %s": "Hoʻoponopono HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Hoʻonohonoho i nā ʻekolu ma lalo o ka pale piha, hoʻokahi kiʻi hoʻāʻo i ka manawa.",
"A key that opens this screen from the game. Not bound at first.": "He kī e wehe ana i kēia pale mai ka pāʻani. ʻAʻole paʻa i ka wā mua.",
"Your display didn't say how bright it gets: calibrate to set it up.": "ʻAʻole i haʻi mai kāu hōʻike i ke ʻano o kona ʻālohilohi: calibrate e hoʻonohonoho iā ia.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Ua hoʻonohonoho ʻia no kāu hōʻike. Hoʻopili wale inā ʻike hewa ka HDR.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Hoʻihoʻi i ka Peak Brightness a me ka Pepa Keʻokeʻo i ka hoʻonohonoho o kāu hōʻike.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "מרים צללים אל השחור הכהה ביותר של הצג שלך. 0: שחור אמיתי, ללא הרמה.",
"How bright highlights get. Auto: the peak your system reports.": "כמה מדגישים בהירים מקבלים. אוטומטי: השיא שהמערכת שלך מדווחת עליו.",
"How bright white looks in the world. Auto: your desktop's white.": "כמה בהיר נראה לבן בעולם. אוטומטי: הלבן של שולחן העבודה שלך.",
"How bright the HUD and menus are, against paper white.": "כמה בהירים ה-HUD והתפריטים, מול נייר לבן.",
"How far mobs, crops and machines keep working. CPU time.": "עד כמה המון, יבולים ומכונות ממשיכים לעבוד. זמן מעבד.",
"How far away mobs and items are drawn, as a share of normal.": "כמה רחוק נמשכים אספסוף ופריטים, כחלק מהרגיל.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "מדלג על שטח מוסתר מאחורי קטעים סגורים. חופש עולה הרבה.",
@@ -367,7 +366,6 @@
"Advanced": "מתקדם",
"Undo": "בטל",
"Search options": "אפשרויות חיפוש",
"Shortcut": "קיצור דרך",
"Distance": "מרחק",
"Display": "תצוגה",
"Interface": "ממשק",
@@ -438,11 +436,9 @@
"Calibrate...": "כיול...",
"Calibrate Anyway...": "כייל בכל מקרה...",
"Use Automatic Setup": "השתמש בהגדרה אוטומטית",
"Shortcut: %s": "קיצור דרך: %s",
"Next": "הבא",
"HDR Calibration %s/%s: %s": "כיול HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "מגדיר את שלושת המסך המלא מתחת, תמונת בדיקה אחת בכל פעם.",
"A key that opens this screen from the game. Not bound at first.": "מפתח שפותח את המסך הזה מהמשחק. לא קשור בהתחלה.",
"Your display didn't say how bright it gets: calibrate to set it up.": "הצג שלך לא אמר כמה הוא בהיר: כייל כדי להגדיר אותו.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "כבר מוגדר לתצוגה שלך. כייל רק אם HDR נראה שגוי.",
"Puts Peak Brightness and Paper White back to your display's setup.": "מחזיר את Peak Brightness ואת נייר לבן להגדרות התצוגה.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "परछाइयों को आपके डिस्प्ले के सबसे गहरे काले तक उठाता है। 0: शुद्ध काला, कोई उठाव नहीं।",
"How bright highlights get. Auto: the peak your system reports.": "हाइटलाइट्स कितने चमकीले होते हैं। ऑटो: आपका सिस्टम जो शिखर रिपोर्ट करता है।",
"How bright white looks in the world. Auto: your desktop's white.": "दुनिया में सफेद कितना चमकीला दिखता है। ऑटो: आपके डेस्कटॉप का सफेद।",
"How bright the HUD and menus are, against paper white.": "पेपर व्हाइट की तुलना में HUD और मेनू कितने चमकीले हैं।",
"How far mobs, crops and machines keep working. CPU time.": "मॉब्स, फसलें और मशीनें कितनी दूर तक काम करती रहती हैं। CPU समय।",
"How far away mobs and items are drawn, as a share of normal.": "सामान्य के हिस्से के रूप में, मॉब्स और आइटम कितनी दूर खींचे जाते हैं।",
"Skips terrain hidden behind closed sections. Off costs a lot.": "बंद खंडों के पीछे छिपा भू-भाग छोड़ देता है। बंद करने पर अधिक लागत आती है।",
@@ -367,7 +366,6 @@
"Advanced": "उन्नत",
"Undo": "पूर्ववत करें",
"Search options": "विकल्प खोजें",
"Shortcut": "शॉर्टकट",
"Distance": "दूरी",
"Display": "डिस्प्ले",
"Interface": "इंटरफ़ेस",
@@ -438,11 +436,9 @@
"Calibrate...": "कैलिब्रेट करें...",
"Calibrate Anyway...": "वैसे भी कैलिब्रेट करें...",
"Use Automatic Setup": "स्वचालित सेटअप का उपयोग करें",
"Shortcut: %s": "शॉर्टकट: %s",
"Next": "अगला",
"HDR Calibration %s/%s: %s": "HDR कैलिब्रेशन %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "एक समय में एक परीक्षण चित्र के साथ, नीचे दिए गए तीनों को पूर्ण स्क्रीन पर सेट करता है।",
"A key that opens this screen from the game. Not bound at first.": "एक कुंजी जो गेम से इस स्क्रीन को खोलती है। पहले से बाउंड नहीं है।",
"Your display didn't say how bright it gets: calibrate to set it up.": "आपके डिस्प्ले ने यह नहीं बताया कि यह कितना चमकीला होता है: इसे सेट अप करने के लिए कैलिब्रेट करें।",
"Already set up for your display. Only calibrate if HDR looks wrong.": "आपके डिस्प्ले के लिए पहले से सेट अप है। केवल तभी कैलिब्रेट करें जब HDR गलत लगे।",
"Puts Peak Brightness and Paper White back to your display's setup.": "पीक ब्राइटनेस और पेपर व्हाइट को आपके डिस्प्ले के सेटअप पर वापस रखता है।",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Podiže sjene do najtamnije crne vašeg zaslona. 0: prava crna, bez podizanja.",
"How bright highlights get. Auto: the peak your system reports.": "Koliko svijetli postaju svijetli dijelovi. Automatski: vršna vrijednost koju prijavljuje vaš sustav.",
"How bright white looks in the world. Auto: your desktop's white.": "Koliko bijela izgleda svijetlo u svijetu. Automatski: bijela vaše radne površine.",
"How bright the HUD and menus are, against paper white.": "Koliko su svijetli HUD i izbornici u odnosu na papirnato bijelu.",
"How far mobs, crops and machines keep working. CPU time.": "Koliko daleko čudovišta, usjevi i strojevi nastavljaju raditi. Vrijeme CPU-a.",
"How far away mobs and items are drawn, as a share of normal.": "Koliko se daleko crtaju čudovišta i predmeti, kao udio normalnog.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Preskače teren skriven iza zatvorenih dijelova. Isključeno opterećuje sustav.",
@@ -367,7 +366,6 @@
"Advanced": "Napredno",
"Undo": "Poništi",
"Search options": "Opcije pretraživanja",
"Shortcut": "Prečac",
"Distance": "Udaljenost",
"Display": "Zaslon",
"Interface": "Sučelje",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibriraj...",
"Calibrate Anyway...": "Svejedno kalibriraj...",
"Use Automatic Setup": "Koristi automatsku postavu",
"Shortcut: %s": "Prečac: %s",
"Next": "Sljedeće",
"HDR Calibration %s/%s: %s": "HDR kalibracija %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Postavlja tri donja na cijeli zaslon, jednu testnu sliku po jednu.",
"A key that opens this screen from the game. Not bound at first.": "Tipka koja otvara ovaj zaslon iz igre. U početku nije povezana.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Vaš zaslon nije javio koliko svijetli: kalibrirajte ga za postavljanje.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Već postavljeno za vaš zaslon. Kalibrirajte samo ako HDR izgleda pogrešno.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Vraća najveću svjetlinu i papirnato bijelu na postavke vašeg zaslona.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Az árnyékokat a kijelző legmélyebb feketéjére emeli. 0: valódi fekete, emelés nélkül.",
"How bright highlights get. Auto: the peak your system reports.": "Milyen fényesek a csúcsfények. Automatikus: a rendszer által jelentett csúcs.",
"How bright white looks in the world. Auto: your desktop's white.": "Milyen fényesnek tűnik a fehér a világban. Automatikus: az asztal fehérje.",
"How bright the HUD and menus are, against paper white.": "Milyen fényesek a HUD és a menük papírfehérrel szemben.",
"How far mobs, crops and machines keep working. CPU time.": "Meddig dolgoznak a csőcselék, a termények és a gépek. CPU idő.",
"How far away mobs and items are drawn, as a share of normal.": "Milyen messzire húzódnak a mobok és tárgyak, a normál arányaként.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Kihagyja a zárt szakaszok mögé rejtett terepet. Az off sokba kerül.",
@@ -367,7 +366,6 @@
"Advanced": "Haladó",
"Undo": "Visszavonás",
"Search options": "Keresési lehetőségek",
"Shortcut": "Parancsikon",
"Distance": "Távolság",
"Display": "Kijelző",
"Interface": "Interfész",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibrálás...",
"Calibrate Anyway...": "Mindenesetre kalibráld...",
"Use Automatic Setup": "Használja az Automatikus beállítást",
"Shortcut: %s": "Parancsikon: %s",
"Next": "Következő",
"HDR Calibration %s/%s: %s": "HDR kalibrálás %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "A három alatti teljes képernyőt állítja be, egyszerre egy tesztképet.",
"A key that opens this screen from the game. Not bound at first.": "Kulcs, amely megnyitja ezt a képernyőt a játékból. Először nem kötve.",
"Your display didn't say how bright it gets: calibrate to set it up.": "A kijelzője nem jelezte, hogy mennyire lesz fényes: kalibrálja a beállításhoz.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Már be van állítva a kijelzőjéhez. Csak akkor kalibráljon, ha a HDR rossznak tűnik.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Visszaállítja a csúcsfényerőt és a papírfehéret a kijelző beállításához.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Ստվերները բարձրացնում է ձեր էկրանի ամենամուգ սևին։ 0՝ իրական սև, առանց բարձրացման։",
"How bright highlights get. Auto: the peak your system reports.": "Որքան վառ ընդգծումներ են ստանում: Ավտոմատ. ձեր համակարգի գագաթնակետը հաղորդում է:",
"How bright white looks in the world. Auto: your desktop's white.": "Թե որքան պայծառ է սպիտակը երևում աշխարհում։ Ավտոմատ՝ ձեր աշխատասեղանի սպիտակը։",
"How bright the HUD and menus are, against paper white.": "Որքան վառ են HUD-ն ու մենյուները՝ թղթի սպիտակի դեմ:",
"How far mobs, crops and machines keep working. CPU time.": "Որքան հեռու են աշխատում ամբոխները, մշակաբույսերը և մեքենաները: Պրոցեսորի ժամանակ.",
"How far away mobs and items are drawn, as a share of normal.": "Որքա՜ն հեռու են քաշված ամբոխներն ու իրերը՝ որպես նորմալի մասնաբաժին։",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Բաց է թողնում փակ հատվածների հետևում թաքնված տեղանքը: Off ծախսերը շատ.",
@@ -367,7 +366,6 @@
"Advanced": "Ընդլայնված",
"Undo": "Հետարկել",
"Search options": "Որոնման ընտրանքներ",
"Shortcut": "Դյուրանցում",
"Distance": "Հեռավորությունը",
"Display": "Ցուցադրել",
"Interface": "Ինտերֆեյս",
@@ -438,11 +436,9 @@
"Calibrate...": "Կալիբրացիա...",
"Calibrate Anyway...": "Կալիբրացիա, այնուամենայնիվ...",
"Use Automatic Setup": "Օգտագործեք ավտոմատ կարգավորում",
"Shortcut: %s": "Դյուրանցում՝ %s",
"Next": "Հաջորդը",
"HDR Calibration %s/%s: %s": "HDR չափորոշում %s/%s՝ %s",
"Sets the three below full screen, one test picture at a time.": "Սահմանում է ներքևում գտնվող երեքը լիաէկրան, միաժամանակ մեկ փորձնական նկար:",
"A key that opens this screen from the game. Not bound at first.": "Բանալին, որը բացում է այս էկրանը խաղից: Սկզբում կապված չէ:",
"Your display didn't say how bright it gets: calibrate to set it up.": "Ձեր էկրանը չի ասում, թե որքան պայծառ է այն դառնում. չափաբերեք այն կարգավորելու համար:",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Արդեն ստեղծվել է ձեր ցուցադրման համար: Չափորոշեք միայն այն դեպքում, եթե HDR-ն սխալ տեսք ունի:",
"Puts Peak Brightness and Paper White back to your display's setup.": "Վերադարձնում է առավելագույն պայծառությունն ու թղթի սպիտակը ձեր էկրանի կարգավորումներին:",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Mengangkat bayangan ke hitam tergelap layar Anda. 0: hitam sejati, tanpa pengangkatan.",
"How bright highlights get. Auto: the peak your system reports.": "Seberapa terang sorotan cahaya. Otomatis: puncak yang dilaporkan oleh sistem Anda.",
"How bright white looks in the world. Auto: your desktop's white.": "Seberapa terang warna putih di dunia. Otomatis: putih desktop Anda.",
"How bright the HUD and menus are, against paper white.": "Seberapa terang HUD dan menu dibandingkan dengan putih kertas.",
"How far mobs, crops and machines keep working. CPU time.": "Seberapa jauh mob, tanaman, dan mesin tetap bekerja. Waktu CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Seberapa jauh mob dan item digambar, sebagai persentase dari normal.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Lewati medan yang tersembunyi di balik bagian tertutup. Nonaktif memakan banyak sumber daya.",
@@ -367,7 +366,6 @@
"Advanced": "Lanjutan",
"Undo": "Urungkan",
"Search options": "Cari opsi",
"Shortcut": "Pintasan",
"Distance": "Jarak",
"Display": "Tampilan",
"Interface": "Antarmuka",
@@ -438,11 +436,9 @@
"Calibrate...": "Kalibrasi...",
"Calibrate Anyway...": "Tetap Kalibrasi...",
"Use Automatic Setup": "Gunakan Pengaturan Otomatis",
"Shortcut: %s": "Pintasan: %s",
"Next": "Berikutnya",
"HDR Calibration %s/%s: %s": "Kalibrasi HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Mengatur ketiga hal di bawah ini ke mode layar penuh, satu gambar uji dalam satu waktu.",
"A key that opens this screen from the game. Not bound at first.": "Tombol untuk membuka layar ini dari dalam game. Belum diatur sebelumnya.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Layar Anda tidak melaporkan tingkat kecerahannya: lakukan kalibrasi untuk mengaturnya.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Sudah diatur untuk layar Anda. Kalibrasi ulang hanya jika tampilan HDR terlihat keliru.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Mengembalikan Kecerahan Puncak dan Putih Kertas ke pengaturan awal layar Anda.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Na-ebuli onyinyo ruo n'ojii kacha njọ nke ihuenyo gị. 0: ojii zuru oke, enweghị ebuli.",
"How bright highlights get. Auto: the peak your system reports.": "Kedu ka isi ihe na-egbuke egbuke si enweta. Akpaaka: ọnụ ọgụgụ kasị elu nke sistemụ gị na-akọ.",
"How bright white looks in the world. Auto: your desktop's white.": "Otú ọcha si acha na ụwa. Akpaaka: ọcha nke desktọp gị.",
"How bright the HUD and menus are, against paper white.": "Lee ka HUD na menus si na-enwu, megide ọcha akwụkwọ.",
"How far mobs, crops and machines keep working. CPU time.": "Ogologo oge ole na-eme mkpọtụ, ihe ọkụkụ na igwe na-arụ ọrụ. CPU oge.",
"How far away mobs and items are drawn, as a share of normal.": "Kedu ebe a na-adọta mobs na ihe dị anya, dịka òkè nke nkịtị.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Na-amafe ala zoro ezo n'azụ ngalaba mechiri emechi. Gbanyụọ na-efu nnukwu ego.",
@@ -367,7 +366,6 @@
"Advanced": "Nke dị elu",
"Undo": "Megharịa",
"Search options": "Chọọ nhọrọ",
"Shortcut": "Ụzọ mkpirisi",
"Distance": "Anya",
"Display": "Ngosipụta",
"Interface": "Nhazi ihu",
@@ -438,11 +436,9 @@
"Calibrate...": "Hazie...",
"Calibrate Anyway...": "Hazie agbanyeghị...",
"Use Automatic Setup": "Jiri Ntọala akpaaka",
"Shortcut: %s": "Ụzọ mkpirisi: %s",
"Next": "Na-esote",
"HDR Calibration %s/%s: %s": "HDR calibration %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Tọọ atọ n'okpuru ihuenyo zuru oke, otu foto nnwale n'otu oge.",
"A key that opens this screen from the game. Not bound at first.": "Igodo na-emepe ihuenyo a site na egwuregwu. Ekebeghị ya na mbụ.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Ngosipụta gị ekwughị ka ọ na-egbuke egbuke: hazie ka ịtọlite ya.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Ahaziri maka ngosi gị. Naanị hazie ma ọ bụrụ na HDR adịghị mma.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Na-eweghachi Peak Brightness na White Akwụkwọ na ntọlite ihe ngosi gị.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Lyftir skuggum upp að dökkasta svarti skjásins. 0: ekta svart, engin lyfting.",
"How bright highlights get. Auto: the peak your system reports.": "Hversu bjartir hápunktar verða. Sjálfvirkt: hámarkið sem kerfið þitt tilkynnir.",
"How bright white looks in the world. Auto: your desktop's white.": "Hversu bjart hvítt lítur út í heiminum. Sjálfvirk: hvítt skjáborðsins þíns.",
"How bright the HUD and menus are, against paper white.": "Hversu björt HUD og valmyndir eru, á móti pappírshvítum.",
"How far mobs, crops and machines keep working. CPU time.": "Hversu langt múgur, uppskera og vélar halda áfram að vinna. CPU tími.",
"How far away mobs and items are drawn, as a share of normal.": "Hversu langt í burtu múgur og hlutir eru dregnir, sem hlutfall af venjulegum.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Sleppir landslagi sem er falið á bak við lokaða kafla. Off kostar mikið.",
@@ -367,7 +366,6 @@
"Advanced": "Ítarlegri",
"Undo": "Afturkalla",
"Search options": "Leitarmöguleikar",
"Shortcut": "Flýtileið",
"Distance": "Fjarlægð",
"Display": "Skjár",
"Interface": "Viðmót",
@@ -438,11 +436,9 @@
"Calibrate...": "Kvarða...",
"Calibrate Anyway...": "Kvörðuðu samt...",
"Use Automatic Setup": "Notaðu sjálfvirka uppsetningu",
"Shortcut: %s": "Flýtileið: %s",
"Next": "Næst",
"HDR Calibration %s/%s: %s": "HDR kvörðun %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Stillir þrjár fyrir neðan allan skjáinn, eina prufumynd í einu.",
"A key that opens this screen from the game. Not bound at first.": "Lykill sem opnar þennan skjá úr leiknum. Ekki bundið í fyrstu.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Skjárinn þinn sagði ekki hversu bjartur hann verður: kvarðaðu til að setja hann upp.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Nú þegar sett upp fyrir skjáinn þinn. Kvörðaðu aðeins ef HDR lítur rangt út.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Setur Peak Brightness og Paper White aftur í uppsetningu skjásins þíns.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Solleva le ombre fino al nero più cupo del tuo schermo. 0: nero reale, nessun sollevamento.",
"How bright highlights get. Auto: the peak your system reports.": "Come diventano brillanti le luci. Auto: il picco segnalato dal sistema.",
"How bright white looks in the world. Auto: your desktop's white.": "Quanto luminoso appare il bianco nel mondo. Auto: il bianco del tuo desktop.",
"How bright the HUD and menus are, against paper white.": "Quanto sono luminosi l'HUD e i menu, rispetto al bianco della carta.",
"How far mobs, crops and machines keep working. CPU time.": "Fino a che punto mob, raccolti e macchine continuano a funzionare. Tempo della CPU.",
"How far away mobs and items are drawn, as a share of normal.": "Quanto lontano vengono disegnati mob e oggetti, come percentuale del normale.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Salta il terreno nascosto dietro le sezioni chiuse. Spendere costa molto.",
@@ -367,7 +366,6 @@
"Advanced": "Avanzato",
"Undo": "Annulla",
"Search options": "Opzioni di ricerca",
"Shortcut": "Scorciatoia",
"Distance": "Distanza",
"Display": "Visualizzazione",
"Interface": "Interfaccia",
@@ -438,11 +436,9 @@
"Calibrate...": "Calibra...",
"Calibrate Anyway...": "Calibra comunque...",
"Use Automatic Setup": "Utilizza l'impostazione automatica",
"Shortcut: %s": "Scorciatoia: %s",
"Next": "Avanti",
"HDR Calibration %s/%s: %s": "Calibrazione HDR %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Imposta le tre immagini sottostanti a schermo intero, un'immagine di prova alla volta.",
"A key that opens this screen from the game. Not bound at first.": "Una chiave che apre questa schermata del gioco. All'inizio non legato.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Il display non ha indicato la luminosità: calibralo per configurarlo.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Già predisposto per il tuo display. Calibrare solo se l'HDR sembra sbagliato.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Riporta la luminosità di picco e il bianco della carta alla configurazione del display.",
@@ -315,7 +315,6 @@
"Sets the options below together. Changing one makes it Custom.": "以下のオプションをまとめて設定します。どれかを変更するとカスタムになります。",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "溶岩、火、太陽光は白よりも明るく輝きます。 HDRディスプレイが必要です。",
"How bright highlights get. Auto: the peak your system reports.": "ハイライトがどれだけ明るくなるか。自動: システムが報告するピーク。",
"How bright the HUD and menus are, against paper white.": "紙の白に対する HUD とメニューの明るさ。",
"How far mobs, crops and machines keep working. CPU time.": "暴徒、作物、機械がどこまで機能し続けるか。 CPU時間。",
"How far away mobs and items are drawn, as a share of normal.": "通常の割合として、Mob とアイテムが描画される距離。",
"Skips terrain hidden behind closed sections. Off costs a lot.": "閉じたセクションの後ろに隠れている地形をスキップします。オフには多額の費用がかかります。",
@@ -368,7 +367,6 @@
"Advanced": "上級者向け",
"Undo": "元に戻す",
"Search options": "検索オプション",
"Shortcut": "ショートカット",
"Distance": "距離",
"Display": "ディスプレイ",
"Interface": "インターフェース",
@@ -438,11 +436,9 @@
"Calibrate...": "校正...",
"Calibrate Anyway...": "とにかく校正してください...",
"Use Automatic Setup": "自動セットアップを使用する",
"Shortcut: %s": "ショートカット: %s",
"Next": "次へ",
"HDR Calibration %s/%s: %s": "HDR キャリブレーション %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "フルスクリーンの下の 3 つを、一度に 1 つのテスト画像を設定します。",
"A key that opens this screen from the game. Not bound at first.": "ゲームからこの画面を開くキー。最初は縛られてない。",
"Your display didn't say how bright it gets: calibrate to set it up.": "ディスプレイにはどのくらい明るくなるかは記載されていません。調整して設定してください。",
"Already set up for your display. Only calibrate if HDR looks wrong.": "ディスプレイ用にすでにセットアップされています。 HDR が間違っていると思われる場合にのみ調整してください。",
"Puts Peak Brightness and Paper White back to your display's setup.": "ピークの明るさとペーパー ホワイトをディスプレイの設定に戻します。",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "ჩრდილებს ასწევს თქვენი ეკრანის ყველაზე ბნელ შავამდე. 0: ნამდვილი შავი, აწევის გარეშე.",
"How bright highlights get. Auto: the peak your system reports.": "რამდენად ნათელი ხაზგასმა ხდება. ავტომატური: თქვენი სისტემის ანგარიშების პიკი.",
"How bright white looks in the world. Auto: your desktop's white.": "რამდენად ნათელია თეთრი სამყაროში. ავტო: თქვენი სამუშაო მაგიდის თეთრი.",
"How bright the HUD and menus are, against paper white.": "რამდენად ნათელია HUD და მენიუები, ქაღალდის თეთრის წინააღმდეგ.",
"How far mobs, crops and machines keep working. CPU time.": "რამდენად შორს მუშაობენ ბრბოები, ნათესები და მანქანები. CPU დრო.",
"How far away mobs and items are drawn, as a share of normal.": "რამდენად შორს არის დახატული ბრბო და ნივთები, როგორც ნორმალური წილი.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "გამოტოვებს დახურულ მონაკვეთებს მიღმა დამალულ რელიეფს. გამორთვა ბევრი ღირს.",
@@ -367,7 +366,6 @@
"Advanced": "გაფართოებული",
"Undo": "გაუქმება",
"Search options": "ძიების პარამეტრები",
"Shortcut": "მალსახმობი",
"Distance": "მანძილი",
"Display": "ჩვენება",
"Interface": "ინტერფეისი",
@@ -438,11 +436,9 @@
"Calibrate...": "დაკალიბრება...",
"Calibrate Anyway...": "დაკალიბრება მაინც...",
"Use Automatic Setup": "გამოიყენეთ ავტომატური დაყენება",
"Shortcut: %s": "მალსახმობი: %s",
"Next": "შემდეგი",
"HDR Calibration %s/%s: %s": "HDR კალიბრაცია %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "აყენებს სრულ ეკრანზე ქვემოთ მოცემულ სამს, თითო სატესტო სურათს ერთდროულად.",
"A key that opens this screen from the game. Not bound at first.": "გასაღები, რომელიც ხსნის ამ ეკრანს თამაშიდან. თავდაპირველად არ არის შეკრული.",
"Your display didn't say how bright it gets: calibrate to set it up.": "თქვენს ეკრანს არ უთქვამს რამდენად ნათელი ხდება: დააკალიბრეთ მის დასაყენებლად.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "უკვე დაყენებულია თქვენი ეკრანისთვის. დაკალიბრება მხოლოდ იმ შემთხვევაში, თუ HDR არასწორად გამოიყურება.",
"Puts Peak Brightness and Paper White back to your display's setup.": "აბრუნებს მაქსიმალურ სიკაშკაშეს და ქაღალდის თეთრს თქვენი ეკრანის პარამეტრებში.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Көлеңкелерді дисплейіңіздің ең терең қарасына көтереді. 0: таза қара, көтерілусіз.",
"How bright highlights get. Auto: the peak your system reports.": "Жарқын нүктелер қаншалықты жарқын болады. Автоматты: жүйе есептерінің шыңы.",
"How bright white looks in the world. Auto: your desktop's white.": "Әлемде ақ түстің қаншалықты жарық көрінетіні. Авто: жұмыс үстеліңіздің ақ түсі.",
"How bright the HUD and menus are, against paper white.": "HUD және мәзірлер ақ қағазға қарағанда қаншалықты жарқын.",
"How far mobs, crops and machines keep working. CPU time.": "Топтар, дақылдар мен машиналар қаншалықты алыс жұмыс істейді. CPU уақыты.",
"How far away mobs and items are drawn, as a share of normal.": "Қалыпты үлес ретінде тобыр мен заттар қаншалықты алыс тартылады.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Жабық бөліктердің артына жасырылған жерді өткізіп жібереді. Өшіру көп шығынға ұшырайды.",
@@ -367,7 +366,6 @@
"Advanced": "Жетілдірілген",
"Undo": "Болдырмау",
"Search options": "Іздеу опциялары",
"Shortcut": "Таңбаша",
"Distance": "Қашықтық",
"Display": "Дисплей",
"Interface": "Интерфейс",
@@ -438,11 +436,9 @@
"Calibrate...": "Калибрлеу...",
"Calibrate Anyway...": "Қалай болғанда да калибрлеу...",
"Use Automatic Setup": "Автоматты орнатуды пайдаланыңыз",
"Shortcut: %s": "Таңбаша: %s",
"Next": "Келесі",
"HDR Calibration %s/%s: %s": "HDR калибрлеу %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Төмендегі үш толық экранды, бір уақытта бір сынақ суретін орнатады.",
"A key that opens this screen from the game. Not bound at first.": "Ойыннан осы экранды ашатын перне. Басында байланысты емес.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Дисплей қаншалықты жарық болатынын айтпады: оны орнату үшін калибрленіңіз.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Дисплей үшін әлдеқашан орнатылған. HDR дұрыс емес болып көрінсе ғана калибрлендіріңіз.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Ең жоғары жарықтық пен ақ қағазды дисплей параметрлеріне қайтарады.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "ನೆರಳುಗಳನ್ನು ನಿಮ್ಮ ಡಿಸ್‌ಪ್ಲೇಯ ಕಪ್ಪು ಮಟ್ಟಕ್ಕೆ ಎತ್ತುತ್ತದೆ. 0: ನಿಜವಾದ ಕಪ್ಪು, ಎತ್ತುವಿಕೆ ಇಲ್ಲ.",
"How bright highlights get. Auto: the peak your system reports.": "ಪ್ರಕಾಶಮಾನವಾದ ಮುಖ್ಯಾಂಶಗಳು ಹೇಗೆ ಸಿಗುತ್ತವೆ. ಸ್ವಯಂ: ನಿಮ್ಮ ಸಿಸ್ಟಂ ವರದಿಗಳ ಗರಿಷ್ಠ.",
"How bright white looks in the world. Auto: your desktop's white.": "ಜಗತ್ತಿನಲ್ಲಿ ಬಿಳಿ ಎಷ್ಟು ಪ್ರಕಾಶಮಾನವಾಗಿ ಕಾಣುತ್ತದೆ. ಆಟೋ: ನಿಮ್ಮ ಡೆಸ್ಕ್‌ಟಾಪ್‌ನ ಬಿಳಿ.",
"How bright the HUD and menus are, against paper white.": "ಕಾಗದದ ಬಿಳಿಯ ವಿರುದ್ಧ HUD ಮತ್ತು ಮೆನುಗಳು ಎಷ್ಟು ಪ್ರಕಾಶಮಾನವಾಗಿವೆ.",
"How far mobs, crops and machines keep working. CPU time.": "ಜನಸಮೂಹಗಳು, ಬೆಳೆಗಳು ಮತ್ತು ಯಂತ್ರಗಳು ಎಷ್ಟು ಕೆಲಸ ಮಾಡುತ್ತವೆ. CPU ಸಮಯ.",
"How far away mobs and items are drawn, as a share of normal.": "ಜನಸಮೂಹ ಮತ್ತು ವಸ್ತುಗಳನ್ನು ಎಷ್ಟು ದೂರದಲ್ಲಿ ಎಳೆಯಲಾಗುತ್ತದೆ, ಸಾಮಾನ್ಯ ಪಾಲು.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "ಮುಚ್ಚಿದ ವಿಭಾಗಗಳ ಹಿಂದೆ ಮರೆಮಾಡಲಾಗಿರುವ ಭೂಪ್ರದೇಶವನ್ನು ಬಿಟ್ಟುಬಿಡುತ್ತದೆ. ಆಫ್ ಬಹಳಷ್ಟು ವೆಚ್ಚವಾಗುತ್ತದೆ.",
@@ -367,7 +366,6 @@
"Advanced": "ಸುಧಾರಿತ",
"Undo": "ರದ್ದುಮಾಡು",
"Search options": "ಹುಡುಕಾಟ ಆಯ್ಕೆಗಳು",
"Shortcut": "ಶಾರ್ಟ್‌ಕಟ್",
"Distance": "ದೂರ",
"Display": "ಪ್ರದರ್ಶನ",
"Interface": "ಇಂಟರ್ಫೇಸ್",
@@ -438,11 +436,9 @@
"Calibrate...": "ಮಾಪನಾಂಕ ನಿರ್ಣಯಿಸಿ...",
"Calibrate Anyway...": "ಹೇಗಾದರೂ ಮಾಪನಾಂಕ ಮಾಡಿ...",
"Use Automatic Setup": "ಸ್ವಯಂಚಾಲಿತ ಸೆಟಪ್ ಬಳಸಿ",
"Shortcut: %s": "ಶಾರ್ಟ್‌ಕಟ್: %s",
"Next": "ಮುಂದೆ",
"HDR Calibration %s/%s: %s": "HDR ಮಾಪನಾಂಕ ನಿರ್ಣಯ %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "ಮೂರು ಕೆಳಗಿನ ಪೂರ್ಣ ಪರದೆಯನ್ನು ಹೊಂದಿಸುತ್ತದೆ, ಒಂದು ಸಮಯದಲ್ಲಿ ಒಂದು ಪರೀಕ್ಷಾ ಚಿತ್ರ.",
"A key that opens this screen from the game. Not bound at first.": "ಆಟದಿಂದ ಈ ಪರದೆಯನ್ನು ತೆರೆಯುವ ಕೀ. ಮೊದಲಿಗೆ ಬದ್ಧವಾಗಿಲ್ಲ.",
"Your display didn't say how bright it gets: calibrate to set it up.": "ನಿಮ್ಮ ಡಿಸ್‌ಪ್ಲೇ ಅದು ಎಷ್ಟು ಪ್ರಕಾಶಮಾನವಾಗಿದೆ ಎಂದು ಹೇಳಿಲ್ಲ: ಅದನ್ನು ಹೊಂದಿಸಲು ಮಾಪನಾಂಕ ಮಾಡಿ.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "ನಿಮ್ಮ ಪ್ರದರ್ಶನಕ್ಕಾಗಿ ಈಗಾಗಲೇ ಹೊಂದಿಸಲಾಗಿದೆ. HDR ತಪ್ಪಾಗಿ ಕಂಡುಬಂದರೆ ಮಾತ್ರ ಮಾಪನಾಂಕ ನಿರ್ಣಯಿಸಿ.",
"Puts Peak Brightness and Paper White back to your display's setup.": "ಪೀಕ್ ಬ್ರೈಟ್‌ನೆಸ್ ಮತ್ತು ಪೇಪರ್ ವೈಟ್ ಅನ್ನು ನಿಮ್ಮ ಡಿಸ್‌ಪ್ಲೇಯ ಸೆಟಪ್‌ಗೆ ಹಿಂತಿರುಗಿಸುತ್ತದೆ.",
@@ -315,7 +315,6 @@
"Sets the options below together. Changing one makes it Custom.": "아래 옵션들을 함께 설정합니다. 하나라도 변경하면 사용자 지정으로 전환됩니다.",
"Lava, fire and sunlight shine brighter than white. Needs an HDR display.": "용암, 불, 햇빛이 흰색보다 더 밝게 빛납니다. HDR 디스플레이가 필요합니다.",
"How bright highlights get. Auto: the peak your system reports.": "하라이트의 최대 밝기입니다. 자동: 시스템이 보고하는 최고 값입니다.",
"How bright the HUD and menus are, against paper white.": "페이퍼 화이트 대비 HUD 및 메뉴의 밝기입니다.",
"How far mobs, crops and machines keep working. CPU time.": "몹, 농작물, 기계가 계속 작동하는 거리입니다. CPU 시간이 소모됩니다.",
"How far away mobs and items are drawn, as a share of normal.": "정상 대비 몹과 아이템이 그려지는 거리 비율입니다.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "닫힌 구역 뒤에 숨겨진 지형을 건너뜁니다. 끔으로 설정 시 성능 비용이 큽니다.",
@@ -368,7 +367,6 @@
"Advanced": "고급",
"Undo": "실행 취소",
"Search options": "옵션 검색",
"Shortcut": "단축키",
"Distance": "거리",
"Display": "디스플레이",
"Interface": "인터페이스",
@@ -438,11 +436,9 @@
"Calibrate...": "보정...",
"Calibrate Anyway...": "그래도 보정...",
"Use Automatic Setup": "자동 설정 사용",
"Shortcut: %s": "단축키: %s",
"Next": "다음",
"HDR Calibration %s/%s: %s": "HDR 보정 %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "테스트 사진을 한 장씩 사용하여 아래 세 가지를 전체 화면으로 설정합니다.",
"A key that opens this screen from the game. Not bound at first.": "게임 내에서 이 화면을 여는 키입니다. 기본적으로 바인딩되어 있지 않습니다.",
"Your display didn't say how bright it gets: calibrate to set it up.": "디스플레이의 최대 밝기 정보가 없습니다. 설정하려면 보정하세요.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "디스플레이에 맞게 이미 설정되어 있습니다. HDR이 잘못 표시되는 경우에만 보정하세요.",
"Puts Peak Brightness and Paper White back to your display's setup.": "최대 밝기와 페이퍼 화이트를 디스플레이의 기본 설정으로 되돌립니다.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Көлөкөлөрдү дисплейиңиздин эң кара карасына көтөрөт. 0: чыныгы кара, көтөрүү жок.",
"How bright highlights get. Auto: the peak your system reports.": "Кандай гана жаркыраган жерлер. Авто: тутумуңуздун эң жогорку көрсөткүчү.",
"How bright white looks in the world. Auto: your desktop's white.": "Дүйнөдө ак кандай жарык көрүнөт. Авто: иш тактаңыздын актыгы.",
"How bright the HUD and menus are, against paper white.": "Ак кагазга каршы HUD жана менюлар канчалык жарык.",
"How far mobs, crops and machines keep working. CPU time.": "Канчалык мобу, эгиндер жана машиналар иштей беришет. CPU убактысы.",
"How far away mobs and items are drawn, as a share of normal.": "Кадимкидей үлүшү катары, канча аралыкта мобдор жана буюмдар тартылат.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Жабык бөлүмдөрдүн артына жашырылган жерди өткөрүп жиберет. Чечим көп чыгымдалат.",
@@ -367,7 +366,6 @@
"Advanced": "Өркүндөтүлгөн",
"Undo": "Жок кылуу",
"Search options": "Издөө параметрлери",
"Shortcut": "Жарлык",
"Distance": "Дистанция",
"Display": "Дисплей",
"Interface": "Интерфейс",
@@ -438,11 +436,9 @@
"Calibrate...": "Калибрлөө...",
"Calibrate Anyway...": "Баары бир калибрлөө...",
"Use Automatic Setup": "Автоматтык орнотууну колдонуңуз",
"Shortcut: %s": "Кыска жол: %s",
"Next": "Кийинки",
"HDR Calibration %s/%s: %s": "HDR калибрлөө %s/%s: %s",
"Sets the three below full screen, one test picture at a time.": "Толук экрандын астындагы үчөөнү, бир убакта бир сыноо сүрөтүн орнотот.",
"A key that opens this screen from the game. Not bound at first.": "Бул экранды оюндан ачкан ачкыч. Башында байланган эмес.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Дисплейде анын канчалык жарык болоору айтылган жок: аны жөндөө үчүн калибрлеңиз.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Дисплейиңиз үчүн мурунтан эле орнотулган. HDR туура эмес көрүнсө гана калибрлеңиз.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Дисплейиңиздин орнотулушуна эң жогорку жарыктык жана кагаз ак түсүн коет.",
@@ -310,7 +310,6 @@
"Lifts shadows onto your display's darkest black. 0: true black, no lift.": "Umbras ad nigerrimum ostentus tui attollit. 0: vere nigrum, sine elevatione.",
"How bright highlights get. Auto: the peak your system reports.": "quam claram lucem eg. Auto: ratio tua ad apicem refert.",
"How bright white looks in the world. Auto: your desktop's white.": "Quam candidum album in mundo videatur. Automaticum: album scrinii tui.",
"How bright the HUD and menus are, against paper white.": "Quam clara TESTA et menus sunt contra chartam albam.",
"How far mobs, crops and machines keep working. CPU time.": "Quam longe turbas, fruges et machinis opus servant. CPU tempus.",
"How far away mobs and items are drawn, as a share of normal.": "Quam procul sunt turbae et vasa ducuntur, ut communis pars.",
"Skips terrain hidden behind closed sections. Off costs a lot.": "Skips locorum occultis sectionibus clausis. Off multum constat.",
@@ -367,7 +366,6 @@
"Advanced": "Provecta",
"Undo": "Revoca",
"Search options": "Quaerere optiones",
"Shortcut": "Brevis",
"Distance": "procul",
"Display": "Ostendere",
"Interface": "Interfacies",
@@ -438,11 +436,9 @@
"Calibrate...": "Calibrare...",
"Calibrate Anyway...": "Calibrate Usquam...",
"Use Automatic Setup": "Lorem Setup utere",
"Shortcut: %s": "Brevis:%s",
"Next": "Proximum",
"HDR Calibration %s/%s: %s": "HDR Calibration%s/%s:%s",
"Sets the three below full screen, one test picture at a time.": "Tres infra ponit tentorium plenum, unum tabula testium ad tempus.",
"A key that opens this screen from the game. Not bound at first.": "Clavis quae hoc tegumentum a ludo aperit. Non primo tenetur.",
"Your display didn't say how bright it gets: calibrate to set it up.": "Ostentatio tua non dixit quam clara accipit: calibrate ut eam erigat.",
"Already set up for your display. Only calibrate if HDR looks wrong.": "Iam ad ostentationem tuam constitue. Tantum calibrate si HDR nefas spectat.",
"Puts Peak Brightness and Paper White back to your display's setup.": "Ponit Peak Brightness et Chartam Albam reducere ad tui ostentationis setup.",
Loaded 100 of 389 files, more files were not shown because too many files have changed in this diff. Show more