diff --git a/.claude/skills/vanilla-parity/SKILL.md b/.claude/skills/vanilla-parity/SKILL.md new file mode 100644 index 0000000000..2d867866c8 --- /dev/null +++ b/.claude/skills/vanilla-parity/SKILL.md @@ -0,0 +1,76 @@ +--- +name: vanilla-parity +description: Fix a Horizon bug or gap so it behaves 1:1 like vanilla 26.3, starting from the port ledger (tools/port_ledger.py) instead of grepping. Use for any issue, Discord report or PR that makes Horizon match vanilla: "X doesn't work like in Java", a missing sound, particle, animation, mob behaviour, block rule, screen or packet. +--- + +# Vanilla parity with the port ledger + +The port ledger pairs every vanilla method with the Rust fn that ports it (or says it is missing, or needs +no port) and lists where a port isn't wired like vanilla. Ask it first; grep only for what it can't answer. +Full reference: `docs/port-ledger.md`. + +## 0. Have a fresh ledger + +```sh +python3 tools/port_ledger.py build # ~20 s; again after pulling main +``` + +It needs the vanilla jar (the launcher's `versions/26.3/26.3.jar`, or `--jar`) and/or the lab's decompiled +sources (`python3 tools/lab.py decompile`, or `--sources DIR`). If `build` says it has no vanilla code, run +the lab first; don't guess vanilla from memory. + +## 1. From the report to the code + +```sh +python3 tools/port_ledger.py issue 3236 # a GitHub issue number +python3 tools/port_ledger.py issue "mooshroom stays red after lightning" # or the report's text +``` + +It ranks the vanilla classes the report names and lists, per class, its Rust home files, where each method is +ported, and the known gaps (`missing` rows from the verdict map, seam/client/drift gaps). Often the cause is +already written there ("Type is not saved"). Then drill in: + +```sh +python3 tools/port_ledger.py where MushroomCow.readAdditionalSaveData # ports, callers, callees, gaps +python3 tools/port_ledger.py class MushroomCow # every method: where, or missing +python3 tools/port_ledger.py what crates/horizon-client/src/mobs/farm.rs:120 # which vanilla method a fn ports +``` + +A ledger answer is a lead, not a verdict: about 1 in 12 map rows is wrong. Read the vanilla method (path and +line are in the output) and the Rust fn before changing anything. + +## 2. Port it the vanilla way + +- Translate the vanilla method, don't reinvent it: same order of operations, same constants, the same random + draws in the same order (`horizon_core::random`), Java `Math` through `horizon_core::jmath`. +- Open the fn's doc with the vanilla method it ports: `` /// `MushroomCow.readAdditionalSaveData`: ... ``. + That one line is what makes the next agent find it. +- Fix the path players run: single player is a client of the integrated server, so server logic lives in + `game/lan*.rs` and `src/lan/`, and what only the client's copy shows (particles, local sounds, hand + animation) must be reached from `game/remote*.rs`. A fix on the old in-frame path alone fixes nothing. +- Look at the neighbours the ledger lists for the same class: a `client` or `seam` gap next to your fix is + usually the same bug on the other path. + +## 3. Before the PR + +```sh +python3 tools/port_ledger.py check --base origin/main +``` + +It lists the ports your branch touches with their open gaps, citations to add, stale citations, and (against +the base) the gaps the branch closes and opens. Close what belongs to your fix, cite what you ported, leave +no new stale citation. If you ported a method the verdict map called `missing`, change its row in +`tools/port_map/*.tsv` to `port` with the fn (`file.rs::Owner::fn`) and run +`python3 tools/port_ledger.py map-check tools/port_map/.tsv`. + +Then the usual: the harness for the area (`docs/architecture.md`), `cargo check`/tests per `AGENTS.md`, and +a small PR that says which vanilla method it now matches. + +## Finding work + +```sh +python3 tools/port_ledger.py gaps --kind seam # ported on two paths, wired on one: the best leads +python3 tools/port_ledger.py gaps --kind client # client-only effects the server path never reaches +python3 tools/port_ledger.py gaps --kind drift # a port missing a sound, particle, item or constant +rg -n "player:" tools/port_map/*.tsv # missing behaviour a player would see +``` diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b6443a4e55..aebe53519e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,3 +1,5 @@ + + ## What and why diff --git a/.github/workflows/cancel-on-close.yml b/.github/workflows/cancel-on-close.yml index 9a6e44965e..ded300c318 100644 --- a/.github/workflows/cancel-on-close.yml +++ b/.github/workflows/cancel-on-close.yml @@ -16,12 +16,14 @@ jobs: GH_TOKEN: ${{ github.token }} REPO: ${{ github.repository }} BRANCH: ${{ github.event.pull_request.head.ref }} - SHA: ${{ github.event.pull_request.head.sha }} + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} run: | + # Every head the pull request had, not only its last: a run-parity label added on an older head left that + # run queued after the merge (#3104, 2026-10-29). Its branch in its own repository names its runs (another + # fork can have a branch of the same name). for status in queued in_progress waiting pending requested; do - gh run list -R "$REPO" --branch "$BRANCH" --status "$status" --limit 100 \ - --json databaseId,event,headSha \ - -q ".[] | select(.event == \"pull_request\" and .headSha == \"$SHA\") | .databaseId" + gh api -X GET "repos/$REPO/actions/runs" -f branch="$BRANCH" -f event=pull_request -f status="$status" \ + -f per_page=100 --jq ".workflow_runs[] | select(.head_repository.full_name == \"$HEAD_REPO\") | .id" done | sort -u | while read -r id; do gh run cancel -R "$REPO" "$id" || true done diff --git a/.github/workflows/game-build.yml b/.github/workflows/game-build.yml index 7e075ea636..16a29be747 100644 --- a/.github/workflows/game-build.yml +++ b/.github/workflows/game-build.yml @@ -37,9 +37,10 @@ permissions: jobs: exporters: # This runner holds the maintainer's own pinned Minecraft jars. Only our classes leave it. An untrusted build - # goes to the Mac mini runner that already runs pull requests' parity jobs (macOS + horizon-game: - # ludovics-mac-mini), never the release runner (MAC_RUNNER: ludovics-mac-mini-release) nor parity-main's. - runs-on: ${{ inputs.trusted && vars.MAC_RUNNER || fromJSON('["self-hosted", "macOS", "horizon-game"]') }} + # goes to the Mac mini's nightly runner (macOS + horizon-nightly: ludovics-mac-mini-nightly, beside the one that + # runs pull requests' parity jobs, so a nightly never waits behind their queue), never the release runner + # (MAC_RUNNER: ludovics-mac-mini-release) nor parity-main's. + runs-on: ${{ inputs.trusted && vars.MAC_RUNNER || fromJSON('["self-hosted", "macOS", "horizon-nightly"]') }} # Three full preparations (two isolated, one batched) besides the compile. timeout-minutes: 60 steps: @@ -103,7 +104,7 @@ jobs: mac: true dir: horizon-macos-arm64 asset: horizon-macos-arm64.zip - runs-on: ${{ matrix.mac && (inputs.trusted && vars.MAC_RUNNER || fromJSON('["self-hosted", "macOS", "horizon-game"]')) || matrix.os }} + runs-on: ${{ matrix.mac && (inputs.trusted && vars.MAC_RUNNER || fromJSON('["self-hosted", "macOS", "horizon-nightly"]')) || matrix.os }} # Notarizing the zip can take a few minutes. timeout-minutes: ${{ matrix.mac && 60 || 40 }} env: diff --git a/.github/workflows/run-parity.yml b/.github/workflows/run-parity.yml index 9089a9a096..cc9f2c29fa 100644 --- a/.github/workflows/run-parity.yml +++ b/.github/workflows/run-parity.yml @@ -5,11 +5,15 @@ name: run-parity # skipped `verify`, and GitHub counts a skipped required check as passed. on: pull_request: - types: [labeled] + # A push starts a run only to cancel the old head's (see concurrency): the job runs on the label alone. + types: [labeled, synchronize] permissions: contents: read concurrency: - group: run-parity-${{ github.event.pull_request.number }} + # The run-parity label and pushes share one group per pull request, so the newest run cancels the one before: a + # pushed pull request's run tested a head the merge would no longer land. Any other label gets a group of its own: + # in the shared one, adding needs-proof cancelled the parity run queued seconds before (run 40766180150). + group: run-parity-${{ github.event.pull_request.number }}${{ github.event.action == 'labeled' && github.event.label.name != 'run-parity' && format('-{0}', github.run_id) || '' }} cancel-in-progress: true env: # Test runs never reach the telemetry server: no live-count ping, no uploads (docs/telemetry.md). diff --git a/AGENTS.md b/AGENTS.md index 3ccbdad55c..97c84e9cf1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,13 +13,15 @@ Feature → files → harness for the most-changed areas: [docs/architecture.md] `XDG_CONFIG_HOME`, plus `HORIZON_LAUNCHER_NO_SELF_UPDATE=1` and `HORIZON_NO_AUDIO=1`, and, for the launcher, `HORIZON_LAUNCHER_TRASH`, `HORIZON_LAUNCHER_CLIPBOARD` and `HORIZON_LAUNCHER_DESKTOP` pointing into the scratch home. Never write to `~/.local/share/{applications,horizon,horizon-launcher}` or `~/.local/bin`. -- **Mark what an agent posts.** A PR body starts with a line naming whose agent opened it and with what. - Ludovic's agents write `> 🤖 Opened by Ludovic's AI agent (Claude), not by Ludovic himself.`, and a - contributor's agent names its own person and tool (issues: "Filed by", reviews: "Review by"). Every - comment starts with `🤖 Agent:`. Never write as Ludovic or as anyone else. +- **Mark what an agent posts.** A PR body starts with a line naming the person whose agent opened it and + the tool: `> 🤖 Opened by 's AI agent (), not by personally.`, where `` + is the GitHub account the PR is opened from (`gh api user --jq .login`). Issues say "Filed by", reviews + "Review by". Every comment starts with `🤖 Agent:`. Write only as the person you work for: the lines on + this page that name Ludovic are for his own agents (account `ludovic111`), never for anyone else's. - **Attribution.** Commits end with a `Co-Authored-By:` line for the agent that wrote them, and PR bodies - with that agent's own "Generated with" line. Ludovic's agents use - `Co-Authored-By: Claude ` and + with that agent's own "Generated with" line. Example, Ludovic's agents (Claude Code on `ludovic111`): + first line `> 🤖 Opened by Ludovic's AI agent (Claude), not by Ludovic himself.`, commits + `Co-Authored-By: Claude `, last line `🤖 Generated with [Claude Code](https://claude.com/claude-code)`. - Everything players run is Rust. Python/Java only for dev tools and harness drivers in `tools/`. - Horizon-only features follow [docs/design-system.md](docs/design-system.md) and are off by default where @@ -35,11 +37,12 @@ Game workspace (root `Cargo.toml`); `launcher/` and `crates/horizon-telemetry-se | Crate | Owns | |---|---| -| `horizon-client` | The game: a lib (`src/lib.rs`, ~170 flat modules) and the bin (`src/main.rs` → `game.rs` + `game/`), plus `horizon-ctl` (`src/bin/horizon-ctl/`). Also the dedicated server (`--dedicated`) | +| `horizon-client` | The game: a lib (`src/lib.rs`, ~100 flat modules: gameplay, networking, title screens) and the bin (`src/main.rs` → `game.rs` + `game/`), plus `horizon-ctl` (`src/bin/horizon-ctl/`). Also the dedicated server (`--dedicated`) | | `horizon-items` | Blocks and items below gameplay: `blocks.rs` (block facts), `items.rs`, `item_data.rs` (components), `enchant.rs`, `cooldowns.rs`, `bundle.rs`, `item_nbt.rs`. Re-exported as `horizon_client::items`... | | `horizon-command` | Command parsing (Brigadier): the vanilla tree, argument types, selectors, suggestions, the tree's network form. Re-exported as `horizon_client::command` | | `horizon-client-core` | The client lib's bottom layer: `text.rs`, `lang.rs`, `snbt.rs`, `nbt_path.rs`, `component.rs`, `scoreboard.rs`, `bossbar.rs`, `gamerules.rs`, `block_entity_nbt.rs`, registry/particle/sound tables. Re-exported under the old paths (`horizon_client::text`...) | | `horizon-client-input` | Key bindings and key names, toggle keys, mouse options, controller settings and glyphs, the clipboard, window options (`keybinds.rs`, `gamepad.rs`, `mouse.rs`, `window.rs`...). Re-exported under the old paths | +| `horizon-client-render` | The client's world data, rendering and screens (chunks, meshing, light, streaming, `gfx`, HUD, menus, settings): ~80 modules that horizon-client re-exports under the same paths. Nothing in it reaches gameplay | | `horizon-world` | Vanilla world data with no gameplay loop: NBT, regions, chunks, `datafix/`, `worldgen/`, `level/` (server-side block behaviour, ticks, redstone), entity physics, items, effects, food, damage | | `horizon-core` | Simulation primitives: Java `Random`s, `jmath` (HotSpot-exact `Math`), positions, noise, density, paletted storage, `version.rs` (the pinned version) | | `horizon-protocol` | Network protocol 777: codecs, framing, encryption, `packets/{handshake,status,login,configuration,play}.rs`, `entity_data.rs` | @@ -73,7 +76,7 @@ cargo build --profile fast -p horizon-client # optimized, quick to link: python3 tools/parity.py --changed origin/main # the harnesses your branch can affect (CI's parity job) python3 tools/parity.py --changed-files paths.txt --list # which harnesses cover these files, without running them python3 tools/parity.py mob_ai sculk # named harnesses -python3 tools/lan_e2e.py --check chest # two real games over LAN (server-thread host); --in-frame compares the fallback +python3 tools/lan_e2e.py --check chest # two real games over LAN (server-thread host) python3 tools/agent_control_e2e.py # horizon-ctl / MCP end to end ``` @@ -99,6 +102,9 @@ python3 tools/agent_control_e2e.py # horizon-ctl / MCP end to end ## Vanilla reference +- Start parity work from the port ledger, not grep: `python3 tools/port_ledger.py issue N` (or `where + Class.method`, `what file:line`), and `check --base origin/main` before the PR. How: + [.claude/skills/vanilla-parity/SKILL.md](.claude/skills/vanilla-parity/SKILL.md), [docs/port-ledger.md](docs/port-ledger.md). - Decompiled sources: `.local/minecraft/26.3/sources/{client,server}/` (`python3 tools/lab.py decompile`). - Bytecode queries (callers through lambdas, every overload): `python3 tools/rea_vanilla.py method net.minecraft.world.level.block.DispenserBlock dispenseFrom` @@ -110,7 +116,8 @@ python3 tools/agent_control_e2e.py # horizon-ctl / MCP end to end ## horizon-client: where things live -`L:` = lib (`crates/horizon-client/src/`), `G:` = bin (`crates/horizon-client/src/game/`). The lib can't see +`L:` = lib (`crates/horizon-client/src/`, or `crates/horizon-client-render/src/` for the modules that crate holds, under +the same `horizon_client::` paths), `G:` = bin (`crates/horizon-client/src/game/`). The lib can't see `Game`; bin code reaches the lib as `horizon_client::`. | Subsystem | Files | @@ -162,17 +169,18 @@ python3 tools/agent_control_e2e.py # horizon-ctl / MCP end to end "Server thread" with one level per dimension; the window joins it as a client through memory (`remote`), exactly as a LAN guest or a dedicated server's player does. This is what players run. Open `[server-thread]` issues list the gaps left. -2. **In-frame (`--in-frame`, `--no-server-thread` or `HORIZON_SERVER_THREAD=0`):** the window's `Game` ticks - the world itself, one level that follows the player (`fn enter_dimension`). Deterministic rule fixtures still use this path; player-facing integration uses the default server thread. +2. **In-frame (`--in-frame`, `--no-server-thread` or `HORIZON_SERVER_THREAD=0`): DEPRECATED, never use it, it + will be removed soon.** The window's `Game` ticks the world itself, one level that follows the player + (`fn enter_dimension`). Compatibility does not matter: don't add features or fixes for it, don't write new + tests against it, and move code off it when you touch it. The deterministic rule fixtures still run on it + until they are ported. -LAN guests and dedicated servers always use the server code, so a fix there serves both. Test both paths -when you touch player-facing gameplay: +LAN guests and dedicated servers always use the server code, so a fix there serves both. Test the server +thread when you touch player-facing gameplay: ```sh cargo run --profile fast -p horizon-client -- --resources .local/horizon-client/26.3 --world T # server thread -cargo run --profile fast -p horizon-client -- --resources .local/horizon-client/26.3 --world T --in-frame # in-frame python3 tools/lan_e2e.py --check # host on its server thread, guest is a server client -python3 tools/lan_e2e.py --in-frame --check # comparison (portals, levels, distance, lava need server-thread mode) python3 tools/parity.py dedicated_server server_entities server_player_damage ``` diff --git a/Cargo.lock b/Cargo.lock index c33d195d73..c37d0d3b60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1378,7 +1378,6 @@ dependencies = [ "ab_glyph", "bytemuck", "chrono", - "cpal", "flate2", "gilrs", "glam", @@ -1386,6 +1385,7 @@ dependencies = [ "horizon-bench", "horizon-client-core", "horizon-client-input", + "horizon-client-render", "horizon-command", "horizon-core", "horizon-dualsense", @@ -1412,7 +1412,6 @@ dependencies = [ "serde_json", "sha1", "sha2", - "symphonia", "unicode-bidi", "ureq", "wgpu", @@ -1441,6 +1440,48 @@ dependencies = [ "winit", ] +[[package]] +name = "horizon-client-render" +version = "0.1.0" +dependencies = [ + "ab_glyph", + "bytemuck", + "chrono", + "cpal", + "flate2", + "glam", + "horizon-assets", + "horizon-bench", + "horizon-client-core", + "horizon-client-input", + "horizon-command", + "horizon-core", + "horizon-hdr", + "horizon-items", + "horizon-protocol", + "horizon-shader-api", + "horizon-shaders", + "horizon-telemetry", + "horizon-theme", + "horizon-threads", + "horizon-world", + "log", + "miniz_oxide 0.8.9", + "naga", + "png", + "pollster", + "rustix 1.1.5", + "serde", + "serde_json", + "sha2", + "symphonia", + "unicode-bidi", + "ureq", + "wgpu", + "winit", + "zip", +] + [[package]] name = "horizon-command" version = "0.1.0" @@ -1486,6 +1527,9 @@ name = "horizon-hdr" version = "0.1.0" dependencies = [ "ash", + "wayland-backend", + "wayland-client", + "wayland-protocols", "wgpu", ] diff --git a/Cargo.toml b/Cargo.toml index 6615c140af..fe8f2ed170 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "crates/horizon-client", "crates/horizon-client-core", "crates/horizon-client-input", + "crates/horizon-client-render", "crates/horizon-command", "crates/horizon-items", "crates/horizon-server-files", diff --git a/README.md b/README.md index 16c2ed2e7a..19594f9d49 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Horizon: Minecraft: Java Edition 26.3, rewritten in Rust and checked against vanilla by 281 differential harnesses + Horizon: Minecraft: Java Edition 26.3, rewritten in Rust and checked against vanilla by more than 300 differential harnesses

@@ -23,15 +23,15 @@ # Horizon -Horizon is Minecraft: Java Edition 26.3 rewritten in Rust: a native game that reads the same worlds, follows the same rules and generates the same terrain as the Java game, and is checked against it by 281 differential harnesses. You play it through a small launcher, on the files from your own copy of Minecraft. It is free, open source and unofficial. +Horizon is Minecraft: Java Edition 26.3 rewritten in Rust: a native game that reads the same worlds, follows the same rules and generates the same terrain as the Java game, and is checked against it by more than 300 differential harnesses. You play it through a small launcher, on the files from your own copy of Minecraft. It is free, open source and unofficial. > Not an official Minecraft product. Not approved by or associated with Mojang or Microsoft. Horizon contains no Minecraft code, assets or data: you need your own legally obtained copy of Minecraft: Java Edition 26.3. ## Download -Horizon is installed through **the Horizon launcher**. Get it from [playhorizon.dev](https://playhorizon.dev) (the Download button picks your system) or from [the launcher releases](https://github.com/horizon-rust/horizon/releases?q=launcher-v&expanded=true), for Linux x86-64, macOS on Apple Silicon or Windows x64. The launcher installs and updates the game, prepares its files from your copy of Minecraft (your own install, or downloaded from Mojang for your account), and updates itself. +Horizon is installed through **the Horizon launcher**. Get it from [playhorizon.dev](https://playhorizon.dev) (the Download button picks your system) or from [the launcher releases](https://github.com/horizon-rust/horizon/releases?q=launcher-v&expanded=true), for Linux x86-64, macOS on Apple Silicon or Windows x64. The launcher installs the game and keeps it up to date with each new build (Horizon ships on one channel, the Nightly), prepares its files from your copy of Minecraft (your own install, or downloaded from Mojang for your account), and updates itself. -**You need** a Microsoft account that owns Minecraft: Java Edition (the launcher signs in with it, as other launchers do; see [Microsoft account](docs/microsoft-account.md)) and a GPU with Vulkan on Linux and Windows, or any Apple Silicon Mac. Nothing else: with no Minecraft on your computer, Play downloads 26.3 and the Java it needs from Mojang for your account, as the official launcher does, then prepares it once. An install you already have (the official launcher, Prism, MultiMC and other launchers all work) is used instead. Step by step, with macOS's first-open prompt and the first launch: [Install and first launch](https://playhorizon.dev/docs/install). +**You need** a Microsoft account that owns Minecraft: Java Edition (the launcher signs in with it, as other launchers do; see [Microsoft account](docs/microsoft-account.md)) and a GPU with Vulkan or OpenGL 3.3 on Linux, Vulkan or DirectX 12 on Windows, or any Apple Silicon Mac. Nothing else: with no Minecraft on your computer, Play downloads 26.3 and the Java it needs from Mojang for your account, as the official launcher does, then prepares it once. An install you already have (the official launcher, Prism, MultiMC and other launchers all work) is used instead. Step by step, with macOS's first-open prompt and the first launch: [Install and first launch](https://playhorizon.dev/docs/install). @@ -46,9 +46,9 @@ The [documentation](https://playhorizon.dev/docs/) covers each of these. - **The game:** singleplayer with vanilla's world generation, mobs, villagers, raids, redstone, every command, the HUD and the recipe book, on saves in vanilla's format. Worlds from 1.8 to 26.2 are upgraded as vanilla does. [Status table](#project-status). - **Horizon's own extras:** new menus (vanilla's are one setting away), graphics presets, distant terrain up to 1024 chunks, controller support, optional zoom, minimap and more, all off by default where they would change the vanilla look. See [Settings](https://playhorizon.dev/docs/settings). -- **Resource packs and data packs:** resource packs work, and data packs load functions, predicates and item modifiers. +- **Resource packs and data packs:** resource packs work, and data packs load functions, predicates, item modifiers and world generation (vanilla's heights and biomes for now). - **Playing with others:** invite friends from the pause menu's Friends button with a code, Open to LAN, and joining servers, online-mode ones with the Microsoft account the launcher signs in with. See [Multiplayer](https://playhorizon.dev/docs/multiplayer). -- **Mods:** Horizon has its own mod system (Rust compiled to WebAssembly). Java mod jars do not run. See [Mods and data packs](https://playhorizon.dev/docs/mods). +- **Mods:** Horizon has its own mods, installed from the launcher. The official ones (Zoom, FPS Counter, Keystrokes and Minimap) are native Rust that the launcher builds into your game, with no Rust to install; older WebAssembly mods still run while they move. Java mod jars do not run in Horizon. See [Mods and data packs](https://playhorizon.dev/docs/mods). Screenshots, captured in the Rust client itself with vanilla assets and no mods or shaders: @@ -139,6 +139,11 @@ average; `tests/test_parity.py` keeps it in sync with this table. Last audited rerun whenever one of its inputs changed, and the Mac mini now runs every one of them too (Rust uses Java's own float maths on each platform). +The harnesses measure behaviour. A second measure is method by method: the [port ledger](tools/port_ledger.py) +lists every method of vanilla 26.3 next to the Rust functions that port it, with a verdict for each one +(ported, folded into another function, missing, or needing no port), and the untested ports. Run +`python3 tools/port_ledger.py build`, then `coverage`, with the vanilla data in `.local/`. + | Subsystem | Score | Main gaps | | --- | ---: | --- | | Window, input and game loop | 1 | None: the game loop, options, camera, auto-jump, toggle sprint/sneak, local input and the frame limiter match vanilla | diff --git a/ROADMAP.md b/ROADMAP.md index eacc5e75d0..3cce885bb6 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -16,7 +16,7 @@ Horizon is Minecraft: Java Edition 26.3 rewritten from scratch in Rust, so it ru - **Single player as a server: the last differences**: Single player now runs a small server in the background, as vanilla does. We are fixing what still behaves differently there; every fix also works for LAN and server players. - **Horizon's mod registry**: One place for mods, modpacks and resource packs made for Horizon: browse them on the website and install them from the launcher in one click. -**Next**: Microsoft sign-in (coming soon); CurseForge in the mod browser (coming soon); Fresh benchmarks against vanilla; Pistons push players and mobs; Horizon on Intel Macs; Faster world creation and entry on small CPUs; Servers that hold 20 TPS with 50 to 100 players; Low-end computers: Steam Deck, old Macs, 8 GB; Native upscaling; A batch of targeted optimisations; A Convert to Rust button for what isn't rewritten yet; Mods with code: machines, magic, mobs; Data packs in the mod browser; Prepare a version without Java; Server console, bans and a full admin API; Hosting polish: Windows service, safe updates; Guided hosting with Tailscale and an invite button; An official Docker image for servers; One single-player path: remove the in-frame world; Villages and combat, exactly as in vanilla; Choose the audio output device; Mods that read and change the world; Machines and blocks with inventories (API 0.4); Lua and other languages for mods; Well-known shader looks, rewritten for Horizon; A visible beta label; Wayland and Hyprland polish; A versioned agent-control protocol; Port mods with your own AI agent; Horizon on X (coming soon); Pipeline protection and a GitHub App for agents; Signed updates everywhere, installers included; A server you install with one command. +**Next**: A new launcher design; CurseForge in the mod browser (coming soon); Fresh benchmarks against vanilla; Pistons push players and mobs; Horizon on Intel Macs; Faster world creation and entry on small CPUs; Servers that hold 20 TPS with 50 to 100 players; Low-end computers: Steam Deck, old Macs, 8 GB; Native upscaling; A batch of targeted optimisations; A Convert to Rust button for what isn't rewritten yet; Mods with code: machines, magic, mobs; Data packs in the mod browser; Prepare a version without Java; Server console, bans and a full admin API; Hosting polish: Windows service, safe updates; Guided hosting with Tailscale and an invite button; An official Docker image for servers; One single-player path: remove the in-frame world; Villages and combat, exactly as in vanilla; Choose the audio output device; Mods that read and change the world; Machines and blocks with inventories (API 0.4); Well-known shader looks, rewritten for Horizon; A visible beta label; Wayland and Hyprland polish; A versioned agent-control protocol; Port mods with your own AI agent; Horizon on X (coming soon); Pipeline protection and a GitHub App for agents; Signed updates everywhere, installers included; A server you install with one command; Ready-made setups for hosting panels. **How to help**: Play it and tell us what differs from vanilla (Discord or GitHub), run the benchmark on your computer, vote for what you want next, or pick one of the Help wanted items. @@ -104,37 +104,49 @@ More: [docs/mods.md](docs/mods.md) +#### Native mods, built by the launcher + +Horizon's own mods become native code that the launcher builds into your game, with no Rust to install: Zoom, FPS Counter, Keystrokes and Minimap already are, and Recipes is next. + +
Technical + +Stable hook points in the game (game/native_hooks.rs, [#3345](https://github.com/horizon-rust/horizon/issues/3345), [#3346](https://github.com/horizon-rust/horizon/issues/3346); v2 with the item panel and recipe pages, [#3434](https://github.com/horizon-rust/horizon/issues/3434), [#3435](https://github.com/horizon-rust/horizon/issues/3435)), horizon-mixins applied by the launcher with its own portable Rust ([#3388](https://github.com/horizon-rust/horizon/issues/3388)), on/off in Options > Mods. The official mods live in horizon-mods/native/ and are in the registry as native mods. Next: Recipes, native mods on automatically when a player installs one (with a notice about the first build), then the WebAssembly runtime is removed ([#3350](https://github.com/horizon-rust/horizon/issues/3350), [#3351](https://github.com/horizon-rust/horizon/issues/3351), [#3353](https://github.com/horizon-rust/horizon/issues/3353), [#3371](https://github.com/horizon-rust/horizon/issues/3371)). + +More: [docs/native-mods.md](docs/native-mods.md) + +
+ #### Mods that run on the server Mods that run on the server side of a world, so a dedicated server or a LAN world can have mods that react to players joining, chat and blocks being broken or placed, and that add commands.
Technical -API 0.3, experimental first ([#1833](https://github.com/horizon-rust/horizon/issues/1833), in review): the same sandboxed component runs on the server thread and the dedicated server, with world events (block broken or placed, entity spawned or killed, player joined or left), game rules and server commands with typed arguments. The world, item and entity interfaces come after (see 'Mods that read and change the world'). [docs/api/mods.md](docs/api/mods.md), [docs/mods.md](docs/mods.md). +The same hook points on the server thread and the dedicated server: world events (block broken or placed, entity spawned or killed, player joined or left), game rules and server commands with typed arguments. The earlier WebAssembly API 0.3 ([#1833](https://github.com/horizon-rust/horizon/issues/1833)) is the reference for what to cover. + +More: [docs/native-mods.md](docs/native-mods.md)
#### Screens, input and drawing for mods -Mods will be able to open their own screens and settings pages and react to keys and item use, so bigger mods, such as the Orbital Laser, can run on the normal game. +Mods will be able to open their own screens and settings pages, react to keys and item use, and draw, so bigger mods, such as the Orbital Laser, run on the normal game.
Technical -Capability `ui` is in review ([#1864](https://github.com/horizon-rust/horizon/issues/1864)): mod-owned screens and a settings page under Options > Mods, built from vanilla's widgets. Still to design: `input` (an item's use, key binds, the crosshair target), `render` (a budgeted draw list on the native shader API) and `world` (changing blocks). Orbital Laser is the acceptance test: it needs all four today through its author's fork (see 'Orbital Laser on the normal game'). - -More: [docs/mods.md](docs/mods.md) +On native hook points ([docs/native-mods.md](docs/native-mods.md)): settings pages under Options > Mods and the item panel are there ([#3346](https://github.com/horizon-rust/horizon/issues/3346), [#3435](https://github.com/horizon-rust/horizon/issues/3435)). Next: mod-owned screens built from vanilla's widgets, an item's use, key binds and the crosshair target, and a budgeted draw list on the native shader API. Orbital Laser is the acceptance test.
#### Tools for people who make mods -Better tools for mod makers: a test runner that needs no game window, templates for each kind of mod, and clear promises about what keeps working from one version to the next. +Better tools for mod makers: templates for each kind of mod, a test run with no game window, and clear promises about which hook points keep working from one version to the next.
Technical -`cargo horizon-mod test` runs a mod's test scripts against a fake game snapshot ([#1816](https://github.com/horizon-rust/horizon/issues/1816)) and, for server mods, scripted server events ([#1837](https://github.com/horizon-rust/horizon/issues/1837)); the content template and the guides are merged ([#1859](https://github.com/horizon-rust/horizon/issues/1859), [#1810](https://github.com/horizon-rust/horizon/issues/1810), [docs/api/stability.md](docs/api/stability.md)). Left: templates for hud, server and machine mods, typed wrappers in the SDK, a frozen 0.1 compatibility mod that every runtime must load, a CI check that fails when a stable reference entry is removed without a new major version, and the SDK on crates.io once the API is stable. +Native mod template and the content mod template (horizon-mods), `native.py check` that applies mods on a chosen Horizon and builds them together, a weekly check of every official mod on the newest Nightly. Left: a versioned list of stable hook points (NATIVE_HOOKS_API), a test runner without a window, and docs on the site for native mods. -More: [https://playhorizon.dev/docs/test-a-mod](https://playhorizon.dev/docs/test-a-mod) +More: [#3388](https://github.com/horizon-rust/horizon/issues/3388) · [docs/native-mods.md](docs/native-mods.md)
@@ -244,6 +256,20 @@ Horizon's own way to make shader packs, with tools to write and test them. Every +### Launcher + +#### Minecraft Java instances in the launcher + +Play the real Java game from the Horizon launcher too: vanilla and modded instances, any version, mods from Modrinth filtered to what fits, and your instances imported from other launchers. + +
Technical + +For an account that owns the game: any version downloaded with the Java it needs ([#3406](https://github.com/horizon-rust/horizon/issues/3406)), Java instances, the version list and the CLI ([#3395](https://github.com/horizon-rust/horizon/issues/3395)), the start command read from the version JSON ([#3419](https://github.com/horizon-rust/horizon/issues/3419)). Next: Play for a Java instance, Fabric and Quilt, then NeoForge and Forge; Modrinth with compatibility filtering and dependencies; imports from Prism, MultiMC, the CurseForge app, the Modrinth App, ATLauncher and the official launcher, each from its documented on-disk format. + +More: [#3506](https://github.com/horizon-rust/horizon/issues/3506) + +
+ ### Horizon extras #### Discord Rich Presence @@ -268,6 +294,16 @@ A review of the whole tree: dead code and stubs removed ([#1432](https://github. +#### Every vanilla method, tracked + +A public list of every method of the Java game and where Horizon does it, so anyone can see what is ported, what is missing and pick something to fix. + +
Technical + +[tools/port_ledger.py](tools/port_ledger.py) and tools/port_map/ ([#3139](https://github.com/horizon-rust/horizon/issues/3139)): every 26.3 method outside the skipped packages has a verdict (port, inline, missing, skip, trivial), linked to the harnesses that cover it ([#3526](https://github.com/horizon-rust/horizon/issues/3526)), with one tracking issue per area. Bug fixes start from it (.claude/skills/vanilla-parity). + +
+ ## Next ### Performance @@ -444,6 +480,16 @@ A player's idea (#horizon-ideas). `horizon-server` already runs headless; left: +#### Ready-made setups for hosting panels + +Install a Horizon server from the panels hosts use, such as Pterodactyl and AMP, the usual way for each panel. + +
Technical + +Asked in the suggestion queue for Pterodactyl eggs and AMP templates. Builds on the one-command install ('A server you install with one command') and horizon-server's server.toml ([docs/hosting.md](docs/hosting.md)). + +
+ ### Mods and API #### Mods that read and change the world @@ -452,9 +498,9 @@ Mods that can see and change blocks, read inventories and items, and find and af
Technical -After server mods: read blocks, states and biomes in loaded chunks and change them through the server's block log, so clients and the parity harnesses see vanilla's updates; the player's inventory, held item, open container and item stacks with components; entity queries near a position, a safe subset of entity data and data-driven mod entity types. Snapshots and batched calls per tick, never per-block host calls in hot paths. [docs/api/mods.md](docs/api/mods.md) ('Not there yet'). +Hook points for reading blocks, states and biomes and changing them through the server's block log, so clients and the parity harnesses see vanilla's updates; the player's inventory and item stacks with components; entity queries near a position. Batched per tick, never per-block calls in hot paths. -More: [docs/modpacks.md](docs/modpacks.md) +More: [docs/native-mods.md](docs/native-mods.md) · [docs/modpacks.md](docs/modpacks.md)
@@ -468,16 +514,6 @@ Block entities and menus: ticking machines with slots, server menu logic and the -#### Lua and other languages for mods - -Write mods as Lua scripts, in the style of KubeJS, with no compiler, and later in other languages that pass the tests. Every language runs in the same sandbox. - -
Technical - -Lua first: Horizon ships a Lua interpreter that is itself a WebAssembly mod, and a script mod's manifest names it and the capabilities it uses, so a script can do exactly what a compiled mod with the same manifest can, with hot reload of `main.lua`. Then C, C++, Zig, Go (TinyGo), AssemblyScript and JavaScript or TypeScript are measured against Rust on the same tick-heavy example, and each is supported only with a template, an example and a CI test. [docs/api/mods.md](docs/api/mods.md) ('Ways in'). - -
- ### Gameplay parity #### Pistons push players and mobs @@ -536,15 +572,13 @@ More: [docs/shaders.md](docs/shaders.md) ### Launcher -#### Microsoft sign-in (coming soon) +#### A new launcher design -Coming soon: sign in with your Microsoft account to play on online-mode servers. The code is ready; it waits for Microsoft's approval. +A redesigned launcher: every button where you expect it, the fewest clicks to play, add an instance or add mods, smooth animations, and instant startup.
Technical -Device-code sign-in in the launcher and online-mode login in the client ([docs/microsoft-account.md](docs/microsoft-account.md)). It is turned on by setting the app's client id once Minecraft API access is approved; until then the launcher shows "Coming soon". - -More: [#1270](https://github.com/horizon-rust/horizon/issues/1270) +Based on the site concept by the Team (Bricolage Grotesque, a night sky and an animated pixel sunset). Screens and flows are designed first, then small PRs; tokens in [crates/horizon-theme](crates/horizon-theme) and rules in [docs/design-system.md](docs/design-system.md), the game's vanilla look unchanged. Measured before and after: startup, frame time and idle CPU/GPU, reduced motion honoured. Mods screens separate Horizon native mods from Java mods; a running instance can be stopped from the launcher.
@@ -708,6 +742,18 @@ Issue [#1270](https://github.com/horizon-rust/horizon/issues/1270) asks for Ely. +#### Join servers on older versions + +Join servers that run older Minecraft versions, such as 1.21 or 1.8 PvP servers, from Horizon. + +
Technical + +Asked often in the suggestion queue. Horizon speaks 26.3's protocol; the path is a native protocol adapter in Rust that translates older versions' packets, not a downloaded Java proxy. Java instances in the launcher already play any version with the real game. + +More: [#3023](https://github.com/horizon-rust/horizon/issues/3023) + +
+ #### Online-mode checks for Play with Friends Friends who join through an invite code could prove who they are with their Microsoft account, as on online-mode servers. @@ -720,6 +766,18 @@ Play with Friends joins over an encrypted tunnel with an invite code ([docs/play ### Mods and API +#### Scripts for mods + +Small mods written as scripts, in the style of KubeJS, with no compiler. + +
Technical + +Waits for native mods to settle: the earlier plan ran Lua inside the WebAssembly sandbox, which is being removed. A script layer over the native hook points is to be designed. + +More: [docs/native-mods.md](docs/native-mods.md) + +
+ #### Orbital Laser on the normal game The community mod Orbital Laser, made for a fork of Horizon, running on the normal game and listed in the registry as the showcase of the mod API. @@ -732,13 +790,13 @@ A pull request on [https://github.com/Stink-O/orbital-laser](https://github.com/ #### Native versions of popular client mods -Built-in versions of the client mods players ask for: block info on hover, a recipe viewer like JEI, a world map and schematic overlays. +Built-in versions of the client mods players ask for: block info on hover, a world map, inventory tweaks and schematic overlays.
Technical -Done as extras: a mod list and menu, a minimap and a zoom ([docs/extras.md](docs/extras.md)). Left: block info tooltips, a recipe viewer (a contributor offered to port JEI if it becomes a native feature), a world map and a schematic overlay. They need the read-only interfaces `inventory-read`, `recipes`, `world-read` and `render` in the mod API, or they go into the game directly. +Done: a mod list and menu, minimap, zoom, keystrokes, an FPS counter and a recipe viewer, as native mods (horizon-mods/native/). Asked for: an angled world map that shows hills, overhangs and caves, inventory tweaks (drag and scroll to move items), block info tooltips and schematic overlays. -More: [docs/mods.md](docs/mods.md) +More: [docs/extras.md](docs/extras.md) · [docs/native-mods.md](docs/native-mods.md)
@@ -784,6 +842,16 @@ The game side is merged ([docs/controllers.md](docs/controllers.md)): pads read +#### Splitscreen + +Play with a friend on the same screen, each with a controller. + +
Technical + +Issue [#2054](https://github.com/horizon-rust/horizon/issues/2054); a contributor is working on it. Two local players on the same world, each with their own camera and HUD, built on the server thread's multiplayer. + +
+ #### The full debug screen Vanilla's complete F3 screen: debug options, a profiler chart, ten-second captures and an atlas dump. @@ -834,7 +902,7 @@ More graphics options: multisample anti-aliasing, anisotropic filtering and an e
Technical -Not implemented ([docs/graphics.md](docs/graphics.md)). Anisotropic filtering needs linear magnification in wgpu, which blurs close blocks, so close-up sharpness has to be kept first; MSAA would sit beside FXAA; exclusive fullscreen where the backend supports it. +Not implemented ([docs/graphics.md](docs/graphics.md)). Anisotropic filtering needs linear magnification in wgpu, which blurs close blocks, so close-up sharpness has to be kept first; MSAA would sit beside FXAA, and SMAA or TAA are asked for too; exclusive fullscreen where the backend supports it.
@@ -862,6 +930,18 @@ Issue [#1709](https://github.com/horizon-rust/horizon/issues/1709): aarch64 buil +#### Horizon on Windows ARM64 + +A native Horizon for Windows on ARM computers, such as Snapdragon laptops. + +
Technical + +Releases are x64 for Windows and Linux and arm64 for macOS. Needs an aarch64-pc-windows-msvc build in the release workflow and a machine to test on. + +More: [docs/release-flow.md](docs/release-flow.md) + +
+ #### Android, iOS and the browser Versions of Horizon for Android, iOS and web browsers. Nothing official yet; contributors have started the first pieces. @@ -968,6 +1048,7 @@ Armour and tools in End city chests should come enchanted, as in vanilla. ## Recently shipped +- **Microsoft sign-in** (launcher@0.2.17): Sign in with your Microsoft account in the launcher to play on online-mode servers. [docs/microsoft-account.md](docs/microsoft-account.md) · [#2879](https://github.com/horizon-rust/horizon/issues/2879) - **OpenGL for old computers** (v0.2.63): Horizon now starts on old PCs whose graphics card has no Vulkan or DirectX 12 driver: it switches to OpenGL by itself, and Options > Graphics can pick OpenGL by hand. [#1917](https://github.com/horizon-rust/horizon/issues/1917) · [#1920](https://github.com/horizon-rust/horizon/issues/1920) · [#1936](https://github.com/horizon-rust/horizon/issues/1936) - **Server resource packs** (v0.2.48): Servers can send you their resource pack: Horizon asks, downloads it and applies it, like vanilla. [#1562](https://github.com/horizon-rust/horizon/issues/1562) · [#1569](https://github.com/horizon-rust/horizon/issues/1569) · [#1570](https://github.com/horizon-rust/horizon/issues/1570) - **Single player on its own server thread** (v0.2.39): Single player runs on a server thread, as vanilla does, so the world keeps ticking smoothly while the window draws. [#1514](https://github.com/horizon-rust/horizon/issues/1514) · [docs/server-levels.md](docs/server-levels.md) diff --git a/crates/horizon-client-core/src/component.rs b/crates/horizon-client-core/src/component.rs index 0826245f04..e279f91c86 100644 --- a/crates/horizon-client-core/src/component.rs +++ b/crates/horizon-client-core/src/component.rs @@ -11,13 +11,18 @@ pub fn parse_snbt(input: &str) -> Result<(Value, usize), String> { let mut p = Parser { s: input.as_bytes(), i: 0, + depth: 0, }; let v = p.value()?; Ok((v, p.i)) } +/// Nesting deeper than NBT's own limit (`NbtAccounter`'s 512) is refused +/// rather than overflowing the stack. +const SNBT_MAX_DEPTH: usize = 512; struct Parser<'a> { s: &'a [u8], i: usize, + depth: usize, } impl Parser<'_> { fn ws(&mut self) { @@ -41,57 +46,22 @@ impl Parser<'_> { self.ws(); match self.peek() { Some(b'{') => { - self.i += 1; - let mut map = Map::new(); - self.ws(); - if self.peek() == Some(b'}') { - self.i += 1; - return Ok(Value::Object(map)); - } - loop { - self.ws(); - let key = self.string()?; - self.expect(b':')?; - map.insert(key, self.value()?); - self.ws(); - match self.peek() { - Some(b',') => self.i += 1, - Some(b'}') => { - self.i += 1; - return Ok(Value::Object(map)); - } - _ => return Err("expected ',' or '}'".into()), - } + if self.depth >= SNBT_MAX_DEPTH { + return Err(format!("tag nesting too deep at {}", self.i)); } + self.depth += 1; + let v = self.object(); + self.depth -= 1; + v } Some(b'[') => { - self.i += 1; - let mut list = Vec::new(); - self.ws(); - // Typed arrays [B;...], [I;...], [L;...]. - if matches!( - self.s.get(self.i..self.i + 2), - Some([b'B' | b'I' | b'L', b';']) - ) { - self.i += 2; - } - self.ws(); - if self.peek() == Some(b']') { - self.i += 1; - return Ok(Value::Array(list)); - } - loop { - list.push(self.value()?); - self.ws(); - match self.peek() { - Some(b',') => self.i += 1, - Some(b']') => { - self.i += 1; - return Ok(Value::Array(list)); - } - _ => return Err("expected ',' or ']'".into()), - } + if self.depth >= SNBT_MAX_DEPTH { + return Err(format!("tag nesting too deep at {}", self.i)); } + self.depth += 1; + let v = self.array(); + self.depth -= 1; + v } Some(b'"' | b'\'') => Ok(Value::String(self.string()?)), Some(_) => { @@ -105,10 +75,20 @@ impl Parser<'_> { _ => { let digits = word .trim_end_matches(['b', 'B', 's', 'S', 'l', 'L', 'f', 'F', 'd', 'D']); - match (digits.parse::(), digits.parse::()) { - (Ok(n), _) if digits.len() + 1 >= word.len() => Value::from(n), - (_, Ok(f)) if digits.len() + 1 >= word.len() => Value::from(f), - _ => Value::String(word), + // Only digits (and an exponent) make a number: `nan` + // or `Infinity`, which Rust's parse would take, stays + // a string. + if digits + .bytes() + .any(|c| c.is_ascii_alphabetic() && c != b'e' && c != b'E') + { + Value::String(word) + } else { + match (digits.parse::(), digits.parse::()) { + (Ok(n), _) if digits.len() + 1 >= word.len() => Value::from(n), + (_, Ok(f)) if digits.len() + 1 >= word.len() => Value::from(f), + _ => Value::String(word), + } } } }) @@ -116,6 +96,59 @@ impl Parser<'_> { None => Err("expected a value".into()), } } + fn object(&mut self) -> Result { + self.i += 1; + let mut map = Map::new(); + self.ws(); + if self.peek() == Some(b'}') { + self.i += 1; + return Ok(Value::Object(map)); + } + loop { + self.ws(); + let key = self.string()?; + self.expect(b':')?; + map.insert(key, self.value()?); + self.ws(); + match self.peek() { + Some(b',') => self.i += 1, + Some(b'}') => { + self.i += 1; + return Ok(Value::Object(map)); + } + _ => return Err("expected ',' or '}'".into()), + } + } + } + fn array(&mut self) -> Result { + self.i += 1; + let mut list = Vec::new(); + self.ws(); + // Typed arrays [B;...], [I;...], [L;...]. + if matches!( + self.s.get(self.i..self.i + 2), + Some([b'B' | b'I' | b'L', b';']) + ) { + self.i += 2; + } + self.ws(); + if self.peek() == Some(b']') { + self.i += 1; + return Ok(Value::Array(list)); + } + loop { + list.push(self.value()?); + self.ws(); + match self.peek() { + Some(b',') => self.i += 1, + Some(b']') => { + self.i += 1; + return Ok(Value::Array(list)); + } + _ => return Err("expected ',' or ']'".into()), + } + } + } fn unquoted(&mut self) -> String { let start = self.i; while let Some(c) = self.peek() { @@ -351,4 +384,22 @@ mod tests { let t = to_text(&v, &mut |_| Text::empty()); assert_eq!(t.spans[0].style.font, None); } + + #[test] + fn snbt_nesting_is_bounded() { + let nest = |n: usize| format!("{}1{}", "[".repeat(n), "]".repeat(n)); + assert!(parse_snbt(&nest(512)).is_ok()); + assert!(parse_snbt(&nest(513)).is_err()); + assert!(parse_snbt(&nest(100_000)).is_err()); + } + + #[test] + fn bare_words_that_look_numeric_stay_strings() { + let (v, _) = parse_snbt("{text:nan}").unwrap(); + assert_eq!(v["text"], Value::String("nan".into())); + let (v, _) = parse_snbt("{text:Infinity}").unwrap(); + assert_eq!(v["text"], Value::String("Infinity".into())); + let (v, _) = parse_snbt("{n:1.5}").unwrap(); + assert_eq!(v["n"], Value::from(1.5)); + } } diff --git a/crates/horizon-client-core/src/lang.rs b/crates/horizon-client-core/src/lang.rs index ad9f1a3582..3f5b7cde40 100644 --- a/crates/horizon-client-core/src/lang.rs +++ b/crates/horizon-client-core/src/lang.rs @@ -40,6 +40,10 @@ static BIDIRECTIONAL: AtomicBool = AtomicBool::new(false); /// A source of resource files: the prepared root plus packs, highest priority last. pub trait ResourceSource { fn read(&self, path: &str) -> Option>; + /// Whether `path` is there, without reading it where the source can tell. + fn exists(&self, path: &str) -> bool { + self.read(path).is_some() + } /// The namespaces under `assets/` (resource packs may add their own). fn namespaces(&self) -> Vec { vec!["minecraft".into()] @@ -56,6 +60,9 @@ impl ResourceSource for Bundled { "assets/horizon/lang/ru_ru.json" => { include_str!("../../horizon-client/assets/horizon/lang/ru_ru.json") } + "assets/horizon/lang/uk_ua.json" => { + include_str!("../../horizon-client/assets/horizon/lang/uk_ua.json") + } _ => return None, }; Some(body.as_bytes().to_vec()) @@ -68,6 +75,9 @@ impl ResourceSource for std::path::PathBuf { fn read(&self, path: &str) -> Option> { std::fs::read(self.join(path)).ok() } + fn exists(&self, path: &str) -> bool { + self.join(path).is_file() + } fn namespaces(&self) -> Vec { std::fs::read_dir(self.join("assets")) .into_iter() @@ -190,7 +200,7 @@ pub fn available(sources: &[&dyn ResourceSource]) -> Vec { for (code, info) in value["language"].as_object().into_iter().flatten() { // Only languages whose file exists somewhere are selectable. let file = format!("assets/minecraft/lang/{code}.json"); - if code != DEFAULT && !sources.iter().any(|s| s.read(&file).is_some()) { + if code != DEFAULT && !sources.iter().any(|s| s.exists(&file)) { continue; } found.insert( @@ -259,6 +269,11 @@ mod tests { assert_eq!(en.get("horizon.options.controller"), Some("Controller")); let ru = Language::load(&[&empty], "ru_ru"); assert_eq!(ru.get("horizon.options.controller"), Some("Геймпад")); + let uk = Language::load(&[&empty], "uk_ua"); + assert_eq!( + uk.get("horizon.options.on_screen_keyboard"), + Some("Екранна клавіатура") + ); } #[test] @@ -284,4 +299,26 @@ mod tests { ); assert_eq!(langs[1].label(), "Français (France)"); } + + #[test] + fn languages_are_listed_without_reading_their_files() { + struct NoLangReads; + impl ResourceSource for NoLangReads { + fn read(&self, path: &str) -> Option> { + assert!(!path.contains("/lang/"), "read {path}"); + (path == "assets/pack.mcmeta").then(|| { + br#"{"language":{"fr_fr":{"name":"French"},"de_de":{"name":"German"}}}"# + .to_vec() + }) + } + fn exists(&self, path: &str) -> bool { + path == "assets/minecraft/lang/fr_fr.json" + } + } + let langs = available(&[&NoLangReads]); + assert_eq!( + langs.iter().map(|l| l.code.as_str()).collect::>(), + ["en_us", "fr_fr"] + ); + } } diff --git a/crates/horizon-client-core/src/potions.rs b/crates/horizon-client-core/src/potions.rs index 044b25e3ae..edebf5aa38 100644 --- a/crates/horizon-client-core/src/potions.rs +++ b/crates/horizon-client-core/src/potions.rs @@ -91,6 +91,7 @@ pub fn effect(id: &str) -> Option<&'static EffectDef> { /// Namespaced effect ids for mob effect storage. pub fn namespaced(id: &'static str) -> &'static str { static IDS: OnceLock> = OnceLock::new(); + let key = id.strip_prefix("minecraft:").unwrap_or(id); IDS.get_or_init(|| { EFFECTS .iter() @@ -102,7 +103,7 @@ pub fn namespaced(id: &'static str) -> &'static str { }) .collect() }) - .get(id) + .get(key) .copied() .unwrap_or(id) } diff --git a/crates/horizon-client-core/src/registries.rs b/crates/horizon-client-core/src/registries.rs index 310115cf8f..d638637c39 100644 --- a/crates/horizon-client-core/src/registries.rs +++ b/crates/horizon-client-core/src/registries.rs @@ -54,6 +54,12 @@ pub struct Registries { pub function_tags: Vec, /// The block the source is looking at (relevant coordinates). pub target_block: Option<[i32; 3]>, + /// `ClientSuggestionProvider.getSelectedEntities`: the UUID of the + /// entity under the crosshair (`Entity.getStringUUID`), if any. + pub selected_entities: Vec, + /// `ClientSuggestionProvider.getAbsoluteCoordinates`: the exact point of + /// that block hit, each coordinate as `%.2f` (`Locale.ROOT`). + pub target_point: Option<[String; 3]>, /// Every registry's element and tag ids (`minecraft:item` -> ...), as /// the server's registry access and reloadable registries hold them. pub by_registry: BTreeMap, diff --git a/crates/horizon-client-core/src/scoreboard.rs b/crates/horizon-client-core/src/scoreboard.rs index 3e820e4eb6..cf43e61bd3 100644 --- a/crates/horizon-client-core/src/scoreboard.rs +++ b/crates/horizon-client-core/src/scoreboard.rs @@ -662,10 +662,22 @@ pub fn name_shown(own: Option<&Team>, viewer: Option<&Team>, v: NameView) -> boo } !v.hud_hidden && !v.is_camera && visible && !v.is_vehicle } -/// `ServerPlayer.die`: whether a player's death message reaches the player -/// themselves (the team broadcasts leave out the dying player). -pub fn own_death_message_shown(team: Option<&Team>) -> bool { - team.is_none_or(|t| t.death_message_visibility == Visibility::Always) +/// `ServerPlayer.die`: whether `other` hears the death message of `dying` +/// (`broadcastSystemMessage`, `broadcastSystemToTeam` or +/// `broadcastSystemToAllExceptTeam`, by the team's death message visibility). +pub fn death_message_reaches(board: &Scoreboard, dying: &str, other: &str) -> bool { + let Some(team) = board.players_team(dying) else { + return true; + }; + let same_team = board + .players_team(other) + .is_some_and(|t| t.name == team.name); + match team.death_message_visibility { + Visibility::Always => true, + Visibility::Never => false, + Visibility::HideForOtherTeams => same_team && other != dying, + Visibility::HideForOwnTeam => !same_team, + } } // ---- objectives and scores --------------------------------------------- diff --git a/crates/horizon-client-core/src/text.rs b/crates/horizon-client-core/src/text.rs index 12cc196d97..3c07b4bcf1 100644 --- a/crates/horizon-client-core/src/text.rs +++ b/crates/horizon-client-core/src/text.rs @@ -403,31 +403,7 @@ impl Text { continue; } let Some(code) = chars.next() else { break }; - let code = code.to_ascii_lowercase(); - let next = if let Some(c) = COLORS.iter().find(|c| c.0 == code) { - // Style.applyLegacyFormat: a colour clears the decorations. - Style { - color: Some(c.2), - bold: Some(false), - italic: Some(false), - underlined: Some(false), - strikethrough: Some(false), - obfuscated: Some(false), - ..style.clone() - } - } else { - let mut s = style.clone(); - match code { - 'k' => s.obfuscated = Some(true), - 'l' => s.bold = Some(true), - 'm' => s.strikethrough = Some(true), - 'n' => s.underlined = Some(true), - 'o' => s.italic = Some(true), - 'r' => s = span.style.clone(), - _ => {} - } - s - }; + let next = legacy_format(&style, code, &span.style); if next != style { if !current.is_empty() { out.spans.push(Span { @@ -588,6 +564,35 @@ fn style_json(style: &Style) -> Vec<(String, Value)> { out } +/// `Style.applyLegacyFormat` for the code after a `§`, `§r` going back to +/// `reset` (`StringDecomposer.iterateFormatted`); an unknown code changes nothing. +pub fn legacy_format(style: &Style, code: char, reset: &Style) -> Style { + let code = code.to_ascii_lowercase(); + if let Some(c) = COLORS.iter().find(|c| c.0 == code) { + // A colour clears the decorations. + return Style { + color: Some(c.2), + bold: Some(false), + italic: Some(false), + underlined: Some(false), + strikethrough: Some(false), + obfuscated: Some(false), + ..style.clone() + }; + } + let mut s = style.clone(); + match code { + 'k' => s.obfuscated = Some(true), + 'l' => s.bold = Some(true), + 'm' => s.strikethrough = Some(true), + 'n' => s.underlined = Some(true), + 'o' => s.italic = Some(true), + 'r' => s = reset.clone(), + _ => {} + } + s +} + /// TranslatableContents.decomposeTemplate: `%s` takes the next argument, /// `%n$s` a numbered one and `%%` is a percent sign. Malformed templates are /// shown verbatim. diff --git a/crates/horizon-client-input/src/clipboard.rs b/crates/horizon-client-input/src/clipboard.rs index 76872091cc..eaf2a2274f 100644 --- a/crates/horizon-client-input/src/clipboard.rs +++ b/crates/horizon-client-input/src/clipboard.rs @@ -60,6 +60,19 @@ pub fn edit_shortcut( }) } +/// Windows reports AltGr as Ctrl plus Alt: the system presses a left Control +/// in front of the right Alt. +pub const ALTGR_IS_CTRL_ALT: bool = cfg!(windows); + +/// Whether a key's text reaches the screen (`KeyboardHandler.textInput`, then +/// `charTyped` per code point). The edit modifier's shortcuts type nothing, +/// but `textInput` takes no modifiers: with AltGr held (Ctrl plus Alt where +/// `altgr_is_ctrl_alt`) the layout's third-level characters are text, as `/` +/// on ABNT2, `@`, `{` and `[` on German or the Polish letters. +pub fn types_text(edit_modifier: bool, alt: bool, altgr_is_ctrl_alt: bool) -> bool { + !edit_modifier || (altgr_is_ctrl_alt && alt) +} + /// `StringUtil.isAllowedChatCharacter`. pub fn is_allowed_chat_character(c: char) -> bool { c != '§' && c >= ' ' && c != '\u{7f}' @@ -303,6 +316,22 @@ pub fn append_limited(value: &mut String, text: &str, max_chars: usize) -> bool mod tests { use super::*; + #[test] + fn altgr_types_text_where_it_is_ctrl_alt() { + // Plain keys and Shift type. + assert!(types_text(false, false, true)); + assert!(types_text(false, false, false)); + // Ctrl (Cmd) shortcuts do not. + assert!(!types_text(true, false, true)); + assert!(!types_text(true, false, false)); + // AltGr on Windows is Ctrl+Alt: ABNT2 `/`, German `@` type. + assert!(types_text(true, true, true)); + // Where AltGr is its own modifier, Ctrl+Alt is a shortcut. + assert!(!types_text(true, true, false)); + // Alt alone (macOS Option, Linux AltGr) types. + assert!(types_text(false, true, false)); + } + #[test] fn shortcuts_need_the_edit_modifier_alone() { let c = Some('c'); diff --git a/crates/horizon-client-input/src/keybinds.rs b/crates/horizon-client-input/src/keybinds.rs index 433b37161a..1c3152b18b 100644 --- a/crates/horizon-client-input/src/keybinds.rs +++ b/crates/horizon-client-input/src/keybinds.rs @@ -300,9 +300,8 @@ impl Default for KeyBinds { } } /// Each default key once, in order: two mappings that share a default key -/// (Advancements and the debug profiler both on L) are one press of it, not -/// two (the second would close the screen the first opened). Vanilla clicks -/// both mappings, but the profiler one only acts with F3 held. +/// (Pick Block and the spectator hotbar both on the middle button) are one +/// press of it, not two. fn distinct(keys: Vec) -> Vec { let mut out = Vec::with_capacity(keys.len()); for k in keys { @@ -368,6 +367,25 @@ impl KeyBinds { .map(|m| m.0) .filter(move |n| self.get(n) == key) } + /// The mappings bound to `key` that [`KeyBinds::translate_all`] follows: + /// all but the debug keys. Those act only with F3 held + /// (`KeyboardHandler.handleDebugKeys`, `debug_keys.rs` reads the physical + /// key and the binding itself); their defaults (A, S, D, T, L, 1 to 4...) + /// would otherwise turn the key back into its old gameplay meaning. + fn translated_mappings<'a>(&'a self, key: &'a str) -> impl Iterator + 'a { + MAPPINGS + .iter() + .filter(|m| m.2 != "debug") + .map(|m| m.0) + .filter(move |n| self.get(n) == key) + } + /// Whether a mapping (other than a debug key) whose default is `key` was + /// bound elsewhere. + fn default_moved(&self, key: &str) -> bool { + MAPPINGS + .iter() + .any(|(n, d, c)| *d == key && *c != "debug" && !self.is_default(n)) + } /// Translate a physical key into the default key of the mapping it is /// bound to, so game code written against vanilla defaults follows the /// player's bindings. `None` means the key now does nothing; mouse @@ -384,7 +402,7 @@ impl KeyBinds { return vec![Ok(key)]; }; let bound: Vec<_> = self - .mappings_for(name) + .translated_mappings(name) .filter_map(|mapping| { let default = Self::default_of(mapping); match key_code(default) { @@ -397,10 +415,7 @@ impl KeyBinds { return distinct(bound); } // An unbound key whose default meaning was moved elsewhere is inert. - if MAPPINGS - .iter() - .any(|(n, d, _)| *d == name && !self.is_default(n)) - { + if self.default_moved(name) { return Vec::new(); } vec![Ok(key)] @@ -413,7 +428,7 @@ impl KeyBinds { pub fn translate_mouse_all(&self, button: MouseButton) -> Vec> { let name = mouse_name(button); let bound: Vec<_> = self - .mappings_for(&name) + .translated_mappings(&name) .filter_map(|mapping| { let default = Self::default_of(mapping); match mouse_button(default) { @@ -425,10 +440,7 @@ impl KeyBinds { if !bound.is_empty() { return distinct(bound); } - if MAPPINGS - .iter() - .any(|(n, d, _)| *d == name && !self.is_default(n)) - { + if self.default_moved(&name) { return Vec::new(); } vec![Err(button)] @@ -508,6 +520,29 @@ mod tests { assert_eq!(k.conflicts("key.forward"), ["key.jump"]); } #[test] + fn a_moved_key_keeps_no_default_action() { + // #3158: the debug keys (F3+A, F3+1...) share these keys' defaults, + // and must not hand them back their gameplay meaning. + let mut k = KeyBinds::default(); + k.set("key.hotbar.1", "key.keyboard.q"); + assert_eq!(k.translate_all(KeyCode::Digit1), []); + assert_eq!( + k.translate_all(KeyCode::KeyQ), + [Ok(KeyCode::KeyQ), Ok(KeyCode::Digit1)] + ); + k.set("key.inventory", "key.keyboard.a"); + k.set("key.left", "key.keyboard.e"); + assert_eq!(k.translate_all(KeyCode::KeyA), [Ok(KeyCode::KeyE)]); + assert_eq!(k.translate_all(KeyCode::KeyE), [Ok(KeyCode::KeyA)]); + k.set("key.back", "key.keyboard.down"); + assert_eq!(k.translate_all(KeyCode::KeyS), []); + // Keys left alone still act, and a moved debug key changes nothing. + k.set("key.debug.fpsCharts", "key.keyboard.9"); + assert_eq!(k.translate_all(KeyCode::Digit2), [Ok(KeyCode::Digit2)]); + assert_eq!(k.translate_all(KeyCode::Digit9), [Ok(KeyCode::Digit9)]); + assert_eq!(k.translate_all(KeyCode::F3), [Ok(KeyCode::F3)]); + } + #[test] fn display_names() { assert_eq!(display_name("key.keyboard.w").plain(), "W"); assert_eq!(display_name("key.keyboard.slash").plain(), "/"); diff --git a/crates/horizon-client-render/Cargo.toml b/crates/horizon-client-render/Cargo.toml new file mode 100644 index 0000000000..75cf5ee855 --- /dev/null +++ b/crates/horizon-client-render/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "horizon-client-render" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version = "1.90" +description = "The client's world data, rendering and screens: chunks, meshing, light, streaming, the renderer, the HUD, menus and settings; no gameplay" + +[dependencies] +horizon-assets = { path = "../horizon-assets" } +horizon-bench = { path = "../horizon-bench" } +horizon-client-core = { path = "../horizon-client-core" } +horizon-client-input = { path = "../horizon-client-input" } +horizon-command = { path = "../horizon-command" } +horizon-core = { path = "../horizon-core" } +horizon-hdr = { path = "../horizon-hdr" } +horizon-items = { path = "../horizon-items" } +horizon-protocol = { path = "../horizon-protocol" } +horizon-shader-api = { path = "../horizon-shader-api" } +horizon-shaders = { path = "../horizon-shaders" } +horizon-telemetry = { path = "../horizon-telemetry", default-features = false, features = ["live"] } +horizon-theme = { path = "../horizon-theme" } +horizon-threads = { path = "../horizon-threads" } +horizon-world = { path = "../horizon-world" } +ab_glyph = "0.2.32" +bytemuck = { version = "1.24", features = ["derive"] } +# Sound (ALSA on Linux); the `audio` feature. +cpal = { version = "0.16", optional = true } +chrono = { version = "0.4.42", default-features = false, features = ["clock", "std"] } +flate2 = { version = "1.1.10", default-features = false, features = ["zlib-rs", "runtime_detection"] } +glam = "0.30" +log = "0.4" +miniz_oxide = "0.8" +naga = { version = "30.0.1", features = ["wgsl-in", "hlsl-out"] } +png = "0.18" +pollster = "0.4" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1.0.151", features = ["raw_value"] } +sha2 = "0.10.9" +symphonia = { version = "0.5", default-features = false, features = ["ogg", "vorbis"] } +unicode-bidi = "0.3.18" +ureq = { version = "2", default-features = false, features = ["tls"] } +wgpu = { version = "30.0.1", default-features = false, features = ["std", "parking_lot", "vulkan", "metal", "wgsl", "naga-ir"] } +winit = "0.30.13" +zip = { version = "2", default-features = false, features = ["deflate"] } + +[target.'cfg(unix)'.dependencies] +rustix = { version = "1", default-features = false, features = ["std", "time"] } + +[target.'cfg(windows)'.dependencies] +wgpu = { version = "30.0.1", default-features = false, features = ["dx12"] } + +[target.'cfg(not(target_os = "macos"))'.dependencies] +wgpu = { version = "30.0.1", default-features = false, features = ["gles"] } + +[features] +# Sound output: horizon-client's `audio` feature. +audio = ["dep:cpal"] +# Helpers horizon-client's tests build their fixtures with. +test-support = [] + +[lints] +workspace = true + +[dev-dependencies] +horizon-items = { path = "../horizon-items", features = ["test-support"] } +naga = { version = "30.0.1", features = ["wgsl-in", "hlsl-out", "msl-out", "spv-out"] } diff --git a/crates/horizon-client-render/examples/compositor.rs b/crates/horizon-client-render/examples/compositor.rs new file mode 100644 index 0000000000..541109c239 --- /dev/null +++ b/crates/horizon-client-render/examples/compositor.rs @@ -0,0 +1,151 @@ +//! Two offscreen frames presented in one window, without game resources or player data. +//! `cargo run --profile fast -p horizon-client-render --example compositor -- --smoke` +use horizon_client_render::gfx::compositor::Compositor; +use std::sync::Arc; +use winit::{ + application::ApplicationHandler, + event::WindowEvent, + event_loop::{ActiveEventLoop, EventLoop}, + window::{Window, WindowId}, +}; + +struct Frames { + window: Arc, + compositor: Compositor, + textures: [wgpu::Texture; 2], +} + +impl Frames { + fn new(event_loop: &ActiveEventLoop) -> horizon_client_render::Result { + let window = Arc::new( + event_loop.create_window( + Window::default_attributes() + .with_title("Compositor: red and blue views") + .with_inner_size(winit::dpi::PhysicalSize::new(640, 360)), + )?, + ); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_with_display_handle( + Box::new(window.clone()), + )); + let surface = instance.create_surface(window.clone())?; + let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { + compatible_surface: Some(&surface), + ..Default::default() + }))?; + let (device, queue) = pollster::block_on(adapter.request_device(&Default::default()))?; + let textures = [[0, 0, 255, 255], [255, 0, 0, 255]].map(|pixel| { + let size = wgpu::Extent3d { + width: 320, + height: 360, + depth_or_array_layers: 1, + }; + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("Offscreen view"), + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Bgra8Unorm, + usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + queue.write_texture( + texture.as_image_copy(), + &pixel.repeat(320 * 360), + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(320 * 4), + rows_per_image: Some(360), + }, + size, + ); + texture + }); + let compositor = Compositor::new( + surface, + &adapter, + &device, + &queue, + window.inner_size().into(), + )?; + Ok(Self { + window, + compositor, + textures, + }) + } +} + +#[derive(Default)] +struct Demo { + frames: Option, + error: Option, + presented: usize, + smoke: bool, +} + +impl ApplicationHandler for Demo { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + if self.frames.is_none() { + match Frames::new(event_loop) { + Ok(frames) => self.frames = Some(frames), + Err(error) => { + self.error = Some(error.to_string()); + event_loop.exit(); + } + } + } + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _: WindowId, event: WindowEvent) { + let Some(f) = &mut self.frames else { return }; + match event { + WindowEvent::CloseRequested => event_loop.exit(), + WindowEvent::Resized(size) => f.compositor.resize(size.width, size.height), + // Uncovered pixels are black; resize smaller to exercise clipping. + WindowEvent::RedrawRequested + if f.compositor + .present(&[(&f.textures[0], [0, 0]), (&f.textures[1], [320, 0])]) => + { + self.presented += 1; + if self.smoke && self.presented == 10 { + f.compositor.set_vsync(false); + f.compositor.resize(0, 0); + assert!(!f.compositor.present(&[])); + f.compositor.resize(u32::MAX, u32::MAX); + assert!(!f.compositor.present(&[])); + f.compositor.resize(400, 240); + let _ = f + .window + .request_inner_size(winit::dpi::PhysicalSize::new(400, 240)); + } + if self.smoke && self.presented == 20 { + println!("compositor: 20 frames, resize, suspension and clipping passed"); + event_loop.exit(); + } + } + _ => {} + } + } + + fn about_to_wait(&mut self, _: &ActiveEventLoop) { + if let Some(f) = &self.frames { + f.window.request_redraw(); + } + } +} + +fn main() -> horizon_client_render::Result<()> { + let mut demo = Demo { + smoke: std::env::args().any(|a| a == "--smoke"), + ..Default::default() + }; + EventLoop::new()?.run_app(&mut demo)?; + if let Some(error) = demo.error { + return Err(error.into()); + } + if demo.smoke && demo.presented < 20 { + return Err("smoke test closed early".into()); + } + Ok(()) +} diff --git a/crates/horizon-client/src/assets.rs b/crates/horizon-client-render/src/assets.rs similarity index 98% rename from crates/horizon-client/src/assets.rs rename to crates/horizon-client-render/src/assets.rs index 63c237c9e8..b2943134de 100644 --- a/crates/horizon-client/src/assets.rs +++ b/crates/horizon-client-render/src/assets.rs @@ -42,11 +42,11 @@ impl HeightmapState for StateFacts { } #[derive(Clone)] pub struct Properties(Arc>); -#[cfg(test)] +#[cfg(any(test, feature = "test-support"))] impl Properties { /// Heightmap facts for tests, per state: (air, has a fluid, blocks /// motion, blocks motion but is not leaves). State 0 is minecraft:air. - pub(crate) fn for_tests(states: &[(bool, bool, bool, bool)]) -> Self { + pub fn for_tests(states: &[(bool, bool, bool, bool)]) -> Self { Self(Arc::new( states .iter() @@ -196,7 +196,7 @@ impl Assets { impl RenderAssets { pub fn load(root: &Path, assets: &Assets) -> Result { let registry = assets.registry; - let mut sprites = crate::sprites::Sprites::default(); + let mut sprites = crate::sprites::Sprites::with_disk_cache(root); for name in MATERIALS { sprites.insert(root, &format!("minecraft:block/{name}"))?; } @@ -217,6 +217,7 @@ impl RenderAssets { for stage in 0..10 { sprites.insert(root, &format!("minecraft:block/destroy_stage_{stage}"))?; } + sprites.save_disk_cache(); sprites.finish(); // `finish` grows the buffer by doubling: keep only the bytes in use. sprites.pixels.shrink_to_fit(); diff --git a/crates/horizon-client/src/atlas_sources.rs b/crates/horizon-client-render/src/atlas_sources.rs similarity index 97% rename from crates/horizon-client/src/atlas_sources.rs rename to crates/horizon-client-render/src/atlas_sources.rs index fbed22fd20..c3e9b2a5da 100644 --- a/crates/horizon-client/src/atlas_sources.rs +++ b/crates/horizon-client-render/src/atlas_sources.rs @@ -187,7 +187,14 @@ pub fn list_textures(root: &Path) -> Vec { }; for e in entries.flatten() { let p = e.path(); - if p.is_dir() { + // `file_type` comes with the listing; `Path::is_dir` is a stat per + // entry, kept for links (which it follows). + let dir = match e.file_type() { + Ok(t) if t.is_symlink() => p.is_dir(), + Ok(t) => t.is_dir(), + Err(_) => p.is_dir(), + }; + if dir { stack.push(p); } else if p.extension().is_some_and(|x| x == "png") && let Ok(rel) = p.strip_prefix(&base) diff --git a/crates/horizon-client/src/audio.rs b/crates/horizon-client-render/src/audio.rs similarity index 100% rename from crates/horizon-client/src/audio.rs rename to crates/horizon-client-render/src/audio.rs diff --git a/crates/horizon-client/src/auth.rs b/crates/horizon-client-render/src/auth.rs similarity index 100% rename from crates/horizon-client/src/auth.rs rename to crates/horizon-client-render/src/auth.rs diff --git a/crates/horizon-client/src/bidi.rs b/crates/horizon-client-render/src/bidi.rs similarity index 100% rename from crates/horizon-client/src/bidi.rs rename to crates/horizon-client-render/src/bidi.rs diff --git a/crates/horizon-client/src/biome.rs b/crates/horizon-client-render/src/biome.rs similarity index 99% rename from crates/horizon-client/src/biome.rs rename to crates/horizon-client-render/src/biome.rs index dbffbad39f..4538166fa0 100644 --- a/crates/horizon-client/src/biome.rs +++ b/crates/horizon-client-render/src/biome.rs @@ -256,6 +256,10 @@ impl Biomes { .copied() .unwrap_or(0) } + /// Whether the game has the biome `name`. + pub fn contains(&self, name: &str) -> bool { + self.index.contains_key(name) + } pub fn get(&self, id: u16) -> &BiomeInfo { &self.list[usize::from(id).min(self.list.len() - 1)] } diff --git a/crates/horizon-client/src/block_entities.rs b/crates/horizon-client-render/src/block_entities.rs similarity index 100% rename from crates/horizon-client/src/block_entities.rs rename to crates/horizon-client-render/src/block_entities.rs diff --git a/crates/horizon-client/src/block_entity_anim.rs b/crates/horizon-client-render/src/block_entity_anim.rs similarity index 100% rename from crates/horizon-client/src/block_entity_anim.rs rename to crates/horizon-client-render/src/block_entity_anim.rs diff --git a/crates/horizon-client/src/block_entity_render.rs b/crates/horizon-client-render/src/block_entity_render.rs similarity index 96% rename from crates/horizon-client/src/block_entity_render.rs rename to crates/horizon-client-render/src/block_entity_render.rs index b415748c4a..913b2def9c 100644 --- a/crates/horizon-client/src/block_entity_render.rs +++ b/crates/horizon-client-render/src/block_entity_render.rs @@ -654,10 +654,20 @@ fn sign_text_calls( .collect() } +/// How far the text of an outlined call sits in front of its outline, in +/// text units (`Font.DisplayMode.POLYGON_OFFSET`, see `draw_text`). +const TEXT_OVER_OUTLINE: f32 = 0.05; + /// Draws a text call with the font atlas: 8x8 glyph cells, the outline as /// eight one-pixel offsets under the text. +/// +/// `TextFeatureRenderer.renderText` draws the outline glyphs NORMAL and the +/// text over them POLYGON_OFFSET, so the coplanar text always wins the depth +/// test. Here both share one mesh, so the text is lifted a hair towards the +/// reader instead (a twentieth of a font pixel, invisible) rather than +/// z-fighting with the outline under it. fn draw_text(scene: &Scene, call: &TextCall, out: &mut Vec) { - let mut emit = |dx: f32, dy: f32, argb: u32| { + let mut emit = |dx: f32, dy: f32, z: f32, argb: u32| { let mut x = call.x + dx; let y0 = call.y + dy; for ch in call.text.chars() { @@ -672,7 +682,7 @@ fn draw_text(scene: &Scene, call: &TextCall, out: &mut Vec) { normal: [0; 4], pos: call .matrix - .transform_point3(Vec3::new(cx, cy, 0.0)) + .transform_point3(Vec3::new(cx, cy, z)) .to_array(), uv, layer: 0, @@ -697,10 +707,15 @@ fn draw_text(scene: &Scene, call: &TextCall, out: &mut Vec) { (0.0, 1.0), (1.0, 1.0), ] { - emit(dx, dy, call.outline); + emit(dx, dy, 0.0, call.outline); } } - emit(0.0, 0.0, call.color); + let z = if call.outline != 0 { + TEXT_OVER_OUTLINE + } else { + 0.0 + }; + emit(0.0, 0.0, z, call.color); } /// Block-entity fields the level ticks, at one position (vanilla's own field @@ -2258,6 +2273,69 @@ mod tests { assert_eq!(out[2].uv, [0.5, 0.5]); } + /// TextFeatureRenderer.renderText: an outlined call's text is drawn over + /// its outline (Font.DisplayMode.POLYGON_OFFSET), never level with it, + /// and plain text stays on the sign's plane. + #[test] + fn outlined_sign_text_sits_in_front_of_its_outline() { + let world = World::new(BlockStateId(0), 1); + let sprites = HashMap::new(); + let glyph = |_: char| { + let rect = Rect { + u0: 0.0, + v0: 0.0, + u1: 0.0625, + v1: 0.0625, + w: 8, + h: 8, + }; + (Some(rect), 6.0) + }; + let scene = Scene { + camera: DVec3::ZERO, + orientation: Quat::IDENTITY, + anim: &|_| BlockAnim::default(), + texts: None, + partial: 0.0, + game_time: 0, + max_y: 320, + world: &world, + sprites: &sprites, + nether: false, + lid: &|_| 0.0, + sign: &|_| None, + banner: &|_| Vec::new(), + skull: &|_| None, + glyph: &glyph, + glyphs: 0, + }; + let mut call = TextCall { + matrix: Mat4::IDENTITY, + x: 0.0, + y: 0.0, + text: "Hi".into(), + color: 0xFFFF_FF00, + outline: 0xFF33_3300, + light: 240 | 240 << 8, + }; + let mut out = Vec::new(); + draw_text(&scene, &call, &mut out); + // Eight outline copies, then the text, four corners a glyph. + assert_eq!(out.len(), 9 * 2 * 4); + let (outline, text) = out.split_at(8 * 2 * 4); + let front = outline.iter().map(|v| v.pos[2]).fold(f32::MIN, f32::max); + assert!( + text.iter().all(|v| v.pos[2] > front), + "text z {:?} not in front of the outline at {front}", + text.iter().map(|v| v.pos[2]).collect::>() + ); + call.outline = 0; + out.clear(); + draw_text(&scene, &call, &mut out); + assert_eq!(out.len(), 2 * 4); + assert!(out.iter().all(|v| v.pos[2] == 0.0)); + } + #[test] fn cached_sign_text_matches_and_follows_changes() { let renderer = BlockEntityRenderer { diff --git a/crates/horizon-client/src/border.wgsl b/crates/horizon-client-render/src/border.wgsl similarity index 100% rename from crates/horizon-client/src/border.wgsl rename to crates/horizon-client-render/src/border.wgsl diff --git a/crates/horizon-client/src/border_render.rs b/crates/horizon-client-render/src/border_render.rs similarity index 100% rename from crates/horizon-client/src/border_render.rs rename to crates/horizon-client-render/src/border_render.rs diff --git a/crates/horizon-client/src/breaking.rs b/crates/horizon-client-render/src/breaking.rs similarity index 100% rename from crates/horizon-client/src/breaking.rs rename to crates/horizon-client-render/src/breaking.rs diff --git a/crates/horizon-client/src/brewing.rs b/crates/horizon-client-render/src/brewing.rs similarity index 88% rename from crates/horizon-client/src/brewing.rs rename to crates/horizon-client-render/src/brewing.rs index 7fc044642f..acf546f6ff 100644 --- a/crates/horizon-client/src/brewing.rs +++ b/crates/horizon-client-render/src/brewing.rs @@ -14,14 +14,19 @@ pub use horizon_client_core::potions::{ BASE_POTION_COLOR, Category, EFFECTS, EffectDef, POTIONS, PotionDef, effect, namespaced, potion, potion_color, }; -/// `MobEffectUtil.formatDuration` at 20 TPS: m:ss or h:mm:ss. +/// `MobEffectUtil.formatDuration` at 20 TPS. pub fn format_duration(ticks: i32) -> String { - let secs = ticks / 20; + format_tick_duration(ticks, 20.0) +} +/// `StringUtil.formatTickDuration`: mm:ss, or hh:mm:ss from an hour up, every +/// field two digits. +pub fn format_tick_duration(ticks: i32, tick_rate: f32) -> String { + let secs = (ticks as f32 / tick_rate).floor() as i32; let (h, m, s) = (secs / 3600, secs / 60 % 60, secs % 60); if h > 0 { - format!("{h}:{m:02}:{s:02}") + format!("{h:02}:{m:02}:{s:02}") } else { - format!("{m}:{s:02}") + format!("{m:02}:{s:02}") } } /// `item.minecraft.potion.effect.` style translation key for a potion item. @@ -317,10 +322,14 @@ mod tests { potion_name_key("minecraft:splash_potion", Some("minecraft:long_swiftness")), "item.minecraft.splash_potion.effect.swiftness" ); - assert_eq!(format_duration(9600), "8:00"); - assert_eq!(format_duration(450), "0:22"); + assert_eq!(format_duration(9600), "08:00"); + assert_eq!(format_duration(450), "00:22"); + assert_eq!(format_duration(140), "00:07"); + assert_eq!(format_duration(1800), "01:30"); + assert_eq!(format_duration(72000), "01:00:00"); + assert_eq!(format_tick_duration(9600, 40.0), "04:00"); let t = tooltip(Some("minecraft:strong_poison"), 1.0, &|_| None); - assert_eq!(t[0].0, "poison II (0:21)"); + assert_eq!(t[0].0, "poison II (00:21)"); assert_eq!(POTIONS.len(), 46); } @@ -377,4 +386,23 @@ mod tests { stand.tick(&brewing, &items, &providers); assert_eq!(stand.brew_time, 0); } + + /// #3312: `PotionContents.getPotionDescription` names the effects with + /// translatable components, so a Russian game reads them from ru_ru.json + /// (the prepared one; skipped without it), as the tooltip's other lines. + #[test] + fn effect_lines_read_the_selected_language() { + let root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../.local/horizon-client") + .join(horizon_core::VANILLA_VERSION); + if !root.join("assets/minecraft/lang/ru_ru.json").exists() { + return; + } + let ru = crate::lang::Language::load(&[&root], "ru_ru"); + let translate = |key: &str| ru.get(key).map(str::to_owned); + let lines = tooltip(Some("minecraft:turtle_master"), 0.125, &translate); + assert_eq!(lines.len(), 2); + assert!(lines[0].0.starts_with("Замедление IV ("), "{lines:?}"); + assert!(lines[1].0.starts_with("Сопротивление III ("), "{lines:?}"); + } } diff --git a/crates/horizon-client/src/camera.rs b/crates/horizon-client-render/src/camera.rs similarity index 91% rename from crates/horizon-client/src/camera.rs rename to crates/horizon-client-render/src/camera.rs index 75cbc025ef..e1fcc7aa67 100644 --- a/crates/horizon-client/src/camera.rs +++ b/crates/horizon-client-render/src/camera.rs @@ -653,18 +653,57 @@ pub fn modify_fov_based_on_death_or_fluid( fov } -/// `ClientAvatarState`: the walked distance and view bob of a player. +/// `ClientAvatarState`: the walked distance, view bob and cloak of a player. #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct AvatarBob { pub walk_dist: f32, pub walk_dist_o: f32, pub bob: f32, pub bob_o: f32, + /// `xCloak`, `yCloak`, `zCloak`: a point trailing the player, which the cape leans from. + pub cloak: [f64; 3], + pub cloak_o: [f64; 3], } impl AvatarBob { - /// `ClientAvatarState.tick` (at the start of `AbstractClientPlayer.tick`). - pub fn tick(&mut self) { + /// `ClientAvatarState.tick` (at the start of `AbstractClientPlayer.tick`) + /// with the player's position then: `moveCloak` eases the cloak a quarter + /// of the way there, or puts it there from 10 blocks away. + pub fn tick(&mut self, position: [f64; 3]) { self.walk_dist_o = self.walk_dist; + self.cloak_o = self.cloak; + for i in 0..3 { + let d = position[i] - self.cloak[i]; + if d > 10.0 || d < -10.0 { + self.cloak[i] = position[i]; + self.cloak_o[i] = position[i]; + } else { + self.cloak[i] += d * 0.25; + } + } + } + /// `AvatarRenderer.extractCapeState`: the cape's `capeFlap`, `capeLean` + /// and `capeLean2` (degrees) for the player at `position` (interpolated) + /// with its body turned `body_yaw` and its `fallFlyingScale`. + pub fn cape( + &self, + partial: f32, + position: [f64; 3], + body_yaw: f32, + fall_flying_scale: f32, + ) -> [f32; 3] { + let d: [f64; 3] = std::array::from_fn(|i| { + lerp_d(f64::from(partial), self.cloak_o[i], self.cloak[i]) - position[i] + }); + let rad = f64::from(body_yaw * DEG_TO_RAD); + let (sin, cos) = (f64::from(trig::sin(rad)), f64::from(-trig::cos(rad))); + let mut flap = (d[1] as f32 * 10.0).clamp(-6.0, 32.0); + let lean = ((d[0] * sin + d[2] * cos) as f32 * 100.0 * (1.0 - fall_flying_scale)) + .clamp(0.0, 150.0); + let lean2 = ((d[0] * cos - d[2] * sin) as f32 * 100.0).clamp(-20.0, 20.0); + let bob = self.interpolated_bob(partial); + let walk = lerp(partial, self.walk_dist_o, self.walk_dist); + flap += trig::sin(f64::from(walk * 6.0)) * 32.0 * bob; + [flap, lean, lean2] } /// `LocalPlayer.move`: `addWalkedDistance(Mth.length(dx, dz) * 0.6F)` /// with the float horizontal displacement. @@ -795,6 +834,23 @@ pub fn spinning_effect_matrix(intensity: f32, angle_degrees: f32) -> glam::Mat4 #[cfg(test)] mod tests { + /// `moveCloak` trails a quarter of the way, jumps from 10 blocks away; the + /// cape leans back as the player walks forward (south, body yaw 0). + #[test] + fn the_cloak_trails_the_player() { + let mut a = super::AvatarBob::default(); + a.tick([0.0, 64.0, 0.0]); + assert_eq!((a.cloak, a.cloak_o), ([0.0, 64.0, 0.0], [0.0, 64.0, 0.0])); + a.tick([0.0, 64.0, 1.0]); + assert_eq!(a.cloak, [0.0, 64.0, 0.25]); + let [flap, lean, lean2] = a.cape(1.0, [0.0, 64.0, 1.0], 0.0, 0.0); + assert_eq!((flap, lean, lean2), (0.0, 75.0, 0.0)); + // Falling lifts the flap, at most 32 degrees. + assert_eq!(a.cape(1.0, [0.0, 60.0, 0.25], 0.0, 0.0)[0], 32.0); + // Gliding takes the lean away. + assert_eq!(a.cape(1.0, [0.0, 64.0, 1.0], 0.0, 1.0)[1], 0.0); + } + #[test] fn depth_far_covers_the_cloud_range() { // renderDistance 2 with the default Cloud Distance (128 chunks). diff --git a/crates/horizon-client/src/chat.rs b/crates/horizon-client-render/src/chat.rs similarity index 89% rename from crates/horizon-client/src/chat.rs rename to crates/horizon-client-render/src/chat.rs index 63646bcc7e..ab6626a95b 100644 --- a/crates/horizon-client/src/chat.rs +++ b/crates/horizon-client-render/src/chat.rs @@ -653,6 +653,42 @@ pub struct Commands<'a> { pub registries: &'a Registries, } +/// `CommandSuggestions`' constructor arguments: how a screen's suggestions +/// parse and where they are drawn. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SuggestLayout { + /// `commandsOnly`: the input is a command without a leading `/`. + pub commands_only: bool, + /// `onlyShowIfCursorPastError`: completions from the command's start, + /// not only past the `/`. + pub only_past_error: bool, + /// `suggestionLineLimit`. + pub line_limit: usize, + /// `anchorToBottom`: the list and usage over a box at the bottom, else + /// under one at the top (y 72). + pub anchor_to_bottom: bool, + /// `fillColor`: behind the list and the usage lines. + pub fill: i32, +} +impl SuggestLayout { + /// ChatScreen.init's `CommandSuggestions`. + pub const CHAT: Self = Self { + commands_only: false, + only_past_error: false, + line_limit: 10, + anchor_to_bottom: true, + fill: FILL, + }; + /// AbstractCommandBlockEditScreen.init's `CommandSuggestions`. + pub const COMMAND_BLOCK: Self = Self { + commands_only: true, + only_past_error: true, + line_limit: 7, + anchor_to_bottom: false, + fill: i32::MIN, + }; +} + /// The open chat screen: vanilla's `ChatScreen` with its `EditBox` and /// `CommandSuggestions` (argument colouring, usage lines, parse errors, /// the suggestion list, Tab completion, history). @@ -680,6 +716,7 @@ pub struct ChatScreen { pub auto_suggestions: bool, /// `CommandSuggestions.setRestrictions`: what the Chat option allows. pub abilities: ChatAbilities, + pub layout: SuggestLayout, } /// Local completions waiting for the server's reply to request `id`. struct Awaiting { @@ -750,6 +787,43 @@ impl ChatScreen { keep_suggestions: false, auto_suggestions: true, abilities, + layout: SuggestLayout::CHAT, + }; + s.update_command_info(m, c); + s + } + /// `AbstractCommandBlockEditScreen.init`: the command box at the top, + /// focused, holding `command`, with suggestions from the start. + pub fn command_block( + m: &Measure, + c: &Commands, + command: &str, + width: i32, + height: i32, + ) -> Self { + let mut input = TextField::new(width / 2 - 150, 50, 300, 20); + input.max_length = 32500; + input.focused = true; + input.set_value(m, command); + let mut s = Self { + input, + width, + height, + history_buffer: String::new(), + history_pos: 0, + parse: None, + pending: None, + awaiting: None, + request: None, + suggestions: None, + usage: Vec::new(), + usage_position: 0, + usage_width: 0, + allow_suggestions: true, + keep_suggestions: false, + auto_suggestions: true, + abilities: ChatAbilities::new(ChatVisibility::Full), + layout: SuggestLayout::COMMAND_BLOCK, }; s.update_command_info(m, c); s @@ -768,7 +842,11 @@ impl ChatScreen { let value = self.input.value.clone(); let auto = self.auto_suggestions; let abilities = self.abilities; - *self = Self::new(m, c, &value, self.history_pos, width, height, abilities); + *self = if self.layout == SuggestLayout::CHAT { + Self::new(m, c, &value, self.history_pos, width, height, abilities) + } else { + Self::command_block(m, c, &value, width, height) + }; self.auto_suggestions = auto; } } @@ -814,11 +892,18 @@ impl ChatScreen { self.usage.clear(); let cursor = self.input.cursor; let cursor_b = byte_at(&command, cursor); - if command.starts_with('/') { + let slash = command.starts_with('/'); + if self.layout.commands_only || slash { + let start = usize::from(slash); if self.parse.is_none() { - self.parse = Some(c.dispatcher.parse_at(&command, 1, c.registries)); + self.parse = Some(c.dispatcher.parse_at(&command, start, c.registries)); } - if cursor >= 1 && (self.suggestions.is_none() || !self.keep_suggestions) { + let from = if self.layout.only_past_error { + start + } else { + 1 + }; + if cursor >= from && (self.suggestions.is_none() || !self.keep_suggestions) { let parse = self.parse.as_ref().expect("parsed above"); let (range, list, ask) = c.dispatcher @@ -1002,7 +1087,11 @@ impl ChatScreen { 0, self.input.screen_x(m, 0) + self.input.inner_width() - width, ); - let y = self.height - 12; + let y = if self.layout.anchor_to_bottom { + self.height - 12 + } else { + 72 + }; // sortSuggestions: those starting with the word at the cursor first. let partial = &value[..byte_at(&value, self.input.cursor)]; let word = partial[last_word_index(partial)..].to_lowercase(); @@ -1011,9 +1100,16 @@ impl ChatScreen { s.text.starts_with(&word) || s.text.starts_with(&format!("minecraft:{word}")) }); first.extend(rest); - let n = first.len().min(10) as i32; + // SuggestionsList: beside a bordered box, or over an unbordered one. + let n = first.len().min(self.layout.line_limit) as i32; + let bordered = i32::from(self.input.bordered); + let top = if self.layout.anchor_to_bottom { + y - 3 - n * 12 + } else { + y - bordered + }; let mut l = SuggestionList { - rect: [x - 1, y - 3 - n * 12, width + 1, n * 12], + rect: [x - (1 - bordered), top, width + 1, n * 12], original: value, list: first, offset: 0, @@ -1042,11 +1138,12 @@ impl ChatScreen { } fn cycle(&mut self, l: &mut SuggestionList, dir: isize) { self.select(l, l.current as isize + dir); - let max = l.list.len().saturating_sub(10); + let limit = self.layout.line_limit; + let max = l.list.len().saturating_sub(limit); if l.current < l.offset { l.offset = l.current.min(max); - } else if l.current > l.offset + 9 { - l.offset = (l.current as isize + 1 - 10).clamp(0, max as isize) as usize; + } else if l.current >= l.offset + limit { + l.offset = (l.current as isize + 1 - limit as isize).clamp(0, max as isize) as usize; } } /// `SuggestionsList.useSuggestion`. @@ -1074,7 +1171,57 @@ impl ChatScreen { ctrl: bool, ) -> ChatAction { use winit::keyboard::KeyCode as K; - // CommandSuggestions.keyPressed. + if self.suggestions_key(m, c, key, shift) { + return ChatAction::None; + } + match key { + K::Escape => return ChatAction::Close, + K::Enter | K::NumpadEnter => { + // ChatScreen.keyPressed: without `hasAllowedInput` the input + // stays, unsent, and the screen stays open. + if !self.abilities.allows(&self.input.value) { + return ChatAction::None; + } + return ChatAction::Submit(self.normalized()); + } + K::ArrowUp => self.move_in_history(m, c, history, -1), + K::ArrowDown => self.move_in_history(m, c, history, 1), + _ => { + self.field_key(m, c, key, shift, ctrl); + } + } + ChatAction::None + } + /// The input box's own keys (`EditBox.keyPressed`), the suggestions + /// following an edit: whether the box took the key. + pub fn field_key( + &mut self, + m: &Measure, + c: &Commands, + key: winit::keyboard::KeyCode, + shift: bool, + ctrl: bool, + ) -> bool { + match self.input.key(m, key, shift, ctrl) { + Some(changed) => { + if changed { + self.edited(m, c); + } + true + } + None => false, + } + } + /// `CommandSuggestions.keyPressed`: the list's arrows, Tab and Escape, + /// and Tab to show it. True when the suggestions took the key. + pub fn suggestions_key( + &mut self, + m: &Measure, + c: &Commands, + key: winit::keyboard::KeyCode, + shift: bool, + ) -> bool { + use winit::keyboard::KeyCode as K; if let Some(mut l) = self.suggestions.take() { let handled = match key { K::ArrowUp | K::ArrowDown => { @@ -1091,37 +1238,18 @@ impl ChatScreen { } K::Escape => { self.input.suggestion = None; - return ChatAction::None; + return true; } _ => false, }; self.suggestions = Some(l); - if handled { - return ChatAction::None; - } + handled } else if key == K::Tab { self.show_suggestions(m); - return ChatAction::None; + true + } else { + false } - match key { - K::Escape => return ChatAction::Close, - K::Enter | K::NumpadEnter => { - // ChatScreen.keyPressed: without `hasAllowedInput` the input - // stays, unsent, and the screen stays open. - if !self.abilities.allows(&self.input.value) { - return ChatAction::None; - } - return ChatAction::Submit(self.normalized()); - } - K::ArrowUp => self.move_in_history(m, c, history, -1), - K::ArrowDown => self.move_in_history(m, c, history, 1), - _ => { - if self.input.key(m, key, shift, ctrl) == Some(true) { - self.edited(m, c); - } - } - } - ChatAction::None } /// `ChatScreen.moveInHistory`. fn move_in_history(&mut self, m: &Measure, c: &Commands, history: &[String], dir: isize) { @@ -1175,7 +1303,7 @@ impl ChatScreen { if !l.contains(mouse.0, mouse.1) { return false; } - let max = l.list.len().saturating_sub(10) as i32; + let max = l.list.len().saturating_sub(self.layout.line_limit) as i32; l.offset = ((l.offset as f64 - amount.clamp(-1.0, 1.0)) as i32).clamp(0, max) as usize; true } @@ -1246,7 +1374,9 @@ impl ChatScreen { ) -> Vec { let (w, h) = (self.width, self.height); let mut out = Vec::new(); - d::fill(&mut out, 2, h - 14, w - 2, h - 2, background); + if self.layout.anchor_to_bottom { + d::fill(&mut out, 2, h - 14, w - 2, h - 2, background); + } let format = |s: &str, o: usize| self.format(s, o); self.input.draw(&mut out, m, blink_ms, Some(&format)); if let Some(mut l) = self.suggestions.take() { @@ -1254,14 +1384,18 @@ impl ChatScreen { self.suggestions = Some(l); } else { for (i, line) in self.usage.iter().enumerate() { - let y = h - 27 - 12 * i as i32; + let y = if self.layout.anchor_to_bottom { + h - 27 - 12 * i as i32 + } else { + 72 + 12 * i as i32 + }; d::fill( &mut out, self.usage_position - 1, y, self.usage_position + self.usage_width + 1, y + 12, - FILL, + self.layout.fill, ); d::text(&mut out, line.clone(), self.usage_position, y + 2, -1, true); } @@ -1275,7 +1409,7 @@ impl ChatScreen { l: &mut SuggestionList, mouse: (i32, i32), ) { - let limit = l.list.len().min(10); + let limit = l.list.len().min(self.layout.line_limit); let [rx, ry, rw, rh] = l.rect; let has_prev = l.offset > 0; let has_next = l.list.len() > l.offset + limit; @@ -1284,8 +1418,8 @@ impl ChatScreen { l.last_mouse = mouse; } if has_prev || has_next { - d::fill(out, rx, ry - 1, rx + rw, ry, FILL); - d::fill(out, rx, ry + rh, rx + rw, ry + rh + 1, FILL); + d::fill(out, rx, ry - 1, rx + rw, ry, self.layout.fill); + d::fill(out, rx, ry + rh, rx + rw, ry + rh + 1, self.layout.fill); if has_prev { for x in (0..rw).step_by(2) { d::fill(out, rx + x, ry - 1, rx + x + 1, ry, -1); @@ -1300,7 +1434,7 @@ impl ChatScreen { let mut hovered = false; for i in 0..limit { let y = ry + 12 * i as i32; - d::fill(out, rx, y, rx + rw, y + 12, FILL); + d::fill(out, rx, y, rx + rw, y + 12, self.layout.fill); if mouse.0 > rx && mouse.0 < rx + rw && mouse.1 > y && mouse.1 < y + 12 { if moved { self.select(l, (i + l.offset) as isize); diff --git a/crates/horizon-client/src/chat_tests.rs b/crates/horizon-client-render/src/chat_tests.rs similarity index 100% rename from crates/horizon-client/src/chat_tests.rs rename to crates/horizon-client-render/src/chat_tests.rs diff --git a/crates/horizon-client/src/chunk.rs b/crates/horizon-client-render/src/chunk.rs similarity index 100% rename from crates/horizon-client/src/chunk.rs rename to crates/horizon-client-render/src/chunk.rs diff --git a/crates/horizon-client/src/chunk/block_entity.rs b/crates/horizon-client-render/src/chunk/block_entity.rs similarity index 99% rename from crates/horizon-client/src/chunk/block_entity.rs rename to crates/horizon-client-render/src/chunk/block_entity.rs index fe50d3b18e..6dbd09e143 100644 --- a/crates/horizon-client/src/chunk/block_entity.rs +++ b/crates/horizon-client-render/src/chunk/block_entity.rs @@ -230,7 +230,7 @@ pub struct Info { /// `VibrationSelector`: the closest, then highest-frequency candidate of a tick. #[derive(Clone, Debug, Default, PartialEq)] pub struct Selector { - pub(crate) current: Option<(Info, i64)>, + pub current: Option<(Info, i64)>, } /// `VibrationSystem.Data`. #[derive(Clone, Debug, Default, PartialEq)] @@ -278,7 +278,7 @@ pub struct SpawnData { /// Which `SpawnData` is next: one of the potentials (the same object in /// vanilla, so changing it changes the potential) or its own. #[derive(Clone, Debug, PartialEq)] -pub(crate) enum Next { +pub enum Next { Potential(usize), Own(SpawnData), } @@ -288,7 +288,7 @@ pub struct Spawner { pub spawn_delay: i32, /// `SpawnPotentials`: data and weight. pub potentials: Vec<(SpawnData, i32)>, - pub(crate) next: Option, + pub next: Option, pub min_spawn_delay: i32, pub max_spawn_delay: i32, pub spawn_count: i32, diff --git a/crates/horizon-client/src/chunk_map.rs b/crates/horizon-client-render/src/chunk_map.rs similarity index 100% rename from crates/horizon-client/src/chunk_map.rs rename to crates/horizon-client-render/src/chunk_map.rs diff --git a/crates/horizon-client/src/clouds.rs b/crates/horizon-client-render/src/clouds.rs similarity index 100% rename from crates/horizon-client/src/clouds.rs rename to crates/horizon-client-render/src/clouds.rs diff --git a/crates/horizon-client/src/collisions.rs b/crates/horizon-client-render/src/collisions.rs similarity index 100% rename from crates/horizon-client/src/collisions.rs rename to crates/horizon-client-render/src/collisions.rs diff --git a/crates/horizon-client/src/command_block.rs b/crates/horizon-client-render/src/command_block.rs similarity index 100% rename from crates/horizon-client/src/command_block.rs rename to crates/horizon-client-render/src/command_block.rs diff --git a/crates/horizon-client/src/containers.rs b/crates/horizon-client-render/src/containers.rs similarity index 78% rename from crates/horizon-client/src/containers.rs rename to crates/horizon-client-render/src/containers.rs index 4908ed1ee9..2df34dd1d7 100644 --- a/crates/horizon-client/src/containers.rs +++ b/crates/horizon-client-render/src/containers.rs @@ -42,6 +42,45 @@ pub fn recipe_used_key(id: &str) -> String { /// A hopper's slots: the only plain container with five. pub const HOPPER_SLOTS: usize = 5; +/// `getDefaultName` of the `BaseContainerBlockEntity` that `block` holds: the +/// translation key of its menu's title when it has no custom name. Copper +/// chests hold a `ChestBlockEntity`, shulker boxes of every colour a +/// `ShulkerBoxBlockEntity`. +pub fn default_name_key(block: &str) -> Option<&'static str> { + Some(match block { + "minecraft:chest" | "minecraft:trapped_chest" => "container.chest", + n if n.ends_with("copper_chest") => "container.chest", + "minecraft:barrel" => "container.barrel", + n if n.ends_with("shulker_box") => "container.shulkerBox", + "minecraft:dispenser" => "container.dispenser", + "minecraft:dropper" => "container.dropper", + "minecraft:hopper" => "container.hopper", + "minecraft:furnace" => "container.furnace", + "minecraft:smoker" => "container.smoker", + "minecraft:blast_furnace" => "container.blast_furnace", + "minecraft:brewing_stand" => "container.brewing", + "minecraft:crafter" => "container.crafter", + _ => return None, + }) +} + +/// `BaseContainerBlockEntity.getDisplayName` (`getName`), the title of the +/// `ClientboundOpenScreenPacket` that opens it: the block entity's custom +/// name (`CustomName`, a literal), else its default name. +pub fn display_name(custom: Option<&str>, default_key: &str) -> horizon_world::nbt::Tag { + match custom { + Some(name) => horizon_protocol::text::literal(name), + None => horizon_protocol::text::translatable(default_key, &[]), + } +} + +/// `ChestBlock.MENU_PROVIDER_COMBINER`'s `getDisplayName` for a double chest: +/// the first half's custom name (the right half, `DoubleBlockCombiner`'s +/// `FIRST`), else the second's, else `container.chestDouble`. +pub fn double_chest_name(first: Option<&str>, second: Option<&str>) -> horizon_world::nbt::Tag { + display_name(first.or(second), "container.chestDouble") +} + impl Campfire { /// `placeFood`'s slot: the first empty one, given the item's /// `campfire_cooking` time; it takes one item of `held`. @@ -378,6 +417,58 @@ impl BlockEntity { } pub use crate::providers::Providers; +/// A `ChestBlock`, which `ChestBlock.combine` can join into a double chest: +/// chests, trapped chests and copper chests in every weathering state, waxed +/// or not (`CopperChestBlock` extends `ChestBlock`). Ender chests are +/// `EnderChestBlock`s and always single. +pub fn is_chest_block(name: &str) -> bool { + matches!(name, "minecraft:chest" | "minecraft:trapped_chest") || name.ends_with("copper_chest") +} + +/// A chest block's name with its `type` and `facing` properties. +#[derive(Clone, Copy, Debug)] +pub struct ChestHalf<'a> { + pub block: &'a str, + pub kind: &'a str, + pub facing: &'a str, +} + +impl ChestHalf<'_> { + /// `ChestBlock.getConnectedDirection` as an (x, z) step to the other + /// half: clockwise from the facing for a left half, counter-clockwise for + /// a right one. None for a single chest. + pub fn connected_step(&self) -> Option<(i32, i32)> { + let step = |facing: &str| match facing { + "north" => Some((0, -1)), + "south" => Some((0, 1)), + "west" => Some((-1, 0)), + "east" => Some((1, 0)), + _ => None, + }; + let (x, z) = step(self.facing)?; + // Turning clockwise seen from above takes (x, z) to (-z, x). + match self.kind { + "left" => Some((-z, x)), + "right" => Some((z, -x)), + _ => None, + } + } + + /// `DoubleBlockCombiner.combineWithNeigbour` as `ChestBlock.combine` calls + /// it: `other`, the block in this half's connected direction, is its other + /// half when it is the same block, a half of the other side and faces the + /// same way. Copper chests in different weathering states never join + /// (`CopperChestBlock` keeps both halves in one state instead). + pub fn pairs_with(&self, other: &ChestHalf) -> bool { + is_chest_block(self.block) + && self.kind != "single" + && other.block == self.block + && other.kind != "single" + && other.kind != self.kind + && other.facing == self.facing + } +} + /// `AbstractFurnaceBlockEntity.createExperience`: `count * experience`, floored, and one /// more with the chance of what is left over (a level random draw only when there is some). pub fn create_experience( @@ -395,6 +486,24 @@ pub fn create_experience( xp } +/// The `MenuType` the block entity of a one-block storage container opens +/// (`createMenu`): `ChestMenu.threeRows` for chests, trapped and copper chests +/// and barrels, `ShulkerBoxMenu` for shulker boxes of any colour (its +/// `ShulkerBoxSlot`s refuse shulker boxes), `DispenserMenu` for dispensers +/// and droppers, `HopperMenu` for hoppers. None for other blocks. +pub fn block_menu_type(block: &str) -> Option<&'static str> { + Some(match block { + "minecraft:chest" | "minecraft:trapped_chest" | "minecraft:barrel" => { + "minecraft:generic_9x3" + } + n if n.ends_with("copper_chest") => "minecraft:generic_9x3", + n if n.ends_with("shulker_box") => "minecraft:shulker_box", + "minecraft:dispenser" | "minecraft:dropper" => "minecraft:generic_3x3", + "minecraft:hopper" => "minecraft:hopper", + _ => return None, + }) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/horizon-client/src/crash.rs b/crates/horizon-client-render/src/crash.rs similarity index 87% rename from crates/horizon-client/src/crash.rs rename to crates/horizon-client-render/src/crash.rs index 11a150dfe8..b9bb4d5131 100644 --- a/crates/horizon-client/src/crash.rs +++ b/crates/horizon-client-render/src/crash.rs @@ -3,6 +3,7 @@ //! Written to `crash-reports/crash--client.txt` in the game folder when //! the main thread panics or `main` gets an error back. It leaves the machine only scrubbed, and //! only if the player allows crash reports (`telemetry`). +use std::cell::{Cell, RefCell}; use std::fmt::Write as _; use std::path::{Path, PathBuf}; use std::sync::Mutex; @@ -14,6 +15,51 @@ use chrono::{DateTime, Local}; static DETAILS: Mutex> = Mutex::new(Vec::new()); static REPORTED: AtomicBool = AtomicBool::new(false); +thread_local! { + /// How many `contain` calls this thread is inside. + static CONTAINED: Cell = const { Cell::new(0) }; + /// The message of the last panic `contain` caught on this thread, from the hook. + static CAUGHT: RefCell> = const { RefCell::new(None) }; +} + +/// Runs `f`, and returns a panic in it as an error with the panic's message instead of letting +/// it end the game. For work that is not the game's own and whose failure the caller recovers +/// from, such as handing a shader pack's programs to the GPU driver's compiler: the panic goes +/// to the log with its stack, and no crash report is made for it. +pub fn contain(f: impl FnOnce() -> T) -> Result { + CONTAINED.with(|c| c.set(c.get() + 1)); + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)); + CONTAINED.with(|c| c.set(c.get() - 1)); + result.map_err(|payload| { + CAUGHT + .with(|c| c.borrow_mut().take()) + .unwrap_or_else(|| payload_text(&*payload)) + }) +} + +/// For the hook: a panic inside `contain` is logged with its stack and kept for `contain` to +/// return. False for any other panic. +fn contained_panic(info: &std::panic::PanicHookInfo) -> bool { + if CONTAINED.with(Cell::get) == 0 { + return false; + } + let message = panic_message(info); + let stack = stack_lines( + &std::backtrace::Backtrace::force_capture().to_string(), + true, + ); + log::error!( + "Recovered from an error: {message}\n{}", + stack + .lines() + .map(|l| format!("\tat {l}")) + .collect::>() + .join("\n") + ); + CAUGHT.with(|c| *c.borrow_mut() = Some(message)); + true +} + /// Sets one line of the report's details (replacing the last value under `key`). pub fn set_detail(key: &'static str, value: impl Into) { let mut details = DETAILS.lock().unwrap_or_else(|e| e.into_inner()); @@ -308,13 +354,35 @@ fn deliver(home: &Path, report: Report) -> Option { } } -fn panic_message(info: &std::panic::PanicHookInfo) -> String { - let payload = info.payload(); - let text = payload +/// The report for a hung server tick, written by the watchdog thread +/// (`ServerWatchdog`): the stuck thread's stack is not available to Rust, so +/// the report carries none. +pub fn report_watchdog(home: &Path, side: &'static str, message: String) -> Option { + deliver( + home, + Report { + time: Local::now(), + description: "Watching Server".into(), + error: format!("{message} (Watching Server)"), + stack: String::new(), + thread: thread_name(), + side, + details: system_details(), + log: crate::logging::recent_lines(), + }, + ) +} + +fn payload_text(payload: &(dyn std::any::Any + Send)) -> String { + payload .downcast_ref::<&str>() .map(|s| s.to_string()) .or_else(|| payload.downcast_ref::().cloned()) - .unwrap_or_else(|| "Box".into()); + .unwrap_or_else(|| "Box".into()) +} + +fn panic_message(info: &std::panic::PanicHookInfo) -> String { + let text = payload_text(info.payload()); match info.location() { Some(at) => format!("panicked at {at}:\n{text}"), None => format!("panicked:\n{text}"), @@ -330,7 +398,7 @@ pub fn stack_lines(backtrace: &str, from_panic: bool) -> String { // separator the build machine used. let manifest = env!("CARGO_MANIFEST_DIR").replace('\\', "/"); let root = manifest - .strip_suffix("crates/horizon-client") + .strip_suffix("crates/horizon-client-render") .unwrap_or(&manifest); let mut frames: Vec<(String, Option)> = Vec::new(); for line in backtrace.lines() { @@ -417,10 +485,13 @@ pub fn panic_report(info: &std::panic::PanicHookInfo, side: &'static str) -> Rep /// Reports panics: on the main thread a crash report (the panic then ends the game, exit code /// 101); on other threads the error goes to the log, as vanilla's uncaught exception handler -/// does. The default hook still prints the panic. +/// does. The default hook still prints the panic. A panic inside `contain` is only logged. pub fn install(home: PathBuf, side: &'static str) { let default = std::panic::take_hook(); std::panic::set_hook(Box::new(move |info| { + if contained_panic(info) { + return; + } default(info); let main = std::thread::current().name() == Some("main"); if main { @@ -605,10 +676,21 @@ mod tests { ); assert_eq!( stack_lines(&bt, true), - "horizon_client::crash::debug_crash_tick (crates/horizon-client/src/crash.rs:359:9)\nwinit::dispatch (winit-0.30.13/src/event_loop.rs:642:56)\nmain\n" + "horizon_client::crash::debug_crash_tick (crates/horizon-client-render/src/crash.rs:359:9)\nwinit::dispatch (winit-0.30.13/src/event_loop.rs:642:56)\nmain\n" ); } + #[test] + fn contained_panics_come_back_as_errors() { + assert_eq!(contain(|| 7), Ok(7)); + let error = contain(|| -> u8 { panic!("a pack's program") }).unwrap_err(); + assert!(error.contains("a pack's program"), "{error}"); + // Nested, and the thread is no longer inside one after. + let outer = contain(|| contain(|| -> u8 { panic!("inner") })); + assert!(outer.unwrap().unwrap_err().contains("inner")); + assert_eq!(CONTAINED.with(Cell::get), 0); + } + #[test] fn the_debug_crash_is_reported_as_vanillas() { let error: Box = DebugCrash::new().into(); diff --git a/crates/horizon-client/src/datapack.rs b/crates/horizon-client-render/src/datapack.rs similarity index 62% rename from crates/horizon-client/src/datapack.rs rename to crates/horizon-client-render/src/datapack.rs index 51a052ee95..4065a3fdc7 100644 --- a/crates/horizon-client/src/datapack.rs +++ b/crates/horizon-client-render/src/datapack.rs @@ -2,7 +2,10 @@ //! the parts of them commands use: `.mcfunction` functions (with macros and //! line continuation), function tags and predicates. use crate::zip::Zip; -use horizon_world::nbt::Tag; +use horizon_world::{ + nbt::Tag, + worldgen::overrides::{self, PackFile, WorldgenOverrides}, +}; use std::{ collections::BTreeMap, path::{Path, PathBuf}, @@ -125,6 +128,114 @@ pub fn discover(world: &Path) -> Vec { packs } +// ---- Create World ------------------------------------------------------------ + +/// Create World's Data Packs screen (`PackSelectionScreen` on vanilla's +/// `mcworld-` folder): the folder's packs (`PackSource.WORLD`) and the +/// built-in one (`resources` holds its `pack.png`), which can be unselected +/// and stays at the bottom. Horizon has no experiments, so their feature +/// packs are not offered. +pub fn screen_packs(dir: &Path, resources: &Path) -> Vec { + use crate::{ + pack_screen::{Compat, PackInfo, Source, load_icon}, + packs::Position, + text::{Style, Text}, + }; + let format = horizon_core::version::DATA_PACK_FORMAT; + let mut infos: Vec = crate::packs::folder(dir, format) + .into_iter() + .map(|p| PackInfo { + icon: p.icon_png().as_deref().and_then(load_icon), + id: p.id, + title: p.title, + description: p.raw_description, + description_style: p.description_style, + source: Source::World, + compat: p.compatibility, + required: false, + position: Position::Top, + fixed: false, + }) + .collect(); + infos.push(PackInfo { + id: "vanilla".into(), + title: Text::translate("dataPack.vanilla.name", &[]), + description: Text::translate("dataPack.vanilla.description", &[]), + description_style: Style::default(), + source: Source::BuiltIn, + compat: Compat::Compatible, + icon: std::fs::read(resources.join("pack.png")) + .ok() + .as_deref() + .and_then(load_icon), + required: false, + position: Position::Bottom, + fixed: false, + }); + infos +} +/// A new empty folder for Create World's packs +/// (`Files.createTempDirectory("mcworld-")`). +pub fn temp_folder() -> std::io::Result { + let nanos = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_nanos(); + let dir = std::env::temp_dir().join(format!("mcworld-{}-{nanos}", std::process::id())); + std::fs::create_dir_all(&dir)?; + Ok(dir) +} +/// What's in a folder of packs, to notice packs added or removed while the +/// screen shows it (`PackSelectionScreen.Watcher`). +pub fn folder_stamp(dir: &Path) -> Vec<(String, Option)> { + let mut stamp: Vec<_> = std::fs::read_dir(dir) + .into_iter() + .flatten() + .flatten() + .map(|e| { + let modified = e.metadata().and_then(|m| m.modified()).ok(); + (e.file_name().to_string_lossy().into_owned(), modified) + }) + .collect(); + stamp.sort(); + stamp +} +/// Create World's packs into the new world before it opens +/// (`copyTempDataPackDirToNewWorld`): every pack of `dir` goes to +/// `/datapacks`, and `datapacks.json` enables those `selected` +/// (lowest priority first) and disables the rest. +pub fn install(dir: &Path, world: &Path, selected: &[String]) -> Result<(), String> { + let err = |p: &Path, e: std::io::Error| format!("{}: {e}", p.display()); + let target = world.join("datapacks"); + std::fs::create_dir_all(&target).map_err(|e| err(&target, e))?; + let mut disabled = Vec::new(); + for pack in crate::packs::folder(dir, horizon_core::version::DATA_PACK_FORMAT) { + let (crate::packs::Source::Dir(from) | crate::packs::Source::Zip(from)) = &pack.source; + copy(from, &target.join(pack.id.trim_start_matches("file/")))?; + if !selected.contains(&pack.id) { + disabled.push(pack.id); + } + } + if !selected.iter().any(|id| id == "vanilla") { + disabled.push("vanilla".into()); + } + let json = serde_json::json!({ "enabled": selected, "disabled": disabled }); + let file = world.join("datapacks.json"); + std::fs::write(&file, json.to_string()).map_err(|e| err(&file, e)) +} +/// A file, or a folder and everything in it. +fn copy(from: &Path, to: &Path) -> Result<(), String> { + let err = |e: std::io::Error| format!("{} -> {}: {e}", from.display(), to.display()); + if !from.is_dir() { + return std::fs::copy(from, to).map(drop).map_err(err); + } + std::fs::create_dir_all(to).map_err(err)?; + for e in std::fs::read_dir(from).map_err(err)?.flatten() { + copy(&e.path(), &to.join(e.file_name()))?; + } + Ok(()) +} + // ---- functions --------------------------------------------------------------- /// A `.mcfunction`: plain command lines and macro lines. @@ -421,9 +532,138 @@ fn hash_normalize(s: &str) -> String { } } +/// The ids of `packs` (a world's, from `discover`) that are enabled, lowest +/// priority first: those `/datapacks.json` lists as enabled, in its +/// order, then every pack it lists neither way, in name order. +pub fn enabled_ids(world: &Path, packs: &[DataPack]) -> Vec { + let saved: Option = std::fs::read(world.join("datapacks.json")) + .ok() + .and_then(|b| serde_json::from_slice(&b).ok()); + let list = |key: &str| -> Vec { + saved + .as_ref() + .and_then(|v| v[key].as_array()) + .map(|a| { + a.iter() + .filter_map(|s| s.as_str().map(str::to_owned)) + .collect() + }) + .unwrap_or_default() + }; + let disabled = list("disabled"); + let mut enabled: Vec = list("enabled") + .into_iter() + .filter(|id| packs.iter().any(|p| &p.id == id)) + .collect(); + for p in packs { + if !enabled.contains(&p.id) && !disabled.contains(&p.id) { + enabled.push(p.id.clone()); + } + } + enabled +} + +// ---- worldgen ---------------------------------------------------------------- + +/// The worldgen entries of `packs` (lowest priority first, the built-in pack +/// left out: the prepared bundles are it) for the chunk generator, and what +/// it leaves out, for the log. `biomes` says which biomes the game has: a +/// pack's new biome is left out (the game has no colours, sounds or id for it). +pub fn worldgen_overrides( + packs: &[&DataPack], + biomes: impl Fn(&str) -> bool, +) -> (WorldgenOverrides, Vec) { + let mut out = WorldgenOverrides::default(); + let mut not_read: BTreeMap = BTreeMap::new(); + let mut warnings = Vec::new(); + for pack in packs { + for file in pack.list_kinds(&overrides::PACK_KINDS) { + let read = || String::from_utf8(pack.read(&file)?).ok(); + match out.add_file(&pack.id, &file, read) { + PackFile::Read | PackFile::Other => {} + PackFile::DimensionType { id, min_y, height } => warnings.push(format!( + "{}: dimension type {id} (min_y {min_y}, height {height}) is not used: \ + the dimension keeps its vanilla build height", + pack.id + )), + PackFile::NotRead(kind) => { + *not_read.entry(format!("{}: {kind}", pack.id)).or_default() += 1 + } + PackFile::Unreadable => warnings.push(format!("{}: {file}: unreadable", pack.id)), + } + } + } + warnings.extend(out.finish(biomes)); + for (kind, files) in not_read { + warnings.push(format!("{kind}: {files} files the generator does not read")); + } + (out, warnings) +} + #[cfg(test)] mod tests { use super::*; + + #[test] + fn enabled_packs_override_worldgen_in_order() { + let dir = std::env::temp_dir().join(format!("hz-datapack-worldgen-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let write = |pack: &str, p: &str, t: &str| { + let path = dir.join("datapacks").join(pack).join(p); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + std::fs::write(path, t).unwrap(); + }; + for pack in ["a", "b", "off"] { + write(pack, "pack.mcmeta", "{}"); + write(pack, "data/minecraft/worldgen/noise/cave.json", pack); + write( + pack, + "data/minecraft/tags/worldgen/biome/is_hill.json", + "{\"values\":[]}", + ); + } + write("a", "data/minecraft/worldgen/biome/new_peaks.json", "{}"); + write("a", "data/minecraft/worldgen/biome/plains.json", "{}"); + write( + "a", + "data/minecraft/worldgen/world_preset/normal.json", + "{}", + ); + write( + "b", + "data/minecraft/dimension_type/overworld.json", + "{\"min_y\":-64,\"height\":2096}", + ); + // `b` is enabled first, so `a` (enabled after it) wins. + std::fs::write( + dir.join("datapacks.json"), + "{\"enabled\":[\"file/b\",\"file/a\"],\"disabled\":[\"file/off\"]}", + ) + .unwrap(); + let packs = discover(&dir); + let enabled = enabled_ids(&dir, &packs); + assert_eq!(enabled, ["file/b", "file/a"]); + let enabled: Vec<&DataPack> = enabled + .iter() + .map(|id| packs.iter().find(|p| &p.id == id).unwrap()) + .collect(); + let (o, warnings) = worldgen_overrides(&enabled, |b| b == "minecraft:plains"); + assert_eq!( + o.entries("noises").collect::>(), + [("minecraft:cave", "a")] + ); + assert_eq!( + o.entries("biomes").collect::>(), + [("minecraft:plains", "{}")] + ); + let tag_packs: Vec<&str> = o.tags("biome_tags").map(|(_, p, _)| p).collect(); + assert_eq!(tag_packs, ["file/b", "file/a"]); + assert_eq!(warnings.len(), 3, "{warnings:?}"); + assert!(warnings[0].contains("height 2096")); + assert!(warnings[1].contains("minecraft:new_peaks")); + assert!(warnings[2].contains("worldgen/world_preset")); + let _ = std::fs::remove_dir_all(&dir); + } use horizon_world::nbt::parse_snbt; #[test] @@ -566,4 +806,51 @@ mod tests { assert_eq!(p.list_kinds(&kinds), wanted); let _ = std::fs::remove_dir_all(&dir); } + + #[test] + fn create_world_installs_its_packs() { + let root = std::env::temp_dir().join(format!("hz-datapack-install-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&root); + let (dir, world) = (root.join("mcworld"), root.join("world")); + let write = |pack: &str, p: &str, t: &str| { + let path = dir.join(pack).join(p); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + std::fs::write(path, t).unwrap(); + }; + write("fns", "pack.mcmeta", r#"{"pack":{"pack_format":121}}"#); + write("fns", "data/x/function/f.mcfunction", "say hi"); + write("off", "pack.mcmeta", r#"{"pack":{"pack_format":121}}"#); + std::fs::create_dir_all(&dir).unwrap(); + let mut zip = zip::ZipWriter::new(std::fs::File::create(dir.join("old.zip")).unwrap()); + zip.start_file("pack.mcmeta", zip::write::SimpleFileOptions::default()) + .unwrap(); + std::io::Write::write_all(&mut zip, br#"{"pack":{"pack_format":48}}"#).unwrap(); + zip.finish().unwrap(); + let infos = screen_packs(&dir, &root); + let ids: Vec<&str> = infos.iter().map(|p| p.id.as_str()).collect(); + assert_eq!(ids, ["file/fns", "file/off", "file/old.zip", "vanilla"]); + let compat: Vec<_> = infos.iter().map(|p| p.compat).collect(); + use crate::pack_screen::Compat; + assert_eq!( + compat, + [ + Compat::Compatible, + Compat::Compatible, + Compat::TooOld, + Compat::Compatible + ] + ); + let selected = ["vanilla", "file/old.zip", "file/fns"].map(String::from); + install(&dir, &world, &selected).unwrap(); + let packs = discover(&world); + let ids: Vec<&str> = packs.iter().map(|p| p.id.as_str()).collect(); + assert_eq!(ids, ["file/fns", "file/off", "file/old.zip"]); + let saved: serde_json::Value = + serde_json::from_slice(&std::fs::read(world.join("datapacks.json")).unwrap()).unwrap(); + assert_eq!( + saved, + serde_json::json!({"enabled": selected, "disabled": ["file/off"]}) + ); + let _ = std::fs::remove_dir_all(&root); + } } diff --git a/crates/horizon-client/src/dynamic_light.rs b/crates/horizon-client-render/src/dynamic_light.rs similarity index 100% rename from crates/horizon-client/src/dynamic_light.rs rename to crates/horizon-client-render/src/dynamic_light.rs diff --git a/crates/horizon-client/src/effects.rs b/crates/horizon-client-render/src/effects.rs similarity index 100% rename from crates/horizon-client/src/effects.rs rename to crates/horizon-client-render/src/effects.rs diff --git a/crates/horizon-client/src/entity.wgsl b/crates/horizon-client-render/src/entity.wgsl similarity index 99% rename from crates/horizon-client/src/entity.wgsl rename to crates/horizon-client-render/src/entity.wgsl index b674b09418..b734a79929 100644 --- a/crates/horizon-client/src/entity.wgsl +++ b/crates/horizon-client-render/src/entity.wgsl @@ -22,6 +22,7 @@ struct Globals { outline_params: vec4, // Horizon Extras' dynamic lights: camera-relative position and level (dynamic_light.rs). dynamic_lights: array, 16>, + sunrise_fan: array, 32>, } @group(0) @binding(0) var globals: Globals; @group(0) @binding(1) var atlas: texture_2d; diff --git a/crates/horizon-client/src/environment.rs b/crates/horizon-client-render/src/environment.rs similarity index 100% rename from crates/horizon-client/src/environment.rs rename to crates/horizon-client-render/src/environment.rs diff --git a/crates/horizon-client/src/extras.rs b/crates/horizon-client-render/src/extras.rs similarity index 95% rename from crates/horizon-client/src/extras.rs rename to crates/horizon-client-render/src/extras.rs index d2f0b4e9d4..41e9645a3a 100644 --- a/crates/horizon-client/src/extras.rs +++ b/crates/horizon-client-render/src/extras.rs @@ -97,6 +97,8 @@ pub struct Extras { pub minimap_rotate: bool, /// Retry lost multiplayer connections (`reconnect`). pub auto_reconnect: bool, + /// The player's own name tag over its model in third person. + pub own_name_tag: bool, /// Mouse Tweaks-style drags in the inventory and storage containers. pub mouse_tweaks: bool, /// With them, the wheel over a slot moves its items one at a time. @@ -137,6 +139,7 @@ impl Default for Extras { minimap_zoom: 1.0, minimap_rotate: false, auto_reconnect: false, + own_name_tag: false, mouse_tweaks: false, mouse_tweaks_wheel: true, sort: false, @@ -211,6 +214,7 @@ impl Extras { } "minimapRotate" => e.minimap_rotate = flag(e.minimap_rotate), "autoReconnect" => e.auto_reconnect = flag(e.auto_reconnect), + "ownNameTag" => e.own_name_tag = flag(e.own_name_tag), "mouseTweaks" => e.mouse_tweaks = flag(e.mouse_tweaks), "mouseTweaksWheel" => e.mouse_tweaks_wheel = flag(e.mouse_tweaks_wheel), "sort" => e.sort = flag(e.sort), @@ -225,7 +229,7 @@ impl Extras { } pub fn serialize(&self) -> String { format!( - "fullbright:{}\ndynamicLights:{}\nzoom:{}\nzoomKey:{}\nzoomLevel:{}\nzoomSmooth:{}\nzoomSensitivity:{}\nfps:{}\nfpsCorner:{}\nfpsLows:{}\nkeystrokes:{}\nkeystrokesCorner:{}\nkeystrokesCps:{}\nmovedToMods:{}\nminimap:{}\nminimapCorner:{}\nminimapSize:{}\nminimapShape:{}\nminimapZoom:{}\nminimapRotate:{}\nautoReconnect:{}\nmouseTweaks:{}\nmouseTweaksWheel:{}\nsort:{}\nsortKey:{}\n{}:{}\nagentControl:{}\ndiscordPresence:{}\n", + "fullbright:{}\ndynamicLights:{}\nzoom:{}\nzoomKey:{}\nzoomLevel:{}\nzoomSmooth:{}\nzoomSensitivity:{}\nfps:{}\nfpsCorner:{}\nfpsLows:{}\nkeystrokes:{}\nkeystrokesCorner:{}\nkeystrokesCps:{}\nmovedToMods:{}\nminimap:{}\nminimapCorner:{}\nminimapSize:{}\nminimapShape:{}\nminimapZoom:{}\nminimapRotate:{}\nautoReconnect:{}\nownNameTag:{}\nmouseTweaks:{}\nmouseTweaksWheel:{}\nsort:{}\nsortKey:{}\n{}:{}\nagentControl:{}\ndiscordPresence:{}\n", bool_text(self.fullbright), bool_text(self.dynamic_lights), bool_text(self.zoom), @@ -251,6 +255,7 @@ impl Extras { self.minimap_zoom, bool_text(self.minimap_rotate), bool_text(self.auto_reconnect), + bool_text(self.own_name_tag), bool_text(self.mouse_tweaks), bool_text(self.mouse_tweaks_wheel), bool_text(self.sort), @@ -378,6 +383,7 @@ impl Cps { pub enum Item { Fullbright, DynamicLights, + OwnNameTag, AutoReconnect, MouseTweaks, MouseTweaksWheel, @@ -396,6 +402,8 @@ pub fn rows() -> Vec> { Err("Lighting"), Ok(Fullbright), Ok(DynamicLights), + Err("Third person"), + Ok(OwnNameTag), Err("Inventory"), Ok(MouseTweaks), Ok(MouseTweaksWheel), @@ -410,6 +418,7 @@ impl Item { match self { Item::Fullbright => "Fullbright", Item::DynamicLights => "Dynamic lights", + Item::OwnNameTag => "Own name tag", Item::AutoReconnect => "Auto-reconnect", Item::MouseTweaks => "Mouse Tweaks controls", Item::MouseTweaksWheel => "Scroll wheel moves items", @@ -426,7 +435,7 @@ impl Item { Some(match self { MouseTweaksWheel => MouseTweaks, SortKey => Sort, - Fullbright | DynamicLights | MouseTweaks | Sort | AutoReconnect | NoAi + Fullbright | DynamicLights | OwnNameTag | MouseTweaks | Sort | AutoReconnect | NoAi | AgentControl | DiscordPresence => { return None; } @@ -439,6 +448,9 @@ impl Item { Item::DynamicLights => { "Torches and other light sources held, dropped or on fire light the blocks around them." } + Item::OwnNameTag => { + "Shows your name above your head in third person, as other players see it. Hidden when the server hides your team's names or you're invisible." + } Item::AutoReconnect => { "Rejoins a server after a lost connection or a restart, never after a kick or ban." } @@ -471,7 +483,9 @@ impl Item { match self { Item::Fullbright => Some(Cost::None), Item::DynamicLights => Some(Cost::Low), - Item::AutoReconnect | Item::MouseTweaks | Item::Sort => Some(Cost::None), + Item::OwnNameTag | Item::AutoReconnect | Item::MouseTweaks | Item::Sort => { + Some(Cost::None) + } _ => None, } } @@ -480,6 +494,7 @@ impl Item { Some(match self { Item::Fullbright => e.fullbright, Item::DynamicLights => e.dynamic_lights, + Item::OwnNameTag => e.own_name_tag, Item::AutoReconnect => e.auto_reconnect, Item::MouseTweaks => e.mouse_tweaks, Item::MouseTweaksWheel => e.mouse_tweaks_wheel, @@ -506,6 +521,7 @@ impl Item { match self { Item::Fullbright => e.fullbright = !e.fullbright, Item::DynamicLights => e.dynamic_lights = !e.dynamic_lights, + Item::OwnNameTag => e.own_name_tag = !e.own_name_tag, Item::AutoReconnect => e.auto_reconnect = !e.auto_reconnect, Item::MouseTweaks => e.mouse_tweaks = !e.mouse_tweaks, Item::MouseTweaksWheel => e.mouse_tweaks_wheel = !e.mouse_tweaks_wheel, @@ -534,7 +550,7 @@ mod tests { let d = Extras::default(); assert!(!d.fullbright && !d.dynamic_lights); assert!(!d.zoom && !d.fps && !d.keystrokes && !d.minimap && !d.auto_reconnect); - assert!(!d.mouse_tweaks && !d.sort); + assert!(!d.mouse_tweaks && !d.sort && !d.own_name_tag); assert!(d.discord_presence); assert_eq!(Extras::parse(""), d); assert_eq!(Extras::parse(&d.serialize()), d); @@ -558,6 +574,8 @@ mod tests { e.minimap_rotate = true; e.minimap_corner = Corner::BottomLeft; e.auto_reconnect = true; + Item::OwnNameTag.cycle(&mut e, false); + assert_eq!(Item::OwnNameTag.switch(&e), Some(true)); e.mouse_tweaks = true; e.mouse_tweaks_wheel = false; e.sort = true; diff --git a/crates/horizon-client/src/fast_hash.rs b/crates/horizon-client-render/src/fast_hash.rs similarity index 100% rename from crates/horizon-client/src/fast_hash.rs rename to crates/horizon-client-render/src/fast_hash.rs diff --git a/crates/horizon-client/src/fog.rs b/crates/horizon-client-render/src/fog.rs similarity index 84% rename from crates/horizon-client/src/fog.rs rename to crates/horizon-client-render/src/fog.rs index 11ffda1512..e8ef5106e1 100644 --- a/crates/horizon-client/src/fog.rs +++ b/crates/horizon-client-render/src/fog.rs @@ -203,6 +203,13 @@ pub struct FogInputs<'a> { /// `BossHealthOverlay.shouldCreateWorldFog`: a boss bar with /// `createWorldFog` is shown (the Ender Dragon's). pub boss_world_fog: bool, + /// The camera entity is a spectator (`LavaFogEnvironment`). + pub spectator: bool, + /// The camera entity has Fire Resistance (`LavaFogEnvironment`). + pub fire_resistance: bool, + /// `GameRenderer.nightVisionScale` while Night Vision is on without + /// Darkness, else 0 (`FogRenderer.computeFogColor`'s brightenFactor). + pub night_vision: f32, } /// `FogRenderer.setupFog` + `computeFogColor`. @@ -272,7 +279,11 @@ pub fn setup_fog(tables: &FogTables, state: &FogState, input: &FogInputs) -> Fog color[1] += (color[1] * 0.6 - color[1]) * t; color[2] += (color[2] * 0.6 - color[2]) * t; } - let brighten = if eye_in_water { water_vision } else { 0.0 }; + let brighten = if eye_in_water { + water_vision + } else { + input.night_vision + }; if color.iter().all(|&c| c != 0.0) { let scale = 1.0 / color[0].max(color[1]).max(color[2]); color = color.map(|c| c + (c * scale - c) * brighten); @@ -288,10 +299,16 @@ pub fn setup_fog(tables: &FogTables, state: &FogState, input: &FogInputs) -> Fog }; match input.fog_type { FogType::Lava => { - fog.environmental_start = 0.25; - fog.environmental_end = 1.0; - fog.sky_end = 1.0; - fog.cloud_end = 1.0; + // LavaFogEnvironment.setupFog. + (fog.environmental_start, fog.environmental_end) = if input.spectator { + (-8.0, rd * 0.5) + } else if input.fire_resistance { + (0.0, 5.0) + } else { + (0.25, 1.0) + }; + fog.sky_end = fog.environmental_end; + fog.cloud_end = fog.environmental_end; } FogType::PowderSnow => { fog.environmental_start = 0.0; @@ -362,6 +379,22 @@ pub fn setup_fog(tables: &FogTables, state: &FogState, input: &FogInputs) -> Fog } fog } +/// Horizon's Fog option turned off (`horizon-graphics.txt` `fog:false`): +/// no distance fog, in any dimension: the dimension's and biome's own fog +/// (`visual/fog_start_distance`, `fog_end_distance`: the Nether's ends at 96 +/// blocks) and the render-distance fog. The fog of the fluid or powder snow +/// the camera is in, blindness, darkness and a boss's world fog keep their +/// limits. Vanilla has no such option. +pub fn clear_distance_fog(fog: &mut FogData, input: &FogInputs) { + if input.fog_type == FogType::Atmospheric + && input.darkness.is_none() + && !input.boss_world_fog + && input.blindness.is_none() + { + (fog.render_distance_start, fog.render_distance_end) = (1e9, 2e9); + (fog.environmental_start, fog.environmental_end) = (1e9, 2e9); + } +} /// `AtmosphericFogEnvironment.updateRainFogState`. pub fn update_rain_fog( state: &mut FogState, @@ -405,6 +438,9 @@ mod tests { darkness: None, darken_world: 0.0, boss_world_fog: false, + spectator: false, + fire_resistance: false, + night_vision: 0.0, } } #[test] @@ -432,6 +468,56 @@ mod tests { assert_eq!(water.environmental_start, -8.0); } #[test] + fn fog_off_clears_the_nether_fog_too() { + // The Nether's visual/fog_start_distance and fog_end_distance (10, 96) + // end before the render distance; the Fog option still clears them. + let nether = Sky { + fog_start: 10.0, + fog_end: 96.0, + ..Sky::default() + }; + let tables = FogTables { + water_fog_end: vec![1.0], + }; + let mut input = inputs(&nether); + input.skybox = false; + let mut fog = setup_fog(&tables, &FogState::default(), &input); + assert_eq!(fog.environmental_end, 96.0); + clear_distance_fog(&mut fog, &input); + assert!(fog.environmental_start >= 1e9 && fog.render_distance_start >= 1e9); + } + #[test] + fn fog_off_keeps_fluid_and_effect_fog() { + let sky = Sky { + fog_start: 10.0, + fog_end: 96.0, + ..Sky::default() + }; + let tables = FogTables { + water_fog_end: vec![1.0], + }; + let kept = |input: &FogInputs| { + let mut fog = setup_fog(&tables, &FogState::default(), input); + let before = fog; + clear_distance_fog(&mut fog, input); + fog == before + }; + for fog_type in [FogType::Lava, FogType::PowderSnow, FogType::Water] { + let mut input = inputs(&sky); + input.fog_type = fog_type; + assert!(kept(&input), "{fog_type:?}"); + } + let mut input = inputs(&sky); + input.blindness = Some((100, false)); + assert!(kept(&input)); + let mut input = inputs(&sky); + input.darkness = Some(0.5); + assert!(kept(&input)); + let mut input = inputs(&sky); + input.boss_world_fog = true; + assert!(kept(&input)); + } + #[test] fn boss_darkening_dims_fog_color() { // FogRenderer.computeFogColor: lerp toward 70/60/60% of each channel. let sky = Sky { diff --git a/crates/horizon-client/src/font.rs b/crates/horizon-client-render/src/font.rs similarity index 89% rename from crates/horizon-client/src/font.rs rename to crates/horizon-client-render/src/font.rs index a76ed81716..a3c676bdd9 100644 --- a/crates/horizon-client/src/font.rs +++ b/crates/horizon-client-render/src/font.rs @@ -8,7 +8,7 @@ //! offsets), styled drawing and StringSplitter-style line wrapping. use crate::{ hud::Rect, - text::{Style, Text}, + text::{Span, Style, Text, legacy_format}, zip::Zip, }; use ab_glyph::{Font as _, ScaleFont as _}; @@ -966,6 +966,83 @@ impl Font { } out } + /// `Font.split`: `StringSplitter.splitLines(text, width, Style.EMPTY)` over + /// the text's parts with their `§` codes in. A code styles what follows + /// (`§r` back to no style while measuring), and the part a line breaks in + /// goes on in the style at the break. Lines keep their codes for drawing. + pub fn split_formatted(&self, t: &Text, max_width: f32) -> Vec { + let max_width = max_width.max(1.0); + let mut parts: Vec<(Vec, Style)> = t + .spans + .iter() + .filter(|s| !s.text.is_empty()) + .map(|s| (s.text.chars().collect(), s.style.clone())) + .collect(); + let mut flat: Vec = parts.iter().flat_map(|p| p.0.iter().copied()).collect(); + let mut out = Vec::new(); + let mut force_new_line = false; + // One LineBreakFinder per line, over the parts left. + while let Some((at, split_style)) = self.line_break(&parts, max_width) { + let tail = flat[at]; + let new_line = tail == '\n'; + let skip = usize::from(new_line || tail == ' '); + force_new_line = new_line; + out.push(split_at(&mut parts, at, skip, &split_style)); + flat.drain(..at + skip); + } + if !parts.is_empty() { + let mut rest = Text::default(); + rest.spans = parts + .into_iter() + .map(|(text, style)| Span { + text: text.into_iter().collect(), + style, + }) + .collect(); + out.push(rest); + } else if force_new_line { + out.push(Text::empty()); + } + out + } + /// `StringSplitter.LineBreakFinder` fed by `StringDecomposer.iterateFormatted`: + /// where the parts break (a character index) and the style there. + fn line_break(&self, parts: &[(Vec, Style)], max_width: f32) -> Option<(usize, Style)> { + let (mut width, mut had_visible, mut last_space) = (0.0f32, false, None); + let mut offset = 0; + for (contents, part_style) in parts { + let mut style = part_style.clone(); + let mut i = 0; + while i < contents.len() { + let c = contents[i]; + if c == '§' { + // A `§` that ends its part ends only that part. + let Some(&code) = contents.get(i + 1) else { + break; + }; + style = legacy_format(&style, code, &Style::default()); + i += 2; + continue; + } + let at = offset + i; + if c == '\n' { + return Some((at, style)); + } + if c == ' ' { + last_space = Some((at, style.clone())); + } + let advance = self.advance(c, &style); + width += advance; + if had_visible && width > max_width { + return Some(last_space.unwrap_or((at, style))); + } + had_visible |= advance != 0.0; + i += 1; + } + offset += contents.len(); + } + None + } /// `ComponentRenderUtils.wrapComponents`: the split lines, wrapped ones /// indented by an unstyled space; one empty line for empty text. pub fn wrap_components(&self, t: &Text, max_width: i32) -> Vec { @@ -1059,6 +1136,47 @@ impl Font { } } +/// `StringSplitter.FlatComponents.splitAt`: the parts before `at` as a line, +/// `skip` characters dropped, and the part it ends in restyled as `style`. +fn split_at(parts: &mut Vec<(Vec, Style)>, at: usize, skip: usize, style: &Style) -> Text { + let mut line = Text::default(); + let mut push = |text: &[char], style: &Style| { + line.spans.push(Span { + text: text.iter().collect(), + style: style.clone(), + }) + }; + // Each part done is removed, so the one at hand is always the first. + let (mut position, mut in_skip) = (at, false); + while let Some((text, part_style)) = parts.first() { + let size = text.len(); + if !in_skip { + if position > size { + push(text, part_style); + parts.remove(0); + position -= size; + continue; + } + if position > 0 { + push(&text[..position], part_style); + } + position += skip; + in_skip = true; + } + if position <= size { + if position == size { + parts.remove(0); + } else { + parts[0] = (text[position..].to_vec(), style.clone()); + } + break; + } + parts.remove(0); + position -= size; + } + line +} + #[cfg(test)] mod tests { use super::*; @@ -1221,6 +1339,37 @@ mod tests { assert!(!font.take_patches().is_empty()); } + /// StringDecomposer.iterateFormatted: a `§` that ends a part stops that + /// part only; the next part is still measured and can break. + #[test] + fn a_section_sign_ending_a_part_still_breaks_the_next() { + let sheet = png(16, 8, |x, _| x < 2); + let default = br#"{"providers":[{"type":"space","advances":{" ":4}},{"type":"bitmap","file":"minecraft:font/a.png","ascent":7,"chars":["ab"]}]}"#; + let root = root( + "section-sign", + &[ + ("assets/minecraft/font/default.json", default), + ("assets/minecraft/textures/font/a.png", &sheet), + ], + ); + let font = load(&root); + let _ = std::fs::remove_dir_all(&root); + let mut text = Text::default(); + text.spans = ["a§", "a a"] + .map(|part| Span { + text: part.into(), + style: Style::default(), + }) + .into(); + // 3 + 3 + 4 + 3 is past 12: the line breaks at the space. + let lines: Vec = font + .split_formatted(&text, 12.0) + .iter() + .map(Text::plain) + .collect(); + assert_eq!(lines, ["a§a", "a"]); + } + /// A renderer rebuilt over the same font (the loading screen's handoff) /// makes its atlas texture from the textures alone, so the glyphs it had /// rasterized into the previous texture must be placed into the new one. diff --git a/crates/horizon-client/src/gfx.rs b/crates/horizon-client-render/src/gfx.rs similarity index 92% rename from crates/horizon-client/src/gfx.rs rename to crates/horizon-client-render/src/gfx.rs index 7f14c77a6c..9f40ed0721 100644 --- a/crates/horizon-client/src/gfx.rs +++ b/crates/horizon-client-render/src/gfx.rs @@ -39,9 +39,11 @@ mod entity_outline; mod weather; use entity_batch::EntityBatch; pub use entity_batch::{ - ENERGY_SWIRL_OVERLAY, SHADOW_OVERLAY, TRANSLUCENT_GLINT_ITEM_OVERLAY, TRANSLUCENT_ITEM_OVERLAY, - TRANSLUCENT_PARTICLE_OVERLAY, WATER_MASK_OVERLAY, + ENERGY_SWIRL_OVERLAY, SHADOW_OVERLAY, TEXT_POLYGON_OFFSET_OVERLAY, + TRANSLUCENT_GLINT_ITEM_OVERLAY, TRANSLUCENT_ITEM_OVERLAY, TRANSLUCENT_PARTICLE_OVERLAY, + WATER_MASK_OVERLAY, }; +pub use view::{View, ViewSet}; /// Window.getAppropriateLineWidth, in framebuffer pixels. pub fn outline_width(width: u32) -> f32 { @@ -68,6 +70,7 @@ pub fn outline_vertices(edges: &[[f64; 6]], camera: glam::DVec3) -> Vec<[f32; 6] mod animate; mod arena; +pub mod compositor; mod cull; mod end_sky; mod flat; @@ -91,11 +94,14 @@ pub mod pack_uniforms; mod pack_weather; mod page_runs; pub mod panorama; +mod readback; mod resident; mod stars; +mod sunrise_fan; mod timing; pub mod translucency; pub(crate) mod upload; +mod view; mod wgsl_prefix; mod world_post; @@ -188,6 +194,8 @@ struct Globals { outline_params: [f32; 4], /// Horizon Extras' dynamic lights: camera-relative position and level (`dynamic_light`). dynamic_lights: [[f32; 4]; crate::dynamic_light::MAX], + /// The sunrise fan's triangles as the sky shader tests them (`sunrise_fan::normals`). + sunrise_fan: [[f32; 4]; 2 * sunrise_fan::TRIANGLES], } pub type SectionKey = (i32, i32, i32); struct SectionGpu { @@ -349,6 +357,14 @@ struct BorderUniform { color: [f32; 4], offset: [f32; 4], } +/// A frame to save as a PNG: F2's screenshot, an agent's `game_screenshot`, +/// or the icon the Select World list shows. +pub struct ScreenshotRequest { + pub path: PathBuf, + /// `GameRenderer.takeAutoScreenshot`: centre-crop the frame to a square + /// and average it down to this size. `None` saves the frame as drawn. + pub size: Option<[u32; 2]>, +} pub struct Renderer { instance: wgpu::Instance, adapter: wgpu::Adapter, @@ -376,6 +392,11 @@ pub struct Renderer { pub last_wait: std::time::Duration, /// CPU time of the last `render`, waits excluded. pub last_encode: std::time::Duration, + /// What the system says about the window's display, watched only while HDR output + /// is on; before `window`, so it's dropped first. + display: DisplayWatch, + /// The watch was just made: the settings' Auto values take what it reported. + display_fresh: bool, window: Arc, pub device: wgpu::Device, pub queue: wgpu::Queue, @@ -425,6 +446,7 @@ pub struct Renderer { lightning_pipeline: wgpu::RenderPipeline, lightning: lightning::Renderer, see_through_pipeline: wgpu::RenderPipeline, + text_polygon_offset_pipeline: wgpu::RenderPipeline, celestial_pipeline: wgpu::RenderPipeline, celestial_buffer: wgpu::Buffer, end_sky: end_sky::EndSky, @@ -533,7 +555,7 @@ pub struct Renderer { block_entity_revision: u64, /// VisGraph face connectivity per meshed section (cave culling). occlusion: occlusion::Occlusion, - /// Resolution scale and FXAA (Horizon settings). + /// Resolution scale, FXAA and TAA (Horizon settings). world_post: world_post::Post, /// HDR output (Horizon setting): `Some` while the swapchain is HDR. hdr: Option, @@ -561,6 +583,8 @@ pub struct Renderer { pub hud: HudRenderer, /// Vanilla's menu background blur, made on first use. menu_blur: Option, + /// Where a screenshot's frame is drawn when the swapchain cannot be read back. + readback: readback::Readback, pub info: wgpu::AdapterInfo, pub suspended: bool, pub drawn_sections: usize, @@ -570,8 +594,8 @@ pub struct Renderer { pub section_draw_calls: usize, pub uploaded_bytes: u64, started: Instant, - /// Write the next presented frame to this PNG path (control socket). - pub screenshot: Option, + /// The next frame to write to a PNG path (control socket, F2, world icon). + pub screenshot: Option, /// Outcome of the last screenshot: frame size, or the error. pub screenshot_result: Option>, /// Brightness option (lightmap gamma), 0..1. @@ -765,8 +789,14 @@ fn mip_chain( /// pass is also what passes each draw's first instance to shaders reading /// `instance_index`, and DirectX 12 is the fallback, where working matters /// more than CPU time. -fn instance_descriptor(backends: wgpu::Backends) -> wgpu::InstanceDescriptor { +fn instance_descriptor( + backends: wgpu::Backends, + window: Option<&Arc>, +) -> wgpu::InstanceDescriptor { let mut descriptor = wgpu::InstanceDescriptor::new_without_display_handle(); + if let Some(window) = window { + descriptor = descriptor.with_display_handle(Box::new(WindowDisplay(window.clone()))); + } descriptor.backends = backends.with_env(); let dx12 = cfg!(windows) && descriptor.backends.contains(wgpu::Backends::DX12); if !cfg!(debug_assertions) && !dx12 { @@ -776,6 +806,21 @@ fn instance_descriptor(backends: wgpu::Backends) -> wgpu::InstanceDescriptor { } descriptor.with_env() } +/// The window's display connection, so the OpenGL backend creates its EGL +/// display from the X11 or Wayland connection the window lives on. Without +/// one, wgpu-hal falls back to `EGL_MESA_platform_surfaceless`, whose configs +/// cannot present to a window; the backend then reports the surface as +/// incompatible and the game has no OpenGL to fall back to (`gl not +/// compatible with provided surface`). +#[derive(Debug, Clone)] +struct WindowDisplay(Arc); +impl wgpu::rwh::HasDisplayHandle for WindowDisplay { + fn display_handle( + &self, + ) -> std::result::Result, wgpu::rwh::HandleError> { + wgpu::rwh::HasDisplayHandle::display_handle(&*self.0) + } +} /// The backends to open a device on, each tried in turn until one gives a /// device. `api` is the player's choice (`GraphicsApi::with_env` applied); /// `os` is `std::env::consts::OS`; `wgpu_backend`: whether `WGPU_BACKEND` is set, @@ -853,6 +898,14 @@ fn check_layers(limits: &wgpu::Limits, layers: u32) -> Result<()> { } Ok(()) } +/// Whether the swapchain can take a window of `width` x `height`: each side from 1 to the +/// device's `max_texture_dimension_2d` (`max`), else `Surface::configure` panics. 0 is a +/// minimized window. Past `max`: Windows lets a program size a window up to 65535 pixels +/// (`SetWindowPos`), a 984x65496 client area, when an RTX 5080 takes 32768. Vanilla's +/// `Window.onFramebufferResize` likewise keeps the last size for one that is not positive. +fn drawable_size(width: u32, height: u32, max: u32) -> bool { + (1..=max).contains(&width) && (1..=max).contains(&height) +} /// The swapchain format: a non-sRGB target, because vanilla blends and tints in gamma /// space, and 8-bit, because wgpu gives `Rgba16Float` the linear `ExtendedSrgb` color /// space: the compositor would read these gamma-encoded values as linear light and wash @@ -890,7 +943,7 @@ async fn open_device( if attempt > 0 { log::warn!("Horizon renderer: trying {name} ({})", failures.join("; ")); } - let instance = wgpu::Instance::new(instance_descriptor(backends)); + let instance = wgpu::Instance::new(instance_descriptor(backends, Some(window))); let surface = if offscreen { None } else { @@ -1100,9 +1153,14 @@ impl Renderer { let info = adapter.get_info(); crate::graphics::suggest_reduced_far_detail(crate::graphics::lod_capped(&info)); let size = window.inner_size(); + let max_side = device.limits().max_texture_dimension_2d; let (config, present_modes, hdr_output) = if let Some(surface) = &surface { let mut config = surface - .get_default_config(&adapter, size.width.max(1), size.height.max(1)) + .get_default_config( + &adapter, + size.width.clamp(1, max_side), + size.height.clamp(1, max_side), + ) .ok_or("surface has no supported configuration")?; let caps = surface.get_capabilities(&adapter); config.format = surface_format(&caps.formats, config.format); @@ -1128,14 +1186,22 @@ impl Renderer { )); } surface.configure(&device, &config); - (config, caps.present_modes.clone(), hdr::output(&caps)) + let hdr_output = hdr::output(&caps); + crate::graphics::offer_hdr(hdr_output.is_some()); + if previous.is_none() { + log::info!( + "Horizon renderer: HDR output {}", + hdr_output.map_or("not offered by this display", hdr::Output::name) + ); + } + (config, caps.present_modes.clone(), hdr_output) } else { let format = wgpu::TextureFormat::Bgra8Unorm; let config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, format, - width: size.width.max(1), - height: size.height.max(1), + width: size.width.clamp(1, max_side), + height: size.height.clamp(1, max_side), present_mode: wgpu::PresentMode::Immediate, desired_maximum_frame_latency: 2, alpha_mode: wgpu::CompositeAlphaMode::Opaque, @@ -2016,6 +2082,21 @@ impl Renderer { alpha_blend, false, ); + // RenderPipelines.TEXT_POLYGON_OFFSET: a text display's text and + // background win against the surface they lie on (bias 1, 10). + let text_polygon_offset_pipeline = entity_pipeline( + "Polygon offset text", + None, + true, + wgpu::CompareFunction::GreaterEqual, + wgpu::DepthBiasState { + constant: 10, + slope_scale: 1.0, + clamp: 0.0, + }, + alpha_blend, + false, + ); let entity_pipeline = entity_pipeline( "Entities", None, @@ -2519,6 +2600,8 @@ impl Renderer { )); } Ok(Self { + display: DisplayWatch::default(), + display_fresh: false, instance, adapter, surface, @@ -2573,6 +2656,7 @@ impl Renderer { lightning_pipeline, lightning: lightning::Renderer::new(), see_through_pipeline, + text_polygon_offset_pipeline, celestial_pipeline, celestial_buffer, end_sky: end_sky::EndSky::default(), @@ -2660,8 +2744,9 @@ impl Renderer { pack_terrain_flat: false, hud, menu_blur: None, + readback: readback::Readback::default(), info, - suspended: size.width == 0 || size.height == 0, + suspended: !drawable_size(size.width, size.height, max_side), drawn_sections: 0, section_draws: 0, section_draw_calls: 0, @@ -2821,8 +2906,11 @@ impl Renderer { ); } } + /// Follows the window to `width` x `height`. A size the swapchain can't take + /// ([`drawable_size`]) keeps the last one and draws nothing until the next. pub fn resize(&mut self, width: u32, height: u32) { - self.suspended = width == 0 || height == 0; + let max = self.device.limits().max_texture_dimension_2d; + self.suspended = !drawable_size(width, height, max); if self.suspended { return; } @@ -3176,7 +3264,7 @@ impl Renderer { ) { surface.configure(device, config); if let Some(hdr) = hdr { - horizon_hdr::set_metadata(device, surface, hdr.paper_white, hdr.peak); + horizon_hdr::set_metadata(device, surface, hdr.paper_white, hdr.peak, hdr.black); } } fn configure_surface(&self) { @@ -3191,7 +3279,7 @@ impl Renderer { } fn send_hdr_metadata(&self) { if let (Some(surface), Some(hdr)) = (&self.surface, &self.hdr) { - horizon_hdr::set_metadata(&self.device, surface, hdr.paper_white, hdr.peak); + horizon_hdr::set_metadata(&self.device, surface, hdr.paper_white, hdr.peak, hdr.black); } } /// The swapchain's configuration: `config` (what every pipeline draws in), or @@ -3206,9 +3294,30 @@ impl Renderer { } config } + /// The F3 screen's HDR line while the setting is on: the swapchain HDR presents to with + /// its paper white and peak, or that the surface offers none. + pub fn hdr_line(&self) -> String { + match &self.hdr { + Some(hdr) => format!( + "HDR: {}, {}/{} nits", + hdr.output.name(), + hdr.paper_white.round(), + hdr.peak.round() + ), + None => "HDR: not offered by this display".into(), + } + } /// HDR output (Horizon setting): HDR10 or scRGB at `paper_white` nits, the world's /// highlights expanded up to `peak` nits. Off where the surface can't present it. - pub fn set_hdr(&mut self, on: bool, paper_white: f32, peak: f32, marker: &std::path::Path) { + pub fn set_hdr( + &mut self, + on: bool, + paper_white: f32, + peak: f32, + black: f32, + ui: f32, + marker: &std::path::Path, + ) { let output = self.hdr_output.filter(|_| on && self.surface.is_some()); if output.is_some() != self.hdr.is_some() { // If the game dies before HDR output shows a frame, the marker is still there at @@ -3219,21 +3328,61 @@ impl Renderer { } else if let Some(marker) = self.hdr_marker.take() { let _ = std::fs::remove_file(marker); } + // Only HDR output asks the system about the display, so with HDR off the + // game never waits on the compositor for it. + self.display = if output.is_some() { + let (watch, now) = DisplayWatch::new(&self.window); + crate::graphics::report(now); + self.display_fresh = true; + watch + } else { + DisplayWatch::default() + }; let format = self.config.format.remove_srgb_suffix(); self.hdr = output.map(|output| hdr::Hdr::new(&self.device, format, output)); self.configure_surface(); + match output { + Some(output) => log::info!( + "Horizon renderer: HDR on: {} ({:?}), {paper_white}/{peak} nits", + output.name(), + output.format() + ), + None => log::info!("Horizon renderer: HDR off"), + } + } + if let Some(hdr) = &mut self.hdr { + hdr.ui = ui; } if let Some(hdr) = &mut self.hdr - && (hdr.paper_white, hdr.peak) != (paper_white, peak) + && (hdr.paper_white, hdr.peak, hdr.black) != (paper_white, peak, black) { hdr.paper_white = paper_white; hdr.peak = peak; + hdr.black = black; self.send_hdr_metadata(); } } - pub fn set_world_post(&mut self, scale: f32, antialias: bool) { + /// Whether what the system reports for the display changed (another output, the + /// player recalibrating the desktop): the settings' Auto values follow it then, so + /// apply them again. + pub fn display_changed(&mut self) -> bool { + flush_display_log(); + if std::mem::take(&mut self.display_fresh) { + return true; + } + let Some(now) = self.display.changed() else { + return false; + }; + crate::graphics::report(now); + true + } + /// `upscale`: output pixels per rendered pixel, for TAA to rebuild (1: native). + pub fn set_world_post(&mut self, scale: f32, antialias: bool, taa: bool, upscale: f32) { + let taa = taa && self.world_post.supports_taa(); self.world_post.scale = scale.clamp(0.25, 2.0); - self.world_post.antialias = antialias; + self.world_post.antialias = antialias && !taa; + self.world_post.taa = taa; + self.world_post.upscale = if taa { upscale.clamp(1.0, 4.0) } else { 1.0 }; } /// Runs `post` over the world from now on (`None`: no pack), for the dimension `dimension`; /// `block_ids` gives each block sprite layer's `mc_Entity` (id, fluid) pair. @@ -3429,14 +3578,14 @@ impl Renderer { &self.lightmap_sampler, ); } - pub fn view_projection(&self, frame: &Frame) -> Mat4 { - self.projection( + /// The views `render` draws `frame` from. + pub fn views(&self, frame: &Frame) -> ViewSet { + ViewSet::single(self.screen_view( frame.fov_degrees, frame.yaw, frame.pitch, - frame.bob * frame.spin, frame.depth_far, - ) + )) } /// The view frustum `render` will cull with, for culling geometry built /// before the frame (entities, block entities) the same way. @@ -3448,13 +3597,14 @@ impl Renderer { bob: Mat4, depth_far: f32, ) -> Frustum { + let views = ViewSet::single(self.screen_view(fov_degrees, yaw, pitch, depth_far)); Frustum { - planes: frustum(self.projection(fov_degrees, yaw, pitch, bob, depth_far)), + planes: frustum(views.cull().view_proj(bob)), } } - fn projection(&self, fov_degrees: f32, yaw: f32, pitch: f32, bob: Mat4, far: f32) -> Mat4 { + fn screen_view(&self, fov_degrees: f32, yaw: f32, pitch: f32, far: f32) -> View { let aspect = self.config.width as f32 / self.config.height.max(1) as f32; - world_projection(fov_degrees, aspect, far) * bob * crate::camera::view_matrix(yaw, pitch) + View::screen(yaw, pitch, fov_degrees, aspect, far) } fn update_lightmap( &mut self, @@ -3586,7 +3736,12 @@ impl Renderer { let size = [self.config.width, self.config.height]; let target_texture = match (&surface_frame, &mut self.hdr) { (Some(_), Some(hdr)) => hdr.frame(&self.device, size), - (Some(f), None) => f.texture.clone(), + // A screenshot of a swapchain without COPY_SRC (OpenGL) draws + // into a texture that has it, then onto the swapchain. + (Some(f), None) => self + .readback + .target(&self.device, &f.texture, self.screenshot.is_some()) + .unwrap_or_else(|| f.texture.clone()), (None, _) => self.offscreen.clone().ok_or("no offscreen target")?, }; // One command buffer for the frame, after the section copies staged @@ -3614,7 +3769,19 @@ impl Renderer { frame.block_flicker, frame.boss_darken, ); - let view_proj = self.view_projection(frame); + let views = self.views(frame); + let pose = frame.bob * frame.spin; + let view_proj = views.primary().view_proj(pose); + // TAA runs where the world post pass does, not under a shader pack's own. + let taa = !frame.title + && self.pack_post.as_ref().is_none_or(|p| p.1.is_none()) + && self.native.as_ref().is_none_or(|n| n.post.is_none()); + let jittered = self.world_post.jitter( + [self.config.width, self.config.height], + taa, + view_proj, + frame.camera, + ) * view_proj; if frame.title { self.panorama.prepare( &self.queue, @@ -3624,8 +3791,7 @@ impl Renderer { frame.pitch, ); } - let aspect = self.config.width as f32 / self.config.height.max(1) as f32; - let view = crate::camera::view_matrix(frame.yaw, frame.pitch); + let view = views.primary().view; let sun_angle = frame.sky.sun_angle.to_radians(); // Celestial bodies rotate around the X axis; angle 0 = noon. let sun = Vec3::new(0.0, sun_angle.cos(), -sun_angle.sin()); @@ -3666,7 +3832,7 @@ impl Renderer { ) }; let globals = Globals { - view_proj: view_proj.to_cols_array_2d(), + view_proj: jittered.to_cols_array_2d(), fog_color, fog, sky_color: [ @@ -3678,8 +3844,8 @@ impl Renderer { misc: [ 1.0 - frame.sky.sky_light_factor, frame.game_time, - aspect, - (frame.fov_degrees.to_radians() / 2.0).tan(), + views.primary().aspect, + views.primary().tan_half_fov, ], inverse_view: view.inverse().to_cols_array_2d(), sunrise: frame.sky.sunrise_color, @@ -3706,6 +3872,7 @@ impl Renderer { } lights }, + sunrise_fan: sunrise_fan::normals(frame.sky.sunrise_color[3], sun), }; self.uploads .buffer(&mut encoder, &self.globals, 0, bytemuck::bytes_of(&globals)); @@ -3741,14 +3908,15 @@ impl Renderer { bytemuck::bytes_of(&crosshair_globals), ); // Frustum culling and camera-relative origins. + let cull_proj = views.cull().view_proj(pose); let view = Frustum { - planes: frustum(view_proj), + planes: frustum(cull_proj), }; let camera = frame.camera; let camera_section = camera_section(camera); // Solid quads facing away from the eye are left out: the bob moves // the eye off `camera`, so it is found from the projection. - let eye = projection_center(view_proj).filter(|_| self.sides_on); + let eye = projection_center(cull_proj).filter(|_| self.sides_on); let culling = !frame.title && frame.cave_culling; if culling { crate::spans::time("occlusion", || { @@ -4208,6 +4376,7 @@ impl Renderer { } let [ entity_quads, + text_quads, crumbling_quads, translucent_crumbling_quads, water_mask_quads, @@ -4220,6 +4389,7 @@ impl Renderer { .update(entities, frame.additive, frame.hand, frame.see_through); let weather_quads = frame.weather.len() as u32 / 4; let world_quads = entity_quads + + text_quads + crumbling_quads + translucent_crumbling_quads + water_mask_quads @@ -4298,7 +4468,7 @@ impl Renderer { self.stars.prepare( &mut self.uploads, &mut encoder, - view_proj * rotation, + jittered * rotation, brightness, ); } @@ -4471,6 +4641,8 @@ impl Renderer { .flatten(); } let pack_reads_world = pack_post || native_post; + // Phase 2 of occlusion culling draws over the depth, and a shader pack or TAA reads it. + let keep_depth = pack_reads_world || (taa && self.world_post.taa); // The pack's program reads the vertices this frame's draws bind. let pack_draws_terrain = pack_terrain && flat_frame == self.pack_terrain_flat; // The shadow camera's lists, drawn by the pack's shadow programs. @@ -4622,8 +4794,7 @@ impl Renderer { view: world_depth, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(0.0), - // Phase 2 of occlusion culling draws over it; a shader pack reads it. - store: if cull.is_some() || pack_reads_world { + store: if cull.is_some() || keep_depth { wgpu::StoreOp::Store } else { wgpu::StoreOp::Discard @@ -4698,7 +4869,7 @@ impl Renderer { &mut encoder, world_view, world_depth, - pack_reads_world, + keep_depth, ); pass.set_bind_group(0, &self.group, &[]); pack_frame(&mut pass); @@ -4728,7 +4899,7 @@ impl Renderer { &mut encoder, world_view, world_depth, - pack_reads_world, + keep_depth, ); pass.set_bind_group(0, &self.group, &[]); pack_frame(&mut pass); @@ -4757,6 +4928,13 @@ impl Renderer { pass.draw_indexed(0..entity_quads * 6, 0, 0..1); pass.set_bind_group(0, &self.group, &[]); } + if text_quads > 0 { + pass.set_pipeline(&self.text_polygon_offset_pipeline); + pass.set_bind_group(0, &self.entity_group, &[]); + pass.set_vertex_buffer(0, self.entity_buffer.slice(..)); + pass.draw_indexed(0..text_quads * 6, (entity_quads * 4) as i32, 0..1); + pass.set_bind_group(0, &self.group, &[]); + } if translucent_item_quads > 0 { pass.set_pipeline(&self.translucent_item_pipeline); pass.set_bind_group(0, &self.entity_group, &[]); @@ -4776,7 +4954,11 @@ impl Renderer { pass.set_pipeline(&self.crumbling_pipeline); pass.set_bind_group(0, &self.entity_group, &[]); pass.set_vertex_buffer(0, self.entity_buffer.slice(..)); - pass.draw_indexed(0..crumbling_quads * 6, (entity_quads * 4) as i32, 0..1); + pass.draw_indexed( + 0..crumbling_quads * 6, + ((entity_quads + text_quads) * 4) as i32, + 0..1, + ); pass.set_bind_group(0, &self.group, &[]); } if shadow_quads > 0 { @@ -4802,7 +4984,8 @@ impl Renderer { pass.set_vertex_buffer(0, self.entity_buffer.slice(..)); pass.draw_indexed( 0..water_mask_quads * 6, - ((entity_quads + crumbling_quads + translucent_crumbling_quads) * 4) as i32, + ((entity_quads + text_quads + crumbling_quads + translucent_crumbling_quads) + * 4) as i32, 0..1, ); pass.set_bind_group(0, &self.group, &[]); @@ -4820,6 +5003,7 @@ impl Renderer { pass.draw_indexed( 0..swirl_quads * 6, ((entity_quads + + text_quads + crumbling_quads + translucent_crumbling_quads + water_mask_quads) @@ -4913,7 +5097,7 @@ impl Renderer { pass.set_vertex_buffer(0, self.entity_buffer.slice(..)); pass.draw_indexed( 0..translucent_crumbling_quads * 6, - ((entity_quads + crumbling_quads) * 4) as i32, + ((entity_quads + text_quads + crumbling_quads) * 4) as i32, 0..1, ); } @@ -5041,7 +5225,8 @@ impl Renderer { native.timing.as_mut(), ); } else if scaled.is_some() { - self.world_post.finish(&mut encoder, &view_target); + self.world_post + .finish(&self.queue, &mut encoder, &view_target); } if let Some(hdr) = &self.hdr { hdr.keep_world(&mut encoder); @@ -5078,6 +5263,10 @@ impl Renderer { ((world_quads + additive_quads) * 4) as i32, 0..1, ); + drop(pass); + if let Some(hdr) = &self.hdr { + hdr.mark_hand(&mut encoder); + } } // GameRenderer.render: LevelRenderer.blitEntityOutline after the level and the hand. self.entity_outline.draw( @@ -5086,6 +5275,13 @@ impl Renderer { &self.entity_group, &self.indices, ); + // The world icon grabs here: after the level and the hand, before + // the effects and the HUD draw over them (`tryTakeScreenshotIfNeeded` + // runs right after `renderLevel`). + let readback = self + .screenshot + .take_if(|request| request.size.is_some()) + .and_then(|request| self.take_readback(request, &mut encoder, &target_texture)); if !frame.screen.is_empty() { let attachments = [Some(wgpu::RenderPassColorAttachment { view: &view_target, @@ -5189,24 +5385,20 @@ impl Renderer { (&view_target, self.config.format.remove_srgb_suffix()), ); } - let readback = self.screenshot.take().and_then(|path| { - // The copy and PNG writer both assume 8-bit pixels; a float surface would make - // the row stride too small for the copy and abort in validation. - if self.config.format.block_copy_size(None) == Some(4) { - Some((path, self.copy_frame(&mut encoder, &target_texture))) - } else { - self.screenshot_result = Some(Err(format!( - "screenshots need a 32-bit surface format, not {:?}", - self.config.format - ))); - None - } + let readback = readback.or_else(|| { + self.screenshot + .take() + .and_then(|request| self.take_readback(request, &mut encoder, &target_texture)) }); self.uploads.finish(&encoder); if has_section_copies { self.section_copy_bytes = 0; self.section_uploads.finish(&encoder); } + if let Some(f) = &surface_frame { + self.readback + .present(&self.device, &mut encoder, &f.texture); + } if let (Some(hdr), Some(f)) = (&self.hdr, &surface_frame) { hdr.present(&self.queue, &mut encoder, &f.texture, !frame.title); } @@ -5227,9 +5419,9 @@ impl Renderer { if let Some(Some(timer)) = Self::pass_timer(&mut self.pack_post, &mut self.native) { timer.submitted(&self.queue); } - if let Some((path, buffer)) = readback { + if let Some((request, buffer)) = readback { self.screenshot_result = Some( - self.write_screenshot(&path, &buffer) + self.write_screenshot(&request, &buffer) .map_err(|e| e.to_string()), ); } @@ -5279,43 +5471,68 @@ impl Renderer { Ok(true) } } +/// Bytes per row of a `width`-pixel frame read back at 4 bytes a pixel. +fn padded_row(width: u32) -> u32 { + (width * 4).next_multiple_of(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT) +} +/// Encodes the copy of `texture`'s `size` pixels into a mappable buffer. +fn copy_frame( + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + texture: &wgpu::Texture, + [width, height]: [u32; 2], +) -> wgpu::Buffer { + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Horizon screenshot"), + size: u64::from(padded_row(width)) * u64::from(height), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + encoder.copy_texture_to_buffer( + texture.as_image_copy(), + wgpu::TexelCopyBufferInfo { + buffer: &buffer, + layout: wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(padded_row(width)), + rows_per_image: None, + }, + }, + wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + buffer +} impl Renderer { - fn padded_row(&self) -> u32 { - (self.config.width * 4).next_multiple_of(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT) - } - fn copy_frame( - &self, + /// Take a screenshot request: queue the frame's copy, or report why the + /// frame cannot be read back. + fn take_readback( + &mut self, + request: ScreenshotRequest, encoder: &mut wgpu::CommandEncoder, texture: &wgpu::Texture, - ) -> wgpu::Buffer { - let buffer = self.device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Horizon screenshot"), - size: u64::from(self.padded_row()) * u64::from(self.config.height), - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, - mapped_at_creation: false, - }); - encoder.copy_texture_to_buffer( - texture.as_image_copy(), - wgpu::TexelCopyBufferInfo { - buffer: &buffer, - layout: wgpu::TexelCopyBufferLayout { - offset: 0, - bytes_per_row: Some(self.padded_row()), - rows_per_image: None, - }, - }, - wgpu::Extent3d { - width: self.config.width, - height: self.config.height, - depth_or_array_layers: 1, - }, - ); - buffer - } - fn write_screenshot(&self, path: &std::path::Path, buffer: &wgpu::Buffer) -> Result<[u32; 2]> { - if !self.config.usage.contains(wgpu::TextureUsages::COPY_SRC) { - return Err("this surface does not support frame readback".into()); + ) -> Option<(ScreenshotRequest, wgpu::Buffer)> { + // The copy and PNG writer both assume 8-bit pixels; a float surface would make + // the row stride too small for the copy and abort in validation. + if self.config.format.block_copy_size(None) == Some(4) { + let size = [self.config.width, self.config.height]; + Some((request, copy_frame(&self.device, encoder, texture, size))) + } else { + self.screenshot_result = Some(Err(format!( + "screenshots need a 32-bit surface format, not {:?}", + self.config.format + ))); + None } + } + fn write_screenshot( + &self, + request: &ScreenshotRequest, + buffer: &wgpu::Buffer, + ) -> Result<[u32; 2]> { let (sender, receiver) = std::sync::mpsc::channel(); buffer.map_async(wgpu::MapMode::Read, .., move |r| { let _ = sender.send(r); @@ -5330,7 +5547,7 @@ impl Renderer { let (width, height, row) = ( self.config.width, self.config.height, - self.padded_row() as usize, + padded_row(self.config.width) as usize, ); let bgra = matches!( self.config.format.remove_srgb_suffix(), @@ -5350,8 +5567,18 @@ impl Renderer { } } buffer.unmap(); + // NativeImage.resizeSubRectTo on takeAutoScreenshot's grab: the icon + // is the frame's centre square averaged down to its size. + let (width, height, pixels) = match request.size { + Some(size) => ( + size[0], + size[1], + square_downscale(&pixels, self.config.width, self.config.height, size), + ), + None => (self.config.width, self.config.height, pixels), + }; let mut encoder = png::Encoder::new( - std::io::BufWriter::new(std::fs::File::create(path)?), + std::io::BufWriter::new(std::fs::File::create(&request.path)?), width, height, ); @@ -5361,6 +5588,42 @@ impl Renderer { Ok([width, height]) } } +/// Centre-crop the frame to a square and average it down to `size`. +pub(crate) fn square_downscale(pixels: &[u8], width: u32, height: u32, size: [u32; 2]) -> Vec { + let (side, cx, cy) = if width > height { + (height, (width - height) / 2, 0) + } else { + (width, 0, (height - width) / 2) + }; + let [tw, th] = size; + let mut out = vec![0u8; (tw * th * 4) as usize]; + for ty in 0..th { + let y0 = cy + ty * side / th; + let y1 = cy + (ty + 1) * side / th; + for tx in 0..tw { + let x0 = cx + tx * side / tw; + let x1 = cx + (tx + 1) * side / tw; + let (mut r, mut g, mut b) = (0u32, 0u32, 0u32); + for y in y0..y1 { + for x in x0..x1 { + let p = ((y * width + x) * 4) as usize; + r += u32::from(pixels[p]); + g += u32::from(pixels[p + 1]); + b += u32::from(pixels[p + 2]); + } + } + let count = (x1 - x0) * (y1 - y0); + let o = ((ty * tw + tx) * 4) as usize; + out[o..o + 4].copy_from_slice(&[ + (r / count) as u8, + (g / count) as u8, + (b / count) as u8, + 255, + ]); + } + } + out +} /// Camera-relative clip planes (`Frustum`). #[derive(Clone, Copy, Debug)] pub struct Frustum { @@ -5452,6 +5715,23 @@ fn frustum(m: Mat4) -> [Vec4; 6] { #[cfg(test)] mod tests { + #[test] + fn drawable_size_skips_minimized_and_oversized_windows() { + use super::drawable_size; + assert!(drawable_size(1280, 720, 8192)); + assert!(drawable_size(1, 1, 8192)); + assert!(drawable_size(8192, 8192, 8192)); + // Minimized. + assert!(!drawable_size(0, 720, 8192)); + assert!(!drawable_size(1280, 0, 8192)); + assert!(!drawable_size(0, 0, 8192)); + // One past the device's largest texture. + assert!(!drawable_size(8193, 720, 8192)); + assert!(!drawable_size(1280, 8193, 8192)); + // The crash: a window `SetWindowPos` made 65535 pixels tall, on an RTX 5080 (32768). + assert!(!drawable_size(984, 65496, 32768)); + assert!(!drawable_size(984, u32::MAX, 32768)); + } #[test] fn surface_is_8_bit_and_not_srgb() { use super::surface_format; @@ -5516,6 +5796,7 @@ mod tests { (11, 2), (15, 10), (7, 3), + (35, 11), (4, 4), (12, 7), ] @@ -5525,7 +5806,7 @@ mod tests { let mut batch = super::EntityBatch::default(); let counts = batch.update(&entities, &quad(3, 5), &quad(0, 6), &quad(0, 8)); let vertices = &batch.combined; - assert_eq!(counts, [4, 1, 1, 0, 0, 1, 1, 0]); + assert_eq!(counts, [4, 1, 1, 1, 0, 0, 1, 1, 0]); assert_eq!( vertices .as_chunks::<4>() @@ -5533,7 +5814,7 @@ mod tests { .iter() .map(|q| q[0].light) .collect::>(), - [0, 1, 3, 4, 2, 7, 9, 10, 5, 6, 8] + [0, 1, 3, 4, 11, 2, 7, 9, 10, 5, 6, 8] ); } #[test] @@ -5704,7 +5985,7 @@ mod tests { #[test] fn indirect_draws_are_validated_only_in_debug_builds_and_on_directx_12() { let validated = |backends| { - super::instance_descriptor(backends) + super::instance_descriptor(backends, None) .flags .contains(wgpu::InstanceFlags::VALIDATION_INDIRECT_CALL) }; @@ -5873,3 +6154,101 @@ mod sprite_upload_tests { } } } + +/// What the system says about the window's display, kept up to date: on Wayland, the +/// compositor's preferred image description of the surface. +#[derive(Default)] +struct DisplayWatch { + #[cfg(target_os = "linux")] + wayland: Option, +} +impl DisplayWatch { + /// Starts watching, with what the system reports now. + fn new(window: &Window) -> (Self, Option) { + #[cfg(target_os = "linux")] + if let Some((wayland, now)) = horizon_hdr::wayland::Watch::new(window) { + let watch = Self { + wayland: Some(wayland), + }; + return (watch, now.map(reported)); + } + let _ = window; + (Self::default(), None) + } + /// What the system reports, if that changed since the last call. + fn changed(&mut self) -> Option> { + #[cfg(target_os = "linux")] + if let Some(wayland) = &mut self.wayland { + return wayland.changed().map(|now| now.map(reported)); + } + None + } +} + +/// The log of what the system reports about the display: one line per change. The +/// renderer is made anew a few times while the game starts and each asks again, and a +/// slider dragged in the desktop's settings changes the values many times a second, so +/// a change within [`DISPLAY_LOG_QUIET`] of the last line waits until the values have +/// held that long, and only the one they settled on is logged. +struct DisplayLog { + last: String, + at: Option, + pending: Option<(String, std::time::Instant)>, +} +static DISPLAY_LOG: std::sync::Mutex = std::sync::Mutex::new(DisplayLog { + last: String::new(), + at: None, + pending: None, +}); +const DISPLAY_LOG_QUIET: std::time::Duration = std::time::Duration::from_secs(1); +fn log_display(line: String) { + let now = std::time::Instant::now(); + let mut log = DISPLAY_LOG + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if line == log.last { + log.pending = None; + } else if log.at.is_none_or(|at| now - at >= DISPLAY_LOG_QUIET) { + log::info!("{line}"); + (log.last, log.at, log.pending) = (line, Some(now), None); + } else { + log.pending = Some((line, now)); + } +} +/// Logs a change [`log_display`] held back, once the values have held for +/// [`DISPLAY_LOG_QUIET`]. +fn flush_display_log() { + let now = std::time::Instant::now(); + let mut log = DISPLAY_LOG + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some((_, since)) = &log.pending + && now - *since >= DISPLAY_LOG_QUIET + && let Some((line, _)) = log.pending.take() + { + log::info!("{line}"); + (log.last, log.at) = (line, Some(now)); + } +} + +/// What a compositor's luminances mean in HDR output's scale. +/// +/// KWin maps content's reference white (203 nits in PQ, BT.2408) onto its own, so the +/// desktop's white is 203 there and the peak is the target's maximum over that ratio. +/// Checked on KWin. Hyprland (0.57) reports the standard 203, and shows HDR windows below +/// the SDR white of its desktop (which it doesn't report), so Auto can't match its desktop +/// there. Other compositors are unchecked. The peak is kept within the settings' lowest and +/// PQ's top, so the metadata never ends below the black level. +#[cfg(target_os = "linux")] +fn reported(l: horizon_hdr::wayland::Luminances) -> crate::graphics::Reported { + let peak = (l.target_max * 203.0 / l.reference.max(1.0)).round() as u32; + log_display(format!( + "Horizon renderer: the compositor shows this window with reference white {} nits, \ + {}..{} nits: peak {peak} nits in HDR output's scale", + l.reference, l.target_min, l.target_max, + )); + crate::graphics::Reported { + paper_white: 203, + peak: peak.clamp(200, 10_000), + } +} diff --git a/crates/horizon-client/src/gfx/animate.rs b/crates/horizon-client-render/src/gfx/animate.rs similarity index 85% rename from crates/horizon-client/src/gfx/animate.rs rename to crates/horizon-client-render/src/gfx/animate.rs index 5a70f3b0f5..96427cca34 100644 --- a/crates/horizon-client/src/gfx/animate.rs +++ b/crates/horizon-client-render/src/gfx/animate.rs @@ -103,10 +103,11 @@ impl SpriteAnimator { let mut counts: Vec = Vec::new(); let mut sprites = Vec::with_capacity(animations.len()); for animation in animations { - let chains: Vec<_> = (0..animation.frame_count()) - .map(|frame| source_chain(animation.pixels(frame), size, settings, animation.layer)) + let pixels: Vec<_> = (0..animation.frame_count()) + .map(|frame| animation.pixels(frame)) .collect(); - let bytes: u64 = chains.iter().map(|(chain, _)| chain.len() as u64).sum(); + let (chains, edge) = source_chains(&pixels, size, settings, animation.layer); + let bytes: u64 = chains.iter().map(|chain| chain.len() as u64).sum(); if bytes > limit { log::warn!( "Horizon: layer {}'s animation ({bytes} bytes) does not fit in a storage buffer; it stays still", @@ -124,7 +125,7 @@ impl SpriteAnimator { let data = &mut frames[buffer]; let offsets = chains .iter() - .map(|(chain, _)| { + .map(|chain| { let offset = (data.len() / 4) as u32; data.extend_from_slice(chain); offset @@ -142,7 +143,7 @@ impl SpriteAnimator { shown: None, layer: animation.layer % split, frames: offsets, - edge: chains[0].1, + edge, group, }); } @@ -424,15 +425,63 @@ fn mix_level(frames: &[u8], [_, current, next, millis, edge]: [u32; 5], width: u /// array's size from its own size's chain, its first level repeated into the /// larger levels: that chain, which the shader repeats. Any other sprite: its /// chain at the array's size. +#[cfg(test)] fn source_chain(pixels: &[u8], size: u32, settings: &[Settings], layer: u32) -> (Vec, u32) { - let native = settings.get(layer as usize).map_or(0, |s| s.native_size); - let edge = if native.is_power_of_two() && native < size { - native + let (mut chains, edge) = source_chains(&[pixels], size, settings, layer); + (chains.swap_remove(0), edge) +} + +/// Every frame's chain, laid out as one frame's `source_chain`, but mipmapped as +/// vanilla's `SpriteContents.increaseMipLevel` does: over the whole animation +/// sheet (the frames stacked), so `solidify` and the alpha-to-coverage scale +/// of each level are the sheet's, not each frame's. A frame alone has a +/// single texel square at its 2x2 level, and its coverage scale could round +/// a whole far frame transparent (kelp blinking out at distance). +fn source_chains( + frames: &[&[u8]], + size: u32, + settings: &[Settings], + layer: u32, +) -> (Vec>, u32) { + let s = settings.get(layer as usize).copied().unwrap_or_default(); + let edge = if s.native_size.is_power_of_two() && s.native_size < size { + s.native_size } else { size }; - let frame = crate::sprites::resample(pixels, size, edge); - (super::mip_chain(&frame, edge, settings, layer, 1).0, edge) + let mut sheet = crate::mipmap::Image { + width: edge, + height: edge * frames.len() as u32, + pixels: Vec::with_capacity((edge * edge) as usize * frames.len()), + }; + for frame in frames { + let frame = crate::sprites::resample(frame, size, edge); + sheet.pixels.extend_from_slice(frame.as_chunks::<4>().0); + } + let has_transparent = s.has_transparent || sheet.pixels.iter().any(|p| p[3] == 0); + let levels = crate::mipmap::generate( + sheet, + edge.trailing_zeros(), + Settings { + has_transparent, + ..s + }, + ); + let chains = (0..frames.len()) + .map(|frame| { + let mut chain = Vec::new(); + for level in &levels { + let texels = (level.width * level.width) as usize; + chain.extend( + level.pixels[frame * texels..(frame + 1) * texels] + .iter() + .flatten(), + ); + } + chain + }) + .collect(); + (chains, edge) } #[cfg(test)] @@ -572,6 +621,63 @@ mod tests { } } + #[test] + fn kelp_frames_share_the_sheet_mips() { + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../.local/horizon-client") + .join(horizon_core::VANILLA_VERSION); + if !root + .join("assets/minecraft/textures/block/kelp.png") + .exists() + { + return; + } + let mut sprites = crate::sprites::Sprites::default(); + for name in ["kelp", "kelp_plant", "seagrass", "tall_seagrass_top"] { + sprites + .insert(&root, &format!("minecraft:block/{name}")) + .unwrap(); + } + sprites.finish(); + // A level drawn nowhere: no texel passes the cutout's alpha test. + let blank = |chain: &[u8], edge: u32| { + let mut start = 0; + let mut level = edge; + let mut blank = Vec::new(); + while level > 0 { + let bytes = (level * level * 4) as usize; + let texels = &chain[start..start + bytes]; + blank.push(texels.as_chunks::<4>().0.iter().all(|p| p[3] < 128)); + start += bytes; + level >>= 1; + } + blank + }; + let mut blinked = 0; + for animation in &sprites.animations { + let size = sprites.size; + let pixels: Vec<_> = (0..animation.frame_count()) + .map(|f| animation.pixels(f)) + .collect(); + let (chains, edge) = source_chains(&pixels, size, &sprites.mips, animation.layer); + for (frame, chain) in chains.iter().enumerate() { + // The 1x1 level has no texel square to measure coverage on + // (vanilla leaves it unscaled); every larger level shows. + let levels = blank(chain, edge); + assert!( + !levels[..levels.len() - 1].contains(&true), + "layer {} frame {frame}: {levels:?}", + animation.layer + ); + let (alone, _) = source_chain(pixels[frame], size, &sprites.mips, animation.layer); + let alone = blank(&alone, edge); + blinked += usize::from(alone[..alone.len() - 1].contains(&true)); + } + } + // Mipmapped frame by frame, some kelp frames vanished at the 2x2 level. + assert!(blinked > 0); + } + #[test] fn rounding_matches_f32_mix_but_for_exact_halves() { // The CPU blend this replaced: f32 mix, then rounded. They differ @@ -730,8 +836,11 @@ mod tests { queue.submit([encoder.finish()]); for animation in &animations { let blend = animation.blend(tick); - let chain = |f| source_chain(animation.pixels(f), size, &mips, animation.layer); - let ((current, edge), (next, _)) = (chain(blend.frame), chain(blend.next)); + let pixels: Vec<_> = (0..animation.frame_count()) + .map(|f| animation.pixels(f)) + .collect(); + let (chains, edge) = source_chains(&pixels, size, &mips, animation.layer); + let (current, next) = (&chains[blend.frame], &chains[blend.next]); let layers = read_back( &device, &queue, @@ -739,7 +848,7 @@ mod tests { ); assert_eq!( layers[(animation.layer % split) as usize], - reference(¤t, &next, blend.millis, edge, size), + reference(current, next, blend.millis, edge, size), "layer {} at tick {tick}", animation.layer ); diff --git a/crates/horizon-client/src/gfx/animate.wgsl b/crates/horizon-client-render/src/gfx/animate.wgsl similarity index 100% rename from crates/horizon-client/src/gfx/animate.wgsl rename to crates/horizon-client-render/src/gfx/animate.wgsl diff --git a/crates/horizon-client/src/gfx/arena.rs b/crates/horizon-client-render/src/gfx/arena.rs similarity index 100% rename from crates/horizon-client/src/gfx/arena.rs rename to crates/horizon-client-render/src/gfx/arena.rs diff --git a/crates/horizon-client-render/src/gfx/compositor.rs b/crates/horizon-client-render/src/gfx/compositor.rs new file mode 100644 index 0000000000..e3b929d48f --- /dev/null +++ b/crates/horizon-client-render/src/gfx/compositor.rs @@ -0,0 +1,214 @@ +//! Splitscreen: several players' renderers draw offscreen on one device, and the +//! compositor copies each frame into its place on a window's surface. A window holds views but +//! never owns them, so a view can later move to another window (another monitor, detached). +use super::Renderer; +use crate::Result; +use std::sync::Arc; +use winit::window::Window; + +pub struct Compositor { + surface: wgpu::Surface<'static>, + device: wgpu::Device, + queue: wgpu::Queue, + config: wgpu::SurfaceConfiguration, + present_modes: Vec, + suspended: bool, +} + +impl Renderer { + /// A compositor for `window` on this renderer's device, presenting on the surface it gave up. + pub fn compositor(&self, window: Arc) -> Result { + // The surface this renderer set aside for its successor (`release_surface`), else a new + // one: a window can't keep two. + let spare = self + .spare_surface + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .take(); + let surface = match spare { + Some(surface) => surface, + None => self.instance.create_surface(window.clone())?, + }; + Compositor::new( + surface, + &self.adapter, + &self.device, + &self.queue, + window.inner_size().into(), + ) + } + + /// The last offscreen frame (`--offscreen`), for the compositor. + pub fn offscreen_frame(&self) -> Option<&wgpu::Texture> { + self.offscreen.as_ref() + } + + /// Splitscreen turned off: a compositor's surface, set aside for the renderer that draws on + /// the window again (it takes it, as after `release_surface`). + pub fn take_back(&self, compositor: Compositor) { + *self + .spare_surface + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(compositor.surface); + } +} + +impl Compositor { + /// Present offscreen BGRA frames on a compatible surface using their existing device. + /// The surface must support COPY_DST and Bgra8Unorm; unsupported surfaces return an error. + pub fn new( + surface: wgpu::Surface<'static>, + adapter: &wgpu::Adapter, + device: &wgpu::Device, + queue: &wgpu::Queue, + size: [u32; 2], + ) -> Result { + let caps = surface.get_capabilities(adapter); + // ponytail: a plain texture copy needs the offscreen frames' format; a blit pass would + // take any surface format (Rgba8Unorm on some Linux drivers). + if !caps.usages.contains(wgpu::TextureUsages::COPY_DST) { + return Err("surface does not support copying offscreen frames".into()); + } + let format = wgpu::TextureFormat::Bgra8Unorm; + if !caps.formats.contains(&format) { + return Err(format!( + "splitscreen needs a {format:?} surface; this one offers {:?}", + caps.formats + ) + .into()); + } + let max = device.limits().max_texture_dimension_2d; + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, + format, + width: size[0].clamp(1, max), + height: size[1].clamp(1, max), + present_mode: wgpu::PresentMode::Fifo, + desired_maximum_frame_latency: 2, + alpha_mode: wgpu::CompositeAlphaMode::Opaque, + view_formats: vec![], + color_space: wgpu::SurfaceColorSpace::default(), + }; + surface.configure(device, &config); + Ok(Compositor { + surface, + device: device.clone(), + queue: queue.clone(), + config, + present_modes: caps.present_modes, + suspended: !super::drawable_size(size[0], size[1], max), + }) + } + + /// Vsync on: presents wait for the display. Off: they don't (Mailbox, else Immediate). + pub fn set_vsync(&mut self, vsync: bool) { + let mode = [wgpu::PresentMode::Mailbox, wgpu::PresentMode::Immediate] + .into_iter() + .find(|m| !vsync && self.present_modes.contains(m)) + .unwrap_or(wgpu::PresentMode::Fifo); + if mode != self.config.present_mode { + self.config.present_mode = mode; + self.surface.configure(&self.device, &self.config); + } + } + + pub fn resize(&mut self, width: u32, height: u32) { + self.suspended = + !super::drawable_size(width, height, self.device.limits().max_texture_dimension_2d); + if !self.suspended { + self.config.width = width; + self.config.height = height; + self.surface.configure(&self.device, &self.config); + } + } + + /// Copy each view's frame to its top-left corner on the window and present. Frames that + /// don't fit are clipped; parts of the window no view covers (a view before its first frame, + /// one still at its old size after a resize) are black. Nothing when the window has no frame + /// to give (occluded, resized, timed out). Returns true when a frame was submitted. + pub fn present(&mut self, views: &[(&wgpu::Texture, [u32; 2])]) -> bool { + if self.suspended { + return false; + } + let frame = match self.surface.get_current_texture() { + wgpu::CurrentSurfaceTexture::Success(f) + | wgpu::CurrentSurfaceTexture::Suboptimal(f) => f, + wgpu::CurrentSurfaceTexture::Outdated | wgpu::CurrentSurfaceTexture::Lost => { + self.surface.configure(&self.device, &self.config); + return false; + } + _ => return false, + }; + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Splitscreen compositor"), + }); + let target = frame.texture.create_view(&Default::default()); + drop(encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Splitscreen compositor clear"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &target, + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })], + ..Default::default() + })); + for &(texture, [x, y]) in views { + let [width, height] = clipped_size( + [texture.width(), texture.height()], + [x, y], + [self.config.width, self.config.height], + ); + if width == 0 || height == 0 { + continue; + } + encoder.copy_texture_to_texture( + texture.as_image_copy(), + wgpu::TexelCopyTextureInfo { + origin: wgpu::Origin3d { x, y, z: 0 }, + ..frame.texture.as_image_copy() + }, + wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + } + self.queue.submit([encoder.finish()]); + self.queue.present(frame); + true + } +} + +/// Copies never reach past the surface, including while a resized view still has its old frame. +fn clipped_size(view: [u32; 2], at: [u32; 2], surface: [u32; 2]) -> [u32; 2] { + [ + view[0].min(surface[0].saturating_sub(at[0])), + view[1].min(surface[1].saturating_sub(at[1])), + ] +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn frames_are_clipped_to_the_window_during_resize() { + assert_eq!(clipped_size([640, 480], [0, 0], [1280, 720]), [640, 480]); + assert_eq!( + clipped_size([640, 480], [1000, 600], [1280, 720]), + [280, 120] + ); + assert_eq!(clipped_size([640, 480], [1280, 720], [1280, 720]), [0, 0]); + assert_eq!( + clipped_size([640, 480], [u32::MAX, 0], [1280, 720]), + [0, 480] + ); + } +} diff --git a/crates/horizon-client/src/gfx/crumbling_tests.rs b/crates/horizon-client-render/src/gfx/crumbling_tests.rs similarity index 100% rename from crates/horizon-client/src/gfx/crumbling_tests.rs rename to crates/horizon-client-render/src/gfx/crumbling_tests.rs diff --git a/crates/horizon-client/src/gfx/cull.rs b/crates/horizon-client-render/src/gfx/cull.rs similarity index 100% rename from crates/horizon-client/src/gfx/cull.rs rename to crates/horizon-client-render/src/gfx/cull.rs diff --git a/crates/horizon-client/src/gfx/cull.wgsl b/crates/horizon-client-render/src/gfx/cull.wgsl similarity index 100% rename from crates/horizon-client/src/gfx/cull.wgsl rename to crates/horizon-client-render/src/gfx/cull.wgsl diff --git a/crates/horizon-client/src/gfx/end_sky.rs b/crates/horizon-client-render/src/gfx/end_sky.rs similarity index 100% rename from crates/horizon-client/src/gfx/end_sky.rs rename to crates/horizon-client-render/src/gfx/end_sky.rs diff --git a/crates/horizon-client/src/gfx/end_sky/tests.rs b/crates/horizon-client-render/src/gfx/end_sky/tests.rs similarity index 100% rename from crates/horizon-client/src/gfx/end_sky/tests.rs rename to crates/horizon-client-render/src/gfx/end_sky/tests.rs diff --git a/crates/horizon-client/src/gfx/entity_batch.rs b/crates/horizon-client-render/src/gfx/entity_batch.rs similarity index 91% rename from crates/horizon-client/src/gfx/entity_batch.rs rename to crates/horizon-client-render/src/gfx/entity_batch.rs index c9a9303ed4..a59b202850 100644 --- a/crates/horizon-client/src/gfx/entity_batch.rs +++ b/crates/horizon-client-render/src/gfx/entity_batch.rs @@ -2,13 +2,14 @@ use super::Vertex; /// Entity materials by a quad's overlay (`Vertex::layer`), in draw order: -/// ordinary entities, breaking cracks (11), translucent breaking cracks (12), -/// the boat water mask (13, `RenderPipelines.WATER_MASK`), energy swirls -/// (34, `RenderPipelines.ENERGY_SWIRL`), entity shadows (14, +/// ordinary entities, world text drawn with a depth bias (35, +/// `RenderPipelines.TEXT_POLYGON_OFFSET`), breaking cracks (11), translucent +/// breaking cracks (12), the boat water mask (13, `RenderPipelines.WATER_MASK`), +/// energy swirls (34, `RenderPipelines.ENERGY_SWIRL`), entity shadows (14, /// `RenderPipelines.ENTITY_SHADOW`), translucent-layer particles (15, drawn /// after translucent terrain: `SubmitNodeCollection.submitQuadParticleGroup`) /// and translucent item quads (32, drawn without depth writes). -const MATERIALS: usize = 8; +const MATERIALS: usize = 9; /// The overlay of depth-only quads that hide water (`RenderTypes.waterMask`). pub const WATER_MASK_OVERLAY: u32 = 13; /// The overlay of energy swirl quads (`RenderTypes.energySwirl`: the charged creeper's and the @@ -24,17 +25,21 @@ pub const TRANSLUCENT_PARTICLE_OVERLAY: u32 = 15; pub const TRANSLUCENT_ITEM_OVERLAY: u32 = 32; /// A translucent item with an enchantment glint. pub const TRANSLUCENT_GLINT_ITEM_OVERLAY: u32 = 33; +/// The overlay of text in `Font.DisplayMode.POLYGON_OFFSET` (text displays): +/// drawn like an ordinary entity, pulled toward the camera. +pub const TEXT_POLYGON_OFFSET_OVERLAY: u32 = 35; /// The material of a quad by its overlay (`MATERIALS` order). fn material(layer: u32) -> usize { match layer { - 11 => 1, - 12 => 2, - WATER_MASK_OVERLAY => 3, - ENERGY_SWIRL_OVERLAY => 4, - SHADOW_OVERLAY => 5, - TRANSLUCENT_PARTICLE_OVERLAY => 6, - TRANSLUCENT_ITEM_OVERLAY | TRANSLUCENT_GLINT_ITEM_OVERLAY => 7, + TEXT_POLYGON_OFFSET_OVERLAY => 1, + 11 => 2, + 12 => 3, + WATER_MASK_OVERLAY => 4, + ENERGY_SWIRL_OVERLAY => 5, + SHADOW_OVERLAY => 6, + TRANSLUCENT_PARTICLE_OVERLAY => 7, + TRANSLUCENT_ITEM_OVERLAY | TRANSLUCENT_GLINT_ITEM_OVERLAY => 8, _ => 0, } } @@ -102,9 +107,10 @@ mod tests { ) -> (Vec, [u32; MATERIALS]) { let mut combined = Vec::with_capacity(entities.len() + additive.len() + hand.len()); let mut counts = [0; MATERIALS]; - for (i, layer) in [0, 11, 12, 13, 34, 14, 15].into_iter().enumerate() { + for (i, layer) in [0, 35, 11, 12, 13, 34, 14, 15].into_iter().enumerate() { for quad in entities.as_chunks::<4>().0 { let material = match quad[0].layer { + 35 => 35, 11 => 11, 12 => 12, 13 => 13, @@ -130,7 +136,7 @@ mod tests { pos: [index as f32, 2.0, 3.0], uv: [index as f32, 5.0], layer: if mixed { - [7, 11, 14, 12, 4, 13, 15, 0][index / 4 % 8] + [7, 11, 14, 35, 12, 4, 13, 15, 0][index / 4 % 9] } else { 0 }, diff --git a/crates/horizon-client/src/gfx/entity_outline.rs b/crates/horizon-client-render/src/gfx/entity_outline.rs similarity index 100% rename from crates/horizon-client/src/gfx/entity_outline.rs rename to crates/horizon-client-render/src/gfx/entity_outline.rs diff --git a/crates/horizon-client/src/gfx/entity_outline.wgsl b/crates/horizon-client-render/src/gfx/entity_outline.wgsl similarity index 100% rename from crates/horizon-client/src/gfx/entity_outline.wgsl rename to crates/horizon-client-render/src/gfx/entity_outline.wgsl diff --git a/crates/horizon-client/src/gfx/flat.rs b/crates/horizon-client-render/src/gfx/flat.rs similarity index 100% rename from crates/horizon-client/src/gfx/flat.rs rename to crates/horizon-client-render/src/gfx/flat.rs diff --git a/crates/horizon-client/src/gfx/flatten.wgsl b/crates/horizon-client-render/src/gfx/flatten.wgsl similarity index 100% rename from crates/horizon-client/src/gfx/flatten.wgsl rename to crates/horizon-client-render/src/gfx/flatten.wgsl diff --git a/crates/horizon-client/src/gfx/hdr.rs b/crates/horizon-client-render/src/gfx/hdr.rs similarity index 69% rename from crates/horizon-client/src/gfx/hdr.rs rename to crates/horizon-client-render/src/gfx/hdr.rs index 8b9df7af0b..8db50b20f1 100644 --- a/crates/horizon-client/src/gfx/hdr.rs +++ b/crates/horizon-client-render/src/gfx/hdr.rs @@ -14,6 +14,12 @@ pub enum Output { } impl Output { + pub fn name(self) -> &'static str { + match self { + Self::Pq => "HDR10 (PQ)", + Self::ScRgb => "scRGB", + } + } pub fn format(self) -> wgpu::TextureFormat { match self { Self::Pq => wgpu::TextureFormat::Rgb10a2Unorm, @@ -57,6 +63,12 @@ struct Targets { pub struct Hdr { layout: wgpu::BindGroupLayout, pipeline: wgpu::RenderPipeline, + /// Zeroes the frame's alpha once the world is kept (`keep_world`): what draws after, + /// the hand, HUD and menus, raises it, so HDR output knows where they cover the world. + clear_alpha: wgpu::RenderPipeline, + /// Scales the frame's alpha down to 1/255 where the hand drew (`mark_hand`), so HDR + /// output keeps the hand at the world's brightness. + hand_mark: wgpu::RenderPipeline, params: wgpu::Buffer, pub output: Output, /// The frame's format: what every other pipeline draws in. @@ -67,6 +79,10 @@ pub struct Hdr { /// Paper white and peak brightness, in nits. pub paper_white: f32, pub peak: f32, + /// The display's black level, in nits: shadows are lifted onto it (`hdr.wgsl`). + pub black: f32, + /// The HUD and menus' brightness, as a share of paper white (0.25..=1). + pub ui: f32, } impl Hdr { @@ -132,6 +148,48 @@ impl Hdr { multiview_mask: None, cache: None, }); + let alpha_pass = |label, blend| { + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some(label), + layout: Some(&device.create_pipeline_layout(&Default::default())), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_hdr"), + compilation_options: Default::default(), + buffers: &[], + }, + primitive: Default::default(), + depth_stencil: None, + multisample: Default::default(), + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_clear_alpha"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format, + blend, + write_mask: wgpu::ColorWrites::ALPHA, + })], + }), + multiview_mask: None, + cache: None, + }) + }; + let clear_alpha = alpha_pass("Horizon HDR UI mask", None); + // Alpha times the blend constant (1/255): the hand's 1 becomes 1/255, the world's 0 + // stays 0. + let scale_alpha = wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::Zero, + dst_factor: wgpu::BlendFactor::Constant, + operation: wgpu::BlendOperation::Add, + }; + let hand_mark = alpha_pass( + "Horizon HDR hand mark", + Some(wgpu::BlendState { + color: scale_alpha, + alpha: scale_alpha, + }), + ); let params = device.create_buffer(&wgpu::BufferDescriptor { label: Some("Horizon HDR parameters"), size: 32, @@ -141,6 +199,8 @@ impl Hdr { Self { layout, pipeline, + clear_alpha, + hand_mark, params, output, format, @@ -148,6 +208,8 @@ impl Hdr { debug: std::env::var_os("HORIZON_HDR_DEBUG").is_some_and(|v| v == "1"), paper_white: 200.0, peak: 1000.0, + black: 0.0, + ui: 1.0, } } @@ -204,7 +266,9 @@ impl Hdr { self.targets.as_ref().unwrap().frame.clone() } - /// Keeps the world as drawn so far, before the hand and HUD cover it. + /// Keeps the world as drawn so far, before the hand and HUD cover it, then zeroes the + /// frame's alpha: the world's marks live on in the copy, and whatever draws from here + /// on raises the alpha where it covers the world. pub fn keep_world(&self, encoder: &mut wgpu::CommandEncoder) { let Some(t) = &self.targets else { return; @@ -214,6 +278,56 @@ impl Hdr { t.world.as_image_copy(), t.frame.size(), ); + let view = t.frame.create_view(&Default::default()); + let attachments = [Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })]; + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Horizon HDR UI mask"), + color_attachments: &attachments, + ..Default::default() + }); + pass.set_pipeline(&self.clear_alpha); + pass.draw(0..3, 0..1); + } + + /// After the hand: its pixels' alpha goes down to 1/255, below anything the HUD and + /// menus leave, so HDR output tells the hand (the world's brightness, never lifted) + /// from the UI (UI Brightness). + pub fn mark_hand(&self, encoder: &mut wgpu::CommandEncoder) { + let Some(t) = &self.targets else { + return; + }; + let view = t.frame.create_view(&Default::default()); + let attachments = [Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })]; + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Horizon HDR hand mark"), + color_attachments: &attachments, + ..Default::default() + }); + pass.set_pipeline(&self.hand_mark); + let a = 1.0 / 255.0; + pass.set_blend_constant(wgpu::Color { + r: a, + g: a, + b: a, + a, + }); + pass.draw(0..3, 0..1); } /// Writes the finished frame to the swapchain image `target`. @@ -234,8 +348,8 @@ impl Hdr { f32::from(u8::from(world)), f32::from(u8::from(self.debug)), f32::from(u8::from(self.output == Output::Pq)), - 0.0, - 0.0, + self.black.clamp(0.0, paper), + self.ui.clamp(0.25, 1.0), 0.0, ]; queue.write_buffer(&self.params, 0, bytemuck::cast_slice(¶ms)); diff --git a/crates/horizon-client/src/gfx/lightning.rs b/crates/horizon-client-render/src/gfx/lightning.rs similarity index 93% rename from crates/horizon-client/src/gfx/lightning.rs rename to crates/horizon-client-render/src/gfx/lightning.rs index d979bb368c..02f97c718b 100644 --- a/crates/horizon-client/src/gfx/lightning.rs +++ b/crates/horizon-client-render/src/gfx/lightning.rs @@ -15,10 +15,19 @@ pub struct Bolt { #[derive(Default)] pub struct Input<'a> { pub bolts: &'a [Bolt], - /// Current atlas white texel; a changed rect invalidates retained vertices. + /// Current atlas white texel ([`white_uv`]); a changed rect invalidates + /// retained vertices. pub uv: [f32; 2], } +/// The atlas point an untextured quad (`RenderTypes.lightning` has no +/// texture) samples: the middle of the white sprite. Its corner (`u0`, `v0`) +/// is on the sprite's border, where the atlas's nearest sampling picks the +/// neighbouring, transparent texels for most fragments. +pub fn white_uv(white: &crate::hud::Rect) -> [f32; 2] { + [(white.u0 + white.u1) / 2.0, (white.v0 + white.v1) / 2.0] +} + // Four layers, fourteen segments (eight trunk, two branches of three), four quads. pub(super) const QUADS: u32 = 4 * 14 * 4; diff --git a/crates/horizon-client/src/gfx/lightning/tests/mod.rs b/crates/horizon-client-render/src/gfx/lightning/tests/mod.rs similarity index 66% rename from crates/horizon-client/src/gfx/lightning/tests/mod.rs rename to crates/horizon-client-render/src/gfx/lightning/tests/mod.rs index 66959ce540..f709aebb1f 100644 --- a/crates/horizon-client/src/gfx/lightning/tests/mod.rs +++ b/crates/horizon-client-render/src/gfx/lightning/tests/mod.rs @@ -496,3 +496,253 @@ fn retained_gpu_draw_matches_transient_additive_draw() { assert_eq!(pixels[0], pixels[1], "frame {frame}: GPU output differs"); } } + +/// `horizon/white` (4x4 texels) at (40, 12) of a 64x32 atlas. +const ATLAS: (u32, u32) = (64, 32); +fn white_sprite() -> crate::hud::Rect { + let (w, h) = (ATLAS.0 as f32, ATLAS.1 as f32); + crate::hud::Rect { + u0: 40.0 / w, + v0: 12.0 / h, + u1: 44.0 / w, + v1: 16.0 / h, + w: 4, + h: 4, + } +} + +/// The white sprite's corner is on its border with the next atlas texels, +/// which nearest sampling picks for most of a bolt's fragments (#3171): the +/// point sampled is at least a texel inside the sprite. +#[test] +fn white_uv_is_inside_the_white_sprite() { + let [u, v] = white_uv(&white_sprite()); + let (x, y) = (u * ATLAS.0 as f32, v * ATLAS.1 as f32); + assert!((41.0..=43.0).contains(&x), "u at texel {x}"); + assert!((13.0..=15.0).contains(&y), "v at texel {y}"); +} + +/// A bolt seen from 14 blocks through the atlas as the game binds it (nearest +/// sampling, only the white sprite opaque) draws the same pixels as through a +/// texture that is white everywhere (#3171: the sprite's corner left a few +/// scattered pixels). Run explicitly, like the test above. +#[test] +#[ignore = "requires GPU; run separately with --ignored --exact"] +fn bolt_through_the_atlas_white_sprite_is_fully_drawn() { + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle()); + let Ok(adapter) = pollster::block_on(instance.request_adapter(&Default::default())) else { + eprintln!("no GPU adapter: skipped"); + return; + }; + let (device, queue) = pollster::block_on(adapter.request_device(&Default::default())).unwrap(); + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(crate::gfx::entity_shader().into()), + }); + let attributes = wgpu::vertex_attr_array![0=>Float32x3,1=>Float32x2,2=>Uint32,3=>Unorm8x4,4=>Uint8x4,5=>Snorm8x4,6=>Float32x2]; + let offsets = wgpu::vertex_attr_array![7=>Float32x3]; + let buffers = [ + Some(wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &attributes, + }), + Some(wgpu::VertexBufferLayout { + array_stride: 12, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &offsets, + }), + ]; + // The lightning pipeline (gfx.rs): additive, into RGBA8. + let additive = wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::Add, + }; + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_lightning"), + compilation_options: Default::default(), + buffers: &buffers, + }, + primitive: wgpu::PrimitiveState { + cull_mode: None, + ..Default::default() + }, + depth_stencil: None, + multisample: Default::default(), + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_celestial"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format: wgpu::TextureFormat::Rgba8Unorm, + blend: Some(wgpu::BlendState { + color: additive, + alpha: additive, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview_mask: None, + cache: None, + }); + // The game's camera: 70 degrees, reversed depth, the bolt 14 blocks ahead. + let globals = crate::gfx::Globals { + view_proj: (glam::Mat4::perspective_rh(70f32.to_radians(), 1.0, 400.0, 0.05) + * glam::Mat4::look_at_rh(glam::Vec3::ZERO, glam::Vec3::Z, glam::Vec3::Y)) + .to_cols_array_2d(), + ..bytemuck::Zeroable::zeroed() + }; + let globals = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::bytes_of(&globals), + usage: wgpu::BufferUsages::UNIFORM, + }); + let texture = |(width, height): (u32, u32), data: &[u8]| { + device + .create_texture_with_data( + &queue, + &wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }, + wgpu::util::TextureDataOrder::LayerMajor, + data, + ) + .create_view(&Default::default()) + }; + let white = texture((1, 1), &[255; 4]); + // Transparent but for the white sprite. + let mut texels = vec![0u8; (ATLAS.0 * ATLAS.1 * 4) as usize]; + for y in 12..16 { + for x in 40..44 { + let i = ((y * ATLAS.0 + x) * 4) as usize; + texels[i..i + 4].copy_from_slice(&[255; 4]); + } + } + let atlas = texture(ATLAS, &texels); + // The atlas sampler (gfx.rs): wgpu's default, nearest. + let sampler = device.create_sampler(&Default::default()); + let indices = crate::gfx::Renderer::quad_indices(&device, QUADS); + let size = 256; + let extent = wgpu::Extent3d { + width: size, + height: size, + depth_or_array_layers: 1, + }; + let draw = |view: &wgpu::TextureView, uv: [f32; 2]| -> Vec { + let group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &pipeline.get_bind_group_layout(0), + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: globals.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(view), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(&white), + }, + wgpu::BindGroupEntry { + binding: 4, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + ], + }); + let bolts = [Bolt { + seed: 123, + at: [0.0, -1.62, 14.0], + }]; + let input = Input { bolts: &bolts, uv }; + let mut renderer = Renderer::new(); + let mut uploads = Uploads::new(&device); + let mut encoder = device.create_command_encoder(&Default::default()); + renderer.prepare(&device, &mut uploads, &mut encoder, &input, DVec3::ZERO); + let color = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: extent, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); + { + let target = color.create_view(&Default::default()); + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &target, + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + store: wgpu::StoreOp::Store, + }, + })], + ..Default::default() + }); + pass.set_pipeline(&pipeline); + pass.set_bind_group(0, &group, &[]); + pass.set_index_buffer(indices.slice(..), wgpu::IndexFormat::Uint32); + renderer.draw(&mut pass, &input); + } + let read = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: u64::from(size * size * 4), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + encoder.copy_texture_to_buffer( + color.as_image_copy(), + wgpu::TexelCopyBufferInfo { + buffer: &read, + layout: wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(size * 4), + rows_per_image: None, + }, + }, + extent, + ); + uploads.finish(&encoder); + queue.submit([encoder.finish()]); + read.slice(..) + .map_async(wgpu::MapMode::Read, |r| r.unwrap()); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); + read.slice(..).get_mapped_range().unwrap().to_vec() + }; + let lit = |pixels: &[u8]| pixels.chunks(4).filter(|p| p[3] != 0).count(); + let expected = draw(&white, [0.5; 2]); + let drawn = draw(&atlas, white_uv(&white_sprite())); + assert!(lit(&expected) > 1000, "{} pixels", lit(&expected)); + assert_eq!( + lit(&drawn), + lit(&expected), + "pixels drawn through the atlas" + ); + assert!(drawn == expected, "the bolt's colours through the atlas"); +} diff --git a/crates/horizon-client/src/gfx/lod.rs b/crates/horizon-client-render/src/gfx/lod.rs similarity index 100% rename from crates/horizon-client/src/gfx/lod.rs rename to crates/horizon-client-render/src/gfx/lod.rs diff --git a/crates/horizon-client/src/gfx/native_material.rs b/crates/horizon-client-render/src/gfx/native_material.rs similarity index 100% rename from crates/horizon-client/src/gfx/native_material.rs rename to crates/horizon-client-render/src/gfx/native_material.rs diff --git a/crates/horizon-client/src/gfx/native_pack.rs b/crates/horizon-client-render/src/gfx/native_pack.rs similarity index 100% rename from crates/horizon-client/src/gfx/native_pack.rs rename to crates/horizon-client-render/src/gfx/native_pack.rs diff --git a/crates/horizon-client/src/gfx/native_post.rs b/crates/horizon-client-render/src/gfx/native_post.rs similarity index 100% rename from crates/horizon-client/src/gfx/native_post.rs rename to crates/horizon-client-render/src/gfx/native_post.rs diff --git a/crates/horizon-client/src/gfx/native_sky.rs b/crates/horizon-client-render/src/gfx/native_sky.rs similarity index 100% rename from crates/horizon-client/src/gfx/native_sky.rs rename to crates/horizon-client-render/src/gfx/native_sky.rs diff --git a/crates/horizon-client/src/gfx/occlusion.rs b/crates/horizon-client-render/src/gfx/occlusion.rs similarity index 100% rename from crates/horizon-client/src/gfx/occlusion.rs rename to crates/horizon-client-render/src/gfx/occlusion.rs diff --git a/crates/horizon-client/src/gfx/pack_atlas.rs b/crates/horizon-client-render/src/gfx/pack_atlas.rs similarity index 100% rename from crates/horizon-client/src/gfx/pack_atlas.rs rename to crates/horizon-client-render/src/gfx/pack_atlas.rs diff --git a/crates/horizon-client/src/gfx/pack_draw.rs b/crates/horizon-client-render/src/gfx/pack_draw.rs similarity index 100% rename from crates/horizon-client/src/gfx/pack_draw.rs rename to crates/horizon-client-render/src/gfx/pack_draw.rs diff --git a/crates/horizon-client/src/gfx/pack_entities.rs b/crates/horizon-client-render/src/gfx/pack_entities.rs similarity index 100% rename from crates/horizon-client/src/gfx/pack_entities.rs rename to crates/horizon-client-render/src/gfx/pack_entities.rs diff --git a/crates/horizon-client/src/gfx/pack_hand.rs b/crates/horizon-client-render/src/gfx/pack_hand.rs similarity index 100% rename from crates/horizon-client/src/gfx/pack_hand.rs rename to crates/horizon-client-render/src/gfx/pack_hand.rs diff --git a/crates/horizon-client/src/gfx/pack_post.rs b/crates/horizon-client-render/src/gfx/pack_post.rs similarity index 98% rename from crates/horizon-client/src/gfx/pack_post.rs rename to crates/horizon-client-render/src/gfx/pack_post.rs index b9320277ab..7eea498ef1 100644 --- a/crates/horizon-client/src/gfx/pack_post.rs +++ b/crates/horizon-client-render/src/gfx/pack_post.rs @@ -476,10 +476,12 @@ fn sampler( }) } -/// Runs `create` and returns its validation error, if any. +/// Runs `create` and returns its validation error, if any. A panic while it runs is an error +/// too: naga's backends turn a module they cannot write into a panic as the driver gets the +/// pipeline (#3525), which a pack's program must not turn into a crash. fn checked(device: &wgpu::Device, create: impl FnOnce() -> T) -> Result { let scope = device.push_error_scope(wgpu::ErrorFilter::Validation); - let value = create(); + let value = crate::crash::contain(create); match pollster::block_on(scope.pop()) { Some(e) => { // The details are in the error's description or its sources. @@ -494,7 +496,7 @@ fn checked(device: &wgpu::Device, create: impl FnOnce() -> T) -> Result Ok(value), + None => value.map_err(|panic| format!("the shader compiler failed: {panic}")), } } @@ -2629,6 +2631,38 @@ mod tests { assert_eq!(used, [1, 2, 3]); } + /// #3525: naga's SPIR-V writer panicked on Photon's gbuffers programs as the pipeline was + /// made. Such a panic skips the pass, as a validation error does, and the device goes on. + #[test] + fn a_shader_compiler_panic_skips_the_pass() { + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle()); + let Ok(adapter) = pollster::block_on(instance.request_adapter(&Default::default())) else { + return; + }; + let Ok((device, _queue)) = pollster::block_on(adapter.request_device(&Default::default())) + else { + return; + }; + let failed = checked(&device, || -> wgpu::Buffer { + panic!("internal error: entered unreachable code: Expression [89] is not cached!") + }); + let error = failed.expect_err("the panic comes back as an error"); + assert!(error.contains("Expression [89] is not cached!"), "{error}"); + let buffer = |usage| { + checked(&device, || { + device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 16, + usage, + mapped_at_creation: false, + }) + }) + }; + // The device still makes things, and its errors still come back. + assert!(buffer(wgpu::BufferUsages::UNIFORM).is_ok()); + assert!(buffer(wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::MAP_WRITE).is_err()); + } + #[test] fn pack_formats_map_to_renderable_ones() { let f = |name| texture_format(Format::parse(name).unwrap(), wgpu::Features::empty()); diff --git a/crates/horizon-client/src/gfx/pack_post.wgsl b/crates/horizon-client-render/src/gfx/pack_post.wgsl similarity index 100% rename from crates/horizon-client/src/gfx/pack_post.wgsl rename to crates/horizon-client-render/src/gfx/pack_post.wgsl diff --git a/crates/horizon-client/src/gfx/pack_shadow.rs b/crates/horizon-client-render/src/gfx/pack_shadow.rs similarity index 100% rename from crates/horizon-client/src/gfx/pack_shadow.rs rename to crates/horizon-client-render/src/gfx/pack_shadow.rs diff --git a/crates/horizon-client/src/gfx/pack_sky.rs b/crates/horizon-client-render/src/gfx/pack_sky.rs similarity index 100% rename from crates/horizon-client/src/gfx/pack_sky.rs rename to crates/horizon-client-render/src/gfx/pack_sky.rs diff --git a/crates/horizon-client/src/gfx/pack_timing.rs b/crates/horizon-client-render/src/gfx/pack_timing.rs similarity index 100% rename from crates/horizon-client/src/gfx/pack_timing.rs rename to crates/horizon-client-render/src/gfx/pack_timing.rs diff --git a/crates/horizon-client/src/gfx/pack_uniforms.rs b/crates/horizon-client-render/src/gfx/pack_uniforms.rs similarity index 100% rename from crates/horizon-client/src/gfx/pack_uniforms.rs rename to crates/horizon-client-render/src/gfx/pack_uniforms.rs diff --git a/crates/horizon-client/src/gfx/pack_weather.rs b/crates/horizon-client-render/src/gfx/pack_weather.rs similarity index 100% rename from crates/horizon-client/src/gfx/pack_weather.rs rename to crates/horizon-client-render/src/gfx/pack_weather.rs diff --git a/crates/horizon-client/src/gfx/page_runs.rs b/crates/horizon-client-render/src/gfx/page_runs.rs similarity index 100% rename from crates/horizon-client/src/gfx/page_runs.rs rename to crates/horizon-client-render/src/gfx/page_runs.rs diff --git a/crates/horizon-client/src/gfx/panorama.rs b/crates/horizon-client-render/src/gfx/panorama.rs similarity index 100% rename from crates/horizon-client/src/gfx/panorama.rs rename to crates/horizon-client-render/src/gfx/panorama.rs diff --git a/crates/horizon-client/src/gfx/panorama.wgsl b/crates/horizon-client-render/src/gfx/panorama.wgsl similarity index 100% rename from crates/horizon-client/src/gfx/panorama.wgsl rename to crates/horizon-client-render/src/gfx/panorama.wgsl diff --git a/crates/horizon-client-render/src/gfx/readback.rs b/crates/horizon-client-render/src/gfx/readback.rs new file mode 100644 index 0000000000..7a511fe69c --- /dev/null +++ b/crates/horizon-client-render/src/gfx/readback.rs @@ -0,0 +1,213 @@ +//! Screenshots of a swapchain that cannot be copied from. OpenGL's surfaces +//! offer `RENDER_ATTACHMENT` only, so a frame whose pixels are read back +//! (the screenshot key, a world's icon) is drawn into a texture that can be, +//! then drawn onto the swapchain image as a fullscreen triangle. + +/// The texture a frame to read back is drawn into, and the pass that puts it +/// on the swapchain afterwards. +#[derive(Default)] +pub struct Readback { + texture: Option, + blitter: Option<(wgpu::TextureFormat, wgpu::util::TextureBlitter)>, + /// A frame drew into `texture` and still has to reach the swapchain. + drawn: bool, +} + +impl Readback { + /// The texture this frame draws into instead of the swapchain image + /// `surface`: `None` unless the frame is read back (`screenshot`) and the + /// image cannot be copied from. + pub fn target( + &mut self, + device: &wgpu::Device, + surface: &wgpu::Texture, + screenshot: bool, + ) -> Option { + if !screenshot || surface.usage().contains(wgpu::TextureUsages::COPY_SRC) { + return None; + } + let format = surface.format().remove_srgb_suffix(); + if self + .texture + .as_ref() + .is_none_or(|t| t.size() != surface.size() || t.format() != format) + { + self.texture = Some(device.create_texture(&wgpu::TextureDescriptor { + label: Some("Horizon screenshot frame"), + size: surface.size(), + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_SRC + | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + })); + } + self.drawn = true; + self.texture.clone() + } + + /// Draws the frame `target` gave onto the swapchain image `surface`; nothing + /// when this frame drew straight into it. + pub fn present( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + surface: &wgpu::Texture, + ) { + if !std::mem::take(&mut self.drawn) { + return; + } + let Some(texture) = &self.texture else { + return; + }; + // The frame's passes write the swapchain through its non-sRGB view + // (`view_formats`), so the copy goes there unconverted too. + let format = surface.format().remove_srgb_suffix(); + if self.blitter.as_ref().is_none_or(|b| b.0 != format) { + self.blitter = Some((format, wgpu::util::TextureBlitter::new(device, format))); + } + let target = surface.create_view(&wgpu::TextureViewDescriptor { + format: Some(format), + ..Default::default() + }); + let source = texture.create_view(&Default::default()); + if let Some((_, blitter)) = &self.blitter { + blitter.copy(device, encoder, &source, &target); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn device() -> Option<(wgpu::Device, wgpu::Queue)> { + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle()); + let adapter = pollster::block_on(instance.request_adapter(&Default::default())).ok()?; + pollster::block_on(adapter.request_device(&Default::default())).ok() + } + + fn texture(device: &wgpu::Device, usage: wgpu::TextureUsages) -> wgpu::Texture { + device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: 70, + height: 3, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Bgra8Unorm, + usage, + view_formats: &[], + }) + } + + fn clear(encoder: &mut wgpu::CommandEncoder, texture: &wgpu::Texture, color: wgpu::Color) { + let view = texture.create_view(&Default::default()); + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(color), + store: wgpu::StoreOp::Store, + }, + })], + ..Default::default() + }); + } + + fn read(device: &wgpu::Device, buffer: &wgpu::Buffer) -> Vec { + buffer.map_async(wgpu::MapMode::Read, .., |r| r.unwrap()); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); + buffer.get_mapped_range(..).unwrap().to_vec() + } + + /// The last pixel of the last row of a 70x3 frame read back by `copy_frame`. + fn last_pixel(device: &wgpu::Device, buffer: &wgpu::Buffer) -> Vec { + let pixels = read(device, buffer); + let row = 70usize * 4; + let last = row.next_multiple_of(256) * 2 + row - 4; + pixels[last..last + 4].to_vec() + } + + /// The renderer's screenshot path on a surface like OpenGL's (#3510): the + /// frame is drawn into `target`, read back with `copy_frame` and drawn on + /// the swapchain, with no validation error (the copy used to need the + /// swapchain's missing `COPY_SRC`), and the swapchain shows the frame. + #[test] + fn screenshot_of_a_surface_without_copy_src() { + let Some((device, queue)) = device() else { + return; + }; + // Sampled only so the test can look at what reached it. + let surface = texture( + &device, + wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + ); + let check = texture( + &device, + wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + ); + let blitter = wgpu::util::TextureBlitter::new(&device, wgpu::TextureFormat::Bgra8Unorm); + let mut readback = Readback::default(); + let colors = [ + (1.0, [0, 0, 255, 255]), + // A second screenshot reuses the texture. + (0.0, [255, 0, 0, 255]), + ]; + for (r, bgra) in colors { + let color = wgpu::Color { + r, + g: 0.0, + b: 1.0 - r, + a: 1.0, + }; + let scope = device.push_error_scope(wgpu::ErrorFilter::Validation); + let mut encoder = device.create_command_encoder(&Default::default()); + let frame = readback + .target(&device, &surface, true) + .unwrap_or_else(|| surface.clone()); + clear(&mut encoder, &frame, color); + let buffer = crate::gfx::copy_frame(&device, &mut encoder, &frame, [70, 3]); + readback.present(&device, &mut encoder, &surface); + queue.submit([encoder.finish()]); + if let Some(error) = pollster::block_on(scope.pop()) { + panic!("{error}"); + } + assert_eq!(last_pixel(&device, &buffer), bgra); + let mut encoder = device.create_command_encoder(&Default::default()); + let view = |t: &wgpu::Texture| t.create_view(&Default::default()); + blitter.copy(&device, &mut encoder, &view(&surface), &view(&check)); + let buffer = crate::gfx::copy_frame(&device, &mut encoder, &check, [70, 3]); + queue.submit([encoder.finish()]); + assert_eq!(last_pixel(&device, &buffer), bgra); + } + assert!(!readback.drawn); + } + + /// A surface that can be copied from, or a frame nobody reads back, + /// draws straight into the swapchain image. + #[test] + fn frames_draw_straight_to_a_readable_surface() { + let Some((device, _)) = device() else { + return; + }; + let mut readback = Readback::default(); + let readable = texture( + &device, + wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + ); + assert!(readback.target(&device, &readable, true).is_none()); + let surface = texture(&device, wgpu::TextureUsages::RENDER_ATTACHMENT); + assert!(readback.target(&device, &surface, false).is_none()); + assert!(readback.texture.is_none() && !readback.drawn); + } +} diff --git a/crates/horizon-client/src/gfx/resident.rs b/crates/horizon-client-render/src/gfx/resident.rs similarity index 100% rename from crates/horizon-client/src/gfx/resident.rs rename to crates/horizon-client-render/src/gfx/resident.rs diff --git a/crates/horizon-client/src/gfx/stars.rs b/crates/horizon-client-render/src/gfx/stars.rs similarity index 100% rename from crates/horizon-client/src/gfx/stars.rs rename to crates/horizon-client-render/src/gfx/stars.rs diff --git a/crates/horizon-client/src/gfx/stars.wgsl b/crates/horizon-client-render/src/gfx/stars.wgsl similarity index 100% rename from crates/horizon-client/src/gfx/stars.wgsl rename to crates/horizon-client-render/src/gfx/stars.wgsl diff --git a/crates/horizon-client-render/src/gfx/sunrise_fan.rs b/crates/horizon-client-render/src/gfx/sunrise_fan.rs new file mode 100644 index 0000000000..423f810442 --- /dev/null +++ b/crates/horizon-client-render/src/gfx/sunrise_fan.rs @@ -0,0 +1,131 @@ +//! The sunrise/sunset fan (`SkyRenderer.renderSunriseAndSunset`) as the sky +//! shader tests it. The fan's 16 triangles depend only on the sunrise alpha +//! and the horizon the sun is on, so their vertices are built here once a +//! frame instead of in every sky pixel (#3143: with the per-pixel build, four +//! `sin`/`cos` per triangle, the sky pass took 10.2 ms at dawn at 1080p on an +//! Intel UHD 730 against 0.6 ms by day; now 1.8 ms). +//! +//! Möller-Trumbore from the camera reduces to triple products: with apex A +//! and rim vertices r0, r1, a ray d hits at barycentrics proportional to +//! (`[d, r0, r1]`, `[A, d, r1]`, `[A, r0, d]`), each a dot of d with a +//! constant normal, and their sum S is the negated determinant. A hit needs +//! S on the side of `[A, r0, r1]`, so only the triangles whose rim normals' +//! dots change sign there are tested in full. +use glam::Vec3; + +pub const TRIANGLES: usize = 16; + +/// Per triangle: xyz `r0 × r1` (its dot with a ray is the apex's weight +/// times S), w `[A, r0, r1]` (its sign is S's where the hit lies ahead). +/// Then per rim vertex: xyz `r × A`. +pub fn normals(alpha: f32, sun: Vec3) -> [[f32; 4]; 2 * TRIANGLES] { + let (apex, rim) = vertices(alpha, sun); + let mut out = [[0.0; 4]; 2 * TRIANGLES]; + for i in 0..TRIANGLES { + let (r0, r1) = (rim[i], rim[(i + 1) % TRIANGLES]); + out[i] = r0.cross(r1).extend(apex.dot(r0.cross(r1))).to_array(); + out[TRIANGLES + i] = r0.cross(apex).extend(0.0).to_array(); + } + out +} + +/// The fan's apex and rim in camera space, as vanilla builds them: the +/// fan turned to the horizon the sun sets on (or rises from), its rim +/// tilted by the alpha. +fn vertices(alpha: f32, sun: Vec3) -> (Vec3, [Vec3; TRIANGLES]) { + // sin(sunAngle) < 0 flips the fan to the other horizon; sun.z = -sin(sunAngle). + let flip = if -sun.z < 0.0 { 180f32 } else { 0.0 }; + let (s, c) = (flip + 90.0).to_radians().sin_cos(); + let rotate_x90 = |v: Vec3| Vec3::new(v.x, -v.z, v.y); + let apex = rotate_x90(Vec3::new(-100.0 * s, 100.0 * c, 0.0)); + let rim = std::array::from_fn(|i| { + let a = i as f32 * std::f32::consts::TAU / TRIANGLES as f32; + let l = Vec3::new(a.sin() * 120.0, a.cos() * 120.0, -a.cos() * 40.0 * alpha); + rotate_x90(Vec3::new(l.x * c - l.y * s, l.x * s + l.y * c, l.z)) + }); + (apex, rim) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// terrain.wgsl's `sunrise_weight` over the normals. + fn weight(normals: &[[f32; 4]; 2 * TRIANGLES], d: Vec3) -> f32 { + let n = |i: usize| Vec3::from_slice(&normals[i][..3]); + let mut best = 0.0f32; + let mut k0 = d.dot(n(TRIANGLES)); + for i in 0..TRIANGLES { + let k1 = d.dot(n(TRIANGLES + (i + 1) % TRIANGLES)); + let w = normals[i][3]; + if k0 * w <= 0.0 && k1 * w >= 0.0 { + let apex = d.dot(n(i)); + let s = apex + k1 - k0; + if s.abs() >= 1e-6 + && s * w > 0.0 + && apex * s >= 0.0 + && k1 * s >= 0.0 + && k0 * s <= 0.0 + { + best = best.max(apex / s); + } + } + k0 = k1; + } + best + } + + /// The shader's former per-pixel Möller-Trumbore over the same vertices. + fn reference(alpha: f32, sun: Vec3, d: Vec3) -> f32 { + let (apex, rim) = vertices(alpha, sun); + let mut best = 0.0f32; + for i in 0..TRIANGLES { + let e1 = rim[i] - apex; + let e2 = rim[(i + 1) % TRIANGLES] - apex; + let pv = d.cross(e2); + let det = e1.dot(pv); + if det.abs() < 1e-6 { + continue; + } + let tv = -apex; + let u = tv.dot(pv) / det; + let qv = tv.cross(e1); + let v = d.dot(qv) / det; + let t = e2.dot(qv) / det; + if u >= 0.0 && v >= 0.0 && u + v <= 1.0 && t > 0.0 { + best = best.max(1.0 - u - v); + } + } + best + } + + #[test] + fn normals_weigh_every_ray_as_the_per_pixel_fan_did() { + let mut hits = 0; + for (alpha, sun_z) in [(1.0f32, -0.2f32), (0.6, 0.3), (0.05, -0.9), (0.9, 0.05)] { + let sun = Vec3::new(0.0, (1.0 - sun_z * sun_z).sqrt(), sun_z); + let normals = normals(alpha, sun); + for yaw in 0..180 { + for pitch in -45..=45 { + let (yaw, pitch) = ( + (yaw as f32 * 2.0).to_radians(), + (pitch as f32 * 2.0).to_radians(), + ); + let d = Vec3::new( + yaw.cos() * pitch.cos(), + pitch.sin(), + yaw.sin() * pitch.cos(), + ); + let (got, want) = (weight(&normals, d), reference(alpha, sun, d)); + assert!( + (got - want).abs() < 1e-4, + "{alpha} {sun_z} {d}: {got} vs {want}" + ); + hits += usize::from(want > 0.0); + } + } + } + // The fan covers part of the sky, not none of it. + assert!(hits > 1000, "{hits}"); + } +} diff --git a/crates/horizon-client/src/gfx/timing.rs b/crates/horizon-client-render/src/gfx/timing.rs similarity index 100% rename from crates/horizon-client/src/gfx/timing.rs rename to crates/horizon-client-render/src/gfx/timing.rs diff --git a/crates/horizon-client/src/gfx/translucency.rs b/crates/horizon-client-render/src/gfx/translucency.rs similarity index 100% rename from crates/horizon-client/src/gfx/translucency.rs rename to crates/horizon-client-render/src/gfx/translucency.rs diff --git a/crates/horizon-client/src/gfx/upload.rs b/crates/horizon-client-render/src/gfx/upload.rs similarity index 100% rename from crates/horizon-client/src/gfx/upload.rs rename to crates/horizon-client-render/src/gfx/upload.rs diff --git a/crates/horizon-client-render/src/gfx/view.rs b/crates/horizon-client-render/src/gfx/view.rs new file mode 100644 index 0000000000..173a665d15 --- /dev/null +++ b/crates/horizon-client-render/src/gfx/view.rs @@ -0,0 +1,133 @@ +//! The views a frame is drawn from. A flat screen has one; a headset has +//! one per eye. Every view shares the frame's camera position, so the +//! camera-relative world (section origins, entity offsets) is built once and +//! only each view's rotation and lens differ. + +use glam::Mat4; + +/// The most views one frame draws. +const MAX_VIEWS: usize = 2; + +/// One eye's view of the camera-relative world. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct View { + /// Camera-relative world to eye: a rotation, plus the eye's offset from + /// the camera on a headset. + pub view: Mat4, + /// Reversed-Z perspective: the near plane at depth 1, far at 0. + pub projection: Mat4, + /// Width over height of the image this view fills. + pub aspect: f32, + /// `tan(fov_y / 2)`, for shaders rebuilding view rays from NDC. + pub tan_half_fov: f32, +} + +impl View { + /// The flat-screen view: vanilla's camera rotation and a symmetric + /// vertical field of view. + pub fn screen(yaw: f32, pitch: f32, fov_degrees: f32, aspect: f32, far: f32) -> Self { + Self { + view: crate::camera::view_matrix(yaw, pitch), + projection: super::world_projection(fov_degrees, aspect, far), + aspect, + tan_half_fov: (fov_degrees.to_radians() / 2.0).tan(), + } + } + + /// Clip space from the camera-relative world, with `pose` (view bobbing, + /// nausea spin) between the lens and the rotation. + pub fn view_proj(&self, pose: Mat4) -> Mat4 { + self.projection * pose * self.view + } +} + +/// Every view of a frame, and the one geometry is culled with. +#[derive(Clone, Copy, Debug)] +pub struct ViewSet { + views: [View; MAX_VIEWS], + len: usize, + cull: View, +} + +impl ViewSet { + /// A frame drawn from `view` alone, culled with it. + pub fn single(view: View) -> Self { + Self { + views: [view; MAX_VIEWS], + len: 1, + cull: view, + } + } + + /// The view the HUD, hand and screen-space effects follow. + pub fn primary(&self) -> &View { + &self.views[0] + } + + pub fn views(&self) -> &[View] { + &self.views[..self.len] + } + + /// The view whose frustum covers every other view's. + pub fn cull(&self) -> &View { + &self.cull + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// The formulas `Renderer` used before views existed, kept to pin the + /// screen view to them bit for bit. + fn legacy_view_proj(fov: f32, yaw: f32, pitch: f32, pose: Mat4, aspect: f32, far: f32) -> Mat4 { + super::super::world_projection(fov, aspect, far) + * pose + * crate::camera::view_matrix(yaw, pitch) + } + + fn poses() -> [Mat4; 3] { + [ + Mat4::IDENTITY, + Mat4::from_translation(glam::Vec3::new(0.013, -0.027, 0.0)) + * Mat4::from_rotation_z(0.021), + Mat4::from_rotation_y(0.4) * Mat4::from_scale(glam::Vec3::new(1.1, 0.9, 1.0)), + ] + } + + #[test] + fn the_screen_view_matches_the_legacy_projection_bit_for_bit() { + for (fov, yaw, pitch, aspect, far) in [ + (70.0, 0.0, 0.0, 16.0 / 9.0, 512.0), + (110.0, -137.25, 61.5, 21.0 / 9.0, 8192.0), + (30.0, 359.9, -90.0, 4.0 / 3.0, 96.0), + (82.333, 12.5, 7.75, 1.0, 1024.0), + ] { + let view = View::screen(yaw, pitch, fov, aspect, far); + for pose in poses() { + let old = legacy_view_proj(fov, yaw, pitch, pose, aspect, far); + let new = view.view_proj(pose); + assert_eq!( + old.to_cols_array().map(f32::to_bits), + new.to_cols_array().map(f32::to_bits), + "fov {fov} yaw {yaw} pitch {pitch}" + ); + } + assert_eq!(view.view, crate::camera::view_matrix(yaw, pitch)); + assert_eq!( + view.tan_half_fov.to_bits(), + (fov.to_radians() / 2.0).tan().to_bits() + ); + assert_eq!(view.aspect.to_bits(), aspect.to_bits()); + } + } + + #[test] + fn a_single_view_set_draws_and_culls_with_its_view() { + let view = View::screen(45.0, 10.0, 70.0, 16.0 / 9.0, 512.0); + let set = ViewSet::single(view); + assert_eq!(set.views(), &[view]); + assert_eq!(*set.primary(), view); + assert_eq!(*set.cull(), view); + } +} diff --git a/crates/horizon-client/src/gfx/weather.rs b/crates/horizon-client-render/src/gfx/weather.rs similarity index 100% rename from crates/horizon-client/src/gfx/weather.rs rename to crates/horizon-client-render/src/gfx/weather.rs diff --git a/crates/horizon-client/src/gfx/wgsl_prefix.rs b/crates/horizon-client-render/src/gfx/wgsl_prefix.rs similarity index 100% rename from crates/horizon-client/src/gfx/wgsl_prefix.rs rename to crates/horizon-client-render/src/gfx/wgsl_prefix.rs diff --git a/crates/horizon-client-render/src/gfx/world_post.rs b/crates/horizon-client-render/src/gfx/world_post.rs new file mode 100644 index 0000000000..a923f2facd --- /dev/null +++ b/crates/horizon-client-render/src/gfx/world_post.rs @@ -0,0 +1,564 @@ +//! Resolution scale, FXAA and TAA (Horizon settings): the world renders into its +//! own target at the scaled size, then one full-screen pass copies or +//! anti-aliases it onto the screen, under the hand and HUD. With none on, +//! the world renders straight to the screen. +//! +//! TAA jitters the world's projection by a sub-pixel offset each frame and blends +//! the frame into a history reprojected through the depth buffer: its motion +//! vectors are the camera's, so what moves on its own leans on the neighbourhood +//! clamp alone. + +use glam::{DMat4, DVec3, Mat4, Vec2}; + +/// The TAA history format: 8 bits would band as frames accumulate. +const HISTORY_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float; + +struct WorldTarget { + size: [u32; 2], + color: wgpu::TextureView, + depth: wgpu::TextureView, + group: wgpu::BindGroup, + taa: Option, +} + +/// TAA's two history textures at the output size (the world's, times the upscale), written +/// in turn: `resolve[i]` writes `history[i]` from the world and `history[1 - i]`; `show[i]` +/// copies `history[i]` to the screen. +struct History { + size: [u32; 2], + history: [wgpu::TextureView; 2], + resolve: [wgpu::BindGroup; 2], + show: [wgpu::BindGroup; 2], +} + +/// `world_post.wgsl`'s `Taa`. +#[repr(C)] +#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] +struct TaaUniform { + /// This frame's unjittered clip space to the previous frame's. + reproject: [[f32; 4]; 4], + /// xy: this frame's jitter in clip space; z: 1 when the history is unusable. + jitter: [f32; 4], +} + +/// TAA's GPU resources, absent when the device cannot compile the shader +/// (the OpenGL backend's naga GLSL emitter does not support `textureLoad` +/// from depth textures). +struct TaaResources { + layout: wgpu::BindGroupLayout, + pipeline: wgpu::RenderPipeline, + uniform: wgpu::Buffer, +} + +pub struct Post { + layout: wgpu::BindGroupLayout, + sampler: wgpu::Sampler, + copy: wgpu::RenderPipeline, + fxaa: wgpu::RenderPipeline, + format: wgpu::TextureFormat, + /// World resolution relative to the window (0.5..=2.0). + pub scale: f32, + pub antialias: bool, + pub taa: bool, + /// Output pixels per rendered pixel while TAA is on (1: native): the world renders + /// this much smaller and TAA rebuilds it at the resolution scale's size. + pub upscale: f32, + /// TAA runs this frame (`jitter`): not on the title screen or under a shader pack. + taa_on: bool, + target: Option, + taa_resources: Option, + /// Frames jittered so far (the Halton index), and which history the next resolve writes. + taa_frame: u32, + /// The last TAA frame's unjittered `view_proj` and camera, `None` once a frame ran without it. + taa_previous: Option<(DMat4, DVec3)>, + taa_this_frame: TaaUniform, +} + +impl Post { + pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Horizon post"), + source: wgpu::ShaderSource::Wgsl(include_str!("../world_post.wgsl").into()), + }); + let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("Horizon post bindings"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + }); + let texture = |binding, sample_type| wgpu::BindGroupLayoutEntry { + binding, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }; + let float = wgpu::TextureSampleType::Float { filterable: true }; + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Horizon post"), + bind_group_layouts: &[Some(&layout)], + ..Default::default() + }); + let pipeline = |entry: &str, layout: &wgpu::PipelineLayout, format| { + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Horizon post"), + layout: Some(layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_post"), + compilation_options: Default::default(), + buffers: &[], + }, + primitive: Default::default(), + depth_stencil: None, + multisample: Default::default(), + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some(entry), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview_mask: None, + cache: None, + }) + }; + // The OpenGL backend's GLSL translator does not support textureLoad + // from depth textures: on devices running through OpenGL (such as older + // Intel GPUs on Linux), TAA cannot compile its pipeline. + let taa_resources = { + let taa_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("Horizon TAA bindings"), + entries: &[ + texture(0, float), + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + texture(2, float), + texture(3, wgpu::TextureSampleType::Depth), + wgpu::BindGroupLayoutEntry { + binding: 4, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ], + }); + let taa_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Horizon TAA"), + bind_group_layouts: &[Some(&taa_layout)], + ..Default::default() + }); + let validation = device.push_error_scope(wgpu::ErrorFilter::Validation); + let internal = device.push_error_scope(wgpu::ErrorFilter::Internal); + let taa_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Horizon TAA"), + layout: Some(&taa_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_post"), + compilation_options: Default::default(), + buffers: &[], + }, + primitive: Default::default(), + depth_stencil: None, + multisample: Default::default(), + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_taa"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format: HISTORY_FORMAT, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview_mask: None, + cache: None, + }); + let internal = pollster::block_on(internal.pop()); + match internal.or(pollster::block_on(validation.pop())) { + None => { + let uniform = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Horizon TAA"), + size: std::mem::size_of::() as u64, + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + Some(TaaResources { + layout: taa_layout, + pipeline: taa_pipeline, + uniform, + }) + } + Some(e) => { + log::warn!("Horizon renderer: temporal anti-aliasing unavailable ({e})"); + None + } + } + }; + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label: Some("Horizon post"), + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + ..Default::default() + }); + Self { + copy: pipeline("fs_copy", &pipeline_layout, format), + fxaa: pipeline("fs_fxaa", &pipeline_layout, format), + layout, + sampler, + format, + scale: 1.0, + antialias: false, + taa: false, + upscale: 1.0, + taa_on: false, + target: None, + taa_resources, + taa_frame: 0, + taa_previous: None, + taa_this_frame: bytemuck::Zeroable::zeroed(), + } + } + + /// Whether TAA is supported on this GPU/backend. + pub fn supports_taa(&self) -> bool { + self.taa_resources.is_some() + } + + /// The world's rendered size and the size it is shown at (TAA's history). + fn sizes(&self, window: [u32; 2]) -> ([u32; 2], [u32; 2]) { + let output = window.map(|v| ((v as f32 * self.scale).round() as u32).clamp(1, 8192)); + let upscale = if self.taa_on { self.upscale } else { 1.0 }; + ( + output.map(|v| ((v as f32 / upscale).round() as u32).max(1)), + output, + ) + } + + /// TAA's sub-pixel jitter this frame, as a clip-space matrix to multiply into the + /// world's projection (identity while `on` is false); `view_proj` is the unjittered + /// projection, relative to `camera`. Call it before `prepare`, once per frame. + pub fn jitter(&mut self, window: [u32; 2], on: bool, view_proj: Mat4, camera: DVec3) -> Mat4 { + self.taa_on = on && self.taa && self.taa_resources.is_some(); + if !self.taa_on { + self.taa_previous = None; + return Mat4::IDENTITY; + } + self.taa_frame = self.taa_frame.wrapping_add(1); + let (size, _) = self.sizes(window); + // Halton (2, 3) over 8 frames per output pixel (FSR's count), in rendered + // pixels around the centre. + let phases = (8.0 * self.upscale * self.upscale).ceil() as u32; + let i = self.taa_frame % phases + 1; + let offset = Vec2::new(halton(i, 2), halton(i, 3)) - 0.5; + let jitter = offset * 2.0 / Vec2::new(size[0] as f32, size[1] as f32); + let current = view_proj.as_dmat4(); + let (reproject, reset) = match self.taa_previous { + Some((previous, previous_camera)) => ( + reprojection(previous, previous_camera, current, camera), + 0.0, + ), + None => (DMat4::IDENTITY, 1.0), + }; + self.taa_previous = Some((current, camera)); + self.taa_this_frame = TaaUniform { + reproject: reproject.as_mat4().to_cols_array_2d(), + jitter: [jitter.x, jitter.y, reset, 0.0], + }; + Mat4::from_translation(jitter.extend(0.0)) + } + + /// The world's colour and depth targets this frame, or `None` to draw + /// it straight to the screen. + /// `always`: a target even at scale 1 without anti-aliasing (a shader pack reads it). + pub fn prepare( + &mut self, + device: &wgpu::Device, + window: [u32; 2], + always: bool, + ) -> Option<(wgpu::TextureView, wgpu::TextureView)> { + if self.scale == 1.0 && !self.antialias && !self.taa_on && !always { + self.target = None; + return None; + } + let (size, output) = self.sizes(window); + let history_size = self.taa_on.then_some(output); + if self + .target + .as_ref() + .is_none_or(|t| t.size != size || t.taa.as_ref().map(|h| h.size) != history_size) + { + let make_tex = |label, size: [u32; 2], format, usage| { + device.create_texture(&wgpu::TextureDescriptor { + label: Some(label), + size: wgpu::Extent3d { + width: size[0], + height: size[1], + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage, + view_formats: &[], + }) + }; + let sampled = + wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING; + let color = make_tex("Scaled world", size, self.format, sampled) + .create_view(&Default::default()); + // TEXTURE_BINDING: occlusion culling and TAA read it. + let depth = make_tex( + "Scaled world depth", + size, + wgpu::TextureFormat::Depth32Float, + sampled, + ) + .create_view(&Default::default()); + let group = self.bind(device, &color); + let taa = history_size.and_then(|output| { + let res = self.taa_resources.as_ref()?; + let history = [0, 1].map(|_| { + make_tex("TAA history", output, HISTORY_FORMAT, sampled) + .create_view(&Default::default()) + }); + let resolve = [0, 1].map(|i| { + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("Horizon TAA"), + layout: &res.layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&color), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&self.sampler), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView(&history[1 - i]), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(&depth), + }, + wgpu::BindGroupEntry { + binding: 4, + resource: res.uniform.as_entire_binding(), + }, + ], + }) + }); + let show = [0, 1].map(|i| self.bind(device, &history[i])); + Some(History { + size: output, + history, + resolve, + show, + }) + }); + // New textures: no history yet. + self.taa_this_frame.jitter[2] = 1.0; + self.target = Some(WorldTarget { + size, + color, + depth, + group, + taa, + }); + } + self.target + .as_ref() + .map(|t| (t.color.clone(), t.depth.clone())) + } + + /// The size of the world's own target, when it has one. + pub fn size(&self) -> Option<[u32; 2]> { + self.target.as_ref().map(|t| t.size) + } + + /// Bindings that copy (or anti-alias) `view` onto the screen in `finish_with`. + pub fn bind(&self, device: &wgpu::Device, view: &wgpu::TextureView) -> wgpu::BindGroup { + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("Horizon post"), + layout: &self.layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&self.sampler), + }, + ], + }) + } + + /// Draw the scaled world onto `screen`, through TAA when this frame's `jitter` had it on. + pub fn finish( + &self, + queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, + screen: &wgpu::TextureView, + ) { + let Some(target) = &self.target else { return }; + let Some(taa) = target.taa.as_ref().filter(|_| self.taa_previous.is_some()) else { + self.finish_with(encoder, screen, &target.group); + return; + }; + let Some(res) = &self.taa_resources else { + self.finish_with(encoder, screen, &target.group); + return; + }; + let i = (self.taa_frame % 2) as usize; + queue.write_buffer(&res.uniform, 0, bytemuck::bytes_of(&self.taa_this_frame)); + let attachments = [Some(wgpu::RenderPassColorAttachment { + view: &taa.history[i], + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })]; + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Horizon TAA"), + color_attachments: &attachments, + ..Default::default() + }); + pass.set_pipeline(&res.pipeline); + pass.set_bind_group(0, &taa.resolve[i], &[]); + pass.draw(0..3, 0..1); + drop(pass); + self.finish_with(encoder, screen, &taa.show[i]); + } + + /// Draw what `group` binds (`bind`) onto `screen`. + pub fn finish_with( + &self, + encoder: &mut wgpu::CommandEncoder, + screen: &wgpu::TextureView, + group: &wgpu::BindGroup, + ) { + let attachments = [Some(wgpu::RenderPassColorAttachment { + view: screen, + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })]; + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Horizon post"), + color_attachments: &attachments, + ..Default::default() + }); + pass.set_pipeline(if self.antialias { + &self.fxaa + } else { + &self.copy + }); + pass.set_bind_group(0, group, &[]); + pass.draw(0..3, 0..1); + } +} + +/// This frame's clip space to the previous frame's, for camera-relative projections: +/// a point `camera - previous_camera` further from the previous camera than from this one. +fn reprojection(previous: DMat4, previous_camera: DVec3, current: DMat4, camera: DVec3) -> DMat4 { + previous * DMat4::from_translation(camera - previous_camera) * current.inverse() +} + +/// The radical inverse of `i` in `base`: a low-discrepancy sequence in 0..1. +fn halton(mut i: u32, base: u32) -> f32 { + let (mut f, mut r) = (1.0, 0.0); + while i > 0 { + f /= base as f32; + r += f * (i % base) as f32; + i /= base; + } + r +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reprojection_finds_where_a_block_was_last_frame() { + let projection = DMat4::perspective_rh(1.2, 16.0 / 9.0, 1024.0, 0.05); + let previous = projection * DMat4::from_rotation_y(0.3); + let current = projection * DMat4::from_rotation_y(0.35); + let (previous_camera, camera) = (DVec3::new(10.0, 64.0, 5.0), DVec3::new(10.5, 64.2, 4.0)); + let block = DVec3::new(14.0, 66.0, -8.0); + let clip = |m: DMat4, eye: DVec3| m * (block - eye).extend(1.0); + let now = clip(current, camera); + let then = reprojection(previous, previous_camera, current, camera) * (now / now.w); + let expected = clip(previous, previous_camera); + assert!((then.truncate() / then.w - expected.truncate() / expected.w).length() < 1e-9); + } + + #[test] + fn jitter_stays_inside_the_pixel() { + for i in 1..=8 { + let (x, y) = (halton(i, 2), halton(i, 3)); + assert!((0.0..1.0).contains(&x) && (0.0..1.0).contains(&y)); + } + assert_eq!( + [halton(1, 2), halton(2, 2), halton(3, 2)], + [0.5, 0.25, 0.75] + ); + } + + fn test_device() -> Option<(wgpu::Device, wgpu::Queue)> { + let instance = + wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle().with_env()); + let adapter = pollster::block_on(instance.request_adapter(&Default::default())).ok()?; + pollster::block_on(adapter.request_device(&Default::default())).ok() + } + + #[test] + fn world_post_pipelines_compile_or_fall_back() { + let Some((device, _)) = test_device() else { + return; + }; + let post = Post::new(&device, wgpu::TextureFormat::Rgba8Unorm); + // Copy and FXAA pipelines always compile; TAA compiles if supported by backend. + let _ = post.supports_taa(); + } +} diff --git a/crates/horizon-client/src/graphics.rs b/crates/horizon-client-render/src/graphics.rs similarity index 75% rename from crates/horizon-client/src/graphics.rs rename to crates/horizon-client-render/src/graphics.rs index f3e4c94872..5b64cf5883 100644 --- a/crates/horizon-client/src/graphics.rs +++ b/crates/horizon-client-render/src/graphics.rs @@ -2,13 +2,39 @@ //! `horizon-graphics.txt` next to `options.txt` so that file stays exactly //! vanilla's. Every default reproduces vanilla's rendering; only choices the //! player makes here depart from it. -use std::{fs, path::Path}; +use std::{ + fs, + path::Path, + sync::atomic::{AtomicU32, Ordering}, +}; pub const FILE: &str = "horizon-graphics.txt"; /// Written beside it before HDR output is switched on and removed once the first HDR frame is /// presented. Found at the next start, it means HDR output took the game down, so HDR starts off. pub const HDR_MARKER: &str = "horizon-hdr-pending"; +/// What the system reports for the display HDR output goes to, in nits in HDR output's +/// scale: where its SDR white sits (the desktop's white) and the peak. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Reported { + pub paper_white: u32, + pub peak: u32, +} + +static REPORTED: [AtomicU32; 2] = [AtomicU32::new(0), AtomicU32::new(0)]; + +/// Sets what the system reports (the renderer, when it opens its surface). +pub fn report(reported: Option) { + let (paper_white, peak) = reported.map_or((0, 0), |r| (r.paper_white, r.peak)); + REPORTED[0].store(paper_white, Ordering::Relaxed); + REPORTED[1].store(peak, Ordering::Relaxed); +} +/// What the system reports, if it does. +pub fn reported() -> Option { + let [paper_white, peak] = REPORTED.each_ref().map(|n| n.load(Ordering::Relaxed)); + (peak > 0).then_some(Reported { paper_white, peak }) +} + /// Simple-level presets. `Custom` leaves every option as the player set it. #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum Preset { @@ -96,9 +122,9 @@ impl PresentMode { } } -/// The graphics API the renderer runs on. Only Windows has a choice: the -/// other systems always use their one backend (Vulkan on Linux, Metal on -/// macOS). Applies at the next start. +/// The graphics API the renderer runs on. Windows picks Vulkan, DirectX 12 or +/// OpenGL; Linux picks Vulkan or OpenGL; macOS always uses its one backend +/// (Metal). Applies at the next start. #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum GraphicsApi { /// Vulkan, then DirectX 12 (Windows), then OpenGL when neither has a surface, adapter or device. @@ -116,6 +142,26 @@ impl GraphicsApi { GraphicsApi::Dx12, GraphicsApi::OpenGl, ]; + /// The APIs this system offers, in the order the setting cycles through + /// them: DirectX 12 is Windows' third backend, and macOS has no choice. + pub fn choices() -> &'static [GraphicsApi] { + const WINDOWS: [GraphicsApi; 4] = [ + GraphicsApi::Auto, + GraphicsApi::Vulkan, + GraphicsApi::Dx12, + GraphicsApi::OpenGl, + ]; + const LINUX: [GraphicsApi; 3] = + [GraphicsApi::Auto, GraphicsApi::Vulkan, GraphicsApi::OpenGl]; + const MACOS: [GraphicsApi; 1] = [GraphicsApi::Auto]; + if cfg!(windows) { + &WINDOWS + } else if cfg!(target_os = "macos") { + &MACOS + } else { + &LINUX + } + } pub fn name(self) -> &'static str { match self { GraphicsApi::Auto => "auto", @@ -200,6 +246,61 @@ impl FarDetail { } } +/// TAA upscaling: the world renders this much smaller than the resolution scale's size, +/// and TAA builds the full-size picture from the jittered frames (FSR's and DLSS's ratios). +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum Upscaling { + #[default] + Native, + Quality, + Balanced, + Performance, + UltraPerformance, +} +impl Upscaling { + pub const ALL: [Upscaling; 5] = [ + Upscaling::Native, + Upscaling::Quality, + Upscaling::Balanced, + Upscaling::Performance, + Upscaling::UltraPerformance, + ]; + pub fn name(self) -> &'static str { + match self { + Upscaling::Native => "native", + Upscaling::Quality => "quality", + Upscaling::Balanced => "balanced", + Upscaling::Performance => "performance", + Upscaling::UltraPerformance => "ultraPerformance", + } + } + pub fn label(self) -> &'static str { + match self { + Upscaling::Native => "Native", + Upscaling::Quality => "Quality", + Upscaling::Balanced => "Balanced", + Upscaling::Performance => "Performance", + Upscaling::UltraPerformance => "Ultra Performance", + } + } + /// Output pixels per rendered pixel, along each axis. + pub fn ratio(self) -> f32 { + match self { + Upscaling::Native => 1.0, + Upscaling::Quality => 1.5, + Upscaling::Balanced => 1.7, + Upscaling::Performance => 2.0, + Upscaling::UltraPerformance => 3.0, + } + } + fn parse(s: &str) -> Self { + Self::ALL + .into_iter() + .find(|u| u.name() == s) + .unwrap_or_default() + } +} + static REDUCED_SUGGESTED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); /// Whether the settings suggest Reduced far detail: set when the renderer /// starts, on the GPUs `lod_capped` names (far terrain slows them most). @@ -210,6 +311,16 @@ pub fn reduced_far_detail_suggested() -> bool { REDUCED_SUGGESTED.load(std::sync::atomic::Ordering::Relaxed) } +static HDR_OFFERED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); +/// Whether the display path offers HDR output (HDR10 or scRGB): set when the renderer +/// opens its surface; the settings open HDR's screen only then. +pub fn offer_hdr(on: bool) { + HDR_OFFERED.store(on, std::sync::atomic::Ordering::Relaxed); +} +pub fn hdr_offered() -> bool { + HDR_OFFERED.load(std::sync::atomic::Ordering::Relaxed) +} + /// The farthest LOD Distance, in chunks, on integrated GPUs other than /// Apple's: they share the CPU's memory bandwidth, and past it far terrain /// costs them more frame time than it is worth. @@ -262,12 +373,24 @@ pub struct Graphics { pub resolution_scale: u32, /// FXAA over the world. pub fxaa: bool, + /// TAA over the world (FXAA is off while it is on). + pub taa: bool, + /// TAA upscaling; anything but `Native` comes with `taa` on. + pub upscaling: Upscaling, /// HDR output (HDR10, or scRGB), where the display path offers it. pub hdr: bool, - /// HDR paper white (where SDR white sits), in nits. + /// HDR paper white (where SDR white sits), in nits; 0 is Auto: the desktop's SDR + /// white, as the system reports it ([`Graphics::paper_white_nits`]). pub hdr_paper_white: u32, - /// HDR peak brightness the world's highlights reach, in nits. + /// HDR peak brightness the world's highlights reach, in nits; 0 is Auto: + /// what the system reports for the display ([`Graphics::peak_nits`]). pub hdr_peak: u32, + /// HDR black level: the display's darkest black, in hundredths of a nit (0 for + /// an OLED's true black). 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, /// Battery mode: at most 60 frames per second in a world and 30 in menus /// or behind other windows, and half the worker threads. Off by default; /// it changes when frames are drawn, never what they show or the ticks. @@ -299,9 +422,13 @@ impl Default for Graphics { overlay: Overlay::Off, resolution_scale: 100, fxaa: false, + taa: false, + upscaling: Upscaling::Native, hdr: false, - hdr_paper_white: 200, - hdr_peak: 1000, + hdr_paper_white: 0, + hdr_peak: 0, + hdr_black_level: 0, + hdr_ui_brightness: 100, battery: false, api: GraphicsApi::Auto, shader_pack: String::new(), @@ -361,9 +488,25 @@ impl Graphics { "workers" => g.workers = number(g.workers).min(64), "resolutionScale" => g.resolution_scale = number(g.resolution_scale).clamp(50, 200), "fxaa" => g.fxaa = flag(g.fxaa), + "taa" => g.taa = flag(g.taa), + "upscaling" => g.upscaling = Upscaling::parse(value), "hdr" => g.hdr = flag(g.hdr), - "hdrPaperWhite" => g.hdr_paper_white = number(g.hdr_paper_white).clamp(80, 480), - "hdrPeak" => g.hdr_peak = number(g.hdr_peak).clamp(200, 2000), + "hdrPaperWhite" => { + g.hdr_paper_white = match number(g.hdr_paper_white) { + 0 => 0, + n => n.clamp(80, 480), + } + } + "hdrPeak" => { + g.hdr_peak = match number(g.hdr_peak) { + 0 => 0, + n => n.clamp(200, 2000), + } + } + "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) + } "batteryMode" => g.battery = flag(g.battery), "graphicsApi" => g.api = GraphicsApi::from_name(value).unwrap_or_default(), "pickedShaderPack" => g.shader_pack = value.to_owned(), @@ -381,7 +524,7 @@ impl Graphics { } pub fn serialize(&self) -> String { format!( - "preset:{}\nlodDistance:{}\nfarDetail:{}\ncaveCulling:{}\nentityCulling:{}\nchunkUpdates:{}\nuploadBudgetKiB:{}\nsky:{}\nfog:{}\nweather:{}\nanimatedTextures:{}\npresentMode:{}\nframePacing:{}\nworkers:{}\noverlay:{}\nresolutionScale:{}\nfxaa:{}\nhdr:{}\nhdrPaperWhite:{}\nhdrPeak:{}\nbatteryMode:{}\ngraphicsApi:{}\npickedShaderPack:{}\n", + "preset:{}\nlodDistance:{}\nfarDetail:{}\ncaveCulling:{}\nentityCulling:{}\nchunkUpdates:{}\nuploadBudgetKiB:{}\nsky:{}\nfog:{}\nweather:{}\nanimatedTextures:{}\npresentMode:{}\nframePacing:{}\nworkers:{}\noverlay:{}\nresolutionScale:{}\nfxaa:{}\ntaa:{}\nupscaling:{}\nhdr:{}\nhdrPaperWhite:{}\nhdrPeak:{}\nhdrBlackLevel:{}\nhdrUiBrightness:{}\nbatteryMode:{}\ngraphicsApi:{}\npickedShaderPack:{}\n", self.preset.name(), self.lod_distance, self.far_detail.name(), @@ -406,9 +549,13 @@ impl Graphics { }, self.resolution_scale, bool_text(self.fxaa), + bool_text(self.taa), + self.upscaling.name(), bool_text(self.hdr), self.hdr_paper_white, self.hdr_peak, + self.hdr_black_level, + self.hdr_ui_brightness, bool_text(self.battery), self.api.name(), self.shader_pack, @@ -417,6 +564,22 @@ impl Graphics { pub fn save(&self, dir: &Path) { let _ = fs::write(dir.join(FILE), self.serialize()); } + /// Paper white, in nits: the player's, or on Auto the desktop's SDR white as the + /// system reports it, else 203 (BT.2408's reference white). + pub fn paper_white_nits(&self) -> u32 { + match self.hdr_paper_white { + 0 => reported().map_or(203, |r| r.paper_white), + n => n, + } + } + /// The peak the world's highlights reach, in nits: the player's, or on Auto + /// what the system reports, else 1000. + pub fn peak_nits(&self) -> u32 { + match self.hdr_peak { + 0 => reported().map_or(1000, |r| r.peak), + n => n, + } + } /// Worker threads for chunk generation and meshing. pub fn worker_threads(&self) -> usize { if self.workers > 0 { @@ -486,6 +649,21 @@ pub fn apply_preset(preset: Preset, s: &mut crate::settings::Settings) { mod tests { use super::*; + #[test] + fn the_api_choice_matches_the_system() { + let choices = GraphicsApi::choices(); + if cfg!(windows) { + assert_eq!(choices, GraphicsApi::ALL); + } else if cfg!(target_os = "macos") { + assert_eq!(choices, [GraphicsApi::Auto]); + } else { + assert_eq!( + choices, + [GraphicsApi::Auto, GraphicsApi::Vulkan, GraphicsApi::OpenGl] + ); + } + } + #[test] fn round_trips() { let g = Graphics { @@ -506,15 +684,24 @@ mod tests { overlay: Overlay::Graph, resolution_scale: 75, fxaa: true, + taa: true, + upscaling: Upscaling::Balanced, hdr: true, hdr_paper_white: 240, hdr_peak: 1400, + hdr_black_level: 12, + hdr_ui_brightness: 60, battery: true, api: GraphicsApi::Dx12, shader_pack: "Golden Hour".into(), }; assert_eq!(Graphics::parse(&g.serialize()), g); assert_eq!(Graphics::parse(""), Graphics::default()); + // 0 is Auto; anything else is a peak the slider reaches. + assert_eq!(Graphics::parse("hdrPeak:0\n").hdr_peak, 0); + assert_eq!(Graphics::parse("hdrPeak:50\n").hdr_peak, 200); + assert_eq!(Graphics::parse("hdrPaperWhite:0\n").hdr_paper_white, 0); + assert_eq!(Graphics::parse("hdrPaperWhite:5\n").hdr_paper_white, 80); // The old menus' Simple / Advanced choice is ignored, and so is the // shader pack older versions saved. assert_eq!(Graphics::parse("level:advanced\n"), Graphics::default()); diff --git a/crates/horizon-client/src/graphics_style.rs b/crates/horizon-client-render/src/graphics_style.rs similarity index 98% rename from crates/horizon-client/src/graphics_style.rs rename to crates/horizon-client-render/src/graphics_style.rs index 62979903b1..bf0ffbc0cd 100644 --- a/crates/horizon-client/src/graphics_style.rs +++ b/crates/horizon-client-render/src/graphics_style.rs @@ -130,8 +130,9 @@ impl Style { label: &Text, value: f32, hover: bool, + active: bool, ) { - ui::slider(batch, atlas, x, y, w, label, value, hover, true); + ui::slider(batch, atlas, x, y, w, label, value, hover && active, active); } /// The cost tag and the description beside a control, wrapped to two lines. pub fn description( diff --git a/crates/horizon-client/src/graphics_ui.rs b/crates/horizon-client-render/src/graphics_ui.rs similarity index 69% rename from crates/horizon-client/src/graphics_ui.rs rename to crates/horizon-client-render/src/graphics_ui.rs index cb097c114a..3bb558ab1d 100644 --- a/crates/horizon-client/src/graphics_ui.rs +++ b/crates/horizon-client-render/src/graphics_ui.rs @@ -1,8 +1,8 @@ -//! Horizon's graphics settings screens, Simple and Advanced: which settings +//! Horizon's graphics settings screens, Simple, Advanced and HDR: which settings //! they show, in what groups, with what description and cost, and their //! input. How they look comes from `graphics_style`. use crate::{ - graphics::{FarDetail, GraphicsApi, Overlay, Pacing, PresentMode, Preset}, + graphics::{FarDetail, GraphicsApi, Overlay, Pacing, PresentMode, Preset, Upscaling}, graphics_style::{Cost, STYLE}, hud::{HudAtlas, HudBatch}, settings::{Opt, Settings}, @@ -14,6 +14,8 @@ use crate::{ pub enum Level { Simple, Advanced, + /// HDR output's own settings, opened from Advanced while HDR is on. + Hdr, } /// Horizon's own settings (`graphics::Graphics`). @@ -36,9 +38,14 @@ pub enum H { Overlay, ResolutionScale, Antialiasing, + Upscaling, Hdr, - HdrPaperWhite, + /// Opens `Level::Hdr`. + HdrSettings, + HdrBlackLevel, HdrPeak, + HdrPaperWhite, + HdrUiBrightness, BatteryMode, GraphicsApi, } @@ -119,6 +126,34 @@ pub fn rows(level: Level) -> Vec { ); let mut rows = match level { Level::Simple => vec![preset, render, lod, far, fov, fps, vsync, gui], + Level::Hdr => vec![ + Row::Header("HDR"), + h( + H::Hdr, + None, + "Lava, fire and sunlight shine brighter than white. Needs an HDR display.", + ), + h( + H::HdrBlackLevel, + None, + "Your display's darkest black. 0 for OLED; LCDs glow a little.", + ), + h( + H::HdrPeak, + None, + "How bright highlights get. Auto: the peak your system reports.", + ), + h( + H::HdrPaperWhite, + None, + "How bright white paper and menus look. Auto: your desktop's white.", + ), + h( + H::HdrUiBrightness, + None, + "How bright the HUD and menus are, against paper white.", + ), + ], Level::Advanced => vec![ Row::Header("Rendering"), render, @@ -221,23 +256,18 @@ pub fn rows(level: Level) -> Vec { h( H::Antialiasing, Medium, - "FXAA smooths jagged edges of the world; slightly softer image.", + "Smooths jagged edges. FXAA: softer. TAA: smoothest, moving things may trail.", ), h( - H::Hdr, + H::Upscaling, + High, + "Renders the world smaller and rebuilds it with TAA. Faster, softer.", + ), + h( + H::HdrSettings, None, "HDR output on an HDR display: lava, fire and sunlight shine brighter.", ), - h( - H::HdrPaperWhite, - None, - "HDR: how bright white paper and menus look. Match your desktop.", - ), - h( - H::HdrPeak, - None, - "HDR: how bright the brightest highlights get. Your display's peak.", - ), h( H::PresentMode, None, @@ -276,7 +306,7 @@ pub fn rows(level: Level) -> Vec { h( H::GraphicsApi, None, - "Vulkan, or DirectX 12 where Vulkan fails. Next start.", + "Vulkan, then DirectX 12 (Windows) or OpenGL (Linux) where it fails. Next start.", ), h( H::UploadBudget, @@ -291,8 +321,8 @@ pub fn rows(level: Level) -> Vec { ), ], }; - // Only Windows has more than one graphics API. - if !cfg!(windows) { + // Only Windows and Linux have more than one graphics API. + if cfg!(target_os = "macos") { rows.retain( |row| !matches!(row, Row::Setting(s) if s.item == Item::Horizon(H::GraphicsApi)), ); @@ -303,9 +333,13 @@ pub fn rows(level: Level) -> Vec { const UPLOAD_STEPS: [u32; 8] = [0, 256, 512, 1024, 2048, 4096, 8192, 16384]; /// Horizon's graphics screens supplement the vanilla options on Video Settings. -fn extra_rows(level: Level) -> Vec { +/// Upscaling is only there while TAA is, which does it. +fn extra_rows(level: Level, settings: &Settings) -> Vec { let mut rows = rows(level); rows.retain(|row| !matches!(row, Row::Setting(s) if matches!(s.item, Item::Vanilla(_)))); + if !settings.horizon.taa { + rows.retain(|row| !matches!(row, Row::Setting(s) if s.item == Item::Horizon(H::Upscaling))); + } // Drop headings whose settings were all on the parent screens. let mut has_setting = false; rows.reverse(); @@ -341,8 +375,18 @@ impl H { } H::Workers => Some(g.workers as f32 / max_workers() as f32), H::ResolutionScale => Some((g.resolution_scale as f32 - 50.0) / 150.0), - H::HdrPaperWhite => Some((g.hdr_paper_white as f32 - 80.0) / 400.0), - H::HdrPeak => Some((g.hdr_peak as f32 - 200.0) / 1800.0), + // Auto, then 80..480 by 10. + H::HdrPaperWhite => Some(match g.hdr_paper_white { + 0 => 0.0, + n => (n as f32 - 70.0) / 410.0, + }), + // Auto, then 200..2000 by 50. + H::HdrPeak => Some(match g.hdr_peak { + 0 => 0.0, + n => (n as f32 - 150.0) / 1850.0, + }), + 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, } } @@ -383,10 +427,33 @@ impl H { n => labelled("Worker Threads", n), }, H::ResolutionScale => labelled("Resolution Scale", format!("{}%", g.resolution_scale)), - H::Antialiasing => labelled("Anti-aliasing", if g.fxaa { "FXAA" } else { "Off" }), + H::Antialiasing => labelled( + "Anti-aliasing", + match (g.fxaa, g.taa) { + (_, true) => "TAA", + (true, false) => "FXAA", + (false, false) => "Off", + }, + ), + H::Upscaling => labelled("Upscaling", g.upscaling.label()), H::Hdr => labelled("HDR", on_off(g.hdr)), - H::HdrPaperWhite => labelled("Paper White", format!("{} nits", g.hdr_paper_white)), - H::HdrPeak => labelled("Peak Brightness", format!("{} nits", g.hdr_peak)), + H::HdrSettings => Text::literal("HDR Settings..."), + H::HdrPaperWhite => match g.hdr_paper_white { + 0 => labelled( + "Paper White", + format!("Auto ({} nits)", g.paper_white_nits()), + ), + n => labelled("Paper White", format!("{n} nits")), + }, + H::HdrPeak => match g.hdr_peak { + 0 => labelled("Peak Brightness", format!("Auto ({} nits)", g.peak_nits())), + n => labelled("Peak Brightness", format!("{n} nits")), + }, + H::HdrBlackLevel => labelled( + "Black Level", + format!("{:.2} nits", g.hdr_black_level as f32 / 100.0), + ), + H::HdrUiBrightness => labelled("UI Brightness", format!("{}%", g.hdr_ui_brightness)), H::BatteryMode => labelled("Battery Mode", on_off(g.battery)), H::GraphicsApi => labelled("Graphics API", g.api.label()), H::Overlay => labelled( @@ -409,8 +476,20 @@ 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::HdrPaperWhite => g.hdr_paper_white = (80.0 + (v * 40.0).round() * 10.0) as u32, - H::HdrPeak => g.hdr_peak = (200.0 + (v * 36.0).round() * 50.0) as u32, + H::HdrPaperWhite => { + g.hdr_paper_white = match (v * 41.0).round() as u32 { + 0 => 0, + step => 70 + step * 10, + } + } + H::HdrPeak => { + g.hdr_peak = match (v * 37.0).round() as u32 { + 0 => 0, + step => 150 + step * 50, + } + } + 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, _ => {} } } @@ -441,10 +520,25 @@ impl H { H::Weather => g.weather = !g.weather, H::AnimatedTextures => g.animated_textures = !g.animated_textures, H::PresentMode => g.present_mode = step(&PresentMode::ALL, g.present_mode, back), - H::Antialiasing => g.fxaa = !g.fxaa, + // Off, FXAA, TAA. + H::Antialiasing => { + let i = if g.taa { 2 } else { usize::from(g.fxaa) }; + let i = (i + if back { 2 } else { 1 }) % 3; + (g.fxaa, g.taa) = (i == 1, i == 2); + if !g.taa { + g.upscaling = Upscaling::Native; + } + } + // Upscaling is TAA's: picking a ratio turns it on. + H::Upscaling => { + g.upscaling = step(&Upscaling::ALL, g.upscaling, back); + if g.upscaling != Upscaling::Native { + (g.fxaa, g.taa) = (false, true); + } + } H::Hdr => g.hdr = !g.hdr, H::BatteryMode => g.battery = !g.battery, - H::GraphicsApi => g.api = step(&GraphicsApi::ALL, g.api, back), + H::GraphicsApi => g.api = step(GraphicsApi::choices(), g.api, back), H::Pacing => { g.pacing = match g.pacing { Pacing::Efficient => Pacing::Precise, @@ -471,6 +565,25 @@ impl Item { fn available(self) -> bool { !matches!(self, Item::Vanilla(o) if !o.available()) } + /// Why a control can't be used now, when it can't: HDR's screen opens only where + /// the display offers HDR output, whatever `horizon-graphics.txt` says (a copied game + /// folder, a display swapped for an SDR one: the renderer leaves HDR off there, and + /// the saved values wait for an HDR display), and its sliders do something only + /// while it is on. + fn blocked(self, s: &Settings) -> Option<&'static str> { + if !self.available() { + return Some("This feature is not yet available in Horizon."); + } + match self { + Item::Horizon(H::HdrSettings | H::Hdr) if !crate::graphics::hdr_offered() => { + Some("Your display or system doesn't offer HDR output.") + } + Item::Horizon( + H::HdrBlackLevel | H::HdrPeak | H::HdrPaperWhite | H::HdrUiBrightness, + ) if !s.horizon.hdr => Some("Turn HDR on to use it."), + _ => None, + } + } /// On/off settings and their value. pub fn switch(self, s: &Settings) -> Option { let g = &s.horizon; @@ -481,7 +594,6 @@ impl Item { Item::Horizon(H::Fog) => g.fog, Item::Horizon(H::Weather) => g.weather, Item::Horizon(H::AnimatedTextures) => g.animated_textures, - Item::Horizon(H::Antialiasing) => g.fxaa, Item::Horizon(H::Hdr) => g.hdr, Item::Horizon(H::BatteryMode) => g.battery, Item::Vanilla(Opt::ViewBobbing) => s.view_bobbing, @@ -570,8 +682,10 @@ impl Item { pub enum Action { ButtonClick, Changed, + SliderDrag, OpenAdvanced, OpenSimple, + OpenHdr, Done, } @@ -591,20 +705,25 @@ struct Widget { #[derive(Default)] pub struct GraphicsUi { - scroll: f32, + /// The list's scroll amount and its held scrollbar (`AbstractScrollArea`). + scroll: ui::Scroll, dragging: Option, } impl GraphicsUi { pub fn reset(&mut self) { - self.scroll = 0.0; + self.scroll = ui::Scroll::default(); self.dragging = None; } + /// The scrollbar's column: right of the descriptions. + fn scrollbar_x(w: f32) -> f32 { + STYLE.row_x(w) + STYLE.control_width + STYLE.gap + STYLE.text_width(w) + 6.0 + } fn bounds(h: f32) -> (f32, f32) { (STYLE.band, h - STYLE.band) } - fn content_height(level: Level) -> f32 { - extra_rows(level).iter().fold(4.0, |height, row| { + fn content_height(level: Level, s: &Settings) -> f32 { + extra_rows(level, s).iter().fold(4.0, |height, row| { height + match row { Row::Header(_) => STYLE.header_height, @@ -616,17 +735,18 @@ impl GraphicsUi { fn layout( &mut self, level: Level, + s: &Settings, w: f32, h: f32, ) -> (Vec, Vec<(f32, &'static str)>, f32) { - let content = Self::content_height(level); + let content = Self::content_height(level, s); self.clamp_scroll(h, content); let (top, _) = Self::bounds(h); let x = STYLE.row_x(w); - let mut y = top + 4.0 - self.scroll; + let mut y = top + 4.0 - self.scroll.amount; let mut widgets = Vec::new(); let mut headers = Vec::new(); - for row in extra_rows(level) { + for row in extra_rows(level, s) { match row { Row::Header(name) => { headers.push((y, name)); @@ -649,6 +769,17 @@ impl GraphicsUi { let other = match level { Level::Simple => Action::OpenAdvanced, Level::Advanced => Action::OpenSimple, + // One Done, back to Advanced. + Level::Hdr => { + widgets.push(Widget { + x: cx - 100.0, + y: by, + w: 200.0, + target: Target::Button(Action::Done), + list: false, + }); + return (widgets, headers, content); + } }; widgets.push(Widget { x: cx - 155.0, @@ -668,7 +799,7 @@ impl GraphicsUi { } fn clamp_scroll(&mut self, h: f32, content: f32) { let (top, bottom) = Self::bounds(h); - self.scroll = self.scroll.clamp(0.0, (content - (bottom - top)).max(0.0)); + self.scroll.clamp(top, bottom, content); } pub fn draw( &mut self, @@ -680,7 +811,7 @@ impl GraphicsUi { in_world: bool, ) { let (w, h) = (batch.width, batch.height); - let (widgets, headers, content) = self.layout(level, w, h); + let (widgets, headers, content) = self.layout(level, s, w, h); let (top, bottom) = Self::bounds(h); STYLE.background(batch, atlas, top, bottom, in_world); let shown = |y: f32, height: f32| y >= top && y + height <= bottom; @@ -690,7 +821,7 @@ impl GraphicsUi { STYLE.header(batch, atlas, &Text::literal(name), x, y); } } - let descriptions: Vec<(Cost, &'static str)> = extra_rows(level) + let descriptions: Vec<(Cost, &'static str)> = extra_rows(level, s) .into_iter() .filter_map(|r| match r { Row::Setting(s) => Some((s.cost, s.text)), @@ -710,12 +841,10 @@ impl GraphicsUi { continue; } let label = item.label(s); - let live = item.available(); - if hover && !live { - tooltip = Some(atlas.font.split( - &Text::literal("This feature is not yet available in Horizon."), - 170.0, - )); + let blocked = item.blocked(s); + let live = blocked.is_none(); + if hover && let Some(why) = blocked { + tooltip = Some(atlas.font.split(&Text::literal(why), 170.0)); } match item.slider(s) { Some(value) => STYLE.slider( @@ -725,6 +854,7 @@ impl GraphicsUi { &label, value, hover || self.dragging == Some(item), + live, ), None => STYLE.button( batch, @@ -765,15 +895,16 @@ impl GraphicsUi { ui::scrollbar( batch, atlas, - x + STYLE.control_width + STYLE.gap + STYLE.text_width(w) + 6.0, + Self::scrollbar_x(w), top, bottom, content, - self.scroll, + self.scroll.amount, ); let title = match level { Level::Simple => "Graphics", Level::Advanced => "Graphics: Advanced", + Level::Hdr => "Graphics: HDR", }; STYLE.title(batch, atlas, &Text::literal(title)); if let Some(lines) = tooltip { @@ -791,22 +922,34 @@ impl GraphicsUi { drag: bool, right: bool, ) -> Vec { - let (widgets, _, _) = self.layout(level, w, h); + let (widgets, _, content) = self.layout(level, s, w, h); let (top, bottom) = Self::bounds(h); if drag { + // AbstractScrollArea.mouseDragged: the held scrollbar follows the pointer. + if self.scroll.held() { + self.scroll.drag(y, top, bottom, content); + return Vec::new(); + } if let Some(item) = self.dragging && let Some(wd) = widgets .iter() .find(|wd| matches!(wd.target, Target::Item(i) if i == item)) { item.set_slider(s, ui::slider_value(wd.x, wd.w, x)); - return vec![Action::Changed]; + return vec![Action::SliderDrag]; } return Vec::new(); } if right { return Vec::new(); } + // AbstractScrollArea.updateScrolling: a press on the scrollbar holds it, silently. + if self + .scroll + .press((x, y), Self::scrollbar_x(w), top, bottom, content) + { + return Vec::new(); + } for wd in widgets { if !ui::hovered((x, y), wd.x, wd.y, wd.w, 20.0) { continue; @@ -817,29 +960,34 @@ impl GraphicsUi { return match wd.target { Target::Button(action) => vec![Action::ButtonClick, action], // A disabled control does nothing: no sound, no preset change. - Target::Item(item) if !item.available() => Vec::new(), + Target::Item(item) if item.blocked(s).is_some() => Vec::new(), + Target::Item(Item::Horizon(H::HdrSettings)) => { + vec![Action::ButtonClick, Action::OpenHdr] + } Target::Item(item) => { if item.slider(s).is_some() { self.dragging = Some(item); item.set_slider(s, ui::slider_value(wd.x, wd.w, x)); + vec![Action::SliderDrag] } else { item.cycle(s, right, framebuffer); - return vec![Action::ButtonClick, Action::Changed]; + vec![Action::ButtonClick, Action::Changed] } - vec![Action::Changed] } }; } Vec::new() } + /// Lets go of a slider (true: it plays its click) or the scrollbar. pub fn release(&mut self) -> bool { + self.scroll.release(); self.dragging.take().is_some() } - pub fn scroll(&mut self, level: Level, h: f32, amount: f32) { - let content = Self::content_height(level); - self.clamp_scroll(h, content); - self.scroll -= amount * 18.0; - self.clamp_scroll(h, content); + pub fn scroll(&mut self, level: Level, s: &Settings, h: f32, amount: f32) { + let content = Self::content_height(level, s); + let (top, bottom) = Self::bounds(h); + self.scroll.clamp(top, bottom, content); + self.scroll.wheel(amount, 18.0, top, bottom, content); } } @@ -847,12 +995,59 @@ impl GraphicsUi { mod tests { use super::*; + #[test] + fn hdr_settings_open_where_hdr_is_offered() { + let mut s = Settings::default(); + let mut ui = GraphicsUi::default(); + let size = (480.0, 2000.0); + let at = |ui: &mut GraphicsUi, s: &Settings, level, item| { + let (widgets, _, _) = ui.layout(level, s, size.0, size.1); + let w = widgets + .iter() + .find(|w| matches!(w.target, Target::Item(i) if i == item)) + .unwrap(); + (w.x + 1.0, w.y + 1.0) + }; + let button = at(&mut ui, &s, Level::Advanced, Item::Horizon(H::HdrSettings)); + let click = |ui: &mut GraphicsUi, s: &mut Settings, level, point| { + ui.click(level, s, point, size, [480, 2000], false, false) + }; + crate::graphics::offer_hdr(false); + assert!(click(&mut ui, &mut s, Level::Advanced, button).is_empty()); + // Not even with HDR saved on, from another computer. + s.horizon.hdr = true; + assert!(click(&mut ui, &mut s, Level::Advanced, button).is_empty()); + s.horizon.hdr = false; + crate::graphics::offer_hdr(true); + assert_eq!( + click(&mut ui, &mut s, Level::Advanced, button), + [Action::ButtonClick, Action::OpenHdr] + ); + // On HDR's screen, the sliders wait for the switch above them. + let peak = at(&mut ui, &s, Level::Hdr, Item::Horizon(H::HdrPeak)); + assert!(click(&mut ui, &mut s, Level::Hdr, peak).is_empty()); + s.horizon.hdr = true; + assert_eq!( + click(&mut ui, &mut s, Level::Hdr, peak), + [Action::SliderDrag] + ); + let on = |level, item| { + extra_rows(level, &s) + .iter() + .any(|r| matches!(r, Row::Setting(x) if x.item == Item::Horizon(item))) + }; + assert!(on(Level::Hdr, H::Hdr) && !on(Level::Advanced, H::Hdr)); + assert!(on(Level::Hdr, H::HdrPeak) && !on(Level::Advanced, H::HdrPeak)); + for item in [H::HdrBlackLevel, H::HdrPaperWhite, H::HdrUiBrightness] { + assert!(on(Level::Hdr, item) && !on(Level::Advanced, item)); + } + } #[test] fn graphics_buttons_sound_on_press_and_sliders_on_release() { let mut s = Settings::default(); let mut ui = GraphicsUi::default(); let size = (480.0, 320.0); - let (widgets, _, _) = ui.layout(Level::Advanced, size.0, size.1); + let (widgets, _, _) = ui.layout(Level::Advanced, &s, size.0, size.1); let button = widgets .iter() .find(|w| matches!(w.target, Target::Button(_))) @@ -897,7 +1092,7 @@ mod tests { false, false ), - vec![Action::Changed] + vec![Action::SliderDrag] ); assert_eq!( ui.click( @@ -909,7 +1104,7 @@ mod tests { true, false ), - vec![Action::Changed] + vec![Action::SliderDrag] ); assert!(ui.release()); assert!(!ui.release()); @@ -917,53 +1112,94 @@ mod tests { #[test] fn scrolling_stops_at_both_limits_before_layout() { + let s = Settings::default(); for level in [Level::Simple, Level::Advanced] { let mut ui = GraphicsUi::default(); let h = 2.0 * STYLE.band + 10.0; let (top, bottom) = GraphicsUi::bounds(h); - let max = GraphicsUi::content_height(level) - (bottom - top); + let max = GraphicsUi::content_height(level, &s) - (bottom - top); assert!(max > 4.5); for _ in 0..3 { - ui.scroll(level, h, 1000.0); - assert_eq!(ui.scroll, 0.0); + ui.scroll(level, &s, h, 1000.0); + assert_eq!(ui.scroll.amount, 0.0); } for _ in 0..3 { - ui.scroll(level, h, -1000.0); - assert_eq!(ui.scroll, max); + ui.scroll(level, &s, h, -1000.0); + assert_eq!(ui.scroll.amount, max); } - ui.scroll(level, h, 0.25); - assert_eq!(ui.scroll, max - 4.5); - ui.scroll(level, h, 1000.0); - ui.scroll(level, h, -0.25); - assert_eq!(ui.scroll, 4.5); + ui.scroll(level, &s, h, 0.25); + assert_eq!(ui.scroll.amount, max - 4.5); + ui.scroll(level, &s, h, 1000.0); + ui.scroll(level, &s, h, -0.25); + assert_eq!(ui.scroll.amount, 4.5); } } + #[test] + fn the_scrollbar_can_be_held_and_dragged() { + // #3552: AbstractScrollArea.updateScrolling holds the scrollbar on a press over its + // column, mouseDragged moves the list with the pointer, onRelease lets go. + let mut s = Settings::default(); + let size = (480.0, 2.0 * STYLE.band + 60.0); + let (top, bottom) = GraphicsUi::bounds(size.1); + // On the scrollbar column, as `draw` paints it. + let x = + STYLE.row_x(size.0) + STYLE.control_width + STYLE.gap + STYLE.text_width(size.0) + 8.0; + let first_row = |ui: &mut GraphicsUi| { + ui.layout(Level::Advanced, &Settings::default(), size.0, size.1) + .0[0] + .y + }; + let mut ui = GraphicsUi::default(); + let start = first_row(&mut ui); + let at = |y| (x, y); + let press = |ui: &mut GraphicsUi, s: &mut Settings, y, drag| { + ui.click(Level::Advanced, s, at(y), size, [854, 480], drag, false) + }; + // The press itself makes no sound and changes nothing. + assert!(press(&mut ui, &mut s, top + 5.0, false).is_empty()); + assert!(press(&mut ui, &mut s, top + 15.0, true).is_empty()); + let moved = first_row(&mut ui); + assert!(moved < start, "dragging down scrolls the list down"); + // Past the bottom of the list: the last row. + press(&mut ui, &mut s, bottom + 50.0, true); + let content = GraphicsUi::content_height(Level::Advanced, &s); + assert_eq!(first_row(&mut ui), start - (content - (bottom - top))); + // Above the list: the first row again. + press(&mut ui, &mut s, top - 50.0, true); + assert_eq!(first_row(&mut ui), start); + // Released: drags no longer scroll, and no slider click plays. + assert!(!ui.release()); + press(&mut ui, &mut s, bottom + 50.0, true); + assert_eq!(first_row(&mut ui), start); + } + #[test] fn resizing_clamps_before_positioning_graphics_rows() { + let s = Settings::default(); for level in [Level::Simple, Level::Advanced] { let mut ui = GraphicsUi::default(); - ui.scroll(level, 2.0 * STYLE.band + 10.0, -1000.0); - assert!(ui.scroll > 0.0); - let (widgets, headers, _) = ui.layout(level, 480.0, 4000.0); - assert_eq!(ui.scroll, 0.0); + ui.scroll(level, &s, 2.0 * STYLE.band + 10.0, -1000.0); + assert!(ui.scroll.amount > 0.0); + let (widgets, headers, _) = ui.layout(level, &s, 480.0, 4000.0); + assert_eq!(ui.scroll.amount, 0.0); if let Some((y, _)) = headers.first() { assert_eq!(*y, STYLE.band + 4.0); } - let (expected, _, _) = GraphicsUi::default().layout(level, 480.0, 4000.0); + let (expected, _, _) = GraphicsUi::default().layout(level, &s, 480.0, 4000.0); assert_eq!( widgets.iter().map(|w| w.y).collect::>(), expected.iter().map(|w| w.y).collect::>() ); - ui.scroll(level, 4000.0, -1000.0); - assert_eq!(ui.scroll, 0.0); + ui.scroll(level, &s, 4000.0, -1000.0); + assert_eq!(ui.scroll.amount, 0.0); } } #[test] fn classic_submenus_only_add_horizon_settings_without_empty_groups() { for level in [Level::Simple, Level::Advanced] { - let rows = extra_rows(level); + let rows = extra_rows(level, &Settings::default()); assert!(rows.iter().any(|row| matches!(row, Row::Setting(_)))); for (i, row) in rows.iter().enumerate() { match row { @@ -1051,6 +1287,37 @@ mod tests { assert_eq!(s.horizon.upload_budget_kib, 0); } + #[test] + fn antialiasing_cycles_off_fxaa_taa() { + let mut s = Settings::default(); + let item = Item::Horizon(H::Antialiasing); + for label in ["FXAA", "TAA", "Off"] { + item.cycle(&mut s, false, [854, 480]); + assert_eq!(item.label(&s).plain(), format!("Anti-aliasing: {label}")); + } + item.cycle(&mut s, true, [854, 480]); + assert_eq!((s.horizon.fxaa, s.horizon.taa), (false, true)); + + // Upscaling needs TAA: a ratio turns it on, leaving TAA goes back to Native. + s.horizon.taa = false; + let upscaling = Item::Horizon(H::Upscaling); + upscaling.cycle(&mut s, false, [854, 480]); + assert_eq!(upscaling.label(&s).plain(), "Upscaling: Quality"); + assert!(s.horizon.taa); + item.cycle(&mut s, false, [854, 480]); + assert_eq!(s.horizon.upscaling, Upscaling::Native); + + // The Upscaling row is only on the screen while TAA is on. + let shown = |s: &Settings| { + extra_rows(Level::Advanced, s) + .iter() + .any(|row| matches!(row, Row::Setting(r) if r.item == upscaling)) + }; + assert!(!shown(&s)); + s.horizon.taa = true; + assert!(shown(&s)); + } + #[test] fn entity_culling_is_opt_in_and_toggles_from_advanced_graphics() { let mut s = Settings::default(); @@ -1067,7 +1334,7 @@ mod tests { let mut ui = GraphicsUi::default(); let size = (480.0, 4000.0); - let (widgets, _, _) = ui.layout(Level::Advanced, size.0, size.1); + let (widgets, _, _) = ui.layout(Level::Advanced, &s, size.0, size.1); let switch = widgets .iter() .find(|w| matches!(w.target, Target::Item(i) if i == item)) diff --git a/crates/horizon-client/src/hdr.wgsl b/crates/horizon-client-render/src/hdr.wgsl similarity index 65% rename from crates/horizon-client/src/hdr.wgsl rename to crates/horizon-client-render/src/hdr.wgsl index ec2ae118a0..7625c75063 100644 --- a/crates/horizon-client/src/hdr.wgsl +++ b/crates/horizon-client-render/src/hdr.wgsl @@ -16,6 +16,10 @@ struct Params { debug: f32, // 1 for an HDR10 swapchain, 0 for scRGB. pq: f32, + // The display's black level in nits; 0 leaves the picture as it is. + black: f32, + // The HUD and menus' brightness as a share of paper white. + ui: f32, }; @group(0) @binding(2) var params: Params; @@ -56,20 +60,51 @@ fn pq(nits: vec3) -> vec3 { return pow((0.8359375 + 18.8515625 * y) / (1.0 + 18.6875 * y), vec3(78.84375)); } +// ST 2084's inverse: the PQ signal to nits. +fn pq_nits(e: vec3) -> vec3 { + let p = pow(clamp(e, vec3(0.0), vec3(1.0)), vec3(1.0 / 78.84375)); + let y = max(p - 0.8359375, vec3(0.0)) / (18.8515625 - 18.6875 * p); + return 10000.0 * pow(y, vec3(1.0 / 0.1593017578125)); +} + +// ITU-R BT.2390's black level lift (its EETF without the knee) on the PQ signal +// `e`: black moves up to the display's black level, the peak stays, and the lift +// fades out by the fourth power on the way up, so only the shadows move. A display +// that can't go darker than its black level would crush everything below it. +fn lift_black(e: vec3) -> vec3 { + let top = pq(vec3(params.peak * params.paper * 80.0)).x; + let n = min(e / top, vec3(1.0)); + return e + pq(vec3(params.black)).x * pow(vec3(1.0) - n, vec3(4.0)); +} + @vertex fn vs_hdr(@builtin(vertex_index) i: u32) -> @builtin(position) vec4 { let p = vec2(f32((i << 1u) & 2u), f32(i & 2u)); return vec4(p * 2.0 - 1.0, 0.0, 1.0); } +// Hdr::keep_world: once the world is kept, the frame's alpha starts at 0 again. +@fragment fn fs_clear_alpha() -> @location(0) vec4 { + return vec4(0.0); +} + @fragment fn fs_hdr(@builtin(position) pos: vec4) -> @location(0) vec4 { let at = vec2(pos.xy); - let c = textureLoad(frame, at, 0).rgb; + let f = textureLoad(frame, at, 0); + let c = f.rgb; + // What covers the world here (Hdr::keep_world): nothing at 0, the hand at + // 1/255 (Hdr::mark_hand: drawn at the world's brightness), else the HUD or a + // menu, by how much. + let covered = f.a > 0.0; + let ui = select(f.a, 0.0, f.a < 1.5 / 255.0); // Gamma 2.2, as SDR displays and the compositors' SDR paths decode it. var light = pow(max(c, vec3(0.0)), vec3(2.2)); let w = textureLoad(world, at, 0); // The world's alpha is 0 on light-emitting blocks, the sun and the moon - // (terrain.wgsl, the celestial pipeline): only those shine. - if params.world > 0.5 && params.peak > 1.0 && w.a < 0.75 && shows_world(c, w.rgb) { + // (terrain.wgsl, the celestial pipeline): only those shine, and only where + // nothing was drawn over them since: grey text over the white sun still looks + // like the sun darkened, but it raised the frame's alpha. + if params.world > 0.5 && params.peak > 1.0 && w.a < 0.75 && !covered + && shows_world(c, w.rgb) { // Brightness of the world pixel, before the vignette darkens the frame // toward its edges: a light source shines the same anywhere on screen. let wl = pow(max(w.rgb, vec3(0.0)), vec3(2.2)); @@ -84,9 +119,15 @@ fn pq(nits: vec3) -> vec3 { } } } + // The HUD and menus at their own brightness, by how much they cover. + light *= mix(1.0, params.ui, ui); let scrgb = light * params.paper; if params.pq > 0.5 { - return vec4(pq(BT709_TO_BT2020 * scrgb * 80.0), 1.0); + let e = pq(BT709_TO_BT2020 * scrgb * 80.0); + return vec4(select(e, lift_black(e), params.black > 0.0), 1.0); + } + if params.black > 0.0 { + return vec4(pq_nits(lift_black(pq(scrgb * 80.0))) / 80.0, 1.0); } return vec4(scrgb, 1.0); } diff --git a/crates/horizon-client/src/hud.rs b/crates/horizon-client-render/src/hud.rs similarity index 92% rename from crates/horizon-client/src/hud.rs rename to crates/horizon-client-render/src/hud.rs index fda0094d83..d083fcdc5c 100644 --- a/crates/horizon-client/src/hud.rs +++ b/crates/horizon-client-render/src/hud.rs @@ -1001,6 +1001,35 @@ impl HudBatch { y: f32, color: u32, shadow: bool, + ) -> f32 { + self.styled_text(atlas, text, x, y, color, shadow, false) + } + /// `text_styled` for text drawn in the world (`TextRenderable.render` + /// with `flat` false): each quad's `depth` is its z in text units, the + /// glyphs `Font.SHADOW_DEPTH` in front of their shadow, a bold copy 0.001 + /// in front of its glyph and the lines 0.01 (`BakedSheetGlyph`), so they + /// do not fight over the same pixels. + pub fn text_styled_world( + &mut self, + atlas: &HudAtlas, + text: &crate::text::Text, + x: f32, + y: f32, + color: u32, + shadow: bool, + ) -> f32 { + self.styled_text(atlas, text, x, y, color, shadow, true) + } + #[allow(clippy::too_many_arguments)] + fn styled_text( + &mut self, + atlas: &HudAtlas, + text: &crate::text::Text, + x: f32, + y: f32, + color: u32, + shadow: bool, + world: bool, ) -> f32 { // StringDecomposer.iterateFormatted inside Font.drawInternal: // `§` codes inside a span restyle what follows and are not drawn. @@ -1011,7 +1040,13 @@ impl HudBatch { let alpha = (color >> 24) as u8; let mut glyphs = Vec::new(); let mut cursor = x; + let mut previous = None; for (i, (ch, style)) in text.chars().enumerate() { + // Font.PreparedTextBuilder.accept: lines start 1 left of a + // sequence's first character. Language.getVisualOrder makes a + // sequence of each run of one style, which world text has. + let first = i == 0 || world && previous != Some(style); + previous = Some(style); // The style's font (`Style.getFont`), the default one unless set. let mut g = font.glyph_styled(ch, style); // Font.StringRenderOutput.accept: obfuscated characters draw a @@ -1032,7 +1067,7 @@ impl HudBatch { style.is_italic(), style.underlined == Some(true), style.strikethrough == Some(true), - i == 0, + first, advance, )); cursor += advance; @@ -1040,19 +1075,25 @@ impl HudBatch { self.patches.extend(font.take_patches()); let white = font.white(); let passes: &[bool] = if shadow { &[true, false] } else { &[false] }; + // ARGB.scaleRGB(color, 0.25): the shadow's colour. + let shade = |c: u32| { + let b = c.to_le_bytes(); + rgba( + (f32::from(b[0]) * 0.25) as u8, + (f32::from(b[1]) * 0.25) as u8, + (f32::from(b[2]) * 0.25) as u8, + b[3], + ) + }; for &is_shadow in passes { for &(gx, g, c, bold, italic, underline, strike, first, advance) in &glyphs { + // In the world a glyph whose shadow is 0 has none (hasShadow). + let has_shadow = shadow && !(world && shade(c) == 0); + if is_shadow && !has_shadow { + continue; + } let (dx, col) = if is_shadow { - let b = c.to_le_bytes(); - ( - g.offset, - rgba( - (f32::from(b[0]) * 0.25) as u8, - (f32::from(b[1]) * 0.25) as u8, - (f32::from(b[2]) * 0.25) as u8, - b[3], - ), - ) + (g.offset, shade(c)) } else { (0.0, c) }; @@ -1092,21 +1133,62 @@ impl HudBatch { } None => {} }; + let base = if has_shadow && !is_shadow { 0.03 } else { 0.0 }; + let lift = |batch: &mut Self, start: usize, z: f32| { + if world { + for v in &mut batch.vertices[start..] { + v.depth = z; + } + } + }; + let start = self.vertices.len(); draw(self, 0.0); + lift(self, start, base); if bold { + let start = self.vertices.len(); draw(self, g.offset); + lift(self, start, base + 0.001); } let x0 = if first { px - 1.0 } else { px }; + let start = self.vertices.len(); if strike { self.rect_uv(x0, py + 3.5, px + advance - x0, 1.0, white, col); } if underline { self.rect_uv(x0, py + 8.0, px + advance - x0, 1.0, white, col); } + lift(self, start, base + 0.01); } } cursor - x } + /// SubmitNodeCollection.submitNameTag's text in its own space (x right, + /// y down, in GUI pixels): centred at `-Font.width / 2`, drawn as + /// Font.prepareText draws any text (styles, fonts and glyph boxes; no + /// shadow), over `background` (RGBA) unless its alpha is 0. Returns how + /// many vertices at the start of the batch are the background. + pub fn name_tag( + &mut self, + atlas: &HudAtlas, + text: &crate::text::Text, + y: f32, + color: u32, + background: u32, + ) -> usize { + let x = -atlas.font.width_text(text) / 2.0; + let start = self.vertices.len(); + let advance = self.text_styled(atlas, text, x, y, color, false); + if background >> 24 == 0 { + return 0; + } + // PreparedTextBuilder.markBackground: one pixel round the advances, + // from a pixel above the line to 9 below its top. + let glyphs = self.vertices.split_off(start); + self.fill(atlas, x - 1.0, y - 1.0, advance + 1.0, 10.0, background); + let behind = self.vertices.len() - start; + self.vertices.extend(glyphs); + behind + } } #[repr(C)] #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] @@ -1822,6 +1904,65 @@ mod tests { assert!(missing.rect.is_none() && missing.right == 5.0); } + #[test] + fn name_tags_draw_bold_text_and_tall_glyphs_like_any_text() { + // `ascii.png`'s 8x8 'a' (4 pixels wide) and `accented.png`'s 9x12 + // 'й' (height 12, ascent 10: the breve above the line), 7 wide. + let root = std::env::temp_dir().join(format!("horizon-name-tag-{}", std::process::id())); + let textures = root.join("assets/minecraft/textures/font"); + fs::create_dir_all(root.join("assets/minecraft/font")).unwrap(); + fs::create_dir_all(&textures).unwrap(); + fs::write( + root.join("assets/minecraft/font/default.json"), + r#"{"providers":[ + {"type":"bitmap","file":"minecraft:font/a.png","ascent":7,"chars":["a"]}, + {"type":"bitmap","file":"minecraft:font/tall.png","height":12,"ascent":10,"chars":["й"]}]}"#, + ) + .unwrap(); + for (name, (w, h), used) in [("a", (8, 8), 4), ("tall", (9, 12), 7)] { + let pixels: Vec = (0..w * h) + .flat_map(|i| [255, 255, 255, if i % w < used { 255 } else { 0 }]) + .collect(); + let file = fs::File::create(textures.join(format!("{name}.png"))).unwrap(); + let mut encoder = png::Encoder::new(file, w, h); + encoder.set_color(png::ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + let mut writer = encoder.write_header().unwrap(); + writer.write_image_data(&pixels).unwrap(); + } + let atlas = HudAtlas::build(&root).unwrap(); + let _ = fs::remove_dir_all(&root); + let text = crate::text::Text::literal("aй").bold(); + let mut batch = HudBatch::new([1, 1]); + let behind = batch.name_tag(&atlas, &text, 0.0, u32::MAX, 0x3F00_0000); + // Each quad's box: x0, y0, x1, y1. + let boxes: Vec<[f32; 4]> = batch + .vertices + .as_chunks::<6>() + .0 + .iter() + .map(|q| [q[0].pos[0], q[0].pos[1], q[2].pos[0], q[2].pos[1]]) + .collect(); + // Advances 5 and 8, plus 1 each for bold: Font.width 15, centred. + let x = -7.5; + // Each glyph twice (bold), the copy a pixel right; 'й' in its 9x12 + // box from 3 pixels above the line, each bold quad 0.1 larger. + let near = |a: [f32; 4], b: [f32; 4]| a.iter().zip(b).all(|(a, b)| (a - b).abs() < 1e-4); + let expected = [ + [x - 0.1, -0.1, x + 8.1, 8.1], + [x + 0.9, -0.1, x + 9.1, 8.1], + [x + 5.9, -3.1, x + 15.1, 9.1], + [x + 6.9, -3.1, x + 16.1, 9.1], + ]; + for (got, want) in boxes[1..].iter().zip(expected) { + assert!(near(*got, want), "{got:?} != {want:?}"); + } + assert_eq!(boxes.len(), 1 + expected.len()); + // The background first: a pixel round the advances, 10 pixels tall. + assert_eq!(behind, 6); + assert_eq!(boxes[0], [x - 1.0, -1.0, x + 15.0, 9.0]); + } + #[test] fn effect_icons_are_painted_from_the_mob_effect_directory() { use crate::{effects::EffectInstance, hud_layout, hud_overlays, inventory_effects}; diff --git a/crates/horizon-client/src/hud.wgsl b/crates/horizon-client-render/src/hud.wgsl similarity index 100% rename from crates/horizon-client/src/hud.wgsl rename to crates/horizon-client-render/src/hud.wgsl diff --git a/crates/horizon-client/src/hud_layout.rs b/crates/horizon-client-render/src/hud_layout.rs similarity index 100% rename from crates/horizon-client/src/hud_layout.rs rename to crates/horizon-client-render/src/hud_layout.rs diff --git a/crates/horizon-client/src/hud_overlays.rs b/crates/horizon-client-render/src/hud_overlays.rs similarity index 100% rename from crates/horizon-client/src/hud_overlays.rs rename to crates/horizon-client-render/src/hud_overlays.rs diff --git a/crates/horizon-client/src/inventory_effects.rs b/crates/horizon-client-render/src/inventory_effects.rs similarity index 95% rename from crates/horizon-client/src/inventory_effects.rs rename to crates/horizon-client-render/src/inventory_effects.rs index 2bb75ba239..058c343d35 100644 --- a/crates/horizon-client/src/inventory_effects.rs +++ b/crates/horizon-client-render/src/inventory_effects.rs @@ -59,13 +59,7 @@ pub fn panels( translate("effect.duration.infinite").unwrap_or_else(|| "∞".to_owned()) } else { // StringUtil.formatTickDuration uses the level's current rate. - let seconds = (effect.duration as f32 / tick_rate) as i32; - let (hours, minutes, seconds) = (seconds / 3600, seconds / 60 % 60, seconds % 60); - if hours > 0 { - format!("{hours:02}:{minutes:02}:{seconds:02}") - } else { - format!("{minutes:02}:{seconds:02}") - } + crate::brewing::format_tick_duration(effect.duration, tick_rate) }; let width = max_width.min(39 + text_width(&name).max(text_width(&duration))); EffectPanel { diff --git a/crates/horizon-client/src/java_text.rs b/crates/horizon-client-render/src/java_text.rs similarity index 100% rename from crates/horizon-client/src/java_text.rs rename to crates/horizon-client-render/src/java_text.rs diff --git a/crates/horizon-client/src/level_loading.rs b/crates/horizon-client-render/src/level_loading.rs similarity index 100% rename from crates/horizon-client/src/level_loading.rs rename to crates/horizon-client-render/src/level_loading.rs diff --git a/crates/horizon-client-render/src/lib.rs b/crates/horizon-client-render/src/lib.rs new file mode 100644 index 0000000000..0957b66e7f --- /dev/null +++ b/crates/horizon-client-render/src/lib.rs @@ -0,0 +1,116 @@ +//! The client's world data, rendering and screens: chunks, meshing, light, streaming, the renderer, +//! the HUD, menus and settings. Gameplay (horizon-client) builds on it; nothing here reaches gameplay. +#![forbid(unsafe_code)] +pub mod assets; +pub mod atlas_sources; +pub mod audio; +pub mod auth; +pub mod bidi; +pub mod biome; +pub mod block_entities; +pub mod block_entity_anim; +pub mod block_entity_render; +pub mod border_render; +pub mod breaking; +pub mod brewing; +pub mod camera; +pub mod chat; +pub mod chunk; +pub mod chunk_map; +pub mod clouds; +pub mod collisions; +pub mod command_block; +pub mod containers; +pub mod crash; +pub mod datapack; +pub mod dynamic_light; +pub mod effects; +pub mod environment; +pub mod extras; +pub mod fast_hash; +pub mod fog; +pub mod font; +pub mod gfx; +pub mod graphics; +pub mod graphics_style; +pub mod graphics_ui; +pub mod hud; +pub mod hud_layout; +pub mod hud_overlays; +pub mod inventory_effects; +pub mod java_text; +pub mod level_loading; +pub mod light; +pub mod lod; +pub mod logging; +pub mod menu_blur; +pub mod mesher; +pub mod minimap; +pub mod mipmap; +pub mod mod_list; +pub mod model_library; +pub mod models; +pub mod options_file; +pub mod options_fixes; +pub mod options_ui; +pub mod overlay; +pub mod pack_screen; +pub mod packs; +pub mod par; +pub mod particles; +pub mod poi_sections; +pub mod providers; +pub mod screen_draw; +pub mod screen_effects; +pub mod sculk; +pub mod settings; +pub mod shader_pack_list; +pub mod signs; +pub mod skins; +pub mod sound_engine; +pub mod sound_events; +pub mod spans; +pub mod split_layout; +mod sprite_cache; +pub mod sprites; +pub mod streaming; +pub mod tab_list; +pub mod telemetry; +pub mod testgen; +pub mod title_edition; +pub mod trims; +pub mod ui; +pub mod vault; +pub mod weather; +pub mod widgets; +pub mod world; +pub mod worldborder; +pub mod zip; +pub use horizon_client_core::Result; +pub use horizon_client_core::block_entity_nbt; +pub use horizon_client_core::bossbar; +pub use horizon_client_core::component; +pub use horizon_client_core::fastutil_order; +pub use horizon_client_core::gamerules; +pub use horizon_client_core::lang; +pub use horizon_client_core::nbt_path; +pub use horizon_client_core::scoreboard; +pub use horizon_client_core::snbt; +pub use horizon_client_core::text; +pub use horizon_client_input::clipboard; +pub use horizon_client_input::gamepad; +pub use horizon_client_input::input; +pub use horizon_client_input::interaction_input; +pub use horizon_client_input::key_names; +pub use horizon_client_input::keybinds; +pub use horizon_client_input::keymapping; +pub use horizon_client_input::mouse; +pub use horizon_client_input::window; +pub use horizon_command as command; +pub use horizon_items::blocks; +pub use horizon_items::bundle; +pub use horizon_items::cooldowns; +pub use horizon_items::enchant; +pub use horizon_items::item_data; +pub use horizon_items::items; +pub use horizon_items::random_sequences; diff --git a/crates/horizon-client/src/light.rs b/crates/horizon-client-render/src/light.rs similarity index 100% rename from crates/horizon-client/src/light.rs rename to crates/horizon-client-render/src/light.rs diff --git a/crates/horizon-client/src/lod.rs b/crates/horizon-client-render/src/lod.rs similarity index 88% rename from crates/horizon-client/src/lod.rs rename to crates/horizon-client-render/src/lod.rs index 1ee61cdf4b..ccba798f8f 100644 --- a/crates/horizon-client/src/lod.rs +++ b/crates/horizon-client-render/src/lod.rs @@ -25,6 +25,16 @@ const MAX_SAVED: usize = 1024; /// (`Lod::observing`). pub const OBSERVE_AHEAD: usize = 8; const SAMPLE_JOBS: usize = 4; +/// Threads that sample far tiles beside the mesher. Machines with 12 or more +/// threads use up to 6 (measured: a cold 1024-chunk LoD loads about a quarter +/// sooner than with 3); the rest keep a quarter of their threads, at most 3. +fn sampling_threads(cores: usize) -> usize { + if cores >= 12 { + (cores / 2).min(6) + } else { + (cores / 4).clamp(1, 3) + } +} /// While the near terrain loads on a machine with four threads or fewer, far /// terrain starts one sample job at most this often and no detail: it would /// otherwise take the cores the near chunks and the frame need. @@ -56,7 +66,7 @@ pub struct Column { impl Column { /// Reduce an entire horizontal footprint, rather than inflating its corner /// column (a single leaf used to become a whole distant tree-sized slab). - pub(crate) fn reduced(columns: &[Column; 256], x: usize, z: usize, width: usize) -> Self { + pub fn reduced(columns: &[Column; 256], x: usize, z: usize, width: usize) -> Self { if width == 1 { return columns[z * 16 + x].clone(); } @@ -139,7 +149,7 @@ impl Column { } column } - pub(crate) fn push(&mut self, span: Span) { + pub fn push(&mut self, span: Span) { if let Some(last) = self.spans.last_mut() && last.max == span.min && last.state == span.state @@ -250,8 +260,17 @@ fn state_kinds(facts: &BlockFacts) -> Vec { let state = BlockStateId(i as u32); let f = facts.get(state); let fluid = matches!(&*f.name, "minecraft:water" | "minecraft:lava"); + // Chorus is thin and non-solid but a visible part of the far End. + let chorus = matches!( + &*f.name, + "minecraft:chorus_plant" | "minecraft:chorus_flower" + ); let kept = !facts.is_air(state) - && (f.solid_render || f.full_collision || f.name.ends_with("_leaves") || fluid); + && (f.solid_render + || f.full_collision + || f.name.ends_with("_leaves") + || chorus + || fluid); (u8::from(kept) * KEPT) | (u8::from(fluid) * FLUID) | (u8::from(state == facts.air) * AIR) @@ -416,6 +435,28 @@ impl Plan { false } } + /// Whether chunk column (x, z) is drawn by the tile planned there (ready; + /// at level 0, also `filled`) rather than by a coarser ancestor standing + /// in for it, or is past the far terrain. + fn replaces( + &self, + (x, z): (i32, i32), + ready: &FxHashMap, + filled: impl Fn(TileKey) -> bool, + ) -> bool { + let Some(key) = (0..=MAX_LEVEL) + .map(|level| TileKey { + x: (x * 16).div_euclid(16 << level), + z: (z * 16).div_euclid(16 << level), + level, + }) + .find(|key| self.nodes.contains(key)) + else { + return true; + }; + // A split node's child there is out of range: nothing is drawn. + self.split.contains(&key) || (ready.contains_key(&key) && (key.level > 0 || filled(key))) + } } /// Visited chunks' summaries (`Lod::saved`) and their reductions. type Saved = FxHashMap<(i32, i32), Arc<[Column; 256]>>; @@ -515,6 +556,8 @@ struct Shared { kinds: std::sync::OnceLock>, wake: Condvar, stop: AtomicBool, + /// Reduced far detail: samplers approximate wide cells. + approximate: AtomicBool, } /// One background worker and at most two outstanding sample jobs. Only the /// current view's quadtree and a bounded set of visited summaries are retained. @@ -541,6 +584,8 @@ pub struct Lod { detail_request_at: std::time::Instant, /// Four threads or fewer: far work waits for the near terrain (`hold`). small: bool, + /// Sample jobs that may be queued or running at once. + sample_jobs: usize, /// The near terrain is loading (`set_near_loading`). near_loading: bool, /// When `request_samples` last started a job while held. @@ -550,8 +595,17 @@ pub struct Lod { active: FxHashSet, dirty: FxHashSet, removed: Vec, + /// The columns left out: `full` and `retiring`. covered: FxHashSet<(i32, i32)>, covered_bounds: Option<[i32; 4]>, + /// The full-detail columns `update` was last given. + full: FxHashSet<(i32, i32)>, + /// Columns that left the full-detail area, still left out (their + /// full-detail meshes still drawn) until the tile planned there is ready + /// (`Plan::replaces`). Tiles inside that area are never sampled: when the + /// render distance shrinks, their coarsest ancestors would otherwise + /// stand in for it for minutes. + retiring: FxHashSet<(i32, i32)>, /// The covered columns the active meshes leave out, when a mesh job /// has finished since the last `take_cutout`. cutout: Option>, @@ -561,12 +615,14 @@ pub struct Lod { } impl Lod { pub fn new(source: Arc, context: Arc) -> Self { + let cores = thread::available_parallelism().map_or(1, |n| n.get()); let shared = Arc::new(Shared { jobs: Mutex::new(VecDeque::new()), observed: Mutex::new(FxHashMap::default()), kinds: std::sync::OnceLock::new(), wake: Condvar::new(), stop: AtomicBool::new(false), + approximate: AtomicBool::new(false), }); let worker = shared.clone(); let mesh_context = context.clone(); @@ -574,10 +630,10 @@ impl Lod { let handle = thread::Builder::new() .name("Far terrain".into()) .spawn(move || { + // Ticks and frames come first (horizon_threads). + horizon_threads::behind_server(); let mut sampler = source.sampler(); - let count = thread::available_parallelism() - .map_or(1, |n| n.get() / 4) - .clamp(1, 3); + let count = sampling_threads(cores); let mut sampling = Vec::new(); for _ in 0..count { let Some(mut sample) = sampler.fork() else { @@ -589,6 +645,7 @@ impl Lod { thread::Builder::new() .name("Far sampling".into()) .spawn(move || { + horizon_threads::behind_server(); loop { let key = { let mut jobs = shared.jobs.lock().unwrap(); @@ -610,6 +667,9 @@ impl Lod { } }; let [x, _, z] = key.origin(); + sample.set_approximate( + shared.approximate.load(Ordering::Relaxed), + ); let samples = sample.grid(x, z, key.size() / CELLS as i32, CELLS); if shared.stop.load(Ordering::Relaxed) { @@ -672,6 +732,7 @@ impl Lod { } Job::Sample(key) => { let [x, _, z] = key.origin(); + sampler.set_approximate(worker.approximate.load(Ordering::Relaxed)); let samples = sampler.grid(x, z, key.size() / CELLS as i32, CELLS); Done::Sample { key, samples } } @@ -737,7 +798,8 @@ impl Lod { detail_masks: FxHashMap::default(), detail_request_pending: false, detail_request_at: std::time::Instant::now(), - small: thread::available_parallelism().map_or(1, |n| n.get()) <= 4, + small: cores <= 4, + sample_jobs: SAMPLE_JOBS.max(sampling_threads(cores) + 1), near_loading: false, held_sample_at: std::time::Instant::now(), ready: FxHashMap::default(), @@ -746,6 +808,8 @@ impl Lod { removed: Vec::new(), covered: FxHashSet::default(), covered_bounds: None, + full: FxHashSet::default(), + retiring: FxHashSet::default(), cutout: None, saved: FxHashMap::default(), reduced: FxHashMap::default(), @@ -967,9 +1031,11 @@ impl Lod { self.near_loading = loading; } /// Reduced far detail (`graphics::FarDetail::Reduced`): the next update - /// replans when it changes. + /// replans when it changes, and tiles sampled from then on approximate + /// wide cells (`GeneratorWorker::lod_grid`). pub fn set_reduced_detail(&mut self, reduced: bool) { self.reduced_detail = reduced; + self.shared.approximate.store(reduced, Ordering::Relaxed); } fn held(&self) -> bool { self.small && self.near_loading @@ -1005,7 +1071,8 @@ impl Lod { (ox + (index as i32 % 16) * stride).div_euclid(16), (oz + (index as i32 / 16) * stride).div_euclid(16), ); - if self.covered.contains(&chunk) { + // A retiring column's tile is drawn soon: it needs its detail. + if self.full.contains(&chunk) { self.detail_masks.entry(key).or_default()[index / 64] |= 1 << (index % 64); } else { chunks.insert(chunk); @@ -1127,31 +1194,13 @@ impl Lod { } /// Takes the new full-detail columns, when they changed (`None`: not). fn cover(&mut self, covered: Option>) { - if let Some(covered) = covered - && self.covered != covered + if let Some(full) = covered + && self.full != full { - let changes: Vec<_> = self - .covered - .symmetric_difference(&covered) - .copied() - .collect(); - for (x, z) in changes { - self.invalidate_chunk(x, z); - } - self.covered_bounds = covered.iter().fold(None, |bounds, &(x, z)| { - Some(match bounds { - None => [x * 16, z * 16, (x + 1) * 16, (z + 1) * 16], - Some([x0, z0, x1, z1]) => [ - x0.min(x * 16), - z0.min(z * 16), - x1.max((x + 1) * 16), - z1.max((z + 1) * 16), - ], - }) - }); - self.covered = covered; - self.replanned = true; - self.reselect = true; + self.retiring.extend(self.full.difference(&full).copied()); + self.retiring.retain(|column| !full.contains(column)); + self.full = full; + self.leave_out(); } // Complete resident columns need no duplicate noise work. Their real // summaries are retained when the full-detail chunks leave the view. @@ -1165,12 +1214,55 @@ impl Lod { } } } + /// Leaves out the full-detail and retiring columns: the tiles touching + /// one that changed are meshed again. + fn leave_out(&mut self) { + let covered: FxHashSet<_> = self.full.union(&self.retiring).copied().collect(); + if self.covered == covered { + return; + } + let changes: Vec<_> = self + .covered + .symmetric_difference(&covered) + .copied() + .collect(); + for (x, z) in changes { + self.invalidate_chunk(x, z); + } + self.covered_bounds = covered.iter().fold(None, |bounds, &(x, z)| { + Some(match bounds { + None => [x * 16, z * 16, (x + 1) * 16, (z + 1) * 16], + Some([x0, z0, x1, z1]) => [ + x0.min(x * 16), + z0.min(z * 16), + x1.max((x + 1) * 16), + z1.max((z + 1) * 16), + ], + }) + }); + // Only new covered columns need stand-ins. + self.replanned |= !covered.is_subset(&self.covered); + self.covered = covered; + self.reselect = true; + } /// Chooses the tiles to draw and meshes those that changed, when the /// plan, the samples, the covered columns or the active tiles changed. fn select(&mut self) { if !std::mem::take(&mut self.reselect) && self.dirty.is_empty() { return; } + if !self.retiring.is_empty() { + let (plan, ready, saved, masks) = + (&self.plan, &self.ready, &self.saved, &self.detail_masks); + // A level-0 tile there is an empty stand-in (`cover`) until the + // chunk's summary or generated detail fills it (`apply_fine`). + self.retiring.retain(|&column| { + !plan.replaces(column, ready, |key| { + saved.contains_key(&column) || masks.get(&key) == Some(&[u64::MAX; 4]) + }) + }); + self.leave_out(); + } { let mut selected = Vec::new(); for &key in &self.plan.roots { @@ -1234,7 +1326,13 @@ impl Lod { self.pending.is_empty() && self.held_sample_at.elapsed() >= HELD_SAMPLE_STEP, ) } else { - SAMPLE_JOBS.saturating_sub(self.pending.len()) + // The extra sampling threads wait until the near terrain is in. + let slots = if self.near_loading { + SAMPLE_JOBS + } else { + self.sample_jobs + }; + slots.saturating_sub(self.pending.len()) }; if free == 0 { return; @@ -1282,8 +1380,8 @@ impl Lod { } // Only the first `free` keys not already pending are taken. A key is // listed at most twice (as a root and as a node), so the best - // `2 * (SAMPLE_JOBS + pending)` entries hold them, in order. - let best = 2 * (SAMPLE_JOBS + self.pending.len()); + // `2 * (sample_jobs + pending)` entries hold them, in order. + let best = 2 * (self.sample_jobs + self.pending.len()); if wanted.len() > best { wanted.select_nth_unstable(best); wanted.truncate(best); @@ -1581,6 +1679,48 @@ mod tests { assert_eq!(selected, parent.children()); } #[test] + fn a_shrunk_render_distance_keeps_columns_until_their_planned_tiles_are_ready() { + // Render distance 64 under LOD 64: every tile inside it is a level-0 + // stand-in for a covered column, and only the roots are sampled. + let camera = [8, 8]; + let before = Plan::new(camera, 64, 64); + let after = Plan::with_previous(camera, 8, 64, &before, 0, false); + let mut ready: FxHashMap = before + .nodes + .iter() + .filter(|key| key.level == 0 || before.roots.contains(key)) + .map(|&key| (key, ())) + .collect(); + ready.retain(|key, _| after.nodes.contains(key)); + let contains = |key: &TileKey, (x, z): (i32, i32)| { + let [ox, _, oz] = key.origin(); + (ox..ox + key.size()).contains(&(x * 16)) && (oz..oz + key.size()).contains(&(z * 16)) + }; + // 24 chunks out, now past the render distance: its planned tile was + // split before, so never sampled, and a root stands in for it. + let column = (24, 0); + let mut selected = Vec::new(); + for &root in &after.roots { + after.select(root, &ready, &mut selected); + } + let drawn = selected.iter().filter(|key| contains(key, column)); + assert_eq!(drawn.map(|key| key.level).min(), Some(6)); + assert!(!after.replaces(column, &ready, |_| true)); + let planned = TileKey { + x: 12, + z: 0, + level: 1, + }; + assert!(after.nodes.contains(&planned) && before.split.contains(&planned)); + ready.insert(planned, ()); + assert!(after.replaces(column, &ready, |_| true)); + // 12 chunks out, its level-0 tile is the stand-in until filled. + assert!(!after.replaces((12, 0), &ready, |_| false)); + assert!(after.replaces((12, 0), &ready, |_| true)); + // Past the far terrain nothing waits. + assert!(after.replaces((100, 0), &ready, |_| false)); + } + #[test] fn detail_survives_movement_across_a_split_threshold() { let before = Plan::new([0, 0], 2, 256); let after = Plan::with_previous([16, 0], 2, 256, &before, 0, false); @@ -1810,6 +1950,23 @@ mod tests { for y in 55..64 { chunk.set(4, y, 4, water.0); } + // Chorus is thin and non-solid but must survive the visit summary. + let end_stone = assets + .registry + .default_state("minecraft:end_stone") + .unwrap(); + let chorus = assets + .registry + .default_state("minecraft:chorus_plant") + .unwrap(); + let flower = assets + .registry + .default_state("minecraft:chorus_flower") + .unwrap(); + chunk.set(8, 70, 8, end_stone.0); + chunk.set(8, 71, 8, chorus.0); + chunk.set(8, 72, 8, chorus.0); + chunk.set(8, 73, 8, flower.0); lod.observe(Arc::new(chunk.clone()), &facts); receive_observed(&mut lod); let observed = &lod.saved[&(-1, -1)]; @@ -1817,6 +1974,16 @@ mod tests { assert_eq!(observed[0].surface.unwrap().state, stone); assert_eq!(observed[68].surface.unwrap().height, 55); assert_eq!(observed[68].surface.unwrap().water, Some((64, water))); + let chorus_column = &observed[8 * 16 + 8]; + assert_eq!(chorus_column.surface.unwrap().state, flower); + assert_eq!( + chorus_column + .spans + .iter() + .map(|span| (span.min, span.max, span.state)) + .collect::>(), + vec![(70, 71, end_stone), (71, 73, chorus), (73, 74, flower)] + ); let fine = TileKey { x: -1, @@ -1964,6 +2131,7 @@ mod tests { lod.ready .insert(parent, Arc::new(vec![Column::default(); 256])); lod.active = [parent].into_iter().collect(); + lod.sample_jobs = SAMPLE_JOBS; lod.pending = (0..SAMPLE_JOBS - 1) .map(|i| TileKey { x: 999 + i as i32, @@ -2022,6 +2190,7 @@ mod tests { } lod.active = [near, far].into_iter().collect(); lod.shared.jobs.lock().unwrap().clear(); + lod.sample_jobs = SAMPLE_JOBS; lod.pending = (0..SAMPLE_JOBS - 1) .map(|i| TileKey { x: 999 + i as i32, @@ -2055,6 +2224,34 @@ mod tests { (1..=32).map(|x| (x, 0)).collect::>(), "one unfinished fine tile must not starve neighbouring requests" ); + + // The extra sampling threads' slots open once the near terrain is in. + lod.small = false; + lod.sample_jobs = SAMPLE_JOBS + 3; + lod.plan = Plan::new([-256, -256], 2, 64); + lod.ready.clear(); + lod.active.clear(); + lod.pending.clear(); + lod.meshing = true; + lod.shared.jobs.lock().unwrap().clear(); + let queued = |lod: &mut Lod, near_loading: bool| { + lod.set_near_loading(near_loading); + lod.shared.jobs.lock().unwrap().clear(); + lod.pending.clear(); + lod.update([-256., 64., -256.], 0., 2, 64, Some(FxHashSet::default())); + let jobs = lod.shared.jobs.lock().unwrap(); + jobs.iter() + .filter(|job| matches!(job, Job::Sample(_))) + .count() + }; + assert_eq!(queued(&mut lod, true), SAMPLE_JOBS); + assert_eq!(queued(&mut lod, false), SAMPLE_JOBS + 3); Ok(()) } + + #[test] + fn extra_sampling_threads_only_on_large_machines() { + assert_eq!([1, 2, 4, 8, 11].map(sampling_threads), [1, 1, 1, 2, 2]); + assert_eq!([12, 16, 64].map(sampling_threads), [6, 6, 6]); + } } diff --git a/crates/horizon-client/src/lod/mesh.rs b/crates/horizon-client-render/src/lod/mesh.rs similarity index 100% rename from crates/horizon-client/src/lod/mesh.rs rename to crates/horizon-client-render/src/lod/mesh.rs diff --git a/crates/horizon-client/src/logging.rs b/crates/horizon-client-render/src/logging.rs similarity index 100% rename from crates/horizon-client/src/logging.rs rename to crates/horizon-client-render/src/logging.rs diff --git a/crates/horizon-client/src/menu_blur.rs b/crates/horizon-client-render/src/menu_blur.rs similarity index 100% rename from crates/horizon-client/src/menu_blur.rs rename to crates/horizon-client-render/src/menu_blur.rs diff --git a/crates/horizon-client/src/menu_blur.wgsl b/crates/horizon-client-render/src/menu_blur.wgsl similarity index 100% rename from crates/horizon-client/src/menu_blur.wgsl rename to crates/horizon-client-render/src/menu_blur.wgsl diff --git a/crates/horizon-client/src/mesher.rs b/crates/horizon-client-render/src/mesher.rs similarity index 100% rename from crates/horizon-client/src/mesher.rs rename to crates/horizon-client-render/src/mesher.rs diff --git a/crates/horizon-client/src/mesher/fluid.rs b/crates/horizon-client-render/src/mesher/fluid.rs similarity index 100% rename from crates/horizon-client/src/mesher/fluid.rs rename to crates/horizon-client-render/src/mesher/fluid.rs diff --git a/crates/horizon-client/src/mesher/lighting.rs b/crates/horizon-client-render/src/mesher/lighting.rs similarity index 100% rename from crates/horizon-client/src/mesher/lighting.rs rename to crates/horizon-client-render/src/mesher/lighting.rs diff --git a/crates/horizon-client/src/mesher/particle_tests.rs b/crates/horizon-client-render/src/mesher/particle_tests.rs similarity index 100% rename from crates/horizon-client/src/mesher/particle_tests.rs rename to crates/horizon-client-render/src/mesher/particle_tests.rs diff --git a/crates/horizon-client/src/mesher/sides.rs b/crates/horizon-client-render/src/mesher/sides.rs similarity index 100% rename from crates/horizon-client/src/mesher/sides.rs rename to crates/horizon-client-render/src/mesher/sides.rs diff --git a/crates/horizon-client/src/mesher/visibility_tests.rs b/crates/horizon-client-render/src/mesher/visibility_tests.rs similarity index 100% rename from crates/horizon-client/src/mesher/visibility_tests.rs rename to crates/horizon-client-render/src/mesher/visibility_tests.rs diff --git a/crates/horizon-client/src/minimap.rs b/crates/horizon-client-render/src/minimap.rs similarity index 100% rename from crates/horizon-client/src/minimap.rs rename to crates/horizon-client-render/src/minimap.rs diff --git a/crates/horizon-client/src/mipmap.rs b/crates/horizon-client-render/src/mipmap.rs similarity index 100% rename from crates/horizon-client/src/mipmap.rs rename to crates/horizon-client-render/src/mipmap.rs diff --git a/crates/horizon-client/src/mod_list.rs b/crates/horizon-client-render/src/mod_list.rs similarity index 100% rename from crates/horizon-client/src/mod_list.rs rename to crates/horizon-client-render/src/mod_list.rs diff --git a/crates/horizon-client/src/model_library.rs b/crates/horizon-client-render/src/model_library.rs similarity index 96% rename from crates/horizon-client/src/model_library.rs rename to crates/horizon-client-render/src/model_library.rs index 5edf0f5a4e..065f6c8478 100644 --- a/crates/horizon-client/src/model_library.rs +++ b/crates/horizon-client-render/src/model_library.rs @@ -4,12 +4,12 @@ use horizon_assets::{model::CuboidModel, resolve::ModelLibrary}; use std::{collections::BTreeMap, fs, path::Path, sync::Arc}; #[derive(Default)] -pub(crate) struct ParsedModels { +pub struct ParsedModels { models: BTreeMap>, } impl ParsedModels { /// Every `models/**.json` of a namespace directory, by identifier. - pub(crate) fn load(root: &Path) -> Self { + pub fn load(root: &Path) -> Self { let Ok(namespaces) = fs::read_dir(root.join("assets")) else { return Self::default(); }; @@ -57,14 +57,14 @@ impl ParsedModels { /// Blocks require every referenced resource, including parents. Items instead /// skip broken resources and use the resolver's missing-model fallback. - pub(crate) fn block_library(&self, root: &Path, pending: Vec) -> Result { + pub fn block_library(&self, root: &Path, pending: Vec) -> Result { self.block_models(root, pending, false) } /// [`Self::block_library`], but a model that does not read takes the /// built-in missing model, logged, as vanilla's model discovery does /// ("Missing block model"). Mods' content gets this; vanilla's resources /// always read. - pub(crate) fn block_library_or_missing( + pub fn block_library_or_missing( &self, root: &Path, pending: Vec, @@ -112,7 +112,7 @@ impl ParsedModels { } Ok(ModelLibrary::from_shared(models)?) } - pub(crate) fn into_item_library(self) -> std::result::Result, String> { + pub fn into_item_library(self) -> std::result::Result, String> { ModelLibrary::with_shared_item_generator(self.models) .map(Arc::new) .map_err(|e| format!("item models: {e:?}")) diff --git a/crates/horizon-client/src/models.rs b/crates/horizon-client-render/src/models.rs similarity index 100% rename from crates/horizon-client/src/models.rs rename to crates/horizon-client-render/src/models.rs diff --git a/crates/horizon-client/src/models/catalog.rs b/crates/horizon-client-render/src/models/catalog.rs similarity index 100% rename from crates/horizon-client/src/models/catalog.rs rename to crates/horizon-client-render/src/models/catalog.rs diff --git a/crates/horizon-client/src/models/cube.rs b/crates/horizon-client-render/src/models/cube.rs similarity index 100% rename from crates/horizon-client/src/models/cube.rs rename to crates/horizon-client-render/src/models/cube.rs diff --git a/crates/horizon-client/src/options_file.rs b/crates/horizon-client-render/src/options_file.rs similarity index 98% rename from crates/horizon-client/src/options_file.rs rename to crates/horizon-client-render/src/options_file.rs index 49c94613fc..6c7834fbb2 100644 --- a/crates/horizon-client/src/options_file.rs +++ b/crates/horizon-client-render/src/options_file.rs @@ -87,6 +87,9 @@ const MODEL_PARTS: [&str; 7] = [ ]; const GRAPHICS_BACKEND: &[&str] = &["default", "opengl", "vulkan"]; const GRAPHICS_PRESET: &[&str] = &["fast", "fancy", "fabulous", "custom"]; +/// Horizon's render distance cap (vanilla stops at 32): the server protocol's +/// view distance tops out at 127 anyway. +pub const MAX_RENDER_DISTANCE: i32 = 128; const INACTIVITY: &[&str] = &["minimized", "afk"]; const CLOUDS: &[&str] = &["false", "fast", "true"]; const MAIN_HAND: &[&str] = &["left", "right"]; @@ -159,7 +162,7 @@ static TABLE: LazyLock> = LazyLock::new(|| { b("reducedDebugInfo", false), def("renderClouds", O(Clouds), V::Int(2)), i("cloudRange", 2, 128, 128), - i("renderDistance", 2, 64, 12), + i("renderDistance", 2, MAX_RENDER_DISTANCE, 12), i("simulationDistance", 5, 32, 12), u("screenEffectScale", 1.0), def("soundDevice", O(AnyStr), V::Str(String::new())), @@ -570,6 +573,15 @@ impl OptionsFile { self.values[i] = V::Int(p as i32); } } + /// `Options.modelCustomisation`: the PlayerModelPart bits the player shows + /// (cape first, as `modelPart_*` is in `MODEL_PARTS`). + pub fn model_customisation(&self) -> u8 { + MODEL_PARTS + .iter() + .enumerate() + .filter(|(_, part)| self.bool(&format!("modelPart_{part}"))) + .fold(0, |bits, (bit, _)| bits | 1 << bit) + } /// Key mapping value by mapping name. pub fn key_binding(&self, name: &str) -> String { self.string(&format!("key_{name}")) diff --git a/crates/horizon-client/src/options_fixes.rs b/crates/horizon-client-render/src/options_fixes.rs similarity index 100% rename from crates/horizon-client/src/options_fixes.rs rename to crates/horizon-client-render/src/options_fixes.rs diff --git a/crates/horizon-client/src/options_ui.rs b/crates/horizon-client-render/src/options_ui.rs similarity index 90% rename from crates/horizon-client/src/options_ui.rs rename to crates/horizon-client-render/src/options_ui.rs index f7ceb4e20e..a8ab29afcb 100644 --- a/crates/horizon-client/src/options_ui.rs +++ b/crates/horizon-client-render/src/options_ui.rs @@ -45,6 +45,16 @@ pub(crate) enum Kind { Slider(f32), Cycle, } +/// `PlayerModelPart` keys, in vanilla's bit order (`modelPart_*` in options.txt). +const MODEL_PART_KEYS: [&str; 7] = [ + "modelPart_cape", + "modelPart_jacket", + "modelPart_left_sleeve", + "modelPart_right_sleeve", + "modelPart_left_pants_leg", + "modelPart_right_pants_leg", + "modelPart_hat", +]; /// The Accessibility Settings page: /// `AccessibilityOptionsScreen.options`, with the Controls link inserted separately. pub(crate) const ACCESSIBILITY_OPTIONS: &[Opt] = &[ @@ -74,6 +84,27 @@ pub(crate) const ACCESSIBILITY_OPTIONS: &[Opt] = &[ Opt::HighContrastBlockOutlines, ]; +/// `ControlsScreen.options`: the controls added by Java Edition 26.3, in +/// their vanilla order. The macOS-only Control-click switch is omitted on +/// other platforms just as `InputQuirks.EMULATE_RIGHT_CLICK_WITH_CTRL_KEY` +/// does in Java. +pub(crate) fn vanilla_control_options() -> Vec { + let mut options = vec![ + Opt::ToggleCrouch, + Opt::ToggleSprint, + Opt::ToggleAttack, + Opt::ToggleUse, + Opt::AutoJump, + Opt::SprintWindow, + Opt::QuitShortcuts, + ]; + if cfg!(target_os = "macos") { + options.push(Opt::CtrlClickEmulatesRightClick); + } + options.push(Opt::OperatorItemsTab); + options +} + /// `MouseSettingsScreen.options` in Java Edition 26.3, in vanilla order. pub(crate) const MOUSE_OPTIONS: &[Opt] = &[ Opt::Sensitivity, @@ -97,13 +128,24 @@ impl Opt { | Opt::HideMatchedNames | Opt::OnlySecureChat | Opt::SaveChatDrafts - | Opt::DistortionEffects | Opt::MonochromeLogo | Opt::RotateWithMinecarts + // Saved, but no Horizon system reads them yet: no Realms, no + // friends list, no directional audio, no now-playing toast. + | Opt::RealmsNotifications + | Opt::InGameNotification + | Opt::SharePresence + | Opt::DirectionalAudio + | Opt::MusicToast // Saved, but nothing in rendering reads them yet. | Opt::ChunkFade | Opt::ImprovedTransparency | Opt::ChunkBuilder + // Saved, but the creative tabs, the quit keys and the mouse + // don't read them yet. + | Opt::OperatorItemsTab + | Opt::QuitShortcuts + | Opt::CtrlClickEmulatesRightClick ) } fn file_key(self) -> Option<&'static str> { @@ -128,6 +170,16 @@ impl Opt { Opt::RotateWithMinecarts => "rotateWithMinecart", Opt::HighContrastBlockOutlines => "highContrastBlockOutline", Opt::MusicFrequency => "musicFrequency", + Opt::OperatorItemsTab => "operatorItemsTab", + Opt::QuitShortcuts => "quitShortcuts", + Opt::CtrlClickEmulatesRightClick => "ctrlClickEmulatesRightClick", + Opt::ModelPart(i) => MODEL_PART_KEYS[i], + Opt::AllowServerListing => "allowServerListing", + Opt::RealmsNotifications => "realmsNotifications", + Opt::InGameNotification => "inGameNotification", + Opt::SharePresence => "sharePresence", + Opt::DirectionalAudio => "directionalAudio", + Opt::MusicToast => "musicToast", _ => return None, }) } @@ -171,7 +223,13 @@ impl Opt { Opt::MenuBlur => "options.accessibility.menu_background_blurriness", Opt::ToggleCrouch => "key.sneak", Opt::ToggleSprint => "key.sprint", + Opt::ToggleAttack => "key.attack", + Opt::ToggleUse => "key.use", Opt::AutoJump => "options.autoJump", + Opt::SprintWindow => "options.sprintWindow", + Opt::QuitShortcuts => "options.quitShortcuts", + Opt::CtrlClickEmulatesRightClick => "options.ctrlClickEmulatesRightClick", + Opt::OperatorItemsTab => "options.operatorItemsTab", Opt::ChatVisibility => "options.chat.visibility", Opt::ChatColors => "options.chat.color", Opt::ChatLinks => "options.chat.links", @@ -225,6 +283,22 @@ impl Opt { Opt::PadGlyphs => "horizon.options.button_glyphs", Opt::PadMenuNav => "horizon.options.menu_navigation", Opt::PadKeyboard => "horizon.options.on_screen_keyboard", + Opt::ModelPart(i) => [ + "options.modelPart.cape", + "options.modelPart.jacket", + "options.modelPart.left_sleeve", + "options.modelPart.right_sleeve", + "options.modelPart.left_pants_leg", + "options.modelPart.right_pants_leg", + "options.modelPart.hat", + ][i], + Opt::MainHand => "options.mainHand", + Opt::AllowServerListing => "options.allowServerListing", + Opt::RealmsNotifications => "options.realmsNotifications.button", + Opt::InGameNotification => "options.inGameNotification", + Opt::SharePresence => "options.sharePresence", + Opt::DirectionalAudio => "options.directionalAudio", + Opt::MusicToast => "options.musicToast", } } pub(crate) fn kind(self, s: &Settings) -> Kind { @@ -239,7 +313,11 @@ impl Opt { | Opt::GlintStrength | Opt::PanoramaSpeed => Kind::Slider(s.file.double(self.file_key().unwrap()) as f32), Opt::Fov => unit(s.fov, 30.0, 110.0), - Opt::RenderDistance => unit(s.render_distance as f32, 2.0, 64.0), + Opt::RenderDistance => unit( + s.render_distance as f32, + 2.0, + crate::options_file::MAX_RENDER_DISTANCE as f32, + ), Opt::SimulationDistance => unit(s.simulation_distance as f32, 5.0, 32.0), Opt::Sensitivity => Kind::Slider(s.sensitivity), Opt::WheelSensitivity => unit(s.mouse_wheel_sensitivity, 0.01, 10.0), @@ -269,6 +347,7 @@ impl Opt { Opt::PadLookSensitivity => Kind::Slider(s.controller.look_sensitivity), Opt::PadMoveDeadzone => unit(s.controller.move_deadzone, 0.0, 0.5), Opt::PadLookDeadzone => unit(s.controller.look_deadzone, 0.0, 0.5), + Opt::SprintWindow => unit(s.sprint_window as f32, 0.0, 10.0), _ => Kind::Cycle, } } @@ -317,6 +396,14 @@ impl Opt { } } Opt::PanoramaSpeed => percent(c, s.file.double("panoramaScrollSpeed") as f32), + Opt::SprintWindow => { + let value = if s.sprint_window == 0 { + tr("options.off") + } else { + Text::tr("options.value", &[&s.sprint_window]) + }; + generic(c, value) + } Opt::Fov => match s.fov as i32 { 70 => generic(c, tr("options.fov.min")), 110 => generic(c, tr("options.fov.max")), @@ -460,9 +547,29 @@ impl Opt { Opt::HideLightningFlashes => on_off(s.file.bool("hideLightningFlashes")), Opt::MonochromeLogo => on_off(s.file.bool("darkMojangStudiosBackground")), Opt::HideSplashTexts => on_off(s.file.bool("hideSplashTexts")), - Opt::HideMatchedNames | Opt::OnlySecureChat | Opt::SaveChatDrafts => { - on_off(s.file.bool(self.file_key().unwrap())) - } + Opt::HideMatchedNames + | Opt::OnlySecureChat + | Opt::SaveChatDrafts + | Opt::ModelPart(_) + | Opt::AllowServerListing + | Opt::RealmsNotifications + | Opt::InGameNotification + | Opt::DirectionalAudio => on_off(s.file.bool(self.file_key().unwrap())), + Opt::MainHand => tr(if s.main_hand_left { + "options.mainHand.left" + } else { + "options.mainHand.right" + }), + Opt::SharePresence => tr([ + "options.sharePresence.none", + "options.sharePresence.limited", + "options.sharePresence.all", + ][s.file.int("sharePresence").clamp(0, 2) as usize]), + Opt::MusicToast => tr([ + "options.musicToast.never", + "options.musicToast.pauseMenu", + "options.musicToast.pauseMenuAndToast", + ][s.file.int("musicToast").clamp(0, 2) as usize]), Opt::NarratorHotkey => on_off(s.file.bool("narratorHotkey")), Opt::RotateWithMinecarts => on_off(s.file.bool("rotateWithMinecart")), Opt::HighContrastBlockOutlines => on_off(s.file.bool("highContrastBlockOutline")), @@ -514,7 +621,20 @@ impl Opt { } else { "options.key.hold" }), + Opt::ToggleAttack => tr(if s.toggle_attack { + "options.key.toggle" + } else { + "options.key.hold" + }), + Opt::ToggleUse => tr(if s.toggle_use { + "options.key.toggle" + } else { + "options.key.hold" + }), Opt::AutoJump => on_off(s.auto_jump), + Opt::QuitShortcuts => on_off(s.quit_shortcuts), + Opt::CtrlClickEmulatesRightClick => on_off(s.ctrl_click_emulates_right_click), + Opt::OperatorItemsTab => on_off(s.operator_items_tab), Opt::ChatVisibility => tr([ "options.chat.visibility.full", "options.chat.visibility.system", @@ -579,14 +699,19 @@ impl Opt { self.file_key().unwrap(), crate::options_file::V::Double(f64::from(v).powi(2)), ), - Opt::DamageTilt | Opt::PanoramaSpeed | Opt::GlintSpeed | Opt::GlintStrength => { - s.file.set( - self.file_key().unwrap(), - crate::options_file::V::Double(f64::from(v)), - ) - } + Opt::DistortionEffects + | Opt::DamageTilt + | Opt::PanoramaSpeed + | Opt::GlintSpeed + | Opt::GlintStrength => s.file.set( + self.file_key().unwrap(), + crate::options_file::V::Double(f64::from(v)), + ), Opt::Fov => s.fov = step(30.0, 110.0), - Opt::RenderDistance => s.render_distance = step(2.0, 64.0) as i32, + Opt::RenderDistance => { + s.render_distance = + step(2.0, crate::options_file::MAX_RENDER_DISTANCE as f32) as i32 + } Opt::SimulationDistance => s.simulation_distance = step(5.0, 32.0) as i32, Opt::Sensitivity => s.sensitivity = v, Opt::WheelSensitivity => s.mouse_wheel_sensitivity = 0.01 + v * 9.99, @@ -613,6 +738,7 @@ impl Opt { Opt::PadLookSensitivity => s.controller.look_sensitivity = v, Opt::PadMoveDeadzone => s.controller.move_deadzone = step(0.0, 50.0) / 100.0, Opt::PadLookDeadzone => s.controller.look_deadzone = step(0.0, 50.0) / 100.0, + Opt::SprintWindow => s.sprint_window = step(0.0, 10.0) as i32, _ => {} } if self.affects_preset() { @@ -669,7 +795,12 @@ impl Opt { | Opt::HighContrastBlockOutlines | Opt::HideMatchedNames | Opt::OnlySecureChat - | Opt::SaveChatDrafts => { + | Opt::SaveChatDrafts + | Opt::ModelPart(_) + | Opt::AllowServerListing + | Opt::RealmsNotifications + | Opt::InGameNotification + | Opt::DirectionalAudio => { let key = self.file_key().unwrap(); s.file .set(key, crate::options_file::V::Bool(!s.file.bool(key))); @@ -704,7 +835,14 @@ impl Opt { Opt::ChunkBuilder => s.prioritize_chunk_updates = next(s.prioritize_chunk_updates, 3), Opt::ToggleCrouch => s.toggle_crouch = !s.toggle_crouch, Opt::ToggleSprint => s.toggle_sprint = !s.toggle_sprint, + Opt::ToggleAttack => s.toggle_attack = !s.toggle_attack, + Opt::ToggleUse => s.toggle_use = !s.toggle_use, Opt::AutoJump => s.auto_jump = !s.auto_jump, + Opt::QuitShortcuts => s.quit_shortcuts = !s.quit_shortcuts, + Opt::CtrlClickEmulatesRightClick => { + s.ctrl_click_emulates_right_click = !s.ctrl_click_emulates_right_click + } + Opt::OperatorItemsTab => s.operator_items_tab = !s.operator_items_tab, Opt::ChatVisibility => s.chat_visibility = next(s.chat_visibility, 3), Opt::ChatColors => s.chat_colors = !s.chat_colors, Opt::ChatLinks => s.chat_links = !s.chat_links, @@ -718,6 +856,17 @@ impl Opt { s.file .set("musicFrequency", crate::options_file::V::Int(i32::from(n))); } + Opt::MainHand => s.main_hand_left = !s.main_hand_left, + Opt::SharePresence => { + let n = next(s.file.int("sharePresence").clamp(0, 2) as u8, 3); + s.file + .set("sharePresence", crate::options_file::V::Int(i32::from(n))); + } + Opt::MusicToast => { + let n = next(s.file.int("musicToast").clamp(0, 2) as u8, 3); + s.file + .set("musicToast", crate::options_file::V::Int(i32::from(n))); + } Opt::ForceUnicode => s.force_unicode = !s.force_unicode, Opt::JapaneseGlyphs => s.japanese_glyphs = !s.japanese_glyphs, Opt::PadEnabled => s.controller.enabled = !s.controller.enabled, @@ -844,6 +993,7 @@ pub enum Page { /// Horizon's graphics settings (`graphics_ui`). Graphics, GraphicsAdvanced, + GraphicsHdr, /// Horizon's controller settings and button binds. Controller, PadBinds, @@ -855,6 +1005,10 @@ pub enum Page { Horizon, /// AI Agent: agent control on or off, whether an agent is connected, the connection details. Agent, + /// SkinCustomizationScreen: the skin layers and Main Hand. + Skin, + /// OnlineOptionsScreen: server listing, Realms and friends notifications. + Online, } #[derive(Clone, Debug, PartialEq)] enum Act { @@ -923,13 +1077,19 @@ pub enum Event { ButtonClick, /// Close the options screens (back to the pause menu or title). Close, - /// Settings changed: apply them. + /// Settings changed: apply and save them. Changed, + /// A slider is being dragged: apply settings live but defer saving. + SliderDrag, /// The language changed (reload translations). Language(String), /// The pack selection changed (reload resources). Packs(Vec), + /// Create World's Data Packs screen closed with a new selection. + DataPacks(Vec), OpenPackFolder, + /// Data Packs' Open Pack Folder: create the folder if needed and open it. + OpenDataPackFolder(std::path::PathBuf), OpenModFolder, /// The Shader Packs page opened: list `shaderpacks/` into `shader_packs`. ListShaderPacks, @@ -939,6 +1099,9 @@ pub enum Event { /// Open the mod's own settings page (capability `ui`): the installed /// mod's index in `mods.installed`. ModSettings(usize), + /// Native mod `.0`'s page opened: the game asks the mod for its labels + /// again (they were made with the list, maybe in another language). + NativeSettings(usize), /// Button `.1` of native mod `.0`'s page was pressed: the game tells the /// mod and refreshes the page. NativeButton(usize, usize), @@ -992,6 +1155,13 @@ pub struct OptionsUi { pub pack_selection: Vec, /// The open pack screen, made at its first frame. pub pack_screen: Option, + /// The Packs page is Create World's Data Packs screen on this folder. + data_pack_dir: Option, + /// The game's resources, for the built-in data pack's icon. + data_pack_resources: std::path::PathBuf, + /// The folder's entries when its packs were listed, and when they were + /// last compared with it. + data_pack_stamp: (Vec<(String, Option)>, Instant), /// A reload failure, dismissed before returning to the current page. pub resource_error: Option, /// When the pack screen's search box took the focus (its cursor blink). @@ -1026,6 +1196,9 @@ impl Default for OptionsUi { pack_infos: Vec::new(), pack_selection: Vec::new(), pack_screen: None, + data_pack_dir: None, + data_pack_resources: Default::default(), + data_pack_stamp: (Vec::new(), Instant::now()), resource_error: None, pack_focus: Instant::now(), in_world: false, @@ -1133,9 +1306,48 @@ impl OptionsUi { pub fn open(&mut self, in_world: bool) { self.open_at(in_world, Page::Main); } + /// Create World's Data Packs (`openDataPackSelectionScreen`): the Packs + /// page on the new world's pack folder `dir`, `selected` chosen; + /// `resources` holds the built-in pack's icon. + pub fn open_data_packs( + &mut self, + dir: std::path::PathBuf, + selected: Vec, + resources: std::path::PathBuf, + ) { + self.open_at(false, Page::Packs); + self.pack_infos = crate::datapack::screen_packs(&dir, &resources); + self.data_pack_resources = resources; + self.pack_selection = selected; + self.data_pack_stamp = (crate::datapack::folder_stamp(&dir), Instant::now()); + self.data_pack_dir = Some(dir); + } + /// `PackSelectionScreen.tick`'s watcher: once a second, the Data Packs + /// folder's packs are listed again if it changed, the selection kept + /// (the search text and scroll start over). + fn watch_data_packs(&mut self, atlas: &HudAtlas, size: (f32, f32)) { + let Some(dir) = &self.data_pack_dir else { + return; + }; + if self.data_pack_stamp.1.elapsed().as_secs() < 1 { + return; + } + let stamp = crate::datapack::folder_stamp(dir); + let changed = stamp != self.data_pack_stamp.0; + self.data_pack_stamp = (stamp, Instant::now()); + if !changed { + return; + } + self.pack_infos = crate::datapack::screen_packs(dir, &self.data_pack_resources); + if let Some(p) = self.pack_screen.take() { + self.pack_selection = p.commit(); + self.packs(atlas, size).changed = p.changed; + } + } /// Open a page whose Done/Escape returns directly to the calling screen. pub fn open_at(&mut self, in_world: bool, page: Page) { self.stack.clear(); + self.data_pack_dir = None; self.push(page); self.in_world = in_world; self.selected_key = None; @@ -1156,7 +1368,10 @@ impl OptionsUi { if page == Page::Packs { self.pack_screen = None; } - if matches!(page, Page::Graphics | Page::GraphicsAdvanced) { + if matches!( + page, + Page::Graphics | Page::GraphicsAdvanced | Page::GraphicsHdr + ) { self.graphics.reset(); } } @@ -1165,6 +1380,7 @@ impl OptionsUi { match page { Page::Graphics => Some(crate::graphics_ui::Level::Simple), Page::GraphicsAdvanced => Some(crate::graphics_ui::Level::Advanced), + Page::GraphicsHdr => Some(crate::graphics_ui::Level::Hdr), _ => None, } } @@ -1173,6 +1389,8 @@ impl OptionsUi { Page::Main => "options.title", Page::Video => "options.videoTitle", Page::Sound => "options.sounds.title", + Page::Skin => "options.skinCustomisation.title", + Page::Online => "options.online.title", Page::Controls => "controls.title", Page::KeyBinds => "controls.keybinds.title", Page::Mouse => "options.mouse_settings.title", @@ -1184,7 +1402,7 @@ impl OptionsUi { Page::Mods => "Mods", Page::NativeMod(_) => "Mod Settings", Page::ShaderPacks => "Shader Packs", - Page::Graphics | Page::GraphicsAdvanced => "options.videoTitle", + Page::Graphics | Page::GraphicsAdvanced | Page::GraphicsHdr => "options.videoTitle", Page::Controller => "Controller Settings", Page::PadBinds => "Controller Binds", Page::Extras => "Horizon Extras", @@ -1432,13 +1650,29 @@ impl OptionsUi { self.filter_option_rows(rows) } Page::Sound => { + // SoundOptionsScreen, without Audio Device: Horizon opens the + // output device itself and cannot switch it. let mut rows = vec![Row::Big(Opt::Volume(0))]; let others: Vec = (1..SOUND_SOURCES.len()).map(Opt::Volume).collect(); rows.extend(pairs(&others)); - rows.extend(pairs(&[Opt::ShowSubtitles])); - // SoundOptionsScreen: Music Frequency starts the next row (with - // Music Toast, not in Horizon yet). - rows.extend(pairs(&[Opt::MusicFrequency])); + rows.extend(pairs(&[Opt::ShowSubtitles, Opt::DirectionalAudio])); + rows.extend(pairs(&[Opt::MusicFrequency, Opt::MusicToast])); + rows + } + Page::Skin => { + // SkinCustomizationScreen: the seven layers, then Main Hand. + let mut opts: Vec = (0..MODEL_PART_KEYS.len()).map(Opt::ModelPart).collect(); + opts.push(Opt::MainHand); + pairs(&opts) + } + Page::Online => { + // OnlineOptionsScreen without the Xbox and friends-list parts. + let mut rows = vec![Row::Header("options.online.friends.header")]; + rows.extend(pairs(&[Opt::InGameNotification, Opt::SharePresence])); + rows.push(Row::Header("options.online.servers.header")); + rows.push(Row::Big(Opt::AllowServerListing)); + rows.push(Row::Header("options.online.realms.header")); + rows.push(Row::Big(Opt::RealmsNotifications)); rows } Page::Controls => { @@ -1458,11 +1692,8 @@ impl OptionsUi { Act::Push(Page::KeyBinds), )), )]; - rows.extend(pairs(&[ - Opt::ToggleCrouch, - Opt::ToggleSprint, - Opt::AutoJump, - ])); + let controls = vanilla_control_options(); + rows.extend(pairs(&controls)); // Horizon's controller page, after vanilla's rows. rows.push(Row::Buttons( button( @@ -1633,15 +1864,15 @@ impl OptionsUi { list: false, }); out.push(option_widget(s, Opt::Fov, cx - 154.0, 29.0, 150.0)); - out.push(inactive(button( + out.push(button( cx + 4.0, 29.0, 150.0, tr("options.online"), - Act::None, - ))); + Act::Push(Page::Online), + )); let entries = [ - ("options.skinCustomisation", None), + ("options.skinCustomisation", Some(Page::Skin)), ("options.sounds", Some(Page::Sound)), ("options.video", Some(Page::Video)), ("options.controls", Some(Page::Controls)), @@ -2169,6 +2400,7 @@ impl OptionsUi { return; } if self.page() == Page::Packs { + self.watch_data_packs(atlas, (w, h)); ui::menu_background(batch, atlas, 0.0, 0.0, w, h, self.in_world); let blink = self.pack_focus.elapsed().as_millis() as u64; let m = crate::screen_draw::Measure(&atlas.font); @@ -2427,7 +2659,9 @@ impl OptionsUi { match action { Action::ButtonClick => events.push(Event::ButtonClick), Action::Changed => events.push(Event::Changed), + Action::SliderDrag => events.push(Event::SliderDrag), Action::OpenAdvanced => self.push(Page::GraphicsAdvanced), + Action::OpenHdr => self.push(Page::GraphicsHdr), Action::OpenSimple => { if self.stack.len() >= 2 && self.stack[self.stack.len() - 2] == Page::Graphics @@ -2463,7 +2697,10 @@ impl OptionsUi { self.pack_focus = Instant::now(); } match action { - crate::pack_screen::Action::OpenFolder => events.push(Event::OpenPackFolder), + crate::pack_screen::Action::OpenFolder => events.push(match &self.data_pack_dir { + Some(dir) => Event::OpenDataPackFolder(dir.clone()), + None => Event::OpenPackFolder, + }), crate::pack_screen::Action::Done => events.extend(self.back(s)), crate::pack_screen::Action::None => {} } @@ -2507,7 +2744,7 @@ impl OptionsUi { let v = ui::slider_value(wd.x, wd.w, x); self.slider_value = Some((opt, v, Instant::now())); set_slider(opt, s, v); - events.push(Event::Changed); + events.push(Event::SliderDrag); } return events; } @@ -2612,6 +2849,7 @@ impl OptionsUi { events.push(Event::ButtonClick); if s.mods.installed.get(i).is_some_and(|m| m.native.is_some()) { self.push(Page::NativeMod(i)); + events.push(Event::NativeSettings(i)); } else { events.push(Event::ModSettings(i)); } @@ -2643,7 +2881,7 @@ impl OptionsUi { let v = ui::slider_value(wd.x, wd.w, x); self.slider_value = Some((opt, v, Instant::now())); set_slider(opt, s, v); - events.push(Event::Changed); + events.push(Event::SliderDrag); } WKind::LanguageRow { code, .. } => { if self.language_choice.as_deref() == Some(code.as_str()) { @@ -2676,7 +2914,7 @@ impl OptionsUi { let slider = self.dragging.take().is_some(); let graphics_slider = self.graphics.release(); if slider || graphics_slider { - vec![Event::ButtonClick] + vec![Event::ButtonClick, Event::Changed] } else { Vec::new() } @@ -2692,7 +2930,7 @@ impl OptionsUi { return; } if let Some(level) = Self::graphics_level(self.page()) { - self.graphics.scroll(level, size.1, amount); + self.graphics.scroll(level, s, size.1, amount); return; } match &mut self.pack_screen { @@ -2714,8 +2952,14 @@ impl OptionsUi { } None => { self.pack_focus = Instant::now(); + let title = if self.data_pack_dir.is_some() { + "dataPack.title" + } else { + "resourcePack.title" + }; self.pack_screen = Some(crate::pack_screen::PackScreen::new( &m, + crate::text::Text::translate(title, &[]), w, h, self.in_world, @@ -2747,13 +2991,18 @@ impl OptionsUi { } Page::Packs => { // PackSelectionScreen.onClose: PackSelectionModel.commit. + let data = self.data_pack_dir.take(); if let Some(p) = self.pack_screen.take() && p.changed { let ids = p.commit(); - s.resource_packs = ids.clone(); - self.pack_selection = ids.clone(); - events.push(Event::Packs(ids)); + if data.is_some() { + events.push(Event::DataPacks(ids)); + } else { + s.resource_packs = ids.clone(); + self.pack_selection = ids.clone(); + events.push(Event::Packs(ids)); + } } } _ => {} @@ -3049,10 +3298,48 @@ mod tests { ))); } + #[test] + fn controls_match_java_26_3_option_order() { + let mut ui = OptionsUi::default(); + ui.show(Page::Controls); + let options = + ui.list_rows(&Settings::default()) + .into_iter() + .fold(Vec::new(), |mut options, row| { + match row { + Row::Big(opt) => options.push(opt), + Row::Pair(a, b) => { + options.push(a); + options.extend(b); + } + _ => {} + } + options + }); + assert_eq!(options, vanilla_control_options()); + } + + #[test] + fn sprint_window_and_new_control_switches_round_trip() { + let mut settings = Settings::default(); + set_slider(Opt::SprintWindow, &mut settings, 0.5); + settings.operator_items_tab = true; + settings.quit_shortcuts = false; + settings.ctrl_click_emulates_right_click = true; + settings.toggle_attack = true; + settings.toggle_use = true; + let loaded = Settings::parse(&settings.serialize()); + assert_eq!(loaded.sprint_window, 5); + assert!(loaded.operator_items_tab); + assert!(!loaded.quit_shortcuts); + assert!(loaded.ctrl_click_emulates_right_click); + assert!(loaded.toggle_attack && loaded.toggle_use); + } + #[test] fn render_distance_slider_round_trips_through_extended_options_range() { let mut s = Settings::default(); - for (position, distance) in [(0.0, 2), (0.5, 33), (1.0, 64)] { + for (position, distance) in [(0.0, 2), (0.5, 65), (1.0, 128)] { set_slider(Opt::RenderDistance, &mut s, position); assert_eq!(s.render_distance, distance); let reloaded = Settings::parse(&s.serialize()); @@ -3063,10 +3350,10 @@ mod tests { ); } let mut file = crate::options_file::OptionsFile::default(); - file.set_text("renderDistance", "64").unwrap(); - assert!(file.set_text("renderDistance", "65").is_err()); + file.set_text("renderDistance", "128").unwrap(); + assert!(file.set_text("renderDistance", "129").is_err()); assert!(file.set_text("renderDistance", "1").is_err()); - assert_eq!(file.int("renderDistance"), 64); + assert_eq!(file.int("renderDistance"), 128); } fn atlas() -> HudAtlas { HudAtlas::build(std::path::Path::new("/nonexistent/horizon-test-assets")).unwrap() @@ -3230,6 +3517,50 @@ mod tests { assert_eq!(ui.page(), Page::Main); } + #[test] + fn opening_a_native_mods_page_asks_the_game_for_its_labels_again() { + let atlas = atlas(); + let mut s = Settings::default(); + let mut ui = OptionsUi::default(); + let size = (480.0, 240.0); + s.mods.installed.push(crate::mod_list::Installed { + id: "native".into(), + title: "Native 1.0".into(), + description: String::new(), + problem: None, + dev: false, + has_settings: false, + native: Some(crate::mod_list::NativePage::default()), + }); + ui.show(Page::Mods); + let (widgets, ..) = ui.layout(&s, size.0, size.1); + let settings = widgets + .iter() + .find(|w| { + matches!( + w.kind, + WKind::Button { + act: Act::ModSettings(0), + .. + } + ) + }) + .expect("a native mod's row has a Settings button"); + let events = ui.click( + &mut s, + &atlas, + settings.x + 1.0, + settings.y + 1.0, + size, + [480, 240], + false, + false, + ); + // Its labels were made with the list, maybe in another language. + assert_eq!(events.last(), Some(&Event::NativeSettings(0))); + assert_eq!(ui.page(), Page::NativeMod(0)); + } + #[test] fn option_tooltip_keys_follow_value_dependent_descriptions() { let mut s = Settings::default(); @@ -3666,22 +3997,22 @@ mod tests { let (x, y) = (slider.x + slider.w - 1.0, slider.y + 1.0); assert_eq!( ui.click(&mut s, &atlas, x, y, size, [854, 480], false, false), - vec![Event::Changed] + vec![Event::SliderDrag] ); assert_eq!(s.master_volume, 1.0); - assert_eq!(ui.release(), vec![Event::ButtonClick]); + assert_eq!(ui.release(), vec![Event::ButtonClick, Event::Changed]); assert!(ui.release().is_empty()); assert_eq!( ui.click(&mut s, &atlas, x, y, size, [854, 480], false, false), - vec![Event::Changed] + vec![Event::SliderDrag] ); for cx in [-100.0, 250.0, 1000.0] { assert_eq!( ui.click(&mut s, &atlas, cx, -100.0, size, [854, 480], true, false), - vec![Event::Changed] + vec![Event::SliderDrag] ); } - assert_eq!(ui.release(), vec![Event::ButtonClick]); + assert_eq!(ui.release(), vec![Event::ButtonClick, Event::Changed]); assert!( ui.click(&mut s, &atlas, x, y, size, [854, 480], true, false) .is_empty() diff --git a/crates/horizon-client/src/overlay.rs b/crates/horizon-client-render/src/overlay.rs similarity index 100% rename from crates/horizon-client/src/overlay.rs rename to crates/horizon-client-render/src/overlay.rs diff --git a/crates/horizon-client/src/pack_screen.rs b/crates/horizon-client-render/src/pack_screen.rs similarity index 98% rename from crates/horizon-client/src/pack_screen.rs rename to crates/horizon-client-render/src/pack_screen.rs index 03906030ab..03bbbcafcc 100644 --- a/crates/horizon-client/src/pack_screen.rs +++ b/crates/horizon-client-render/src/pack_screen.rs @@ -318,6 +318,8 @@ pub enum Action { /// The pack screen and its state. #[derive(Clone, Debug)] pub struct PackScreen { + /// `resourcePack.title`, or `dataPack.title` from Create World. + pub title: Text, pub width: i32, pub height: i32, pub in_world: bool, @@ -342,6 +344,7 @@ impl PackScreen { /// `PackSelectionScreen.init`: the lists filled, the search box focused. pub fn new( m: &Measure, + title: Text, width: i32, height: i32, in_world: bool, @@ -360,6 +363,7 @@ impl PackScreen { .filter_map(|p| Some((icon_texture(&p.id), p.icon.clone()?))) .collect(); let mut s = Self { + title, width, height, in_world, @@ -382,7 +386,7 @@ impl PackScreen { } /// The header layout: title, drop info and search box, centred. fn header(&self, m: &Measure) -> [(i32, i32); 3] { - let tw = m.width(&tr("resourcePack.title")); + let tw = m.width(&self.title); let dw = m.width(&tr("pack.dropInfo")); let col = tw.max(dw).max(200); let frame = self.width.max(col); @@ -837,7 +841,7 @@ impl PackScreen { return out; } let [(tx, ty), (dx, dy), _] = self.header(m); - d::string_widget(&mut out, m, &tr("resourcePack.title"), tx, ty, 0); + d::string_widget(&mut out, m, &self.title, tx, ty, 0); d::string_widget( &mut out, m, @@ -1105,7 +1109,7 @@ mod icon_tests { pack("high_contrast", Some(contrast.clone())), pack("file/Iconless", None), ]; - let screen = PackScreen::new(&m, 480, 320, false, packs, &[]); + let screen = PackScreen::new(&m, tr("resourcePack.title"), 480, 320, false, packs, &[]); let mut draws = screen.draw(&m, (0, 0), 0); let mut patches = Vec::new(); screen.bind_icons(&mut draws, &atlas, &mut patches); @@ -1152,7 +1156,8 @@ mod click_sound_tests { crate::hud::HudAtlas::build(std::path::Path::new("/nonexistent/horizon-test-assets")) .unwrap(); let m = Measure(&atlas.font); - let mut screen = PackScreen::new(&m, 480, 320, false, Vec::new(), &[]); + let title = tr("resourcePack.title"); + let mut screen = PackScreen::new(&m, title, 480, 320, false, Vec::new(), &[]); assert!(!screen.button_at(&m, 0.0, 0.0)); assert!(!screen.button_at( &m, diff --git a/crates/horizon-client/src/packs.rs b/crates/horizon-client-render/src/packs.rs similarity index 88% rename from crates/horizon-client/src/packs.rs rename to crates/horizon-client-render/src/packs.rs index 28952ba2cb..9389e85589 100644 --- a/crates/horizon-client/src/packs.rs +++ b/crates/horizon-client-render/src/packs.rs @@ -181,9 +181,15 @@ pub fn compatible(meta: &serde_json::Value) -> bool { /// `PackCompatibility.forVersion`: too old, too new, compatible, or unknown /// when the metadata names no format. pub fn compatibility(meta: &serde_json::Value) -> crate::pack_screen::Compat { + compatibility_for(meta, RESOURCE_FORMAT) +} +/// `compatibility` against `current`, the resource or the data pack format. +pub fn compatibility_for( + meta: &serde_json::Value, + current: (i64, i64), +) -> crate::pack_screen::Compat { use crate::pack_screen::Compat; let pack = &meta["pack"]; - let current = RESOURCE_FORMAT; let range = if let (Some(min), Some(max)) = ( format_of(&pack["min_format"]), format_of(&pack["max_format"]), @@ -275,6 +281,10 @@ fn read_meta(source: &Source) -> Option { serde_json::from_slice(&bytes).ok() } fn pack(id: String, title: Text, source: Source) -> Option { + pack_for(id, title, source, RESOURCE_FORMAT) +} +/// A pack whose compatibility is against `format`. +fn pack_for(id: String, title: Text, source: Source, format: (i64, i64)) -> Option { let meta = read_meta(&source)?; let (raw_description, description_style) = component_root(&meta["pack"]["description"]); Some(Pack { @@ -284,7 +294,7 @@ fn pack(id: String, title: Text, source: Source) -> Option { raw_description, description_style, compatible: compatible(&meta), - compatibility: compatibility(&meta), + compatibility: compatibility_for(&meta, format), overlays: overlays_of(&meta), filter: filter_of(&meta), source, @@ -305,7 +315,13 @@ pub fn discover(resources: &Path, packs_dir: &Path) -> Vec { out.push(p); } } - let mut user: Vec = fs::read_dir(packs_dir) + out.extend(folder(packs_dir, RESOURCE_FORMAT)); + out +} +/// The packs (folders and zips) in `dir`, by name, as `FolderRepositorySource` +/// finds them; their compatibility is against `format`. +pub fn folder(dir: &Path, format: (i64, i64)) -> Vec { + let mut user: Vec = fs::read_dir(dir) .into_iter() .flatten() .flatten() @@ -319,12 +335,11 @@ pub fn discover(resources: &Path, packs_dir: &Path) -> Vec { } else { return None; }; - pack(format!("file/{name}"), Text::literal(name), source) + pack_for(format!("file/{name}"), Text::literal(name), source, format) }) .collect(); user.sort_by(|a, b| a.id.cmp(&b.id)); - out.extend(user); - out + user } /// A file a pack provides: its path (folder packs) and its name inside the pack. type Provided = (Option, String); @@ -380,11 +395,45 @@ fn link(target: &Path, at: &Path) -> io::Result<()> { if target.is_dir() { Ok(()) } else { - fs::copy(target, at).map(|_| ()) + // The mirror only reads these files, and unix links them: a hardlink gives + // the same view without writing the 639 MB prepared root again, which was + // 33 s of a cold Windows launch with a pack on. Another volume (or a + // filesystem without hardlinks) still copies. + fs::hard_link(target, at).or_else(|_| fs::copy(target, at).map(|_| ())) } } +/// A directory junction, which Windows creates without privileges (a directory symlink needs +/// developer mode). The mirror falls back to copying the tree when it fails. +#[cfg(windows)] +fn junction(target: &Path, at: &Path) -> io::Result<()> { + use std::os::windows::process::CommandExt; + // cmd parses its own line: Rust quotes only paths with spaces, so `&` or `^` in an + // unquoted path would split or change the command. A Windows path cannot hold `"`. + let quoted = |path: &Path| -> io::Result { + let mut quoted = std::ffi::OsString::from("\""); + quoted.push(std::path::absolute(path)?); + quoted.push("\""); + Ok(quoted) + }; + let status = std::process::Command::new("cmd") + .args(["/C", "mklink", "/J"]) + .raw_arg(quoted(at)?) + .raw_arg(quoted(target)?) + .creation_flags(0x0800_0000) // CREATE_NO_WINDOW + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .status()?; + status + .success() + .then_some(()) + .ok_or_else(|| io::Error::other("mklink /J failed")) +} +#[cfg(not(windows))] +fn junction(_: &Path, _: &Path) -> io::Result<()> { + Err(io::ErrorKind::Unsupported.into()) +} /// Build `out` as `vanilla` with the packs' files layered on top (packs in -/// load order, last wins). Untouched directories are symlinks into vanilla. +/// load order, last wins). Untouched directories are links into vanilla. pub fn build_overlay(vanilla: &Path, packs: &[Pack], out: &Path) -> io::Result<()> { cache::build(vanilla, packs, out) } @@ -417,6 +466,22 @@ fn build_overlay_uncached( files.insert(name, (i, provided)); } } + // FallbackResourceManager: a file's `.mcmeta` counts only from the pack that + // supplies the file or one above it; vanilla's own sits below every pack. + let hidden: BTreeSet = files + .iter() + .filter(|(name, _)| !name.ends_with(".mcmeta")) + .filter_map(|(name, (owner, _))| { + let meta = format!("{name}.mcmeta"); + match files.get(&meta) { + Some((meta_owner, _)) if meta_owner >= owner => None, + _ => Some(meta), + } + }) + .collect(); + for name in &hidden { + files.remove(name); + } // Vanilla sits below every pack, so filters hide its files too. let filters: Vec<&Filter> = packs.iter().flat_map(|p| &p.filter).collect(); let touched_dirs: BTreeSet = files @@ -468,6 +533,7 @@ fn build_overlay_uncached( vanilla: &Path, out: &Path, touched: &BTreeSet, + hidden: &BTreeSet, files: &BTreeMap, merged: &BTreeMap>, filters: &[&Filter], @@ -504,6 +570,9 @@ fn build_overlay_uncached( } for name in names { let child = format!("{prefix}{name}"); + if hidden.contains(&child) { + continue; + } if let Some(bytes) = merged.get(&child) { fs::write(out.join(&child), bytes)?; } else if let Some((owner, (path, inner))) = files.get(&child) { @@ -516,11 +585,17 @@ fn build_overlay_uncached( } } } + } else if filters.is_empty() + && !touched.contains(&child) + && vanilla.join(&child).is_dir() + && junction(&vanilla.join(&child), &out.join(&child)).is_ok() + { + // Linked whole, like the unix symlink: no file in it is visited or copied. } else if touched.contains(&child) - // Windows cannot link a directory the way unix does (`link` above), so an - // untouched one has to be mirrored: skipping it hid textures/font and the - // title panorama from the overlay, and a font json that names those sheets - // then failed to load (#1688). + // Without a junction Windows cannot link a directory the way unix does (`link` + // above), so an untouched one has to be mirrored: skipping it hid textures/font + // and the title panorama from the overlay, and a font json that names those + // sheets then failed to load (#1688). || (vanilla.join(&child).is_dir() && (cfg!(not(unix)) || !filters.is_empty())) { mirror( @@ -528,6 +603,7 @@ fn build_overlay_uncached( vanilla, out, touched, + hidden, files, merged, filters, @@ -551,6 +627,7 @@ fn build_overlay_uncached( vanilla, out, &touched_dirs, + &hidden, &files, &merged, &filters, @@ -741,7 +818,9 @@ impl SelectionModel { /// `available` in any order; `selected` as options.txt lists them, /// bottom first. pub fn new(mut available: Vec, selected: &[String]) -> Self { - // PackRepository.available is a TreeMap by id (String.compareTo). + // PackRepository.discoverAvailable: every source's packs go into one + // TreeMap by id (String.compareTo), so `file/` packs sort among the + // built-in ones. available.sort_by(|a, b| a.id.encode_utf16().cmp(b.id.encode_utf16())); available.dedup_by(|a, b| a.id == b.id); let mut m = Self { @@ -939,7 +1018,8 @@ mod tests { } #[test] fn overlay_layers_packs_over_vanilla() { - let base = std::env::temp_dir().join(format!("horizon-packs-{}", std::process::id())); + // `&` would split an unquoted `cmd /C mklink` line. + let base = std::env::temp_dir().join(format!("horizon-packs-&{}", std::process::id())); let _ = fs::remove_dir_all(&base); let vanilla = base.join("vanilla"); fs::create_dir_all(vanilla.join("assets/minecraft/textures/block")).unwrap(); @@ -960,6 +1040,13 @@ mod tests { ) .unwrap(); fs::write(vanilla.join("facts.json"), b"[]").unwrap(); + // A directory no pack touches (#1688: a font json names sheets in it). + fs::create_dir_all(vanilla.join("assets/minecraft/textures/font")).unwrap(); + fs::write( + vanilla.join("assets/minecraft/textures/font/ascii.png"), + b"font", + ) + .unwrap(); let pack_dir = base.join("packs/Mine"); fs::create_dir_all(pack_dir.join("assets/minecraft/textures/block")).unwrap(); fs::write( @@ -997,6 +1084,17 @@ mod tests { br#"{"a":"vanilla","b":"pack"}"# ); assert_eq!(fs::read(out.join("facts.json")).unwrap(), b"[]"); + assert_eq!( + fs::read(out.join("assets/minecraft/textures/font/ascii.png")).unwrap(), + b"font" + ); + // Linked, not mirrored: a junction on Windows, a symlink on unix. + assert!( + fs::symlink_metadata(out.join("assets/minecraft/textures/font")) + .unwrap() + .file_type() + .is_symlink() + ); let _ = fs::remove_dir_all(base); } #[test] diff --git a/crates/horizon-client/src/packs/cache.rs b/crates/horizon-client-render/src/packs/cache.rs similarity index 92% rename from crates/horizon-client/src/packs/cache.rs rename to crates/horizon-client-render/src/packs/cache.rs index 51b7a9f1d5..ce8fd94481 100644 --- a/crates/horizon-client/src/packs/cache.rs +++ b/crates/horizon-client-render/src/packs/cache.rs @@ -3,13 +3,13 @@ use super::{Pack, Source, build_overlay_uncached}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; -#[cfg(any(test, not(unix)))] use std::io::Read; use std::{collections::BTreeSet, fs, io, path::Path, path::PathBuf}; // Bump when overlay/merge semantics change. Markers are disposable cache data. +// Version 4 mirrored every untouched directory file by file on Windows. // Version 3 could omit untouched vanilla directories on Windows (#1649, #1688). -const VERSION: u32 = 4; +const VERSION: u32 = 5; #[derive(Default, Serialize, Deserialize)] pub(super) struct Dependencies { @@ -59,7 +59,6 @@ fn field(hash: &mut Sha256, bytes: &[u8]) { hash.update((bytes.len() as u64).to_le_bytes()); hash.update(bytes); } -#[cfg(any(test, not(unix)))] fn content(hash: &mut Sha256, path: &Path) -> io::Result<()> { let mut file = fs::File::open(path)?; hash.update(file.metadata()?.len().to_le_bytes()); @@ -237,7 +236,10 @@ fn pack_signature(packs: &[Pack]) -> io::Result { } Source::Zip(path) => { field(&mut hash, absolute(path)?.as_os_str().as_encoded_bytes()); - stamp(&mut hash, path)?; + // By its bytes, not its stamp: a server pack found in the download cache is + // marked used on every join (`HttpUtil.updateModificationTime`), and a new + // time rebuilt the whole overlay each time. One file reads in a moment. + content(&mut hash, path)?; } } } @@ -348,6 +350,27 @@ mod tests { fs::create_dir_all(path.parent().unwrap()).unwrap(); fs::write(path, bytes).unwrap(); } + /// Waits for the filesystem's clock to move on, as it does between a build and a + /// person's next edit. On Unix the cache compares stamps (length, mtime, ctime, inode), + /// and Linux takes them from a coarse clock (a few milliseconds a tick): an edit in the + /// tick of the build before kept them all, and the tests failed at random. + fn next_tick(&self) { + let probe = self.0.with_extension("tick"); + // New bytes each time: NTFS keeps the time of a write that changes nothing. + let stamp = |n: u32| { + fs::write(&probe, n.to_le_bytes()).unwrap(); + fs::metadata(&probe).unwrap().modified().unwrap() + }; + let start = stamp(0); + // A second at most: no clock is that coarse, and a test never hangs on one. + for n in 1..1000 { + std::thread::sleep(std::time::Duration::from_millis(1)); + if stamp(n) != start { + break; + } + } + let _ = fs::remove_file(&probe); + } fn dir_pack(&self, name: &str) -> Pack { self.write( &format!("{name}/pack.mcmeta"), @@ -370,6 +393,7 @@ mod tests { output_signature(&cached).unwrap(), output_signature(&self.0.join("reference")).unwrap() ); + self.next_tick(); } } impl Drop for Fixture { @@ -646,6 +670,38 @@ mod tests { ); } + /// A ZIP whose bytes are the same keeps its overlay when only its time changes: a server + /// pack found in the download cache is marked used on every join + /// (`HttpUtil.updateModificationTime`), and rebuilding the overlay took most of a minute. + #[test] + fn a_zip_touched_but_unchanged_keeps_its_overlay() { + let f = Fixture::new(); + f.write("vanilla/assets/minecraft/textures/b", b"vanilla"); + let files = vec![ + ( + "pack.mcmeta", + br#"{"pack":{"description":"test","pack_format":97}}"#.to_vec(), + ), + ("assets/minecraft/textures/a", b"aaa".to_vec()), + ]; + f.write("pack.zip", &archive(&files, false)); + let zip = f.0.join("pack.zip"); + let pack = packs::pack("zip".into(), Text::empty(), Source::Zip(zip.clone())).unwrap(); + let (vanilla, out) = (f.0.join("vanilla"), f.0.join("cached")); + packs::build_overlay(&vanilla, std::slice::from_ref(&pack), &out).unwrap(); + // A rebuild empties the output first: this file survives a cache hit only. + fs::write(out.join("kept"), b"").unwrap(); + let later = std::time::SystemTime::now() + std::time::Duration::from_secs(60); + fs::OpenOptions::new() + .append(true) + .open(&zip) + .unwrap() + .set_modified(later) + .unwrap(); + packs::build_overlay(&vanilla, std::slice::from_ref(&pack), &out).unwrap(); + assert!(out.join("kept").exists(), "the overlay was rebuilt"); + } + #[cfg(unix)] #[test] fn unrelated_directory_pack_symlink_cycle_disables_reuse() { diff --git a/crates/horizon-client/src/par.rs b/crates/horizon-client-render/src/par.rs similarity index 100% rename from crates/horizon-client/src/par.rs rename to crates/horizon-client-render/src/par.rs diff --git a/crates/horizon-client/src/particles.rs b/crates/horizon-client-render/src/particles.rs similarity index 100% rename from crates/horizon-client/src/particles.rs rename to crates/horizon-client-render/src/particles.rs diff --git a/crates/horizon-client/src/poi_sections.rs b/crates/horizon-client-render/src/poi_sections.rs similarity index 100% rename from crates/horizon-client/src/poi_sections.rs rename to crates/horizon-client-render/src/poi_sections.rs diff --git a/crates/horizon-client/src/providers.rs b/crates/horizon-client-render/src/providers.rs similarity index 100% rename from crates/horizon-client/src/providers.rs rename to crates/horizon-client-render/src/providers.rs diff --git a/crates/horizon-client/src/screen.wgsl b/crates/horizon-client-render/src/screen.wgsl similarity index 100% rename from crates/horizon-client/src/screen.wgsl rename to crates/horizon-client-render/src/screen.wgsl diff --git a/crates/horizon-client/src/screen_draw.rs b/crates/horizon-client-render/src/screen_draw.rs similarity index 100% rename from crates/horizon-client/src/screen_draw.rs rename to crates/horizon-client-render/src/screen_draw.rs diff --git a/crates/horizon-client/src/screen_effects.rs b/crates/horizon-client-render/src/screen_effects.rs similarity index 100% rename from crates/horizon-client/src/screen_effects.rs rename to crates/horizon-client-render/src/screen_effects.rs diff --git a/crates/horizon-client/src/sculk.rs b/crates/horizon-client-render/src/sculk.rs similarity index 100% rename from crates/horizon-client/src/sculk.rs rename to crates/horizon-client-render/src/sculk.rs diff --git a/crates/horizon-client/src/settings.rs b/crates/horizon-client-render/src/settings.rs similarity index 94% rename from crates/horizon-client/src/settings.rs rename to crates/horizon-client-render/src/settings.rs index eea11d5627..9eae3cdca6 100644 --- a/crates/horizon-client/src/settings.rs +++ b/crates/horizon-client-render/src/settings.rs @@ -3,6 +3,7 @@ //! The option screens live in `options_ui`. use crate::keybinds::KeyBinds; use crate::options_file::{OptionsFile, V}; +use horizon_protocol::packets::configuration::ClientInformation; use std::{fs, path::Path}; pub use horizon_client_core::sound_source::SOUND_SOURCES; @@ -173,6 +174,12 @@ pub struct Settings { pub toggle_use: bool, /// Taps of the sprint key counted as double taps (sprintWindow). pub sprint_window: i32, + /// Show the operator items tab in a creative inventory. + pub operator_items_tab: bool, + /// Allow platform quit shortcuts to close the game window. + pub quit_shortcuts: bool, + /// On macOS, turn Control-click into a right click. + pub ctrl_click_emulates_right_click: bool, /// Horizon's own graphics settings (`horizon-graphics.txt`, not options.txt). pub horizon: crate::graphics::Graphics, /// Controller settings (`horizon-controller.txt`, Horizon's own). @@ -255,6 +262,9 @@ impl Default for Settings { toggle_attack: false, toggle_use: false, sprint_window: 7, + operator_items_tab: false, + quit_shortcuts: true, + ctrl_click_emulates_right_click: false, horizon: Default::default(), controller: Default::default(), extras: Default::default(), @@ -417,6 +427,9 @@ impl Settings { toggle_attack: f.bool("toggleAttack"), toggle_use: f.bool("toggleUse"), sprint_window: f.int("sprintWindow"), + operator_items_tab: f.bool("operatorItemsTab"), + quit_shortcuts: f.bool("quitShortcuts"), + ctrl_click_emulates_right_click: f.bool("ctrlClickEmulatesRightClick"), horizon: Default::default(), controller: Default::default(), extras: Default::default(), @@ -448,6 +461,16 @@ impl Settings { file: f, } } + /// `Options.createInformation`: what the server learns about this player. + pub fn client_information(&self, view_distance: i8) -> ClientInformation { + ClientInformation { + chat_visibility: self.chat_visibility as i32, + model_customisation: self.file.model_customisation(), + main_hand: i32::from(!self.main_hand_left), + allows_listing: self.file.bool("allowServerListing"), + ..ClientInformation::new(view_distance) + } + } /// The options file reflecting the typed fields; values the game did not /// change keep their stored form. pub fn to_file(&self) -> OptionsFile { @@ -507,6 +530,13 @@ impl Settings { put_b(&mut f, "toggleAttack", self.toggle_attack); put_b(&mut f, "toggleUse", self.toggle_use); put_i(&mut f, "sprintWindow", self.sprint_window); + put_b(&mut f, "operatorItemsTab", self.operator_items_tab); + put_b(&mut f, "quitShortcuts", self.quit_shortcuts); + put_b( + &mut f, + "ctrlClickEmulatesRightClick", + self.ctrl_click_emulates_right_click, + ); put_b(&mut f, "autoJump", self.auto_jump); put_s(&mut f, "lang", &self.language); put_b(&mut f, "forceUnicodeFont", self.force_unicode); @@ -653,7 +683,13 @@ pub enum Opt { MenuBlur, ToggleCrouch, ToggleSprint, + ToggleAttack, + ToggleUse, AutoJump, + SprintWindow, + QuitShortcuts, + CtrlClickEmulatesRightClick, + OperatorItemsTab, ChatVisibility, ChatColors, ChatLinks, @@ -706,6 +742,15 @@ pub enum Opt { PadGlyphs, PadMenuNav, PadKeyboard, + /// `options.modelPart.*`: one skin layer (index into `options_file::MODEL_PARTS`). + ModelPart(usize), + MainHand, + AllowServerListing, + RealmsNotifications, + InGameNotification, + SharePresence, + DirectionalAudio, + MusicToast, } #[cfg(test)] diff --git a/crates/horizon-client/src/shader_pack_list.rs b/crates/horizon-client-render/src/shader_pack_list.rs similarity index 100% rename from crates/horizon-client/src/shader_pack_list.rs rename to crates/horizon-client-render/src/shader_pack_list.rs diff --git a/crates/horizon-client/src/signs.rs b/crates/horizon-client-render/src/signs.rs similarity index 100% rename from crates/horizon-client/src/signs.rs rename to crates/horizon-client-render/src/signs.rs diff --git a/crates/horizon-client/src/skins.rs b/crates/horizon-client-render/src/skins.rs similarity index 91% rename from crates/horizon-client/src/skins.rs rename to crates/horizon-client-render/src/skins.rs index 0393e75d0e..53f6c8ed30 100644 --- a/crates/horizon-client/src/skins.rs +++ b/crates/horizon-client-render/src/skins.rs @@ -491,15 +491,69 @@ pub fn texture_hash(url: &str) -> Option { /// `SkinManager.TextureCache.registerTexture`: the texture id of a skin, /// `skins/` and the SHA-1 of its hash's UTF-16 code units. pub fn skin_location(hash: &str) -> String { + location("skins/", hash) +} +/// `registerTexture` in the cape cache: `capes/` and the same SHA-1. +pub fn cape_location(hash: &str) -> String { + location("capes/", hash) +} +fn location(prefix: &str, hash: &str) -> String { let units: Vec = hash.encode_utf16().flat_map(u16::to_le_bytes).collect(); let digest = sha1(&units); - let mut out = String::from("skins/"); + let mut out = String::from(prefix); for b in digest { out.push_str(&format!("{b:02x}")); } out } +/// `PlayerSkin.cape`: the cape texture of textures whose lookup gave a skin +/// (every download ready), if they name one. +pub fn cape(textures: &Textures) -> Option { + textures + .cape + .as_deref() + .and_then(texture_hash) + .map(|hash| cape_location(&hash)) +} + +/// A cape in a 64x64 skin slot: the 64x32 picture (or the older 22x17, +/// which has the same layout) in its top half. Vanilla draws a cape of any +/// size; a larger one is scaled down to 64x32 here. +pub fn process_cape(width: u32, height: u32, rgba: &[u8]) -> Option> { + let (w, h) = (width as usize, height as usize); + if w == 0 || h == 0 || rgba.len() < w * h * 4 { + return None; + } + let mut img = vec![0u8; 64 * 64 * 4]; + let (fit_w, fit_h) = if w <= 64 && h <= 32 { (w, h) } else { (64, 32) }; + for y in 0..fit_h { + for x in 0..fit_w { + let (sx, sy) = (x * w / fit_w, y * h / fit_h); + let (s, d) = ((sy * w + sx) * 4, (y * 64 + x) * 4); + img[d..d + 4].copy_from_slice(&rgba[s..s + 4]); + } + } + Some(img) +} + +/// The cape the launcher gives an offline name (`HORIZON_OFFLINE_CAPE`, a +/// PNG's path; launcher/src/skins.rs), read once as `process_cape` lays it out. +pub fn offline_cape() -> Option<&'static [u8]> { + static CAPE: std::sync::OnceLock>> = std::sync::OnceLock::new(); + CAPE.get_or_init(|| { + let path = std::env::var_os("HORIZON_OFFLINE_CAPE").filter(|p| !p.is_empty())?; + let image = std::fs::read(&path) + .map_err(|e| e.to_string()) + .and_then(|bytes| crate::hud::decode_bytes(&bytes).map_err(|e| e.to_string())) + .and_then(|(w, h, rgba)| process_cape(w, h, &rgba).ok_or("not an image".into())); + image + .inspect_err(|e| eprintln!("Horizon: no offline cape ({}): {e}", path.display())) + .ok() + }) + .as_deref() +} + /// Where a texture download stands. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Download { @@ -650,9 +704,10 @@ impl Downloader { while let Ok((key, image)) = self.done.try_recv() { let state = match image { Some(rgba) => { - if key.0 == Kind::Skin { - let location = skin_location(&key.1); - self.queue(location, rgba); + match key.0 { + Kind::Skin => self.queue(skin_location(&key.1), rgba), + Kind::Cape => self.queue(cape_location(&key.1), rgba), + Kind::Elytra => {} } Download::Ready } @@ -685,7 +740,8 @@ impl Downloader { .and_then(|b| crate::hud::decode_bytes(&b).ok()) .and_then(|(w, h, rgba)| match kind { Kind::Skin => process_legacy_skin(w, h, &rgba), - _ => Some(rgba), + Kind::Cape => process_cape(w, h, &rgba), + Kind::Elytra => Some(rgba), }); let _ = send.send((key, image)); }); @@ -716,16 +772,21 @@ impl Downloader { if self.queued.contains(location) { return true; } - let Some(digest) = location.strip_prefix("skins/") else { - return false; - }; + let (digest, process): (_, fn(u32, u32, &[u8]) -> Option>) = + if let Some(digest) = location.strip_prefix("skins/") { + (digest, process_legacy_skin) + } else if let Some(digest) = location.strip_prefix("capes/") { + (digest, process_cape) + } else { + return false; + }; let image = self.images.get(location).cloned().or_else(|| { let file = self .root .join(if digest.len() > 2 { &digest[..2] } else { "xx" }) .join(digest); let (w, h, rgba) = crate::hud::decode_bytes(&std::fs::read(&file).ok()?).ok()?; - process_legacy_skin(w, h, &rgba) + process(w, h, &rgba) }); let Some(image) = image else { return false; @@ -961,6 +1022,31 @@ mod tests { assert_eq!(d.take_ready(), vec![("skins/def".to_owned(), vec![4])]); } + /// A cape fills the top half of a 64x64 slot; a larger one is scaled to 64x32. + #[test] + fn capes_fill_the_top_half_of_a_slot() { + let at = |x: usize, y: usize| (y * 64 + x) * 4; + let legacy = super::process_cape(22, 17, &[7; 22 * 17 * 4]).unwrap(); + assert_eq!(legacy.len(), 64 * 64 * 4); + assert_eq!(legacy[at(21, 16)], 7); + assert_eq!(legacy[at(22, 16)], 0); + let mut hd = vec![0u8; 128 * 64 * 4]; + hd[(62 * 128 + 126) * 4] = 9; + let hd = super::process_cape(128, 64, &hd).unwrap(); + assert_eq!(hd[at(63, 31)], 9); + assert_eq!(hd[at(0, 32)], 0); + assert!(super::process_cape(64, 32, &[0; 4]).is_none()); + let textures = super::Textures { + cape: Some("http://textures.minecraft.net/texture/abc".into()), + ..Default::default() + }; + let cape = super::cape(&textures).unwrap(); + assert_eq!( + cape["capes/".len()..], + super::skin_location("abc")["skins/".len()..] + ); + } + #[test] fn profile_components_supply_unsigned_head_skins() { let value = "eyJ0ZXh0dXJlcyI6IHsiU0tJTiI6IHsidXJsIjogImh0dHBzOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2FiYzEyMyJ9fX0="; diff --git a/crates/horizon-client/src/sound_engine.rs b/crates/horizon-client-render/src/sound_engine.rs similarity index 95% rename from crates/horizon-client/src/sound_engine.rs rename to crates/horizon-client-render/src/sound_engine.rs index f3e2dd75d0..2ab8c3256c 100644 --- a/crates/horizon-client/src/sound_engine.rs +++ b/crates/horizon-client-render/src/sound_engine.rs @@ -58,6 +58,8 @@ pub enum Ticking { Bee { bee: u32, aggressive: bool }, /// `GuardianAttackSoundInstance`. GuardianAttack { guardian: u32 }, + /// `ElytraOnPlayerSoundInstance`: ticks since it started. + Elytra { time: i32 }, } /// What a minecart's sound instances read about it. #[derive(Clone, Copy, Debug)] @@ -95,6 +97,12 @@ pub struct TickContext { pub mobs: Vec, /// `!tickRateManager().runsNormally()`. pub frozen: bool, + /// The player's position. + pub player_position: [f64; 3], + /// `getDeltaMovement().lengthSqr()` of the player. + pub player_speed_sqr: f64, + /// `isFallFlying()` of the player. + pub player_fall_flying: bool, } /// `AbstractSoundInstance` and its subclasses' fields. #[derive(Clone, Debug)] @@ -390,6 +398,24 @@ impl SoundInstance { }; s } + /// `new ElytraOnPlayerSoundInstance(player)`: looping at volume 0.1 until + /// its first tick. + pub fn elytra(position: [f64; 3], random: LegacyRandom) -> Self { + let mut s = Self::new( + "minecraft:item.elytra.flying", + PLAYERS, + 0.1, + 1.0, + random, + true, + 0, + Attenuation::Linear, + position, + false, + ); + s.ticking = Ticking::Elytra { time: 0 }; + s + } pub fn tickable(&self) -> bool { self.ticking != Ticking::No } @@ -494,6 +520,37 @@ impl SoundInstance { self.volume = 0.0; } } + Ticking::Elytra { time } => { + *time += 1; + let time = *time; + if ctx.player_removed || (time > 20 && !ctx.player_fall_flying) { + self.stop(); + return; + } + let [x, y, z] = ctx.player_position; + self.position = [ + f64::from(x as f32), + f64::from(y as f32), + f64::from(z as f32), + ]; + let speed = ctx.player_speed_sqr as f32; + self.volume = if speed >= 1.0e-7 { + (speed / 4.0).clamp(0.0, 1.0) + } else { + 0.0 + }; + // Silent for a second, then a second's fade in. + if time < 20 { + self.volume = 0.0; + } else if time < 40 { + self.volume *= (time - 20) as f32 / 20.0; + } + self.pitch = if self.volume > 0.8 { + 1.0 + (self.volume - 0.8) + } else { + 1.0 + }; + } Ticking::GuardianAttack { guardian } => { let id = *guardian; // The client's guardian never has a target of its own, so @@ -1231,7 +1288,7 @@ impl Sounds for Bound<'_, B> { .update_category_volume(source, gain, self.backend); } } -#[cfg(test)] +#[cfg(any(test, feature = "test-support"))] pub mod test_backend { use super::{Backend, ChannelId}; use crate::sound_events::Sound; diff --git a/crates/horizon-client/src/sound_events.rs b/crates/horizon-client-render/src/sound_events.rs similarity index 85% rename from crates/horizon-client/src/sound_events.rs rename to crates/horizon-client-render/src/sound_events.rs index 2d7b2e21a7..ef35bbb15d 100644 --- a/crates/horizon-client/src/sound_events.rs +++ b/crates/horizon-client-render/src/sound_events.rs @@ -6,7 +6,11 @@ //! instance's random. use horizon_core::random::RandomSource; use serde_json::Value; -use std::{collections::HashMap, fs, path::Path}; +use std::{ + collections::{HashMap, HashSet}, + fs, + path::Path, +}; /// `SoundManager.EMPTY_SOUND_LOCATION`. pub const EMPTY: &str = "minecraft:empty"; @@ -224,6 +228,34 @@ fn parse_sound(v: &Value) -> Option { sentinel: Sentinel::None, }) } +/// The files under a namespace's `sounds/`, as `sounds/` with `/`. +fn sound_files(namespace: &Path) -> HashSet { + let mut files = HashSet::new(); + let mut stack = vec![namespace.join("sounds")]; + while let Some(dir) = stack.pop() { + let Ok(entries) = fs::read_dir(&dir) else { + continue; + }; + for e in entries.flatten() { + let path = e.path(); + // The listing's own type; a link is followed, as `is_file` does. + let Ok(kind) = e.file_type() else { + continue; + }; + let (dir, file) = if kind.is_symlink() { + (path.is_dir(), path.is_file()) + } else { + (kind.is_dir(), kind.is_file()) + }; + if dir { + stack.push(path); + } else if file && let Ok(rel) = path.strip_prefix(namespace) { + files.insert(rel.to_string_lossy().replace('\\', "/")); + } + } + } + files +} impl SoundRegistry { /// Every `assets//sounds.json` under a resource root, files /// validated against `assets//sounds/`. @@ -236,13 +268,19 @@ impl SoundRegistry { .filter_map(|e| e.file_name().to_str().map(str::to_owned)) .collect(); namespaces.sort(); + // Each namespace's sound files are listed once, as the resource manager + // lists a pack, instead of one file lookup per sound (thousands). + let files: HashMap<&str, HashSet> = namespaces + .iter() + .map(|ns| (ns.as_str(), sound_files(&assets.join(ns)))) + .collect(); + let exists = |ns: &str, path: &str| files.get(ns).is_some_and(|f| f.contains(path)); let mut registry = Self::default(); - for namespace in namespaces { - let Ok(text) = fs::read(assets.join(&namespace).join("sounds.json")) else { + for namespace in &namespaces { + let Ok(text) = fs::read(assets.join(namespace).join("sounds.json")) else { continue; }; - let exists = |ns: &str, path: &str| assets.join(ns).join(path).is_file(); - registry.add_file(&namespace, &text, &exists); + registry.add_file(namespace, &text, &exists); } registry } @@ -369,6 +407,24 @@ mod tests { use super::*; use horizon_core::random::LegacyRandom; + #[test] + fn load_keeps_the_sound_files_a_root_holds() { + let root = std::env::temp_dir().join(format!("horizon-sounds-{}", std::process::id())); + let ns = root.join("assets/minecraft"); + fs::create_dir_all(ns.join("sounds/block/note")).unwrap(); + fs::write(ns.join("sounds/block/note/harp.ogg"), b"").unwrap(); + fs::write( + ns.join("sounds.json"), + br#"{"note": {"sounds": ["block/note/harp", "block/note/gone"]}}"#, + ) + .unwrap(); + let r = SoundRegistry::load(&root); + let _ = fs::remove_dir_all(&root); + let sounds = &r.get("minecraft:note").unwrap().sounds; + let kept: Vec<&str> = sounds.iter().map(|s| s.location.as_str()).collect(); + assert_eq!(kept, ["minecraft:block/note/harp"]); + } + #[test] fn references_weigh_as_much_as_their_event() { let mut r = SoundRegistry::default(); diff --git a/crates/horizon-client/src/spans.rs b/crates/horizon-client-render/src/spans.rs similarity index 100% rename from crates/horizon-client/src/spans.rs rename to crates/horizon-client-render/src/spans.rs diff --git a/crates/horizon-client-render/src/split_layout.rs b/crates/horizon-client-render/src/split_layout.rs new file mode 100644 index 0000000000..a8963492b0 --- /dev/null +++ b/crates/horizon-client-render/src/split_layout.rs @@ -0,0 +1,272 @@ +//! Splitscreen's layout, pure data tested without a window: where each player's view goes in +//! its window (`place`). +use winit::dpi::PhysicalSize; + +/// Players at most: P1 and fifteen more. +pub const MAX_PLAYERS: usize = 16; + +/// A player's view: its window (an index into the windows), its corner there and its size. +pub type View = (usize, [u32; 2], PhysicalSize); + +/// Rows of five to sixteen players sharing a window, widest first (columns on a tall window): every +/// view stays near a screen's shape, where a full grid would leave a long thin one. +const ROWS: [&[u32]; MAX_PLAYERS - 4] = [ + &[3, 2], + &[3, 3], + &[3, 2, 2], + &[3, 3, 2], + &[3, 3, 3], + &[4, 3, 3], + &[4, 4, 3], + &[4, 4, 4], + &[4, 3, 3, 3], + &[4, 4, 3, 3], + &[4, 4, 4, 3], + &[4, 4, 4, 4], +]; + +/// `order` made a whole order of `n` players: those it names (once, if there are), then the +/// others, P1 first. +pub fn ordered(order: &[usize], n: usize) -> Vec { + let mut out: Vec = Vec::with_capacity(n); + for &i in order { + if i < n && !out.contains(&i) { + out.push(i); + } + } + out.extend((0..n).filter(|i| !order.contains(i))); + out +} + +/// `place` with the players sharing a window in `order` (`ordered`): the first left or on top. +/// The views are still by player. +pub fn place_ordered( + windows: &[usize], + order: &[usize], + sizes: &[PhysicalSize], + two: u8, + three: u8, +) -> Vec { + let order = ordered(order, windows.len()); + let in_order: Vec = order.iter().map(|&i| windows[i]).collect(); + let placed = place(&in_order, sizes, two, three); + let mut views = placed.clone(); + for (k, &i) in order.iter().enumerate() { + views[i] = placed[k]; + } + views +} + +/// Each player's view, given the window each is in and the windows' sizes. Players in one window +/// share it in the order they come: two take halves (`two`: 0 side by side when wider than +/// tall, 1 side by side, 2 top and bottom); three (`three`: 0 the first wide on top and two below, +/// 1 the first tall on the left and two on the right, 2 three columns); four its quarters; five +/// to sixteen in balanced rows (`ROWS`). +pub fn place(windows: &[usize], sizes: &[PhysicalSize], two: u8, three: u8) -> Vec { + windows + .iter() + .enumerate() + .map(|(i, &window)| { + let here = windows.iter().filter(|&&w| w == window).count(); + let slot = windows[..i].iter().filter(|&&w| w == window).count() as u32; + let size = sizes + .get(window) + .copied() + .unwrap_or(PhysicalSize::new(1, 1)); + let (w, h) = (size.width, size.height); + let side_by_side = match two { + 1 => true, + 2 => false, + _ => w >= h, + }; + // The view's cell as fractions of the window: (x0, x1, y0, y1) in halves or thirds. + let cell = |x0: u32, x1: u32, y0: u32, y1: u32, of: u32| { + [w * x0 / of, w * x1 / of, h * y0 / of, h * y1 / of] + }; + let [x0, x1, y0, y1] = match (here, slot) { + (1, _) => cell(0, 1, 0, 1, 1), + (2, s) if side_by_side => cell(s, s + 1, 0, 2, 2), + (2, s) => cell(0, 2, s, s + 1, 2), + (3, s) => match (three, s) { + (2, s) => cell(s, s + 1, 0, 3, 3), + (1, 0) => cell(0, 1, 0, 2, 2), + (1, s) => cell(1, 2, s - 1, s, 2), + (_, 0) => cell(0, 2, 0, 1, 2), + (_, s) => cell(s - 1, s, 1, 2, 2), + }, + (4, s) => cell(s % 2, s % 2 + 1, s / 2, s / 2 + 1, 2), + (n, s) => rows(n, s, w, h), + }; + ( + window, + [x0, y0], + PhysicalSize::new((x1 - x0).max(1), (y1 - y0).max(1)), + ) + }) + .collect() +} + +/// Five to sixteen players: `slot`'s cell `[x0, x1, y0, y1]` in balanced rows (`ROWS`), or +/// in columns when the window is taller than wide. +fn rows(n: usize, slot: u32, w: u32, h: u32) -> [u32; 4] { + let counts = ROWS[n.clamp(5, MAX_PLAYERS) - 5]; + let bands = counts.len() as u32; + let (mut band, mut k) = (0, slot); + while band + 1 < bands && k >= counts[band as usize] { + k -= counts[band as usize]; + band += 1; + } + let across = counts[band as usize]; + // Along: the long side, divided between a band's views; down: the bands. + let (along, down) = if w >= h { (w, h) } else { (h, w) }; + let (a0, a1) = (along * k / across, along * (k + 1) / across); + let (d0, d1) = (down * band / bands, down * (band + 1) / bands); + if w >= h { + [a0, a1, d0, d1] + } else { + [d0, d1, a0, a1] + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn size(w: u32, h: u32) -> PhysicalSize { + PhysicalSize::new(w, h) + } + + #[test] + fn players_on_one_screen_share_it() { + let wide = [size(1920, 1080)]; + assert_eq!( + place(&[0, 0], &wide, 0, 0)[1], + (0, [960, 0], size(960, 1080)) + ); + assert_eq!( + place(&[0, 0], &wide, 2, 0)[1], + (0, [0, 540], size(1920, 540)) + ); + // Auto on a tall screen: top and bottom. + assert_eq!(place(&[0, 0], &[size(1080, 1920)], 0, 0)[1].1, [0, 960]); + assert_eq!( + place(&[0; 4], &wide, 0, 0)[3], + (0, [960, 540], size(960, 540)) + ); + } + + #[test] + fn desk_and_couch() { + // P1 alone at the desk; P2 and P3 share the TV. + let screens = [size(3024, 1964), size(3840, 2160)]; + let views = place(&[0, 1, 1], &screens, 0, 0); + assert_eq!(views[0], (0, [0, 0], size(3024, 1964))); + assert_eq!(views[1], (1, [0, 0], size(1920, 2160))); + assert_eq!(views[2], (1, [1920, 0], size(1920, 2160))); + } + + #[test] + fn three_on_a_screen() { + let tv = [size(1920, 1080)]; + // One wide on top, two below. + let top = place(&[0; 3], &tv, 0, 0); + assert_eq!(top[0], (0, [0, 0], size(1920, 540))); + assert_eq!(top[2], (0, [960, 540], size(960, 540))); + // One tall on the left, two stacked on the right. + let left = place(&[0; 3], &tv, 0, 1); + assert_eq!(left[0], (0, [0, 0], size(960, 1080))); + assert_eq!(left[2], (0, [960, 540], size(960, 540))); + // Three columns. + assert_eq!( + place(&[0; 3], &tv, 0, 2)[2], + (0, [1280, 0], size(640, 1080)) + ); + } + + #[test] + fn two_windows_two_players_each() { + // P1 and P2 at the desk, P3 and P4 on the TV: halves in each window. + let screens = [size(3024, 1964), size(3840, 2160)]; + let views = place(&[0, 0, 1, 1], &screens, 0, 0); + assert_eq!(views[1], (0, [1512, 0], size(1512, 1964))); + assert_eq!(views[2], (1, [0, 0], size(1920, 2160))); + assert_eq!(views[3], (1, [1920, 0], size(1920, 2160))); + } + + #[test] + fn five_to_eight_in_rows_of_three_and_two() { + let tv = [size(1920, 1080)]; + let eight = place(&[0; 8], &tv, 0, 0); + assert_eq!(eight[0], (0, [0, 0], size(640, 360))); + assert_eq!(eight[5], (0, [1280, 360], size(640, 360))); + assert_eq!(eight[7], (0, [960, 720], size(960, 360))); + let seven = place(&[0; 7], &tv, 0, 0); + assert_eq!(seven[3], (0, [0, 360], size(960, 360))); + // Every count tiles the window exactly: no gap, no overlap. + for n in 5..=MAX_PLAYERS { + for screen in [size(1920, 1080), size(1080, 1920), size(1711, 997)] { + let views = place(&vec![0; n], &[screen], 0, 0); + for (i, (_, [x, y], size)) in views.iter().enumerate() { + assert!(x + size.width <= screen.width && y + size.height <= screen.height); + for (_, [ox, oy], other) in &views[..i] { + assert!( + x + size.width <= *ox + || ox + other.width <= *x + || y + size.height <= *oy + || oy + other.height <= *y, + "overlapping views for {n} in {screen:?}" + ); + } + } + let area: u64 = views + .iter() + .map(|v| u64::from(v.2.width) * u64::from(v.2.height)) + .sum(); + assert_eq!( + area, + u64::from(screen.width) * u64::from(screen.height), + "{n} in {screen:?}" + ); + } + } + // A tall window: columns. + let tall = place(&[0; 5], &[size(1080, 1920)], 0, 0); + assert_eq!(tall[0], (0, [0, 0], size(540, 640))); + assert_eq!(tall[4], (0, [540, 960], size(540, 960))); + } + + #[test] + fn sixteen_players_form_a_four_by_four_grid() { + let views = place(&[0; 16], &[size(3840, 2160)], 0, 0); + for (i, view) in views.iter().enumerate() { + assert_eq!( + *view, + ( + 0, + [(i as u32 % 4) * 960, (i as u32 / 4) * 540], + size(960, 540) + ) + ); + } + } + + #[test] + fn an_order_names_each_player_once_and_the_others_come_last() { + assert_eq!(ordered(&[], 3), [0, 1, 2]); + assert_eq!(ordered(&[2, 0], 3), [2, 0, 1]); + // Gone players and repeats are dropped. + assert_eq!(ordered(&[5, 1, 1], 3), [1, 0, 2]); + } + + #[test] + fn players_sharing_a_window_fill_it_in_their_order() { + let wide = [size(2000, 1000)]; + // P2 first: P2 on the left, P1 on the right. + let views = place_ordered(&[0, 0], &[1, 0], &wide, 0, 0); + assert_eq!(views[1].1, [0, 0]); + assert_eq!(views[0].1, [1000, 0]); + // P3 first of three: the wide view on top is P3's. + let views = place_ordered(&[0, 0, 0], &[2], &wide, 0, 0); + assert_eq!(views[2].2, size(2000, 500)); + } +} diff --git a/crates/horizon-client-render/src/sprite_cache.rs b/crates/horizon-client-render/src/sprite_cache.rs new file mode 100644 index 0000000000..c440e6ccf9 --- /dev/null +++ b/crates/horizon-client-render/src/sprite_cache.rs @@ -0,0 +1,345 @@ +//! Decoded block sprites kept on disk between launches (M-11): reading and +//! decoding ~1300 small PNGs is most of `Sprites::load`. One file per resource +//! root, valid only while the `textures` trees still have the same paths, +//! sizes and modification times and the format version is the same; anything +//! else (missing, corrupt, stale, unwritable) means the normal decoding path. +use crate::mipmap::Strategy; +use std::{ + collections::HashMap, + fs, + hash::{DefaultHasher, Hash, Hasher}, + path::{Path, PathBuf}, +}; + +const MAGIC: &[u8; 4] = b"HSPR"; +/// Bump when `Decoded` or the decoding in `Sprites::decode` changes. +const VERSION: u32 = 1; +const FILE: &str = "horizon-sprites.cache"; + +/// What decoding one texture gives, before it takes a layer. +#[derive(Clone)] +pub(crate) struct Decoded { + pub side: u32, + pub frames: Vec>, + /// The `.mcmeta` `animation` value, as text. + pub animation: Option, + pub strategy: Strategy, + pub alpha_cutoff_bias: f32, + pub has_transparent: bool, + pub opaque: bool, + pub translucent: bool, +} + +pub(crate) struct SpriteCache { + path: PathBuf, + fingerprint: u64, + entries: HashMap, + dirty: bool, +} + +/// Paths, sizes and modification times of every file under `assets/*/textures`, +/// summed so the directory order does not matter. +fn fingerprint(root: &Path) -> Option { + fn walk(dir: &Path, base: &Path, sum: &mut u64) -> Option<()> { + for entry in fs::read_dir(dir).ok()? { + let entry = entry.ok()?; + let meta = entry.metadata().ok()?; + if meta.is_dir() { + walk(&entry.path(), base, sum)?; + continue; + } + let mut hasher = DefaultHasher::new(); + entry.path().strip_prefix(base).ok()?.hash(&mut hasher); + meta.len().hash(&mut hasher); + meta.modified().ok()?.hash(&mut hasher); + *sum = sum.wrapping_add(hasher.finish()); + } + Some(()) + } + let mut sum = 0; + for namespace in fs::read_dir(root.join("assets")).ok()? { + let textures = namespace.ok()?.path().join("textures"); + if textures.is_dir() { + walk(&textures, root, &mut sum)?; + } + } + Some(sum) +} + +impl SpriteCache { + /// The cache of `root`: its stored entries when still valid, else empty. + /// `None` when the textures cannot be fingerprinted (no caching then). + pub fn open(root: &Path) -> Option { + let fingerprint = fingerprint(root)?; + let path = root.join(FILE); + let entries = fs::read(&path) + .ok() + .and_then(|bytes| parse(&bytes, fingerprint)) + .unwrap_or_default(); + Some(Self { + path, + fingerprint, + entries, + dirty: false, + }) + } + pub fn get(&self, name: &str) -> Option<&Decoded> { + self.entries.get(name) + } + pub fn insert(&mut self, name: &str, decoded: Decoded) { + self.entries.insert(name.to_owned(), decoded); + self.dirty = true; + } + /// Write new entries; a failure only costs the next launch the decoding. + pub fn save(&mut self) { + if !self.dirty { + return; + } + self.dirty = false; + let temporary = self + .path + .with_extension(format!("{}.tmp", std::process::id())); + if fs::write(&temporary, serialize(self.fingerprint, &self.entries)).is_ok() + && fs::rename(&temporary, &self.path).is_err() + { + let _ = fs::remove_file(&temporary); + } + } +} + +fn serialize(fingerprint: u64, entries: &HashMap) -> Vec { + let mut out = Vec::new(); + out.extend_from_slice(MAGIC); + out.extend_from_slice(&VERSION.to_le_bytes()); + out.extend_from_slice(&fingerprint.to_le_bytes()); + out.extend_from_slice(&(entries.len() as u32).to_le_bytes()); + for (name, d) in entries { + put_bytes(&mut out, name.as_bytes()); + out.extend_from_slice(&d.side.to_le_bytes()); + out.extend_from_slice(&(d.frames.len() as u32).to_le_bytes()); + for frame in &d.frames { + put_bytes(&mut out, frame); + } + put_bytes(&mut out, d.animation.as_deref().unwrap_or("").as_bytes()); + out.push(u8::from(d.animation.is_some())); + out.push(d.strategy as u8); + out.extend_from_slice(&d.alpha_cutoff_bias.to_bits().to_le_bytes()); + out.push( + u8::from(d.has_transparent) | u8::from(d.opaque) << 1 | u8::from(d.translucent) << 2, + ); + } + out +} +fn put_bytes(out: &mut Vec, bytes: &[u8]) { + out.extend_from_slice(&(bytes.len() as u32).to_le_bytes()); + out.extend_from_slice(bytes); +} + +struct Reader<'a>(&'a [u8]); +impl<'a> Reader<'a> { + fn take(&mut self, n: usize) -> Option<&'a [u8]> { + let (head, tail) = self.0.split_at_checked(n)?; + self.0 = tail; + Some(head) + } + fn u32(&mut self) -> Option { + Some(u32::from_le_bytes(self.take(4)?.try_into().ok()?)) + } + fn bytes(&mut self) -> Option<&'a [u8]> { + let n = self.u32()? as usize; + self.take(n) + } +} +fn parse(bytes: &[u8], fingerprint: u64) -> Option> { + let mut r = Reader(bytes); + if r.take(4)? != MAGIC || r.u32()? != VERSION { + return None; + } + if u64::from_le_bytes(r.take(8)?.try_into().ok()?) != fingerprint { + return None; + } + let count = r.u32()?; + let mut entries = HashMap::new(); + for _ in 0..count { + let name = String::from_utf8(r.bytes()?.to_vec()).ok()?; + let side = r.u32()?; + let frame_count = r.u32()?; + let mut frames = Vec::new(); + for _ in 0..frame_count { + frames.push(r.bytes()?.to_vec()); + } + let animation = String::from_utf8(r.bytes()?.to_vec()).ok()?; + let has_animation = r.take(1)?[0] != 0; + let strategy = match r.take(1)?[0] { + 0 => Strategy::Auto, + 1 => Strategy::Mean, + 2 => Strategy::Cutout, + 3 => Strategy::StrictCutout, + 4 => Strategy::DarkCutout, + _ => return None, + }; + let alpha_cutoff_bias = f32::from_bits(r.u32()?); + let flags = r.take(1)?[0]; + let frame_len = (side as usize).checked_mul(side as usize)?.checked_mul(4)?; + if frames.is_empty() || frames.iter().any(|f| f.len() != frame_len) { + return None; + } + entries.insert( + name, + Decoded { + side, + frames, + animation: has_animation.then_some(animation), + strategy, + alpha_cutoff_bias, + has_transparent: flags & 1 != 0, + opaque: flags & 2 != 0, + translucent: flags & 4 != 0, + }, + ); + } + r.0.is_empty().then_some(entries) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn sample(blue: u8, animation: Option<&str>) -> Decoded { + Decoded { + side: 2, + frames: vec![vec![blue; 16], vec![blue + 1; 16]], + animation: animation.map(str::to_owned), + strategy: Strategy::Cutout, + alpha_cutoff_bias: 0.25, + has_transparent: true, + opaque: false, + translucent: true, + } + } + fn scratch(tag: &str) -> PathBuf { + let dir = + std::env::temp_dir().join(format!("horizon-sprite-cache-{tag}-{}", std::process::id())); + let _ = fs::remove_dir_all(&dir); + fs::create_dir_all(dir.join("assets/minecraft/textures/block")).unwrap(); + dir + } + #[test] + fn round_trips() { + let mut entries = HashMap::new(); + entries.insert("a:b".to_owned(), sample(1, Some("{\"frametime\":2}"))); + entries.insert("a:c".to_owned(), sample(9, None)); + let back = parse(&serialize(7, &entries), 7).unwrap(); + for (name, d) in &entries { + let e = &back[name]; + assert_eq!( + (d.side, &d.frames, &d.animation), + (e.side, &e.frames, &e.animation) + ); + assert_eq!(d.alpha_cutoff_bias, e.alpha_cutoff_bias); + assert_eq!( + (d.has_transparent, d.opaque, d.translucent, d.strategy as u8), + (e.has_transparent, e.opaque, e.translucent, e.strategy as u8) + ); + } + } + #[test] + fn wrong_fingerprint_truncation_and_garbage_are_misses() { + let mut entries = HashMap::new(); + entries.insert("a:b".to_owned(), sample(1, None)); + let bytes = serialize(7, &entries); + assert!(parse(&bytes, 8).is_none()); + for cut in 0..bytes.len() { + assert!(parse(&bytes[..cut], 7).is_none(), "cut {cut}"); + } + assert!(parse(b"garbage", 7).is_none()); + let mut extra = bytes.clone(); + extra.push(0); + assert!(parse(&extra, 7).is_none()); + } + #[test] + fn changed_texture_invalidates_and_corrupt_file_is_ignored() { + let root = scratch("invalidate"); + let png = root.join("assets/minecraft/textures/block/x.png"); + fs::write(&png, b"one").unwrap(); + let mut cache = SpriteCache::open(&root).unwrap(); + cache.insert("minecraft:block/x", sample(1, None)); + cache.save(); + assert!( + SpriteCache::open(&root) + .unwrap() + .get("minecraft:block/x") + .is_some() + ); + // Same length, new content and time: still stale. + std::thread::sleep(std::time::Duration::from_millis(30)); + fs::write(&png, b"two").unwrap(); + assert!( + SpriteCache::open(&root) + .unwrap() + .get("minecraft:block/x") + .is_none() + ); + // A new file also invalidates. + let mut cache = SpriteCache::open(&root).unwrap(); + cache.insert("minecraft:block/x", sample(1, None)); + cache.save(); + fs::write(root.join("assets/minecraft/textures/block/y.png"), b"y").unwrap(); + assert!( + SpriteCache::open(&root) + .unwrap() + .get("minecraft:block/x") + .is_none() + ); + // Corrupt cache file: empty, no error. + fs::write(root.join(FILE), b"HSPR junk").unwrap(); + assert!( + SpriteCache::open(&root) + .unwrap() + .get("minecraft:block/x") + .is_none() + ); + let _ = fs::remove_dir_all(&root); + } + fn write_png(path: &Path, rgba: [u8; 4]) { + let mut encoder = png::Encoder::new(fs::File::create(path).unwrap(), 16, 16); + encoder.set_color(png::ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + let pixels: Vec = rgba.iter().copied().cycle().take(16 * 16 * 4).collect(); + encoder + .write_header() + .unwrap() + .write_image_data(&pixels) + .unwrap(); + } + fn sprite_pixels(root: &Path) -> Vec { + let mut sprites = crate::sprites::Sprites::with_disk_cache(root); + sprites.insert(root, "minecraft:block/x").unwrap(); + sprites.save_disk_cache(); + sprites.finish(); + sprites.pixels + } + #[test] + fn altered_png_is_not_served_stale_and_cache_matches_decoding() { + let root = scratch("stale"); + let png = root.join("assets/minecraft/textures/block/x.png"); + write_png(&png, [255, 0, 0, 255]); + let decoded = sprite_pixels(&root); + assert!(root.join(FILE).exists()); + assert_eq!( + sprite_pixels(&root), + decoded, + "cache hit differs from decoding" + ); + std::thread::sleep(std::time::Duration::from_millis(30)); + write_png(&png, [0, 255, 0, 255]); + let fresh = sprite_pixels(&root); + assert_eq!(&fresh[..4], &[0, 255, 0, 255]); + // Corrupt and deleted caches fall back to decoding. + fs::write(root.join(FILE), b"HSPR\x01\0\0\0 junk").unwrap(); + assert_eq!(sprite_pixels(&root), fresh); + fs::remove_file(root.join(FILE)).unwrap(); + assert_eq!(sprite_pixels(&root), fresh); + let _ = fs::remove_dir_all(&root); + } +} diff --git a/crates/horizon-client/src/sprites.rs b/crates/horizon-client-render/src/sprites.rs similarity index 89% rename from crates/horizon-client/src/sprites.rs rename to crates/horizon-client-render/src/sprites.rs index 0d5871915d..3a52fa05f6 100644 --- a/crates/horizon-client/src/sprites.rs +++ b/crates/horizon-client-render/src/sprites.rs @@ -89,6 +89,8 @@ pub struct Sprites { /// textures under their own names, and one layer serves them all (the /// GPU's array may hold as few as 2048). still: std::collections::HashMap<(Vec, u32, [u32; 2], [bool; 4]), u32>, + /// Decoded textures kept on disk between launches ([`Sprites::with_disk_cache`]). + disk: Option, } /// Edge length of vanilla's block textures: the smallest array layer. pub const SPRITE_SIZE: u32 = 16; @@ -111,6 +113,20 @@ pub(crate) fn resample(frame: &[u8], from: u32, to: u32) -> Vec { out } impl Sprites { + /// Sprites that reuse textures decoded by an earlier launch, kept in a + /// cache file in `root`. + pub fn with_disk_cache(root: &Path) -> Self { + Self { + disk: crate::sprite_cache::SpriteCache::open(root), + ..Self::default() + } + } + /// Write what this run decoded to the cache file. + pub fn save_disk_cache(&mut self) { + if let Some(disk) = &mut self.disk { + disk.save(); + } + } /// How many array layers the textures take. pub fn count(&self) -> usize { self.native.len() @@ -201,6 +217,21 @@ impl Sprites { } } fn load(&mut self, root: &Path, name: &str) -> Result { + let cached = self.disk.as_ref().and_then(|d| d.get(name)).cloned(); + let decoded = match cached { + Some(decoded) => decoded, + None => { + let decoded = Self::decode(root, name)?; + if let Some(disk) = &mut self.disk { + disk.insert(name, decoded.clone()); + } + decoded + } + }; + self.register(name, decoded) + } + /// Read and decode one texture and its `.mcmeta`. + fn decode(root: &Path, name: &str) -> Result { let (namespace, path) = name.split_once(':').ok_or("invalid sprite identifier")?; let path = root.join(format!("assets/{namespace}/textures/{path}.png")); let metadata = match fs::read_to_string(path.with_extension("png.mcmeta")) { @@ -210,10 +241,13 @@ impl Sprites { Err(error) if error.kind() == std::io::ErrorKind::NotFound => None, Err(error) => return Err(error.into()), }; - let animation = metadata + let animation_text = metadata .as_ref() .and_then(|v| v.get("animation")) - .map(|v| AnimationMetadata::from_json(v.get())) + .map(|v| v.get().to_owned()); + let animation = animation_text + .as_deref() + .map(AnimationMetadata::from_json) .transpose()?; let mut decoder = png::Decoder::new(BufReader::new(File::open(path)?)); decoder.set_transformations(png::Transformations::EXPAND | png::Transformations::STRIP_16); @@ -247,7 +281,6 @@ impl Sprites { }; pixels.extend(rgba); } - let layer = u32::try_from(self.native.len())?; let columns = info.width / width; let count = columns * (info.height / height); // Layers are square; a frame of another shape is nearest-resampled to @@ -268,28 +301,56 @@ impl Sprites { } frames.push(frame); } - let settings = crate::mipmap::Settings { - native_size: side, - strategy: texture_section(metadata.as_ref()) + let texture = texture_section(metadata.as_ref()); + Ok(crate::sprite_cache::Decoded { + side, + frames, + animation: animation_text, + strategy: texture .as_ref() .and_then(|t| t["mipmap_strategy"].as_str()) .and_then(crate::mipmap::Strategy::parse) .unwrap_or_default(), - alpha_cutoff_bias: texture_section(metadata.as_ref()) + alpha_cutoff_bias: texture .as_ref() .and_then(|t| t["alpha_cutoff_bias"].as_f64()) .unwrap_or(0.0) as f32, has_transparent: pixels.as_chunks::<4>().0.iter().any(|p| p[3] == 0), + opaque: pixels.as_chunks::<4>().0.iter().all(|p| p[3] == 255), + translucent: pixels + .as_chunks::<4>() + .0 + .iter() + .any(|p| p[3] != 0 && p[3] != 255), + }) + } + /// Give a decoded texture its layer (or the one an identical still has). + fn register(&mut self, name: &str, decoded: crate::sprite_cache::Decoded) -> Result { + let crate::sprite_cache::Decoded { + side, + frames, + animation: animation_text, + strategy, + alpha_cutoff_bias, + has_transparent, + opaque, + translucent, + } = decoded; + let animation = animation_text + .as_deref() + .map(AnimationMetadata::from_json) + .transpose()?; + let count = frames.len() as u32; + let layer = u32::try_from(self.native.len())?; + let settings = crate::mipmap::Settings { + native_size: side, + strategy, + alpha_cutoff_bias, + has_transparent, item: name .split_once(':') .is_some_and(|(_, p)| p.starts_with("item/")), }; - let opaque = pixels.as_chunks::<4>().0.iter().all(|p| p[3] == 255); - let translucent = pixels - .as_chunks::<4>() - .0 - .iter() - .any(|p| p[3] != 0 && p[3] != 255); let still = (animation.is_none() && frames.len() == 1).then(|| { ( frames[0].clone(), diff --git a/crates/horizon-client/src/streaming.rs b/crates/horizon-client-render/src/streaming.rs similarity index 98% rename from crates/horizon-client/src/streaming.rs rename to crates/horizon-client-render/src/streaming.rs index 7e0e1ebf0b..82885f91dc 100644 --- a/crates/horizon-client/src/streaming.rs +++ b/crates/horizon-client-render/src/streaming.rs @@ -99,6 +99,9 @@ pub trait LodSampler: Send { fn drain_details(&mut self) -> Vec { Vec::new() } + /// Reduced far detail (`graphics::FarDetail::Reduced`): `grid` may then + /// approximate wide cells. + fn set_approximate(&mut self, _approximate: bool) {} fn sample(&mut self, x: i32, z: i32) -> Option; fn column(&mut self, x: i32, z: i32) -> crate::lod::Column { crate::lod::Column::from_surface(self.sample(x, z)) @@ -168,6 +171,9 @@ enum Job { /// Proto chunks checked out to be saved (`ChunkGenerator::save_protos`), /// off the main thread like vanilla's IOWorker. Save(Vec<((i32, i32), Proto)>), + /// Meshes the renderer is done with (`Streaming::free`): their memory + /// is given back here, not in the frame. + Free(Vec<((i32, i32, i32), SectionMesh)>), } pub enum Done { Generated(Chunk), @@ -510,6 +516,10 @@ impl Streaming { generator.save_protos(&refs); Done::Saved(protos) } + Job::Free(meshes) => { + drop(meshes); + continue; + } Job::Light(x, z, region, seq, sky) => { Done::Lit(x, z, light_chunk(&borrow(®ion), &light, sky), seq) } @@ -660,6 +670,17 @@ impl Streaming { } } } + /// Give back meshes the renderer has copied: a worker frees them, ahead of + /// other jobs, as vanilla's compile task closes its mesh data on its own + /// thread once copied (`SectionRenderDispatcher.RenderSection.CompileTask.doTask`). + /// Freed in the frame, the hundreds of meshes a chunk border brings waited + /// on the allocator behind every busy worker: up to 20 ms of a frame each + /// time while flying at render distance 32. + pub fn free(&mut self, meshes: Vec<((i32, i32, i32), SectionMesh)>) { + if !meshes.is_empty() { + self.submit(None, 0, Job::Free(meshes)); + } + } /// A column the server unloaded (ClientboundForgetLevelChunkPacket). pub fn forget(&mut self, world: &mut World, x: i32, z: i32) -> Option> { self.lit.remove(&(x, z)); @@ -676,7 +697,7 @@ impl Streaming { } /// Queue a job for the column `at`, nearest first: its distance (`distance`) /// times four plus `bias`, which orders a column's jobs. A job without a - /// column (a save) goes ahead of every other. + /// column (a save, meshes to free) goes ahead of every other. fn submit(&mut self, at: Option<(i32, i32)>, bias: i64, job: Job) { self.order += 1; let priority = at.map_or(i64::MIN, |(x, z)| self.distance(x, z) * 4 + bias); @@ -910,6 +931,8 @@ impl Streaming { self.relight_edits(world); let deferred = std::mem::take(&mut self.deferred); let received = std::iter::from_fn(|| self.results.try_recv().ok()); + // Meshes made stale meanwhile, freed with the uploaded ones (`free`). + let mut stale = Vec::new(); for done in deferred.into_iter().chain(received.collect::>()) { match done { Done::Proto(x, z, proto, decorated) => { @@ -966,12 +989,14 @@ impl Streaming { } Done::Meshed(x, sy, z, mesh, seq) => { if self.meshing.get(&(x, sy, z)) != Some(&seq) { + stale.push(((x, sy, z), mesh)); continue; } // Its column unloaded while it was meshed: nothing would // ever remove the uploaded section. if !world.chunks.contains_key(&(x, z)) { self.meshing.remove(&(x, sy, z)); + stale.push(((x, sy, z), mesh)); continue; } self.meshing.remove(&(x, sy, z)); @@ -980,6 +1005,7 @@ impl Streaming { } } } + self.free(stale); let mut removed = Vec::new(); if sweep { // Unload far columns. diff --git a/crates/horizon-client/src/tab_list.rs b/crates/horizon-client-render/src/tab_list.rs similarity index 100% rename from crates/horizon-client/src/tab_list.rs rename to crates/horizon-client-render/src/tab_list.rs diff --git a/crates/horizon-client/src/telemetry.rs b/crates/horizon-client-render/src/telemetry.rs similarity index 100% rename from crates/horizon-client/src/telemetry.rs rename to crates/horizon-client-render/src/telemetry.rs diff --git a/crates/horizon-client/src/terrain.wgsl b/crates/horizon-client-render/src/terrain.wgsl similarity index 91% rename from crates/horizon-client/src/terrain.wgsl rename to crates/horizon-client-render/src/terrain.wgsl index 3aea6fc1af..30c5e937e0 100644 --- a/crates/horizon-client/src/terrain.wgsl +++ b/crates/horizon-client-render/src/terrain.wgsl @@ -24,6 +24,9 @@ struct Globals { outline_params: vec4, // Horizon Extras' dynamic lights: camera-relative position and level (dynamic_light.rs). dynamic_lights: array, 16>, + // The sunrise fan's 16 triangles (gfx/sunrise_fan.rs): xyz the apex weight's normal, w the + // triangle's volume with the camera; then each rim vertex's normal against the apex. + sunrise_fan: array, 32>, } @group(0) @binding(0) var globals: Globals; @group(0) @binding(1) var sprites: texture_2d_array; @@ -197,39 +200,26 @@ fn sky_fog(color: vec3, dist: vec2) -> vec3 { let f = max(linear_fog(dist.x, 0.0, end), linear_fog(dist.y, end, end)); return mix(color, globals.fog_color.rgb, f * globals.fog_color.a); } -fn rotate_x90(v: vec3) -> vec3 { return vec3(v.x, -v.z, v.y); } -// Weight of the fan centre where the ray crosses the sunrise fan (0 when missed). +// Weight of the fan centre where the ray crosses the sunrise fan (0 when missed): +// Moller-Trumbore from the origin along d, over normals built once a frame +// (gfx/sunrise_fan.rs). The barycentrics are apex/s, k1/s and -k0/s; s is -det. fn sunrise_weight(d: vec3) -> f32 { - let alpha = globals.sunrise.a; - if alpha <= 0.001 { return 0.0; } - // sin(sunAngle) < 0 flips the fan to the other horizon; sun.z = -sin(sunAngle). - let flip = select(0.0, 180.0, -globals.sun.z < 0.0); - let theta = radians(flip + 90.0); - let c = cos(theta); - let s = sin(theta); + if globals.sunrise.a <= 0.001 { return 0.0; } var best = 0.0; - let apex = rotate_x90(vec3(-100.0 * s, 100.0 * c, 0.0)); + var k0 = dot(d, globals.sunrise_fan[16].xyz); for (var i = 0; i < 16; i++) { - let a0 = f32(i) * 2.0 * PI / 16.0; - let a1 = f32(i + 1) * 2.0 * PI / 16.0; - let l0 = vec3(sin(a0) * 120.0, cos(a0) * 120.0, -cos(a0) * 40.0 * alpha); - let l1 = vec3(sin(a1) * 120.0, cos(a1) * 120.0, -cos(a1) * 40.0 * alpha); - let r0 = rotate_x90(vec3(l0.x * c - l0.y * s, l0.x * s + l0.y * c, l0.z)); - let r1 = rotate_x90(vec3(l1.x * c - l1.y * s, l1.x * s + l1.y * c, l1.z)); - // Moller-Trumbore from the origin along d. - let e1 = r0 - apex; - let e2 = r1 - apex; - let pv = cross(d, e2); - let det = dot(e1, pv); - if abs(det) < 1e-6 { continue; } - let tv = -apex; - let u = dot(tv, pv) / det; - let qv = cross(tv, e1); - let v = dot(d, qv) / det; - let t = dot(e2, qv) / det; - if u >= 0.0 && v >= 0.0 && u + v <= 1.0 && t > 0.0 { - best = max(best, 1.0 - u - v); + let triangle = globals.sunrise_fan[i]; + let k1 = dot(d, globals.sunrise_fan[16 + (i + 1) % 16].xyz); + // A hit needs s on the side of w, so k0 and k1 on either side of 0. + if k0 * triangle.w <= 0.0 && k1 * triangle.w >= 0.0 { + let apex = dot(d, triangle.xyz); + let s = apex + k1 - k0; + if abs(s) >= 1e-6 && s * triangle.w > 0.0 && apex * s >= 0.0 && k1 * s >= 0.0 + && k0 * s <= 0.0 { + best = max(best, apex / s); + } } + k0 = k1; } return best; } diff --git a/crates/horizon-client/src/testgen.rs b/crates/horizon-client-render/src/testgen.rs similarity index 100% rename from crates/horizon-client/src/testgen.rs rename to crates/horizon-client-render/src/testgen.rs diff --git a/crates/horizon-client/src/title_edition.rs b/crates/horizon-client-render/src/title_edition.rs similarity index 100% rename from crates/horizon-client/src/title_edition.rs rename to crates/horizon-client-render/src/title_edition.rs diff --git a/crates/horizon-client/src/trims.rs b/crates/horizon-client-render/src/trims.rs similarity index 100% rename from crates/horizon-client/src/trims.rs rename to crates/horizon-client-render/src/trims.rs diff --git a/crates/horizon-client/src/ui.rs b/crates/horizon-client-render/src/ui.rs similarity index 93% rename from crates/horizon-client/src/ui.rs rename to crates/horizon-client-render/src/ui.rs index e034f2b399..a6d928818e 100644 --- a/crates/horizon-client/src/ui.rs +++ b/crates/horizon-client-render/src/ui.rs @@ -79,27 +79,31 @@ pub fn load_metadata(root: &Path) { } *NINE_SLICE.write().unwrap_or_else(|e| e.into_inner()) = Some(map); } -/// The sprite's nine-slice metadata; without any (no resources loaded), its -/// GUI size and the widgets' usual borders. -fn scaling(sprite: &str, r: &Rect) -> NineSlice { +/// The sprite's nine-slice metadata, when its `.png.mcmeta` has any. +fn nine_slice_metadata(sprite: &str) -> Option { NINE_SLICE .read() .unwrap_or_else(|e| e.into_inner()) .as_ref() .and_then(|m| m.get(sprite).copied()) - .unwrap_or(NineSlice { - width: r.w, - height: r.h, - border: match sprite { - "gui/sprites/widget/button" - | "gui/sprites/widget/button_highlighted" - | "gui/sprites/widget/button_disabled" => [3; 4], - "gui/sprites/widget/slider_handle" - | "gui/sprites/widget/slider_handle_highlighted" => [2, 2, 2, 3], - _ => [1; 4], - }, - stretch_inner: false, - }) +} +/// The sprite's nine-slice metadata; without any (no resources loaded), its +/// GUI size and the widgets' usual borders. +fn scaling(sprite: &str, r: &Rect) -> NineSlice { + nine_slice_metadata(sprite).unwrap_or(NineSlice { + width: r.w, + height: r.h, + border: match sprite { + "gui/sprites/widget/button" + | "gui/sprites/widget/button_highlighted" + | "gui/sprites/widget/button_disabled" => [3; 4], + "gui/sprites/widget/slider_handle" | "gui/sprites/widget/slider_handle_highlighted" => { + [2, 2, 2, 3] + } + _ => [1; 4], + }, + stretch_inner: false, + }) } /// The region `[x, y, w, h]` of a sprite `size` GUI pixels large, in its /// own units (`TextureAtlasSprite.getU(x / width)`). @@ -152,6 +156,35 @@ pub fn nine_slice( draw_nine_slice(batch, *r, scaling(sprite, r), [x, y, w, h], color); } } +/// `GuiGraphicsExtractor.blitSprite`: a sprite over `w` × `h` as its +/// `gui.scaling` says, nine-sliced, or stretched when it has no metadata. +/// (`tile`, which no vanilla sprite uses, isn't read yet: it stretches.) +pub fn blit_sprite( + batch: &mut HudBatch, + atlas: &HudAtlas, + sprite: &str, + x: f32, + y: f32, + w: f32, + h: f32, + color: u32, +) { + if let Some(r) = atlas.sprites.get(sprite) { + draw_sprite(batch, *r, nine_slice_metadata(sprite), [x, y, w, h], color); + } +} +fn draw_sprite( + batch: &mut HudBatch, + r: Rect, + nine_slice: Option, + [x, y, w, h]: [f32; 4], + color: u32, +) { + match nine_slice { + Some(n) => draw_nine_slice(batch, r, n, [x, y, w, h], color), + None => batch.rect_uv(x, y, w, h, r, color), + } +} fn draw_nine_slice( batch: &mut HudBatch, r: Rect, @@ -227,28 +260,33 @@ pub fn tiled( h: f32, size: f32, color: u32, +) { + tiled_sized(batch, atlas, sprite, x, y, w, h, [size, size], color); +} +/// Repeat a texture using its width and height in GUI pixels. +#[allow(clippy::too_many_arguments)] +pub fn tiled_sized( + batch: &mut HudBatch, + atlas: &HudAtlas, + sprite: &str, + x: f32, + y: f32, + w: f32, + h: f32, + size: [f32; 2], + color: u32, ) { let Some(r) = atlas.sprites.get(sprite).copied() else { return; }; - let mut yy = 0.0; - while yy < h { - let th = size.min(h - yy); - let mut xx = 0.0; - while xx < w { - let tw = size.min(w - xx); - batch.rect_uv( - x + xx, - y + yy, - tw, - th, - sub(r, [size, size], [0.0, 0.0, tw, th]), - color, - ); - xx += size; - } - yy += size; - } + tile( + batch, + r, + size, + [0.0, 0.0, size[0], size[1]], + [x, y, w, h], + color, + ); } /// Text drawn at `scale` times its size (a scaled pose). pub fn text_scaled( @@ -434,7 +472,7 @@ pub fn separators( ("gui/header_separator", "gui/footer_separator") }; let w = batch.width; - tiled( + tiled_sized( batch, atlas, header, @@ -442,10 +480,10 @@ pub fn separators( header_bottom - 2.0, w, 2.0, - 32.0, + [32.0, 2.0], u32::MAX, ); - tiled( + tiled_sized( batch, atlas, footer, @@ -453,7 +491,7 @@ pub fn separators( footer_top, w, 2.0, - 32.0, + [32.0, 2.0], u32::MAX, ); } @@ -1291,6 +1329,22 @@ mod tests { parse_nine_slice(&serde_json::from_str(json).unwrap()).unwrap() } #[test] + fn separator_tiles_sample_both_rows_and_clip_the_last_column() { + let mut batch = HudBatch::new([640, 480]); + tile( + &mut batch, + sprite(32, 2), + [32.0, 2.0], + [0.0, 0.0, 32.0, 2.0], + [0.0, 5.0, 40.0, 2.0], + u32::MAX, + ); + let q = quads(&batch); + assert_eq!(q.len(), 2); + assert_quad(q[0], ([0.0, 5.0, 32.0, 7.0], [0.0, 0.0, 1.0, 1.0])); + assert_quad(q[1], ([32.0, 5.0, 40.0, 7.0], [0.0, 0.0, 0.25, 1.0])); + } + #[test] fn a_high_resolution_button_slices_in_its_gui_units() { // Faithful 32x's widget/button: 400x40 texels, gui.scaling 200x20. let n = faithful( @@ -1357,6 +1411,40 @@ mod tests { assert_quad(q[0], ([4.0, 4.0, 54.0, 54.0], [0.0, 0.0, 1.0, 1.0])); } #[test] + fn blit_sprite_nine_slices_a_sprite_whose_metadata_says_so() { + // Vanilla's tooltip/frame: 100x100, border 10, stretched centre. + let n = faithful( + r#"{"gui":{"scaling":{"type":"nine_slice","width":100,"height":100,"border":10,"stretch_inner":true}}}"#, + ); + let mut batch = HudBatch::new([640, 480]); + draw_sprite( + &mut batch, + sprite(100, 100), + Some(n), + [10.0, 10.0, 160.0, 26.0], + u32::MAX, + ); + let q = quads(&batch); + assert_eq!(q.len(), 9); + // The corners keep their 10 pixels instead of stretching with the box. + assert_quad(q[0], ([10.0, 10.0, 20.0, 20.0], [0.0, 0.0, 0.1, 0.1])); + assert_quad(q[5], ([160.0, 26.0, 170.0, 36.0], [0.9, 0.9, 1.0, 1.0])); + } + #[test] + fn blit_sprite_stretches_a_sprite_without_metadata() { + let mut batch = HudBatch::new([640, 480]); + draw_sprite( + &mut batch, + sprite(9, 9), + None, + [0.0, 0.0, 27.0, 18.0], + u32::MAX, + ); + let q = quads(&batch); + assert_eq!(q.len(), 1); + assert_quad(q[0], ([0.0, 0.0, 27.0, 18.0], [0.0, 0.0, 1.0, 1.0])); + } + #[test] fn borders_shrink_to_half_the_drawn_size() { let n = NineSlice { width: 20, diff --git a/crates/horizon-client/src/vault.rs b/crates/horizon-client-render/src/vault.rs similarity index 100% rename from crates/horizon-client/src/vault.rs rename to crates/horizon-client-render/src/vault.rs diff --git a/crates/horizon-client/src/weather.rs b/crates/horizon-client-render/src/weather.rs similarity index 100% rename from crates/horizon-client/src/weather.rs rename to crates/horizon-client-render/src/weather.rs diff --git a/crates/horizon-client/src/widgets.rs b/crates/horizon-client-render/src/widgets.rs similarity index 100% rename from crates/horizon-client/src/widgets.rs rename to crates/horizon-client-render/src/widgets.rs diff --git a/crates/horizon-client/src/world.rs b/crates/horizon-client-render/src/world.rs similarity index 82% rename from crates/horizon-client/src/world.rs rename to crates/horizon-client-render/src/world.rs index 9b38762880..a479c6c271 100644 --- a/crates/horizon-client/src/world.rs +++ b/crates/horizon-client-render/src/world.rs @@ -1,12 +1,10 @@ //! Resident client world: streamed chunk columns plus the bookkeeping the game //! loop needs to relight and remesh after edits. use crate::chunk::{Chunk, MIN_Y, SECTIONS}; +use crate::fast_hash::{FxHashMap, FxHashSet}; use horizon_core::random::LegacyRandom; use horizon_world::registry::BlockStateId; -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; +use std::sync::Arc; /// Sections (x, section index, z) waiting to be remeshed, kept by column /// (a bit per section index), so the mesh scheduler looks at each column once @@ -65,14 +63,14 @@ pub struct World { /// Saved entities of chunks that just loaded, spawned by the game. pub loaded_entities: Vec<((i32, i32), Vec)>, /// Chunks changed since the last save. - pub modified: HashSet<(i32, i32)>, + pub modified: FxHashSet<(i32, i32)>, /// Block entities, and the order they tick in. pub block_entities: crate::block_entities::BlockEntities, /// Each chest's `ContainerOpenersCounter`: opener count and largest /// interaction range. - pub chest_openers: HashMap<(i32, i32, i32), (i32, f64)>, + pub chest_openers: FxHashMap<(i32, i32, i32), (i32, f64)>, /// `BaseContainerBlockEntity.name`: custom names of container block entities. - pub block_names: HashMap<(i32, i32, i32), String>, + pub block_names: FxHashMap<(i32, i32, i32), String>, /// States (by id) whose writes are recorded in `watched_writes` (sculk /// blocks, which need a block entity). pub watched: Vec, @@ -80,7 +78,7 @@ pub struct World { pub watched_writes: Vec<((i32, i32, i32), u32, u32)>, /// `ChunkAccess.inhabitedTime` of resident chunks: ticks a player spent /// within spawning range (local difficulty). - pub inhabited: HashMap<(i32, i32), i64>, + pub inhabited: FxHashMap<(i32, i32), i64>, pub game_time: i64, pub day_time: i64, pub weather: Weather, @@ -177,13 +175,13 @@ impl World { dirty_light: Default::default(), light_edits: Vec::new(), loaded_entities: Vec::new(), - modified: HashSet::new(), + modified: FxHashSet::default(), block_entities: Default::default(), - chest_openers: HashMap::new(), - block_names: HashMap::new(), + chest_openers: FxHashMap::default(), + block_names: FxHashMap::default(), watched: Vec::new(), watched_writes: Vec::new(), - inhabited: HashMap::new(), + inhabited: FxHashMap::default(), game_time: 0, day_time: 1000, weather: Weather::default(), @@ -274,20 +272,40 @@ impl World { log.push((x, y, z)); } self.modified.insert((x >> 4, z >> 4)); + // The mesher reads a 3x3x3 neighbourhood for AO, so a write marks + // the sections it can reach: this section, the one above/below + // only on a section face, and a neighbour column only on a chunk + // border (the diagonal only where both borders meet). let sy = (y - MIN_Y) >> 4; - for dz in -1..=1 { - for dy in -1..=1 { - for dx in -1..=1 { - let (bx, by, bz) = (x + dx, y + dy, z + dz); - let s = (by - MIN_Y) >> 4; - if !(0..SECTIONS as i32).contains(&s) { - continue; - } - self.dirty_sections.insert((bx >> 4, s, bz >> 4)); - } + let local_y = y & 15; + let mark = |dirty: &mut DirtySections, bx: i32, bz: i32| { + dirty.insert((bx >> 4, sy, bz >> 4)); + if local_y == 0 && sy > 0 { + dirty.insert((bx >> 4, sy - 1, bz >> 4)); } + if local_y == 15 && sy + 1 < SECTIONS as i32 { + dirty.insert((bx >> 4, sy + 1, bz >> 4)); + } + }; + let (bx, bz) = (x & 15, z & 15); + mark(&mut self.dirty_sections, x, z); + if bx == 0 { + mark(&mut self.dirty_sections, x - 1, z); + } else if bx == 15 { + mark(&mut self.dirty_sections, x + 1, z); + } + if bz == 0 { + mark(&mut self.dirty_sections, x, z - 1); + } else if bz == 15 { + mark(&mut self.dirty_sections, x, z + 1); + } + if (bx == 0 || bx == 15) && (bz == 0 || bz == 15) { + mark( + &mut self.dirty_sections, + if bx == 0 { x - 1 } else { x + 1 }, + if bz == 0 { z - 1 } else { z + 1 }, + ); } - let _ = sy; if self.chunks.get(&(x >> 4, z >> 4)).is_some_and(|c| c.lit) { self.light_edits.push(((x, y, z), previous, state.0)); } else { @@ -407,11 +425,59 @@ impl Default for HealthRules { #[cfg(test)] mod tests { - use super::World; - use crate::chunk::Chunk; + use super::{DirtySections, World}; + use crate::chunk::{Chunk, MIN_Y, SECTIONS}; use horizon_world::registry::BlockStateId; use std::sync::Arc; + /// Every block write marks the same section set the 27-cell loop did, but + /// only the distinct keys: one in the interior, a neighbour section on a + /// section face, a neighbour column on a chunk border and the diagonal at + /// a corner (eight keys at a corner on a section face). + #[test] + fn writes_mark_exactly_the_reachable_sections() { + let mut world = World::new(BlockStateId(0), 0); + world.chunks.insert((0, 0), Arc::new(Chunk::empty(0, 0))); + for (x, y, z) in [ + (3, 70, 3), + (0, 70, 3), + (15, 70, 3), + (3, 70, 0), + (0, 70, 0), + (15, 70, 15), + (0, MIN_Y, 0), + (0, 80, 0), + (15, MIN_Y + SECTIONS as i32 * 16 - 1, 15), + ] { + world.dirty_sections.clear(); + world.set(x, y, z, BlockStateId(1)).unwrap(); + let mut expected = DirtySections::default(); + for dz in -1..=1 { + for dy in -1..=1 { + for dx in -1..=1 { + let (bx, by, bz) = (x + dx, y + dy, z + dz); + let s = (by - MIN_Y) >> 4; + if (0..SECTIONS as i32).contains(&s) { + expected.insert((bx >> 4, s, bz >> 4)); + } + } + } + } + let mut got: Vec<_> = world.dirty_sections.columns().collect(); + let mut want: Vec<_> = expected.columns().collect(); + got.sort_unstable(); + want.sort_unstable(); + assert_eq!(got, want, "write at {x} {y} {z}"); + if (x & 15, z & 15) == (3, 3) { + assert_eq!(world.dirty_sections.len(), 1); + } + if (x & 15, z & 15, y & 15) == (0, 0, 0) && y > MIN_Y { + // Four columns, each with the section below and this one. + assert_eq!(world.dirty_sections.len(), 8); + } + } + } + /// A box misses a state only where no section it reaches lists it, and /// unloaded columns or heights past the world count as their air. #[test] diff --git a/crates/horizon-client-render/src/world_post.wgsl b/crates/horizon-client-render/src/world_post.wgsl new file mode 100644 index 0000000000..55c7dde9d8 --- /dev/null +++ b/crates/horizon-client-render/src/world_post.wgsl @@ -0,0 +1,133 @@ +// Horizon's world post pass: the world, rendered at the resolution scale, +// copied (bilinear) or anti-aliased with FXAA onto the screen before the +// hand and HUD. Only used when a non-vanilla setting asks for it. +@group(0) @binding(0) var world: texture_2d; +@group(0) @binding(1) var world_sampler: sampler; + +struct PostOut { + @builtin(position) clip: vec4, + @location(0) uv: vec2, +}; + +@vertex fn vs_post(@builtin(vertex_index) i: u32) -> PostOut { + let p = vec2(f32((i << 1u) & 2u), f32(i & 2u)); + var out: PostOut; + out.clip = vec4(p * 2.0 - 1.0, 0.0, 1.0); + out.uv = vec2(p.x, 1.0 - p.y); + return out; +} + +@fragment fn fs_copy(in: PostOut) -> @location(0) vec4 { + // The alpha carries the emissive mark through to HDR output (hdr.wgsl). + return textureSampleLevel(world, world_sampler, in.uv, 0.0); +} + +fn luma(c: vec3) -> f32 { + return dot(c, vec3(0.299, 0.587, 0.114)); +} + +// FXAA 3.11 (console quality): blend along the local edge direction. +@fragment fn fs_fxaa(in: PostOut) -> @location(0) vec4 { + let texel = 1.0 / vec2(textureDimensions(world)); + let centre = textureSampleLevel(world, world_sampler, in.uv, 0.0); + let rgb_m = centre.rgb; + let nw = luma(textureSampleLevel(world, world_sampler, in.uv + vec2(-0.5, -0.5) * texel, 0.0).rgb); + let ne = luma(textureSampleLevel(world, world_sampler, in.uv + vec2(0.5, -0.5) * texel, 0.0).rgb); + let sw = luma(textureSampleLevel(world, world_sampler, in.uv + vec2(-0.5, 0.5) * texel, 0.0).rgb); + let se = luma(textureSampleLevel(world, world_sampler, in.uv + vec2(0.5, 0.5) * texel, 0.0).rgb); + let m = luma(rgb_m); + let lo = min(m, min(min(nw, ne), min(sw, se))); + let hi = max(m, max(max(nw, ne), max(sw, se))); + if hi - lo < max(0.0312, hi * 0.125) { + return centre; + } + var dir = vec2(-((nw + ne) - (sw + se)), (nw + sw) - (ne + se)); + let reduce = max((nw + ne + sw + se) * 0.25 * 0.125, 1.0 / 128.0); + let scale = 1.0 / (min(abs(dir.x), abs(dir.y)) + reduce); + dir = clamp(dir * scale, vec2(-8.0), vec2(8.0)) * texel; + let a = 0.5 * (textureSampleLevel(world, world_sampler, in.uv + dir * (1.0 / 3.0 - 0.5), 0.0).rgb + + textureSampleLevel(world, world_sampler, in.uv + dir * (2.0 / 3.0 - 0.5), 0.0).rgb); + let b = a * 0.5 + 0.25 * (textureSampleLevel(world, world_sampler, in.uv - dir * 0.5, 0.0).rgb + + textureSampleLevel(world, world_sampler, in.uv + dir * 0.5, 0.0).rgb); + let lb = luma(b); + if lb < lo || lb > hi { + return vec4(a, centre.a); + } + return vec4(b, centre.a); +} + +// TAA (`world_post.rs`): blend this frame into the history, reprojected from the +// depth buffer through the camera's motion and clamped to this frame's 3x3 +// neighbourhood so that what moved or appeared does not ghost. +struct Taa { + reproject: mat4x4, + // xy: this frame's jitter in clip space; z: 1 when the history is unusable. + jitter: vec4, +}; +@group(0) @binding(2) var history: texture_2d; +@group(0) @binding(3) var depth: texture_depth_2d; +@group(0) @binding(4) var taa: Taa; + +// Catmull-Rom over the history in 5 bilinear taps: sharper than one under motion. +fn sample_history(uv: vec2) -> vec3 { + let size = vec2(textureDimensions(history)); + let pos = uv * size; + let centre = floor(pos - 0.5) + 0.5; + let f = pos - centre; + let w0 = f * (-0.5 + f * (1.0 - 0.5 * f)); + let w1 = 1.0 + f * f * (-2.5 + 1.5 * f); + let w2 = f * (0.5 + f * (2.0 - 1.5 * f)); + let w3 = f * f * (-0.5 + 0.5 * f); + let w12 = w1 + w2; + let t0 = (centre - 1.0) / size; + let t3 = (centre + 2.0) / size; + let t12 = (centre + w2 / w12) / size; + let c = textureSampleLevel(history, world_sampler, vec2(t12.x, t0.y), 0.0).rgb * (w12.x * w0.y) + + textureSampleLevel(history, world_sampler, vec2(t0.x, t12.y), 0.0).rgb * (w0.x * w12.y) + + textureSampleLevel(history, world_sampler, t12, 0.0).rgb * (w12.x * w12.y) + + textureSampleLevel(history, world_sampler, vec2(t3.x, t12.y), 0.0).rgb * (w3.x * w12.y) + + textureSampleLevel(history, world_sampler, vec2(t12.x, t3.y), 0.0).rgb * (w12.x * w3.y); + let weight = w12.x * w0.y + w0.x * w12.y + w12.x * w12.y + w3.x * w12.y + w12.x * w3.y; + return max(c / weight, vec3(0.0)); +} + +// It runs at the history's size, which upscaling makes larger than the world's. +// The history is unjittered: each output pixel takes the world pixel whose +// jittered sample lands nearest its centre, weighted by how near it lands. +@fragment fn fs_taa(in: PostOut) -> @location(0) vec4 { + let size = vec2(textureDimensions(world)); + // The jitter moved the scene by `jitter` in clip space: where this pixel's + // point was drawn, in world pixels. + let drawn = (in.uv + taa.jitter.xy * vec2(0.5, -0.5)) * size; + let last = vec2(size) - 1; + let p = clamp(vec2(floor(drawn)), vec2(0), last); + let centre = textureLoad(world, p, 0); + var lo = centre.rgb; + var hi = centre.rgb; + // Reversed depth: the nearest of the 3x3 is the largest, and its motion keeps + // the edges of what is in front from trailing. + var near = 0.0; + for (var y = -1; y <= 1; y++) { + for (var x = -1; x <= 1; x++) { + let q = clamp(p + vec2(x, y), vec2(0), last); + let c = textureLoad(world, q, 0).rgb; + lo = min(lo, c); + hi = max(hi, c); + near = max(near, textureLoad(depth, q, 0)); + } + } + let ndc = vec2(in.uv.x * 2.0 - 1.0, 1.0 - in.uv.y * 2.0); + let previous = taa.reproject * vec4(ndc, near, 1.0); + let uv = vec2(0.5, -0.5) * previous.xy / previous.w + 0.5; + if taa.jitter.z > 0.0 || any(uv != clamp(uv, vec2(0.0), vec2(1.0))) { + return vec4(centre.rgb, centre.a); + } + let past = clamp(sample_history(uv), lo, hi); + // How far the sample landed from this pixel's centre, in output pixels: a + // Gaussian of it scales the new frame's share, so that frames whose sample + // fell far away (most of them when upscaling) barely move the pixel. + let off = (drawn - (vec2(p) + 0.5)) * vec2(textureDimensions(history)) / size; + let share = 0.1 * exp(-2.0 * dot(off, off)); + // The alpha carries the emissive mark through to HDR output (hdr.wgsl). + return vec4(mix(past, centre.rgb, share), centre.a); +} diff --git a/crates/horizon-client/src/worldborder.rs b/crates/horizon-client-render/src/worldborder.rs similarity index 100% rename from crates/horizon-client/src/worldborder.rs rename to crates/horizon-client-render/src/worldborder.rs diff --git a/crates/horizon-client/src/zip.rs b/crates/horizon-client-render/src/zip.rs similarity index 86% rename from crates/horizon-client/src/zip.rs rename to crates/horizon-client-render/src/zip.rs index de651d12ee..6e1847beb0 100644 --- a/crates/horizon-client/src/zip.rs +++ b/crates/horizon-client-render/src/zip.rs @@ -14,11 +14,13 @@ use zip::{CompressionMethod, ZipArchive}; /// bomb stops here. const MAX_ENTRY: usize = 1 << 30; -/// One archive with its entries indexed by name (`\` read as `/`, folders -/// left out). +/// One archive with its entries indexed by name (`\` read as `/`), files +/// and folders apart. pub struct Zip { archive: ZipArchive>>, entries: BTreeMap, + /// Folder entries (`name/`) by their name without the slash. + folders: BTreeMap, /// The archive's length: no size an entry claims can be bigger. len: usize, } @@ -33,26 +35,44 @@ impl Zip { normalize_end(&mut data); let len = data.len(); let archive = ZipArchive::new(Cursor::new(Arc::from(data))).map_err(invalid)?; - let entries = (0..archive.len()) - .filter_map(|i| Some((archive.name_for_index(i)?.replace('\\', "/"), i))) - .filter(|(name, _)| !name.ends_with('/')) - .collect(); + let (mut entries, mut folders) = (BTreeMap::new(), BTreeMap::new()); + for i in 0..archive.len() { + let Some(name) = archive.name_for_index(i) else { + continue; + }; + let name = name.replace('\\', "/"); + match name.strip_suffix('/') { + Some(folder) => folders.insert(folder.to_owned(), i), + None => entries.insert(name, i), + }; + } Ok(Self { archive, entries, + folders, len, }) } + /// The files, as `FilePackResources.listResources` lists them (folder + /// entries skipped). pub fn names(&self) -> impl Iterator { self.entries.keys().map(String::as_str) } + /// `FilePackResources.getResource` through `ZipFile.getEntry`: the file + /// `name`, else the folder entry `name/`, whose bytes Java reads like a + /// file's (Loka's pack stores its `pack.mcmeta` and `pack.png` that way). + fn index(&self, name: &str) -> Option { + self.entries + .get(name) + .or_else(|| self.folders.get(name)) + .copied() + } pub fn contains(&self, name: &str) -> bool { - self.entries.contains_key(name) + self.index(name).is_some() } pub fn read(&self, name: &str) -> io::Result> { - let &index = self - .entries - .get(name) + let index = self + .index(name) .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, name.to_owned()))?; // A clone shares the bytes and the central directory: reading needs `&mut`. let mut archive = self.archive.clone(); @@ -133,13 +153,16 @@ mod tests { use super::*; /// A hand-built archive with one stored and one deflated entry. fn archive() -> Vec { - let files: [(&str, &[u8], bool); 2] = [ + archive_of(&[ ("pack.mcmeta", b"{\"pack\":{}}", false), ("a/b.txt", b"hello hello hello hello", true), - ]; + ]) + } + /// A hand-built archive of `(name, body, deflated)` entries. + fn archive_of(files: &[(&str, &[u8], bool)]) -> Vec { let mut out = Vec::new(); let mut central = Vec::new(); - for (name, body, deflate) in files { + for &(name, body, deflate) in files { let data = if deflate { miniz_oxide::deflate::compress_to_vec(body, 6) } else { @@ -174,8 +197,8 @@ mod tests { out.extend(¢ral); out.extend(0x0605_4b50u32.to_le_bytes()); out.extend([0; 4]); - out.extend(2u16.to_le_bytes()); - out.extend(2u16.to_le_bytes()); + out.extend((files.len() as u16).to_le_bytes()); + out.extend((files.len() as u16).to_le_bytes()); out.extend((central.len() as u32).to_le_bytes()); out.extend(start.to_le_bytes()); out.extend([0; 2]); @@ -191,6 +214,29 @@ mod tests { assert!(zip.read("missing").is_err()); } + /// Loka's server pack (#3413) names its metadata `pack.mcmeta/`. + /// `ZipFile.getEntry("pack.mcmeta")` falls back to that folder entry and + /// vanilla reads its bytes; a file of the exact name still comes first. + #[test] + fn a_name_falls_back_to_its_folder_entry() { + let zip = Zip::from_bytes(archive_of(&[ + ("pack.mcmeta/", b"{\"pack\":{\"pack_format\":75}}", true), + ("pack.png/", b"png", false), + ("a/", b"folder", false), + ("a", b"file", false), + ])) + .unwrap(); + assert_eq!(zip.names().collect::>(), ["a"]); + assert!(zip.contains("pack.mcmeta")); + assert_eq!( + zip.read("pack.mcmeta").unwrap(), + b"{\"pack\":{\"pack_format\":75}}" + ); + assert_eq!(zip.read("pack.png").unwrap(), b"png"); + assert_eq!(zip.read("a").unwrap(), b"file"); + assert!(!zip.contains("pack")); + } + /// A pack as zip tools write it: folder entries, a nested pack root, /// stored and deflated files, and a Windows-style name. fn written() -> Vec { diff --git a/crates/horizon-client/Cargo.toml b/crates/horizon-client/Cargo.toml index 66ca987dc8..23188fefc4 100644 --- a/crates/horizon-client/Cargo.toml +++ b/crates/horizon-client/Cargo.toml @@ -11,6 +11,7 @@ description = "Horizon native development client" horizon-assets = { path = "../horizon-assets" } 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" } horizon-items = { path = "../horizon-items" } horizon-core = { path = "../horizon-core" } @@ -43,9 +44,6 @@ serde_json = { version = "1.0.151", features = ["raw_value"] } # hlsl-out: the gfx.rs tests translate every shader as wgpu's DirectX 12 backend does. A normal feature, # not a dev-dependency's, so builds, tests and examples share one build of naga, wgpu and this crate. naga = { version = "30.0.1", features = ["wgsl-in", "hlsl-out"] } -# Sound (ALSA on Linux); the `audio` feature. -cpal = { version = "0.16", optional = true } -symphonia = { version = "0.5", default-features = false, features = ["ogg", "vorbis"] } miniz_oxide = "0.8" ab_glyph = "0.2.32" log = "0.4" @@ -90,7 +88,7 @@ winresource = "0.1" default = ["audio", "controllers"] # Sound output. Without it (and `controllers`) the program links neither libasound nor libudev on # Linux: the server build (`--no-default-features`, released as horizon-engine; docs/server.md). -audio = ["dep:cpal"] +audio = ["horizon-client-render/audio"] # Controllers: gilrs, and the DualSense's own output. Without it only `horizon-ctl pad`'s virtual pad. controllers = ["dep:gilrs", "dep:horizon-dualsense", "horizon-client-input/controllers"] # Runs code mods (wasmtime and cranelift: a quarter of a clean build). On in every release build @@ -108,6 +106,7 @@ path = "examples/probe/main.rs" workspace = true [dev-dependencies] +horizon-client-render = { path = "../horizon-client-render", features = ["test-support"] } horizon-items = { path = "../horizon-items", features = ["test-support"] } # Every WGSL shader translated as wgpu's DirectX 12 backend does (gfx.rs tests). naga = { version = "30.0.1", features = ["wgsl-in", "hlsl-out", "msl-out", "spv-out"] } diff --git a/crates/horizon-client/assets/horizon/lang/uk_ua.json b/crates/horizon-client/assets/horizon/lang/uk_ua.json new file mode 100644 index 0000000000..c279c70585 --- /dev/null +++ b/crates/horizon-client/assets/horizon/lang/uk_ua.json @@ -0,0 +1,15 @@ +{ + "horizon.options.controller": "Геймпад", + "horizon.options.vibration": "Вібрація", + "horizon.options.adaptive_triggers": "Адаптивні тригери", + "horizon.options.lightbar": "Світлова панель", + "horizon.options.look_sensitivity": "Чутливість огляду", + "horizon.options.invert_x": "Інвертувати огляд по X", + "horizon.options.invert_y": "Інвертувати огляд по Y", + "horizon.options.sneak_button": "Кнопка присідання", + "horizon.options.move_deadzone": "Мертва зона руху", + "horizon.options.look_deadzone": "Мертва зона огляду", + "horizon.options.button_glyphs": "Значки кнопок", + "horizon.options.menu_navigation": "Навігація в меню", + "horizon.options.on_screen_keyboard": "Екранна клавіатура" +} diff --git a/crates/horizon-client/examples/probe/camera_probe.rs b/crates/horizon-client/examples/probe/camera_probe.rs index 38f3c23be4..e8cffd88a1 100644 --- a/crates/horizon-client/examples/probe/camera_probe.rs +++ b/crates/horizon-client/examples/probe/camera_probe.rs @@ -302,7 +302,7 @@ fn avatar(c: &Value, out: &mut impl Write) -> Result<()> { for op in c["ops"].as_array().unwrap() { let name = op[0].as_str().unwrap(); match name { - "tick" => state.tick(), + "tick" => state.tick([0.0; 3]), "move" => state.add_walked(fl(&op[1]), fl(&op[2])), "bob" => state.update_bob(b(&op[1]) && !b(&op[2]) && !b(&op[3]), vec3(&op[4])), "reset" => state.reset_bob(), diff --git a/crates/horizon-client/examples/probe/chat_screen_fixture/registries.rs b/crates/horizon-client/examples/probe/chat_screen_fixture/registries.rs index e407dc531d..42be0d206c 100644 --- a/crates/horizon-client/examples/probe/chat_screen_fixture/registries.rs +++ b/crates/horizon-client/examples/probe/chat_screen_fixture/registries.rs @@ -43,6 +43,7 @@ pub fn registries(r: &Value, permission: u8) -> Registries { reg.timelines = strings(&r["timelines"]); reg.dimensions = strings(&r["dimensions"]); reg.players = strings(&r["players"]); + reg.selected_entities = strings(&r["selected_entities"]); reg.teams = strings(&r["teams"]); reg.objectives = strings(&r["objectives"]); reg.tracked = strings(&r["tracked"]); diff --git a/crates/horizon-client/examples/probe/chest_pair_fixture.rs b/crates/horizon-client/examples/probe/chest_pair_fixture.rs new file mode 100644 index 0000000000..2deec57543 --- /dev/null +++ b/crates/horizon-client/examples/probe/chest_pair_fixture.rs @@ -0,0 +1,74 @@ +//! Double chests for tools/test_chest_pair_parity.py: reads one JSON case per +//! line (see VanillaChestPairOracle), a chest's block state at the origin and +//! another block state at the offset "at", and prints what +//! `horizon_client::containers::ChestHalf` pairs them into, as the game's +//! menus and hoppers do: "double" with the right half's x,z then the left +//! half's, or "single". +//! +//! usage: chest_pair_fixture INPUT OUTPUT +use horizon_client::containers::{ChestHalf, is_chest_block}; +use serde_json::Value; +use std::collections::BTreeMap; +use std::fs; + +/// `minecraft:chest[facing=north,type=left]` as its name and properties. +fn parse(state: &str) -> Option<(&str, BTreeMap<&str, &str>)> { + let (name, props) = match state.split_once('[') { + Some((name, rest)) => (name, rest.strip_suffix(']')?), + None => (state, ""), + }; + let props = props + .split(',') + .filter(|p| !p.is_empty()) + .map(|p| p.split_once('=')) + .collect::>()?; + Some((name, props)) +} + +fn half<'a>(name: &'a str, props: &BTreeMap<&str, &'a str>) -> Option> { + Some(ChestHalf { + block: name, + kind: props.get("type")?, + facing: props.get("facing")?, + }) +} + +fn case(line: &str) -> Option { + let case: Value = serde_json::from_str(line).ok()?; + let (name, props) = parse(case["state"].as_str()?)?; + let (other_name, other_props) = parse(case["other"].as_str()?)?; + let at = ( + i32::try_from(case["at"][0].as_i64()?).ok()?, + i32::try_from(case["at"][1].as_i64()?).ok()?, + ); + if !is_chest_block(name) { + return None; + } + let own = half(name, &props)?; + let paired = own.connected_step() == Some(at) + && half(other_name, &other_props).is_some_and(|other| own.pairs_with(&other)); + Some(if !paired { + "single".to_owned() + } else if own.kind == "right" { + format!("double 0,0 {},{}", at.0, at.1) + } else { + format!("double {},{} 0,0", at.0, at.1) + }) +} + +pub fn main() -> horizon_client::Result<()> { + let args: Vec = crate::args().collect(); + let input = fs::read_to_string( + args.get(1) + .ok_or("usage: chest_pair_fixture INPUT OUTPUT")?, + )?; + let lines: Vec = input + .lines() + .map(|line| case(line).unwrap_or_else(|| "error".to_owned())) + .collect(); + fs::write( + args.get(2).ok_or("missing OUTPUT")?, + lines.join("\n") + "\n", + )?; + Ok(()) +} diff --git a/crates/horizon-client/examples/probe/command_parse_fixture.rs b/crates/horizon-client/examples/probe/command_parse_fixture.rs index 17437cd72e..c54e55bebb 100644 --- a/crates/horizon-client/examples/probe/command_parse_fixture.rs +++ b/crates/horizon-client/examples/probe/command_parse_fixture.rs @@ -178,6 +178,7 @@ pub fn main() -> horizon_client::Result<()> { reg.timelines = strings(&r["timelines"]); reg.dimensions = strings(&r["dimensions"]); reg.players = strings(&r["players"]); + reg.selected_entities = strings(&r["selected_entities"]); reg.teams = strings(&r["teams"]); reg.objectives = strings(&r["objectives"]); reg.tracked = strings(&r["tracked"]); diff --git a/crates/horizon-client/examples/probe/container_title_fixture.rs b/crates/horizon-client/examples/probe/container_title_fixture.rs new file mode 100644 index 0000000000..6f7ab15a8d --- /dev/null +++ b/crates/horizon-client/examples/probe/container_title_fixture.rs @@ -0,0 +1,98 @@ +//! Container menu titles for tools/test_container_title_parity.py: reads one +//! JSON case per line (see VanillaContainerTitleOracle) and prints the title +//! the server opens the container's menu with, as canonical NBT. A single +//! block entity is placed as the game places it (`block_entity_item::place`, +//! whose `CustomName` the game keeps in `World::block_names`), then named by +//! `containers::display_name`; a double chest's halves by +//! `containers::double_chest_name`. +//! +//! usage: container_title_fixture INPUT OUTPUT +use horizon_client::block_entity_item::place; +use horizon_client::block_entity_nbt::canonical; +use horizon_client::containers::{default_name_key, display_name, double_chest_name}; +use horizon_client::save::text; +use horizon_world::nbt::{Tag, parse_snbt}; +use serde_json::Value; +use std::fs; + +/// ParityData.canon of a text component: sorted keys. +fn canon(tag: &Tag, out: &mut String) { + match tag { + Tag::Compound(map) => { + let mut keys: Vec<_> = map.iter().map(|(k, v)| (k.to_string_lossy(), v)).collect(); + keys.sort_by(|a, b| a.0.cmp(&b.0)); + out.push('{'); + for (i, (k, v)) in keys.into_iter().enumerate() { + if i > 0 { + out.push(','); + } + out.push_str(&k); + out.push(':'); + canon(v, out); + } + out.push('}'); + } + Tag::List(items) => { + out.push('['); + for (i, item) in items.iter().enumerate() { + if i > 0 { + out.push(','); + } + canon(item, out); + } + out.push(']'); + } + other => out.push_str(&horizon_client::item_data::canon_tag(other)), + } +} + +fn snbt(value: &Value) -> Option { + parse_snbt(value.as_str()?).ok().map(|(tag, _)| tag) +} + +/// The custom name the game keeps for a block entity saved as `value`. +fn custom_name(value: &Value) -> Option> { + let saved = canonical(&snbt(value)?)?; + Some(text(saved.get("CustomName"))) +} + +fn case(line: &str) -> Option { + let case: Value = serde_json::from_str(line).ok()?; + let title = if let Some(first) = case.get("first") { + let first = custom_name(first)?; + let second = custom_name(&case["second"])?; + double_chest_name(first.as_deref(), second.as_deref()) + } else { + let saved = canonical(&snbt(&case["nbt"])?)?; + // BlockItem.updateBlockEntityComponents, as Game::apply_placed_item runs it. + let placed = match case.get("patch") { + Some(p) => match snbt(p)? { + Tag::Compound(patch) => place(&saved, &patch, true), + _ => return None, + }, + None => saved, + }; + let key = default_name_key(case["block"].as_str()?)?; + display_name(text(placed.get("CustomName")).as_deref(), key) + }; + let mut out = String::new(); + canon(&title, &mut out); + Some(out) +} + +pub fn main() -> horizon_client::Result<()> { + let args: Vec = crate::args().collect(); + let input = fs::read_to_string( + args.get(1) + .ok_or("usage: container_title_fixture INPUT OUTPUT")?, + )?; + let lines: Vec = input + .lines() + .map(|line| case(line).unwrap_or_else(|| "error".to_owned())) + .collect(); + fs::write( + args.get(2).ok_or("missing OUTPUT")?, + lines.join("\n") + "\n", + )?; + Ok(()) +} diff --git a/crates/horizon-client/examples/probe/main.rs b/crates/horizon-client/examples/probe/main.rs index 6b4c3d7dda..2f4322277a 100644 --- a/crates/horizon-client/examples/probe/main.rs +++ b/crates/horizon-client/examples/probe/main.rs @@ -24,9 +24,11 @@ mod breaking_fixture; mod camera_probe; mod cauldron_fixture; mod chat_screen_fixture; +mod chest_pair_fixture; mod cloud_fixture; mod command_parse_fixture; mod container_screen_probe; +mod container_title_fixture; mod creative_mining_probe; mod creative_search_fixture; mod cushion_placement_probe; @@ -86,10 +88,12 @@ mod sound_event_fixture; mod spawn_rules_fixture; mod sprint_probe; mod squid_client_probe; +mod storage_menu_fixture; mod subtitle_fixture; mod tab_list_fixture; mod team_rules_fixture; mod text_display_fixture; +mod text_display_render_fixture; mod toggle_keys_probe; mod tooltip_display_fixture; mod trading_fixture; @@ -115,9 +119,11 @@ probes! { cushion_placement_probe, cauldron_fixture, chat_screen_fixture, + chest_pair_fixture, cloud_fixture, command_parse_fixture, container_screen_probe, + container_title_fixture, creative_search_fixture, emitter_particles_probe, enchanting_fixture, @@ -174,12 +180,14 @@ probes! { sound_event_fixture, spawn_rules_fixture, sprint_probe, + storage_menu_fixture, subtitle_fixture, tab_list_fixture, team_rules_fixture, toggle_keys_probe, tooltip_display_fixture, text_display_fixture, + text_display_render_fixture, trading_fixture, translucency_fixture, weather_fixture, diff --git a/crates/horizon-client/examples/probe/mob_ai_probe.rs b/crates/horizon-client/examples/probe/mob_ai_probe.rs index 3d23e15ea2..2f42a062ef 100644 --- a/crates/horizon-client/examples/probe/mob_ai_probe.rs +++ b/crates/horizon-client/examples/probe/mob_ai_probe.rs @@ -67,8 +67,9 @@ struct Scenario { /// The player hits a mob for 1 at the start of a tick: (tick, mob index). hurts: Vec<(i32, usize)>, /// The player feeds an animal at the start of a tick (Animal.mobInteract): - /// (tick, mob index); the scenario only feeds each animal its food. - feeds: Vec<(i32, usize)>, + /// (tick, mob index, None); the scenario only feeds each animal its food. + /// `interact` uses another item on a horse: (tick, mob index, Some(item)). + feeds: Vec<(i32, usize, Option)>, hand: Option, mobs: Vec, } @@ -108,7 +109,14 @@ fn parse(text: &str) -> Vec { } "heart" => s.hearts.push(f.iter().map(|x| (*x).to_owned()).collect()), "hurt" => s.hurts.push((f[1].parse().unwrap(), f[2].parse().unwrap())), - "feed" => s.feeds.push((f[1].parse().unwrap(), f[2].parse().unwrap())), + "feed" => s + .feeds + .push((f[1].parse().unwrap(), f[2].parse().unwrap(), None)), + "interact" => s.feeds.push(( + f[1].parse().unwrap(), + f[2].parse().unwrap(), + Some(f[3].to_owned()), + )), "player" => { s.player = Some([ f[1].parse().unwrap(), @@ -725,6 +733,15 @@ fn print(out: &mut String, t: i32, i: usize, m: &Mob, player_down: bool, now: i6 .map_or("-".into(), |p| format!("{},{},{}", p[0], p[1], p[2])); let _ = write!(out, "\tcg {} {held} {opened}", m.extra.variant3); } + if m.species() == Species::Wolf { + // NeutralMob anger left (the angry texture), -1 when calm. + let anger = if m.extra.anger > 0 { + i64::from(m.extra.anger) + } else { + -1 + }; + let _ = write!(out, "\twolf {anger}"); + } if let Some(b) = horizon_client::mobs::bee::get(m) { let anger = if b.anger_end > 0 { b.anger_end - now @@ -1112,6 +1129,7 @@ pub fn main() -> horizon_client::Result<()> { others_grid: $grid, others_index: None, daylight: (world.day_time.rem_euclid(24000)) < 12000, + monsters_burn: !(12542..23460).contains(&world.day_time.rem_euclid(24000)), game_time: world.game_time, day_time: world.day_time, raining: s.rain, @@ -1208,10 +1226,22 @@ pub fn main() -> horizon_client::Result<()> { } events.append(&mut mobs[i].outbox); } - for &(tick, i) in &s.feeds { + for (tick, i, item) in &s.feeds { + let (tick, i) = (*tick, *i); if tick != t || i >= mobs.len() || mobs[i].removed { continue; } + // Horse.mobInteract's held-item branch (makeMad). + if let Some(item) = item { + horizon_client::mobs::equine::held_item( + &mut mobs[i], + item, + false, + item == "minecraft:golden_dandelion", + &mut events, + ); + continue; + } if let Some(Some((name, volume, pitch))) = horizon_client::mobs::ai::feed_animal(&mut mobs[i], &mut level_random) { diff --git a/crates/horizon-client/examples/probe/pack_screen_fixture.rs b/crates/horizon-client/examples/probe/pack_screen_fixture.rs index 5d84ce0a65..dc905c2489 100644 --- a/crates/horizon-client/examples/probe/pack_screen_fixture.rs +++ b/crates/horizon-client/examples/probe/pack_screen_fixture.rs @@ -126,6 +126,7 @@ pub fn main() -> horizon_client::Result<()> { .collect(); let mut screen = PackScreen::new( &m, + Text::translate(c["title"].as_str().unwrap_or("resourcePack.title"), &[]), int(&c["width"]), int(&c["height"]), c["inWorld"] == true, diff --git a/crates/horizon-client/examples/probe/storage_menu_fixture.rs b/crates/horizon-client/examples/probe/storage_menu_fixture.rs new file mode 100644 index 0000000000..2298e1360d --- /dev/null +++ b/crates/horizon-client/examples/probe/storage_menu_fixture.rs @@ -0,0 +1,37 @@ +//! Storage block menus for tools/test_storage_menu_parity.py: reads one +//! block id per line and prints the menu type the server opens it with +//! (`horizon_client::containers::block_menu_type`, as `lan_open_container` +//! sends it) and whether that menu's slots take a shulker box: the server +//! refuses them in a `minecraft:shulker_box` menu (`ShulkerBoxSlot.mayPlace`, +//! `lan::containers`). +//! +//! usage: storage_menu_fixture INPUT OUTPUT +use horizon_client::containers::block_menu_type; +use std::fs; + +fn case(block: &str) -> Option { + let menu = block_menu_type(block.trim())?; + let accepts = if menu == "minecraft:shulker_box" { + "refuses" + } else { + "accepts" + }; + Some(format!("{menu} {accepts} shulker_box")) +} + +pub fn main() -> horizon_client::Result<()> { + let args: Vec = crate::args().collect(); + let input = fs::read_to_string( + args.get(1) + .ok_or("usage: storage_menu_fixture INPUT OUTPUT")?, + )?; + let lines: Vec = input + .lines() + .map(|line| case(line).unwrap_or_else(|| "error".to_owned())) + .collect(); + fs::write( + args.get(2).ok_or("missing OUTPUT")?, + lines.join("\n") + "\n", + )?; + Ok(()) +} diff --git a/crates/horizon-client/examples/probe/text_display_render_fixture.rs b/crates/horizon-client/examples/probe/text_display_render_fixture.rs new file mode 100644 index 0000000000..2a539e4252 --- /dev/null +++ b/crates/horizon-client/examples/probe/text_display_render_fixture.rs @@ -0,0 +1,70 @@ +//! A text display's text for differential tests against vanilla's +//! `TextDisplayRenderer.submitInner` (tools/test_text_display_render_parity.py): +//! decodes each case's entity data from vanilla's wire bytes, lays the text out +//! with `horizon_client::text_display` and prints the pose and every quad in +//! text units [x, y, z, ARGB], as VanillaTextDisplayRenderOracle does. +//! +//! usage: text_display_render_fixture RESOURCES INPUTS OUTPUT +use horizon_client::{ + hud::{HudAtlas, HudBatch}, + hud_layout::background_color, + text_display::{self, Lines, TextDisplayData}, +}; +use horizon_protocol::{ids, packets::play::Clientbound}; +use serde_json::{Value, json}; +use std::{error::Error, io::Write}; + +pub fn main() -> Result<(), Box> { + let args: Vec = crate::args().collect(); + let [_, resources, inputs, output] = args.as_slice() else { + return Err("usage: text_display_render_fixture RESOURCES INPUTS OUTPUT".into()); + }; + let atlas = HudAtlas::build(std::path::Path::new(resources))?; + let mut out = std::io::BufWriter::new(std::fs::File::create(output)?); + for line in std::fs::read_to_string(inputs)?.lines() { + let input: Value = serde_json::from_str(line)?; + let hex = input["wire"].as_str().unwrap_or_default(); + let bytes: Vec = (0..hex.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&hex[i..i + 2], 16)) + .collect::>()?; + let Clientbound::SetEntityData(packet) = + Clientbound::decode(ids::play::clientbound::SET_ENTITY_DATA, &bytes)? + else { + return Err("not entity data".into()); + }; + let mut d = TextDisplayData::default(); + d.apply(&packet.values); + let lines = Lines::split(&atlas.font, &d.text, d.line_width); + let mut batch = HudBatch::new([1, 1]); + let default_background = background_color( + input["chat_only"].as_bool().unwrap_or(false), + input["background_opacity"].as_f64().unwrap_or(0.0), + 0.25, + ); + text_display::draw(&mut batch, &atlas, &d, &lines, default_background); + let quads: Vec = batch + .vertices + .as_chunks::<6>() + .0 + .iter() + .map(|q| { + let corners: Vec = [0, 1, 2, 5] + .map(|i| { + let v = q[i]; + let [r, g, b, a] = v.color.to_le_bytes().map(u32::from); + let argb = (a << 24 | r << 16 | g << 8 | b) as i32; + json!([v.pos[0], v.pos[1], v.depth, argb]) + }) + .into(); + corners.into() + }) + .collect(); + writeln!( + out, + "{}", + json!({"pose": lines.pose().to_cols_array(), "quads": quads}) + )?; + } + Ok(()) +} diff --git a/crates/horizon-client/examples/sprite_cache_parity.rs b/crates/horizon-client/examples/sprite_cache_parity.rs new file mode 100644 index 0000000000..29a297bec2 --- /dev/null +++ b/crates/horizon-client/examples/sprite_cache_parity.rs @@ -0,0 +1,58 @@ +//! M-11 gate: block assets loaded through the sprite disk cache are byte-identical +//! to the ones decoded from the PNGs. Deletes the cache file in ROOT, loads (decodes +//! and writes it), loads again (reads it) and compares the sprite arrays. +//! cargo run --profile fast -p horizon-client --example sprite_cache_parity -- ROOT +use horizon_client::assets::{Assets, RenderAssets}; +use std::{ + hash::{DefaultHasher, Hash, Hasher}, + path::Path, + time::Instant, +}; + +fn digest(render: &RenderAssets) -> u64 { + let mut h = DefaultHasher::new(); + render.pixels.hash(&mut h); + ( + render.sprite_size, + render.sprite_minimum_size, + render.texture_layers, + ) + .hash(&mut h); + render.sprite_layers.hash(&mut h); + render.translucent_layers.hash(&mut h); + format!("{:?}", render.sprite_mips).hash(&mut h); + for animation in &render.animations { + animation.layer.hash(&mut h); + for frame in 0..animation.frame_count() { + animation.pixels(frame).hash(&mut h); + } + for tick in 0..400 { + format!("{:?}", animation.blend(tick)).hash(&mut h); + } + } + h.finish() +} + +fn main() -> horizon_client::Result<()> { + let root = std::env::args() + .nth(1) + .ok_or("usage: sprite_cache_parity ROOT")?; + let root = Path::new(&root); + let assets = Assets::load(root)?; + let _ = std::fs::remove_file(root.join("horizon-sprites.cache")); + let start = Instant::now(); + let cold = digest(&assets.reload(root)?); + let cold_ms = start.elapsed().as_secs_f64() * 1000.; + let size = std::fs::metadata(root.join("horizon-sprites.cache"))?.len(); + let start = Instant::now(); + let warm = digest(&assets.reload(root)?); + let warm_ms = start.elapsed().as_secs_f64() * 1000.; + println!( + "cold={cold:016x} ({cold_ms:.1} ms) warm={warm:016x} ({warm_ms:.1} ms) cache_bytes={size}" + ); + if cold != warm { + return Err("cached sprites differ from decoded sprites".into()); + } + println!("PARITY OK"); + Ok(()) +} diff --git a/crates/horizon-client/src/advancements.rs b/crates/horizon-client/src/advancements.rs index b0f294da7e..8b78474bdd 100644 --- a/crates/horizon-client/src/advancements.rs +++ b/crates/horizon-client/src/advancements.rs @@ -877,6 +877,22 @@ pub fn item_entity( && item(&v["item"], stack, w) } +/// `RecipeCraftedTrigger.TriggerInstance.matches`: each ingredient predicate +/// takes a different one of the stacks used, in order. +pub fn recipe_ingredients(v: &Value, used: &[ItemView], w: &dyn World) -> bool { + let Some(preds) = v.as_array() else { + return true; + }; + let mut remaining: Vec<&ItemView> = used.iter().collect(); + preds.iter().all(|p| { + let Some(i) = remaining.iter().position(|it| item(p, it, w)) else { + return false; + }; + remaining.remove(i); + true + }) +} + /// A `ContextAwarePredicate`: a list of loot conditions (or one condition). pub fn conditions(v: &Value, ctx: &Entities, w: &dyn World) -> bool { match v { diff --git a/crates/horizon-client/src/bin/horizon-ctl/commands.rs b/crates/horizon-client/src/bin/horizon-ctl/commands.rs index f722e2f9a6..f874080ba1 100644 --- a/crates/horizon-client/src/bin/horizon-ctl/commands.rs +++ b/crates/horizon-client/src/bin/horizon-ctl/commands.rs @@ -146,6 +146,7 @@ const PAGES: Ty = Ty::Enum(&[ "shader-packs", "graphics", "graphics-advanced", + "graphics-hdr", "extras", "horizon", "agent", @@ -425,7 +426,7 @@ pub const COMMANDS: &[Cmd] = &[ &[BOX[0], BOX[1], BOX[2], BOX[3], BOX[4], BOX[5], p("block", Ty::Str, "block state")], mcp: true, local: false), cmd!("container", World, "container X Y Z [ITEM COUNT]", "list a container's items, or add items to it", "", ["horizon-ctl container 3 64 1", "horizon-ctl container 3 64 1 diamond 3"], &[BLOCK_XYZ[0], BLOCK_XYZ[1], BLOCK_XYZ[2], opt("item", Ty::Str, "item to add"), opt("count", Ty::Int, "how many")], mcp: true, local: false), - cmd!("render-distance", World, "render-distance N", "chunks, 2..64", "", ["horizon-ctl render-distance 8"], &[p("chunks", Ty::Int, "render distance")], mcp: false, local: false), + cmd!("render-distance", World, "render-distance N", "chunks, 2..128", "", ["horizon-ctl render-distance 8"], &[p("chunks", Ty::Int, "render distance")], mcp: false, local: false), cmd!("wait", World, "wait TICKS", "wait until TICKS game ticks have passed (20 per second)", "Keys and mouse buttons held with key ... down or mouse ... down stay held while it waits. Release them with key ... up, release or stop.", ["horizon-ctl wait 40", "horizon-ctl key w down && horizon-ctl wait 40 && horizon-ctl key w up"], diff --git a/crates/horizon-client/src/crafting.rs b/crates/horizon-client/src/crafting.rs index 899ce90d92..7262aa2d77 100644 --- a/crates/horizon-client/src/crafting.rs +++ b/crates/horizon-client/src/crafting.rs @@ -27,7 +27,7 @@ impl Ingredient { pub fn of(items: Vec) -> Self { Self(items.iter().cloned().collect(), items) } - fn parse(v: &Value, items: &Items) -> Option { + pub(crate) fn parse(v: &Value, items: &Items) -> Option { let mut list: Vec = Vec::new(); let mut add = |s: &str| { let more: Vec = match s.strip_prefix('#') { @@ -43,14 +43,6 @@ impl Ingredient { match v { Value::String(s) => add(s), Value::Array(a) => a.iter().filter_map(Value::as_str).for_each(&mut add), - Value::Object(o) => { - if let Some(s) = o.get("item").and_then(Value::as_str) { - add(s); - } - if let Some(t) = o.get("tag").and_then(Value::as_str) { - add(&format!("#{t}")); - } - } _ => return None, } (!list.is_empty()).then(|| Self::of(list)) diff --git a/crates/horizon-client/src/creative.rs b/crates/horizon-client/src/creative.rs index a262ad1d5d..7405ae8b4d 100644 --- a/crates/horizon-client/src/creative.rs +++ b/crates/horizon-client/src/creative.rs @@ -527,6 +527,53 @@ impl Creative { pub fn scroller_offset(&self) -> f32 { ((Self::SCROLL_HEIGHT - 17.0) * self.scroll) as i32 as f32 } + /// The tabs whose `CreativeModeTab.contains(stack)` holds, in + /// `CreativeModeTabs.tabs()` order, the search tab left out: the stack is + /// among the tab's search-tab items, by item and components. The export + /// lists each tab's own items; the search tab's stacks that no tab shows + /// (the enchanted books below their top level, `SEARCH_TAB_ONLY`) belong + /// to the tabs that show their item. + pub fn tabs_containing(&self, stack: &ItemStack) -> Vec { + let category = |t: &Tab| !t.search && !t.inventory; + let tabs_where = |shows: &dyn Fn(&ItemStack) -> bool| -> Vec { + (0..self.tabs.len()) + .filter(|&i| category(&self.tabs[i]) && self.tabs[i].items.iter().any(shows)) + .collect() + }; + let found = tabs_where(&|s| same_components(s, stack)); + if found.is_empty() && self.search_items.iter().any(|s| same_components(s, stack)) { + return tabs_where(&|s| s.item == stack.item); + } + found + } + /// The `#namespace:path` lines `getTooltipFromContainerItem` adds for a + /// search tab stack while the search is a `#` query: the item tags + /// `updateVisibleTags` keeps for the text after `#` (the path contains it; + /// with a colon, the namespace and the path each contain their part) that + /// the stack has. + pub fn search_tag_lines(&self, stack: &ItemStack) -> Vec { + let Some(text) = self.search.value.strip_prefix('#') else { + return Vec::new(); + }; + let Some(entry) = self + .search_items + .iter() + .position(|s| same_components(s, stack)) + .and_then(|i| self.search_index.get(i)) + else { + return Vec::new(); + }; + let visible = |(ns, path): &(String, String)| match text.split_once(':') { + None => path.contains(text), + Some((n, p)) => ns.contains(java_trim(n)) && path.contains(java_trim(p)), + }; + entry + .tags + .iter() + .filter(|t| visible(t)) + .map(|(ns, path)| format!("#{ns}:{path}")) + .collect() + } /// Tab button position relative to the screen origin (CreativeModeInventoryScreen.getTabX/Y). pub fn tab_position(tab: &Tab) -> (f32, f32) { let mut x = tab.column as f32 * 27.0; @@ -537,12 +584,35 @@ impl Creative { let y = if tab.top { -28.0 } else { Self::HEIGHT - 4.0 }; (x, y) } + /// `CreativeModeInventoryScreen.extractTabButton`'s cursor: an unselected + /// tab asks for the pointing hand when the whole-pixel mouse, relative to + /// the screen origin, is strictly inside its 26x32 button. + pub fn tab_wants_hand(tab: &Tab, selected: bool, mouse: (f32, f32)) -> bool { + let (x, y) = Self::tab_position(tab); + let (mx, my) = (mouse.0.floor(), mouse.1.floor()); + !selected && mx > x && my > y && mx < x + 26.0 && my < y + 32.0 + } } /// Whether a suffix array of `strings` has a suffix starting with `text`: /// some string contains it (none for no strings, even when `text` is empty). fn any<'a>(strings: impl IntoIterator, text: &str) -> bool { strings.into_iter().any(|s| s.contains(text)) } +/// `ItemStack.isSameItemSameComponents`, the count left out, as the +/// tabs' `ItemStackLinkedSet` compares: no components and empty ones match. +fn same_components(a: &ItemStack, b: &ItemStack) -> bool { + let none = crate::item_data::StackData::default(); + a.item == b.item + && a.damage == b.damage + && a.data().unwrap_or(&none) == b.data().unwrap_or(&none) +} +/// `CreativeModeInventoryScreen.getTooltipFromContainerItem`: the plain +/// tooltip only for a category tab's own grid slots (`CustomCreativeSlot`); +/// everywhere else in the screen the tab names (and, on the search tab's +/// grid, the tag lines) follow the item's name. +pub fn tooltip_lists_tabs(category_tab: bool, grid_slot: bool) -> bool { + !(category_tab && grid_slot) +} /// String.toLowerCase(Locale.ROOT). fn java_lower(s: &str) -> String { s.to_lowercase() @@ -690,6 +760,109 @@ mod tests { (carried, effect) } + /// Tabs as the export lists them: two block tabs sharing the oak log, + /// ingredients with the top-level book only, the search tab with every + /// stack (a level 1 book only there) and its tags, the inventory. + fn tooltip_tabs() -> Creative { + let book = |level: u8| { + format!( + r#"{{components:{{"minecraft:stored_enchantments":{{"minecraft:sharpness":{level}}}}},count:1,id:"minecraft:enchanted_book"}}"# + ) + }; + let log = r#"{count:1,id:"minecraft:oak_log"}"#; + let dirt = r#"{count:1,id:"minecraft:dirt"}"#; + let tab = |id: &str, kind: &str, items: Vec| serde_json::json!({"id": id, "type": kind, "title": id, "icon": log, "items": items}); + let mut search = tab( + "minecraft:search", + "SEARCH", + vec![log.into(), dirt.into(), book(1), book(5)], + ); + search["lines"] = serde_json::json!([[], [], [], []]); + search["tags"] = serde_json::json!([ + [ + "minecraft:logs", + "minecraft:oak_logs", + "minecraft:logs_that_burn" + ], + ["minecraft:dirt"], + [], + [] + ]); + Creative::from_json(&serde_json::json!({"tabs": [ + tab("minecraft:building_blocks", "CATEGORY", vec![log.into()]), + tab("minecraft:natural_blocks", "CATEGORY", vec![log.into(), dirt.into()]), + tab("minecraft:hotbar", "HOTBAR", vec![]), + search, + tab("minecraft:ingredients", "CATEGORY", vec![book(5)]), + tab("minecraft:inventory", "INVENTORY", vec![]), + ]})) + .unwrap() + } + + #[test] + fn tooltip_lists_the_tabs_that_contain_the_stack() { + let c = tooltip_tabs(); + let ids = |s: &ItemStack| -> Vec<&str> { + c.tabs_containing(s) + .into_iter() + .map(|i| c.tabs[i].id.as_str()) + .collect() + }; + // In tab order, the search and inventory tabs left out; the count + // doesn't matter. + assert_eq!( + ids(&stack("oak_log", 37)), + ["minecraft:building_blocks", "minecraft:natural_blocks"] + ); + assert_eq!(ids(&stack("dirt", 1)), ["minecraft:natural_blocks"]); + // A stack no tab holds: other components, or no creative item. + let mut damaged = stack("oak_log", 1); + damaged.damage = 3; + assert!(ids(&damaged).is_empty()); + assert!(ids(&stack("barrier", 1)).is_empty()); + // A level 1 book is a search-tab-only stack of the ingredients tab. + let books: Vec<&ItemStack> = c + .search_items() + .iter() + .filter(|s| &*s.item == "minecraft:enchanted_book") + .collect(); + assert_eq!(ids(books[0]), ["minecraft:ingredients"]); + assert_eq!(ids(books[1]), ["minecraft:ingredients"]); + } + + #[test] + fn tooltip_tab_names_skip_only_a_category_tab_grid() { + assert!(!tooltip_lists_tabs(true, true)); + // The hotbar row of a category tab, the search tab's grid and hotbar, + // the inventory tab. + assert!(tooltip_lists_tabs(true, false)); + assert!(tooltip_lists_tabs(false, true)); + assert!(tooltip_lists_tabs(false, false)); + } + + #[test] + fn tag_search_lists_the_matching_tags_of_the_stack() { + let mut c = tooltip_tabs(); + let log = stack("oak_log", 1); + assert!(c.search_tag_lines(&log).is_empty()); + c.search.set_value("logs"); + assert!(c.search_tag_lines(&log).is_empty()); + c.search.set_value("#logs"); + assert_eq!( + c.search_tag_lines(&log), + [ + "#minecraft:logs", + "#minecraft:oak_logs", + "#minecraft:logs_that_burn" + ] + ); + c.search.set_value("#oak"); + assert_eq!(c.search_tag_lines(&log), ["#minecraft:oak_logs"]); + c.search.set_value("#mine : burn"); + assert_eq!(c.search_tag_lines(&log), ["#minecraft:logs_that_burn"]); + assert!(c.search_tag_lines(&stack("dirt", 1)).is_empty()); + } + #[test] fn search_tab_number_keys_swap_over_a_slot_before_typing() { let filled = Hover::Display { filled: true }; @@ -1006,4 +1179,38 @@ mod tests { c.search.key(KeyCode::Backspace, false, false); assert_eq!(c.search.value, "world"); } + + #[test] + fn unselected_tabs_want_the_hand_strictly_inside() { + let tab = |top, column| Tab { + id: String::new(), + title: String::new(), + icon: String::new(), + items: Vec::new(), + top, + column, + search: false, + inventory: false, + }; + // Top row, second column: the button spans x 27..53, y -28..4. + let t = tab(true, 1); + assert!(Creative::tab_wants_hand(&t, false, (40.0, -10.0))); + assert!(!Creative::tab_wants_hand(&t, true, (40.0, -10.0))); + // Its edges ask for nothing; the mouse counts in whole pixels. + assert!(!Creative::tab_wants_hand(&t, false, (27.0, -10.0))); + assert!(!Creative::tab_wants_hand(&t, false, (27.9, -10.0))); + assert!(Creative::tab_wants_hand(&t, false, (28.0, -10.0))); + assert!(Creative::tab_wants_hand(&t, false, (52.9, -10.0))); + assert!(!Creative::tab_wants_hand(&t, false, (53.0, -10.0))); + assert!(!Creative::tab_wants_hand(&t, false, (40.0, -28.0))); + assert!(Creative::tab_wants_hand(&t, false, (40.0, -27.0))); + assert!(Creative::tab_wants_hand(&t, false, (40.0, 3.9))); + assert!(!Creative::tab_wants_hand(&t, false, (40.0, 4.0))); + // Bottom row, right-aligned inventory column: x 169..195, y 132..164. + let b = tab(false, 6); + assert!(Creative::tab_wants_hand(&b, false, (180.0, 140.0))); + assert!(!Creative::tab_wants_hand(&b, false, (169.0, 140.0))); + assert!(Creative::tab_wants_hand(&b, false, (170.0, 140.0))); + assert!(!Creative::tab_wants_hand(&b, false, (180.0, 132.0))); + } } diff --git a/crates/horizon-client/src/debug_chart.rs b/crates/horizon-client/src/debug_chart.rs index 3d9cc55564..1fe7984139 100644 --- a/crates/horizon-client/src/debug_chart.rs +++ b/crates/horizon-client/src/debug_chart.rs @@ -152,6 +152,14 @@ impl DebugCharts { } self.packet_tick = (self.packet_tick + 1) % 20; } + /// Whether `draw` (or the lightmap view) puts something in the bottom + /// left and the bottom right corner, for a `remote` server or not. + pub fn corners(&self, remote: bool) -> [bool; 2] { + [ + self.fps || (self.network && remote), + (self.fps && !self.tick_times.is_empty()) || self.network || self.lightmap, + ] + } /// The charts along the bottom of the screen (`extractRenderState`): /// frame times left and tick times right, or bandwidth left (remote /// servers only) and ping right. `mspt` is the level's milliseconds @@ -525,6 +533,26 @@ mod tests { assert!(overlay && c.fps); } + #[test] + fn corners_are_where_the_charts_are_drawn() { + let mut c = DebugCharts::default(); + assert_eq!(c.corners(true), [false, false]); + // Frame times left; tick times right once the server logged a tick. + c.fps = true; + assert_eq!(c.corners(false), [true, false]); + c.tick_times.log(1); + assert_eq!(c.corners(false), [true, true]); + // Ping right; bandwidth left on a remote server only. + c.fps = false; + c.network = true; + assert_eq!(c.corners(false), [false, true]); + assert_eq!(c.corners(true), [true, true]); + // The lightmap view, bottom right. + c.network = false; + c.lightmap = true; + assert_eq!(c.corners(true), [false, true]); + } + #[test] fn the_lightmap_puts_full_sky_light_at_the_top_left() { let mut pixels = [0u8; 16 * 16 * 4]; diff --git a/crates/horizon-client/src/dialog.rs b/crates/horizon-client/src/dialog.rs index 7acd3e86af..efafe4d393 100644 --- a/crates/horizon-client/src/dialog.rs +++ b/crates/horizon-client/src/dialog.rs @@ -37,9 +37,11 @@ pub enum Kind { columns: i32, button_width: i32, }, - /// `DialogListDialog`: its dialogs as sent (ids, a tag or inline dialogs). + /// `DialogListDialog`: its dialogs as sent (ids, a tag or inline dialogs), + /// and one button per dialog they resolve to (`set_list`). DialogList { dialogs: Tag, + entries: Vec +
+ + +
+ + +""" + + +def cmd_map_check(led, paths): + """Check verdict-map files against the last build without rebuilding: every class, method and fn a row + names must exist (several people can check their own files at once).""" + fn_at = set() + for f in led["fns"]: + if not f.get("decl"): + fn_at.add((f["file"], f.get("owner") or "", f["name"])) + fn_at.add((f["file"], "*", f["name"])) + classes = led["classes"] + + def has_method(cp, meth, depth=0): + if key(cp, meth) in led["methods"]: + return True + return depth < 12 and any(sup in classes and has_method(sup, meth, depth + 1) for sup in classes.get(cp, {}).get("extends", [])) + + problems, rows = [], 0 + for path in paths: + for n, line in enumerate(Path(path).read_text(encoding="utf-8").splitlines(), 1): + if not line.strip() or line.lstrip().startswith("#"): + continue + rows += 1 + parts = [x.strip() for x in line.split("\t")] + ["", "", ""] + name, verdict, where = parts[0], parts[1], parts[2] + if "#" in name: + if name not in led["methods"]: + problems.append(f"{path}:{n} {name}: no method {name}") + continue + head, _, last = name.rpartition(".") + cls_q, meth = (head, last) if head and (last[:1].islower() or last.startswith("<")) else (name, None) + q = cls_q.replace(".", "$") + cands = [p for p in classes if p.endswith("/" + q) or p.rsplit("/", 1)[-1] == q] + where_ok = True + if verdict in ("port", "inline"): + file_, _, rest = where.partition("::") + owner, _, fname = rest.rpartition("::") + where_ok = (file_, owner, fname) in fn_at or (file_, "*", fname) in fn_at + if len(cands) != 1: + problems.append(f"{path}:{n} {name}: {'no' if not cands else 'ambiguous'} class {cls_q}") + elif meth and not has_method(cands[0], meth): + problems.append(f"{path}:{n} {name}: no method {meth} on {cls_q}") + elif not (verdict in ("port", "inline", "missing") or verdict.startswith("skip:") and verdict[5:] in SKIP_REASONS + or verdict == "home" and not meth): + problems.append(f"{path}:{n} {name}: unknown verdict {verdict!r}") + elif meth is None and verdict in ("port", "inline"): + problems.append(f"{path}:{n} {name}: a class row takes skip:, missing or home") + elif not where_ok: + problems.append(f"{path}:{n} {name}: no fn {where}") + print("\n".join(problems) if problems else f"{rows} rows, no problems.") + + +def todo_batch(led, package=None, limit=150, start_after=None): + """The next methods with no answer, whole classes at a time, in package order: what a mapper reads next.""" + by_class = defaultdict(list) + for k, m in led["methods"].items(): + if m["class"].startswith(SKIP_PACKAGES) or m.get("anonymous") or m["name"] == "": + continue + if package and not m["class"].startswith("net/minecraft/" + package.strip("/") + "/"): + continue + if status_of(led, k) == "missing": + by_class[m["class"]].append(k) + out, n = [], 0 + for cp in sorted(by_class): + if start_after and cp <= start_after: + continue + if n and n + len(by_class[cp]) > limit: + break + out.append((cp, sorted(by_class[cp], key=lambda k: (led["methods"][k]["lines"] or [[0]])[0][0] or 0))) + n += len(by_class[cp]) + return out + + +def cmd_todo(led, package, limit, start_after, as_json): + homes = led.get("homes", {}) + by_file = defaultdict(list) + for f in led["fns"]: + if not f.get("test") and not f.get("decl"): + by_file[f["file"]].append(f) + batch = todo_batch(led, package, limit, start_after) + rows = [] + for cp, ks in batch: + home = homes.get(cp) or homes.get(cp.split("$")[0]) or [] + methods = [] + for k in ks: + m = led["methods"][k] + b = snake_case(m["name"]).removeprefix("get_") + near = sorted({fn_ref(led, f["id"]) for file_ in home for f in by_file[file_] + if name_similarity(b, f["name"]) >= 0.34})[:4] + methods.append({"method": m["name"], "line": (m["lines"] or [[None]])[0][0], "size": m["size"], + "callers": len(led["callers"].get(k, [])), + "hints": near + [fn_ref(led, i["fn"]) + " (shares ids/numbers)" for i in led["inferred"].get(k, [])[:2]]}) + rows.append({"class": short(cp), "path": cp, "source": led["classes"][cp]["file"], "home": home, "methods": methods}) + if as_json: + print(json.dumps(rows, indent=1)) + return + if not rows: + print("Nothing left to answer there.") + return + total = sum(len(r["methods"]) for r in rows) + print(f"# {total} methods in {len(rows)} classes; next batch: --after {rows[-1]['path']}") + for r in rows: + print(f"\n## {r['class']} ({r['source']})") + print("home: " + (", ".join(r["home"]) if r["home"] else "none found")) + for mm in r["methods"]: + hint = (" hints: " + "; ".join(mm["hints"])) if mm["hints"] else "" + print(f"- {mm['method']} :{mm['line']} size {mm['size']} callers {mm['callers']}{hint}") + + +def cmd_report(led): + lines = ["# Port ledger", "", f"Vanilla sources: {', '.join(led['sources'])}", ""] + rows = coverage_rows(led, None, 2) + tot = Counter() + for c in rows.values(): + tot.update(c) + lines += [f"{tot['total']} vanilla methods outside the skipped packages: {tot['ported']} ported by a fn whose doc opens " + f"with them, {tot['named']} by a same-named fn (in a file citing the class, an impl of it or a file named " + f"for it), {tot['mentioned']} named in a comment, {tot['linked']} linked through the call graph, " + f"{tot['inferred']} matched by shared ids and numbers, {tot['trivial']} too small to port alone, " + f"{tot['mapped']} mapped and {tot['skipped']} skipped and {tot['absent']} confirmed missing in tools/port_map, " + f"{tot['structural']} matched by the state and calls they share, " + f"{tot['missing']} with no answer yet ({found_pct(tot):.0f}% of those worth porting found, " + f"{classified_pct(tot):.0f}% of all answered).", "", + "## Coverage by package", "", + "| Package | Methods | " + " | ".join(k.capitalize() for k in COVERAGE_COLS) + " | Found % |", + "|---|---:|" + "---:|" * len(COVERAGE_COLS) + "---:|"] + for p, c in sorted(rows.items(), key=lambda kv: -kv[1]["total"])[:80]: + lines.append(f"| {p} | {c['total']} | " + " | ".join(str(c[k]) for k in COVERAGE_COLS) + f" | {found_pct(c):.0f} |") + counts = Counter(g["kind"] for g in led["gaps"]) + lines += ["", "## Gaps", "", ", ".join(f"{k}: {v}" for k, v in counts.most_common()), ""] + for kind, title in (("seam", "Ported on both paths (in-frame and server-client), wired on only one"), + ("client", "Client-only effects the server-client path never reaches"), + ("drift", "Ports that never name a sound, particle, item or constant vanilla uses"), + ("class", "Whole classes nothing ports, next to ported neighbours"), + ("callee", "Ported callers that never reach a ported callee"), + ("stale", "Citations the pinned version doesn't have")): + items = sorted((g for g in led["gaps"] if g["kind"] == kind), key=gap_rank) + if not items: + continue + lines += [f"### {title} ({len(items)})", ""] + lines += ["- " + gap_text(led, g).split(": ", 1)[1] for g in items[:300]] + lines.append("") + lines += ["## Most-called vanilla methods with no port", ""] + lines += [f"- {u['vanilla']} ({u['callers']} callers, {u['status']})" for u in unported(led, None, 150)] + (OUT / "report.md").write_text("\n".join(lines) + "\n") + write_html(led, rows, tot) + print(OUT / "report.md") + print(OUT / "report.html") + + +def write_html(led, rows, tot): + """report.html: one page to look anything up in, offline, no server.""" + def locs(k): + fids = port_of(led, k) + return [fn_ref(led, f) for f in fids[:3]] or [fn_ref(led, f) + " (mention)" for f in led["mentioned"].get(k, [])[:2]] + methods = [] + for k, m in led["methods"].items(): + if m["class"].startswith(SKIP_PACKAGES) or m["name"] == "" or m.get("anonymous"): + continue + methods.append([short(m["class"], m["name"]), m["class"], status_of(led, k), + f"{m['file']}:{m['lines'][0][0] if m['lines'] else '?'}", locs(k)]) + methods.sort(key=lambda r: (COVERAGE_COLS.index(r[2]) if r[2] in COVERAGE_COLS else 9, r[0])) + gaps = [[g["kind"], gap_text(led, g).split(": ", 1)[1]] for g in sorted(led["gaps"], key=gap_rank)] + covcols = ["package", "methods"] + list(COVERAGE_COLS) + ["found %"] + coverage = [[p, c["total"]] + [c[k] for k in COVERAGE_COLS] + [found_pct(c)] + for p, c in sorted(rows.items(), key=lambda kv: -kv[1]["total"])] + data = {"summary": f"{len(led['methods'])} vanilla methods, {len(led['fns'])} Rust functions; " + f"{found_pct(tot):.0f}% of the methods worth porting have a port found; {len(led['gaps'])} gaps.", + "methods": methods, "gaps": gaps, "covcols": covcols, "coverage": coverage} + payload = json.dumps(data, separators=(",", ":")).replace(" {LEDGER}") + return + led = load() + if args.cmd == "where": + cmd_where(led, args.query, args.json) + elif args.cmd == "what": + cmd_what(led, args.location, args.json) + elif args.cmd == "class": + cmd_class(led, args.query, args.json) + elif args.cmd == "coverage": + cmd_coverage(led, args.package, args.depth, args.json) + elif args.cmd == "gaps": + cmd_gaps(led, args.kind, args.package, args.limit, args.json) + elif args.cmd == "report": + cmd_report(led) + elif args.cmd == "issue": + cmd_issue(led, args.ref, args.json) + elif args.cmd == "diff": + cmd_diff(led, args.other, args.json) + elif args.cmd == "map-check": + cmd_map_check(led, args.paths) + elif args.cmd == "todo": + cmd_todo(led, args.package, args.limit, args.after, args.json) + elif args.cmd == "check": + cmd_check(led, args.base, not args.no_compare, args.json) + + +if __name__ == "__main__": + main() diff --git a/tools/port_map/animal-1.tsv b/tools/port_map/animal-1.tsv new file mode 100644 index 0000000000..0eee92d081 --- /dev/null +++ b/tools/port_map/animal-1.tsv @@ -0,0 +1,143 @@ +AgeableWaterCreature.handleAirSupply inline crates/horizon-client/src/mobs/ai.rs::base_tick the needs_water branch of base_tick; dolphins override it to nothing and refill air in aquatic.rs +AgeableWaterCreature.checkSurfaceAgeableWaterCreatureSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::surface_water +Animal.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt InLove is read; LoveCause is not kept +Animal.canMate port crates/horizon-client/src/mobs/animal_brain.rs::can_mate_with brain breeding; the Breed goal in goals.rs does the same for goal-driven animals +Animal.handleEntityEvent port crates/horizon-client/src/particles.rs::entity_event_burst event 18 love hearts +Animal.getDismountLocationForPassenger port crates/horizon-client/src/game/mounts.rs::Game::dismount_mount gap: tries both sides by the mob's yaw, then on top; not DismountHelper's offsets and pose boxes +Allay.createAttributes skip:data attributes come from entity-types.json +Allay.createNavigation port crates/horizon-client/src/mobs/allay.rs::register flying navigation, required path length 48 +Allay.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta dancing and can_duplicate +Allay.isLikedPlayer inline crates/horizon-client/src/mobs/ai.rs::accepts_damage a tame allay ignores damage from any player (not compared by UUID); considersEntityAsAlly is not ported +Allay.setJukeboxPlaying inline crates/horizon-client/src/game/allays.rs::Game::tick_allays polls playing jukeboxes within 10 blocks every tick instead of listening for jukebox events; no remembered jukebox +Allay.wantsToPickUp inline crates/horizon-client/src/game/allays.rs::Game::tick_allays same item within 32 blocks, mob_griefing on +Allay.allayConsidersItemEqual inline crates/horizon-client/src/game/allays.rs::Game::tick_allays gap: compares item ids only, the held item keeps no potion contents +Allay.hasNonMatchingPotion missing an allay holding a potion also fetches other potion types; the held item is only an id +Allay.updateDynamicGameEventListener inline crates/horizon-client/src/game/allays.rs::Game::tick_allays jukebox is polled; there is no vibration listener +Allay.setDancing inline crates/horizon-client/src/game/allays.rs::Game::tick_allays dancing set while a jukebox plays and not panicking +Allay.shouldStopDancing inline crates/horizon-client/src/game/allays.rs::Game::tick_allays no jukebox within 10 blocks stops the dance +Allay.isSpinning inline crates/horizon-client/src/mobs/allay.rs::animate dance clock modulo 55 below 15 +Allay.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt inventory, DuplicationCooldown and liked player; vibration listener data is not written +Allay.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt +Allay.updateDuplicationCooldown inline crates/horizon-client/src/mobs/allay.rs::ai_step egg_time counts down +Allay.setDuplicationCooldown inline crates/horizon-client/src/game/allays.rs::Game::interact_allay 6000 set here; can_duplicate is derived from egg_time in mob_meta +Allay.getLeashOffset missing lead hangs at the default offset (eye height), not 0.6 of it; leash::leash_offset only special-cases llamas +Allay.handleEntityEvent port crates/horizon-client/src/particles.rs::entity_event_burst event 18, three hearts +Allay.spawnHeartParticle port crates/horizon-client/src/particles.rs::around_self one heart's random placement +Allay.VibrationUser.canReceiveVibration missing allays never listen for vibrations: a note block played near one is not remembered +AllayAi.initCoreActivity inline crates/horizon-client/src/mobs/allay.rs::register panic goal; the cooldown countdowns are plain ints +AllayAi.initIdleActivity inline crates/horizon-client/src/mobs/allay.rs::register fetch, deliver, stay close, stroll and look as goals instead of a brain +AllayAi.hearNoteblock missing no liked note block: an allay never learns one, so items always go to the player +AllayAi.getItemDepositPosition port crates/horizon-client/src/mobs/allay.rs::deposit +AllayAi.shouldDepositItemsAtLikedNoteblock inline crates/horizon-client/src/mobs/allay.rs::deposit cooldown test only; the note block's existence and distance are not rechecked +AllayAi.getLikedPlayerPositionTracker port crates/horizon-client/src/game/allays.rs::Game::allay_liked_player +Armadillo.shouldSwitchToScaredState inline crates/horizon-client/src/mobs/armadillo.rs::ball_up_tick rolling past its animation length +Armadillo.onSyncedDataUpdated port crates/horizon-client/src/mobs/armadillo.rs::set_state resets in_state_ticks on a state change +Armadillo.checkArmadilloSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules +Armadillo.brushOffScute port crates/horizon-client/src/game/creatures.rs::Game::interact_armadillo brush use; dispenser.rs dispense_brush does the same for a dispenser +ArmadilloAi.initCoreActivity inline crates/horizon-client/src/mobs/armadillo.rs::register float and panic goals; rolling out is in server_ai_step +ArmadilloAi.initIdleActivity inline crates/horizon-client/src/mobs/armadillo.rs::register gap: only look, stroll and random look; no AnimalMakeLove, FollowTemptation or BabyFollowAdult, so armadillos never seek a partner, follow food or have babies follow adults +ArmadilloAi.initScaredActivity inline crates/horizon-client/src/mobs/armadillo.rs::server_ai_step the PANIC activity and ArmadilloBallUp +Axolotl. port crates/horizon-client/src/mobs/aquatic.rs::register the Axolotl arm: smooth swimming move and look control, amphibious navigation, water malus 0 +Axolotl.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta variant and playing_dead; from_bucket is not synced +Axolotl.addAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt Variant is written; FromBucket is not +Axolotl.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::read_variant Variant is read; FromBucket is not +Axolotl.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize group variants and baby age +Axolotl.tickBabyAnimations inline crates/horizon-client/src/mobs/axolotl.rs::Animation::tick the baby branch +Axolotl.soloAnimation inline crates/horizon-client/src/mobs/axolotl.rs::Animation::tick one state started, the rest stopped +Axolotl.tickAdultAnimations inline crates/horizon-client/src/mobs/axolotl.rs::Animation::tick the binary animators +Axolotl.rehydrate missing a thrown water bottle does not refill an axolotl's air; splash water has no effect on mobs +Axolotl.get port crates/horizon-client/src/entity_nbt.rs::bucket_components axolotl/variant component written to the bucket +Axolotl.applyImplicitComponent port crates/horizon-client/src/entity_nbt.rs::load_bucket_data axolotl/variant read from the bucket +Axolotl.getBreedOffspring port crates/horizon-client/src/game/creatures.rs::Game::breed gap: baby takes the first parent's colour; no random parent, no 1-in-1200 blue, not set persistent +Axolotl.createAttributes skip:data attributes come from entity-types.json +Axolotl.mobInteract port crates/horizon-client/src/game/creatures.rs::Game::interact_mob a water bucket scoops it up (bucketable) +Axolotl.loadFromBucketTag port crates/horizon-client/src/entity_nbt.rs::load_bucket_data age, age lock and hunting cooldown +Axolotl.applySupportingEffects inline crates/horizon-client/src/game/creatures.rs::Game::handle_mob_events the AxolotlSupportPlayer event: regeneration and no mining fatigue, host player only +Axolotl.usePlayerItem missing feeding a tropical fish bucket uses up the whole bucket; vanilla hands back a water bucket +Axolotl.Variant.getSpawnVariant port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize gap: the common pick for spawn groups only; the rare (blue) pick is never drawn +AxolotlAi.initPlayDeadActivity port crates/horizon-client/src/mobs/animal_brain.rs::axolotl_package +AxolotlAi.initFightActivity port crates/horizon-client/src/mobs/animal_brain.rs::axolotl_package +AxolotlAi.initCoreActivity port crates/horizon-client/src/mobs/animal_brain.rs::axolotl_package +AxolotlAi.initIdleActivity port crates/horizon-client/src/mobs/animal_brain.rs::axolotl_package +AxolotlAi.canSetWalkTargetFromLookTarget port crates/horizon-client/src/mobs/animal_brain.rs::axolotl_action the WalkFromLook arm +PlayDead.canStillUse inline crates/horizon-client/src/mobs/animal_brain.rs::B::tick_or_stop the PlayDead behaviour: in water and ticks present +PlayDead.start inline crates/horizon-client/src/mobs/animal_brain.rs::B::try_start clears targets, regeneration for 200 ticks +Bee.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta flags and anger end time +Bee.registerGoals port crates/horizon-client/src/mobs/bee.rs::register +Bee.spawnFluidParticle port crates/horizon-client/src/mobs/ambient.rs::client_particles the nectar drip +Bee.updateRollAmount inline crates/horizon-client/src/mobs/bee.rs::tick +Bee.isHiveNearFire port crates/horizon-client/src/mobs/bee.rs::hive_near_fire +Bee.doesHiveHaveSpace inline crates/horizon-client/src/mobs/bee.rs::hives_with_space the beehive's full() filter +Bee.getFlag port crates/horizon-client/src/mobs/bee.rs::flag +Bee.createAttributes skip:data attributes come from entity-types.json +Bee.createNavigation port crates/horizon-client/src/mobs/bee.rs::register flying navigation; stable_destination is the isStableDestination override +Bee.isFlapping missing a flying bee gives off no flap vibration, so sculk sensors never hear it +Bee.getLeashOffset missing lead hangs at the default offset, not half the eye height +Bee.registerDebugValues skip:debug +Bee.BaseBeeGoal.canContinueToUse port crates/horizon-client/src/mobs/bee.rs::calm not angry +Bee.BeeBecomeAngryTargetGoal.canContinueToUse port crates/horizon-client/src/mobs/bee.rs::BecomeAngryTarget::can_continue +Bee.BeeGoToHiveGoal.blacklistTarget inline crates/horizon-client/src/mobs/bee.rs::GoToHive::drop_and_blacklist keeps the last three +Bee.BeeLocateHiveGoal.findNearbyHivesWithSpace port crates/horizon-client/src/mobs/bee.rs::hives_with_space +Bee.BeePollinateGoal.canBeeContinueToUse port crates/horizon-client/src/mobs/bee.rs::Pollinate::can_continue +Bee.BeePollinateGoal.setWantedPos port crates/horizon-client/src/mobs/bee.rs::Pollinate::set_wanted +Bee.BeeWanderGoal.findPos inline crates/horizon-client/src/mobs/bee.rs::Wander::start +Bee.BeeWanderGoal.getWanderThreshold inline crates/horizon-client/src/mobs/bee.rs::Wander::start always 24: it only applies with a valid hive +Bee.ValidateHiveGoal.canBeeUse port crates/horizon-client/src/mobs/bee.rs::ValidateHive::can_use +Camel.addAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt LastPoseTick is written as -1 or 0, not the real tick +Camel.readAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_from_nbt sitting is read from the sign of LastPoseTick +Camel.createAttributes skip:data attributes come from entity-types.json +Camel.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta dash and last pose change tick +Camel.checkCamelSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules +Camel.getDefaultDimensions missing a sitting camel keeps its standing hitbox; Mob::dims has no sitting size +Camel.updateWalkAnimation missing legs use the default walk animation: no zero while sitting or dashing, 0.4 smoothing, no x3 for babies +Camel.travel inline crates/horizon-client/src/mobs/camel.rs::ai_step refuse_to_move zeroes horizontal motion and input +Camel.getRiddenSpeed inline crates/horizon-client/src/mobs/equine.rs::ridden_step 0.1 sprint bonus when the dash is ready +Camel.getRiddenRotation missing a ridden camel that is sitting or changing pose still turns with the rider's view; vanilla holds its rotation +Camel.onPlayerJump inline crates/horizon-client/src/mobs/camel.rs::dash the cooldown, ground and refuse_to_move gates +Camel.handleStartJump port crates/horizon-client/src/mobs/camel.rs::dash dash sound and dashing flag; no entity_action game event +Camel.mobInteract port crates/horizon-client/src/game/mounts.rs::Game::interact_mount inventory on sneak, feeding, riding +Camel.onElasticLeashPull missing a leashed sitting camel does not stand up when the lead pulls +Camel.canMate port crates/horizon-client/src/mobs/goals.rs::Breed::can_use gap: no canParent checks for camels (full health, not ridden) +Camel.getBodyAnchorAnimationYOffset port crates/horizon-client/src/game/mounts.rs::Game::position_rider gap: sitting or standing height only, no sit/stand transition interpolation +Camel.getLeashOffset missing lead hangs at the default offset, not at the camel's body anchor +Camel.isCamelSitting port crates/horizon-client/src/mobs/camel.rs::sitting +Camel.isCamelVisuallySitting inline crates/horizon-client/src/mobs/camel.rs::animation_states +Camel.isInPoseTransition port crates/horizon-client/src/mobs/camel.rs::in_transition +Camel.isVisuallySittingDown inline crates/horizon-client/src/mobs/camel.rs::animation_states +Camel.getEquipSound port crates/horizon-client/src/game/dispenser.rs::equip_sound gap: saddling by hand in interact_mount plays the horse sound for camels +CamelAi.initCoreActivity inline crates/horizon-client/src/mobs/camel.rs::register float and CamelPanic goals +CamelAi.initIdleActivity inline crates/horizon-client/src/mobs/camel.rs::register breed, tempt, follow parent, stroll, random sitting, look +CamelAi.CamelPanic.checkExtraStartConditions port crates/horizon-client/src/mobs/camel.rs::CamelPanic::can_use +CamelAi.RandomSitting.checkExtraStartConditions port crates/horizon-client/src/mobs/camel.rs::RandomSitting::can_use gap: no leash or suffocation (canCamelChangePose) check +Chicken.registerGoals port crates/horizon-client/src/mobs/farm.rs::register +Chicken.getSoundSet inline crates/horizon-client/src/mobs/ai.rs::voice_sounds gap: baby sets only; the picky sound variant is stored but never used +Chicken.getBreedOffspring port crates/horizon-client/src/game/creatures.rs::Game::breed gap: baby copies the first parent's variant, no random parent +Chicken.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize variant by biome and a random sound variant +Chicken.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: variant is synced, sound variant is not +Chicken.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt EggLayTime, variant, sound_variant; jockey in riders.rs +Chicken.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt +Chicken.get port crates/horizon-client/src/mobs/finalize.rs::variant_component gap: chicken/variant only, no chicken/sound_variant +Chicken.applyImplicitComponents missing variant components on a spawn egg or item are ignored; the mob gets its biome variant +Chicken.applyImplicitComponent missing see applyImplicitComponents +Chicken.positionRider inline crates/horizon-client/src/game/passengers.rs::Game::position_riders every mob rider copies its vehicle's body yaw, not only chicken riders +ChickenVariants skip:data chicken variants come from the registry data files +AbstractCow.registerGoals port crates/horizon-client/src/mobs/farm.rs::register +AbstractCow.mobInteract port crates/horizon-client/src/game/creatures.rs::Game::interact_mob the Cow/Mooshroom bucket arm gives milk +Cow.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: variant is synced, sound variant is not +Cow.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt +Cow.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt +Cow.getBreedOffspring port crates/horizon-client/src/game/creatures.rs::Game::breed gap: baby copies the first parent's variant, no random parent +Cow.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize variant by biome and a random sound variant +CowVariants skip:data cow variants come from the registry data files +MushroomCow.getWalkTargetValue port crates/horizon-client/src/mobs/spawn_selection.rs::mob_spawn_rules gap: only the spawn check has the mycelium rule; ai.rs walk_target_value uses the grass rule for strolling +MushroomCow.checkMushroomSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules +MushroomCow.thunderHit port crates/horizon-client/src/game/lightning.rs::Game::thunder_hit swaps colour once per bolt, convert sound +MushroomCow.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta the type entry +MushroomCow.mobInteract port crates/horizon-client/src/game/creatures.rs::Game::interact_mob gap: bowl stew and shearing are done; feeding a flower to a brown mooshroom (suspicious stew) is missing +MushroomCow.addAdditionalSaveData missing Type and stew_effects are not written from the live mob: a colour change (lightning) is not saved +MushroomCow.readAdditionalSaveData missing Type is not read: a saved brown mooshroom loads red +MushroomCow.getEffectsFromItemStack missing no suspicious stew effects from flowers: a brown mooshroom never takes a flower +MushroomCow.get missing mooshroom/variant component is not reported; species_variant_component has no mooshroom +MushroomCow.applyImplicitComponent missing mooshroom/variant component on a spawn egg is ignored +MushroomCow.getBreedOffspring port crates/horizon-client/src/game/creatures.rs::Game::breed see getOffspringVariant +MushroomCow.getOffspringVariant missing baby copies the first parent's colour: no 50/50 mix, no 1-in-1024 chance of the other colour diff --git a/tools/port_map/classes-01.tsv b/tools/port_map/classes-01.tsv new file mode 100644 index 0000000000..7586a889a4 --- /dev/null +++ b/tools/port_map/classes-01.tsv @@ -0,0 +1,220 @@ +GlowSquid.aiStep missing player: glow squids shed no glow particles and their dark timer is not kept, so a hit squid does not dim +GlowSquid.checkGlowSquidSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::GlowSquid arm: 33 blocks below sea level, dark, in water +GlowSquid.hurtServer missing player: a glow squid hit does not get the 100-tick dark timer, so it does not dim +WolfSoundVariants skip:data registry bootstrap of the wolf sound sets; sounds are in species.rs and finalize.rs +AbstractDragonPhaseInstance.getTurnSpeed inline crates/horizon-client/src/game/dragon.rs::Game::tick_dragon 0.7 / dist / rot turn for every phase but Landing; only applied while a fly target is set +DragonChargePlayerPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Charging arm: 10-tick countdown once close; gap: minor: no collision re-target +DragonDeathPhase.doClientTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_dragon_death gap: player: the phase's explosion puff every 10 ticks through the whole dying flight is missing; only the tickDeath bursts at 180 to 200 are drawn +DragonDeathPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Dying arm sets health 1 or 0; gap: minor: egg found with the leaves-excluded heightmap, vanilla uses MOTION_BLOCKING +DragonHoldingPatternPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Holding arm; gap: minor: horizontal and vertical collision re-target not tracked (always false) +DragonHoldingPatternPhase.findNewTarget inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Holding arm: landing roll, strafe pick by egg distance, ring path +DragonHoldingPatternPhase.navigateToNextPathNode port crates/horizon-client/src/game/dragon.rs::Game::next_target +DragonHoldingPatternPhase.onCrystalDestroyed inline crates/horizon-client/src/game/dragon.rs::Game::destroy_crystal sets strafe_request for the killer or the nearest; tick_phase strafes from Holding only +DragonHoldingPatternPhase.strafePlayer inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase phase switch to Strafe and setTarget (strafe_begin) in the Holding arm and the strafe_request block +DragonLandingApproachPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase LandingApproach arm; gap: minor: no collision re-target +DragonLandingApproachPhase.findNewTarget inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase LandingApproach arm: path to the node by the nearest enemy to the egg, last node is the podium +DragonLandingApproachPhase.navigateToNextPathNode port crates/horizon-client/src/game/dragon.rs::Game::next_target +DragonLandingPhase.doClientTick missing player: while the dragon comes down to land its head breathes a fan of dragon-breath particles; none are spawned +DragonLandingPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Landing arm: sets flame count 0 and goes to Scanning on reaching the podium +DragonLandingPhase.getTurnSpeed inline crates/horizon-client/src/game/dragon.rs::Game::tick_dragon Landing turn min(rot,40)/rot, the one turn formula that differs from the default +DragonSittingAttackingPhase.doClientTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase gap: player: the growl is played once at the start of the 40 ticks; vanilla replays it on every client tick +DragonSittingAttackingPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Attacking arm: 40 ticks then Flaming +DragonSittingFlamingPhase.doClientTick missing player: during the first 10 ticks of a flame attack the head breathes dragon-breath particles in a fan; none are spawned +DragonSittingFlamingPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Flaming arm: the flame cloud at tick 10, 200 ticks, flame count 4 leads to Takeoff +DragonSittingScanningPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Scanning arm; gap: minor: turns every tick, vanilla skips the turn while the target is within 10 degrees of the facing +DragonTakeoffPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Takeoff arm: first tick finds a new target, later ticks go to Holding once 10 blocks from the podium +DragonTakeoffPhase.findNewTarget inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Takeoff arm; gap: minor: no path leaves an empty path, vanilla leaves none and searches again next tick +DragonTakeoffPhase.navigateToNextPathNode port crates/horizon-client/src/game/dragon.rs::Game::next_target +EnderDragonPhase.create skip:data phase registry; the ids are fixed in Phase::id +EnderDragonPhase.createInstance port crates/horizon-client/src/game/dragon.rs::Phase::from_id builds the default instance for an id +EnderDragonPhase.getById port crates/horizon-client/src/game/dragon.rs::Phase::from_id id to phase, unknown ids fall back to Holding +Mannequin.addAdditionalSaveData missing player: a saved mannequin is written without its profile, so after a reload it wears the default skin +Mannequin.applyImplicitComponent missing minor: the profile from a spawning item is not applied; Horizon has no mannequin item, only summon with a profile tag +Mannequin.defineSynchedData inline crates/horizon-client/src/game/remote_mob_data.rs::apply gap: player: reads the profile (slot 17) only; DATA_IMMOVABLE and DATA_DESCRIPTION are not read +Mannequin.get skip:other data-component getter for the PROFILE component; the profile is Mob.profile, read directly +Mannequin.readAdditionalSaveData missing player: the profile is not read back, so a reloaded mannequin wears the default skin +Mannequin.updateDescription missing player: the mannequin's description (default 'Mannequin') is never drawn under its name +Giant.createAttributes skip:data attributes come from entity-types.json +ShootWhenStuck.checkExtraStartConditions inline crates/horizon-client/src/mobs/breeze.rs::register +Evoker.createAttributes skip:data attributes come from entity-types.json +Evoker.registerGoals port crates/horizon-client/src/mobs/illager.rs::register_evoker +Evoker.EvokerCastingSpellGoal.tick port crates/horizon-client/src/mobs/illager.rs::CastingSpell::tick looks at the target, else the wololo sheep +Pillager.applyRaidBuffs port crates/horizon-client/src/game/mob_gear.rs::Game::apply_raid_buffs +Pillager.createAttributes skip:data attributes come from entity-types.json +Pillager.finalizeSpawn inline crates/horizon-client/src/mobs/finalize.rs::Ctx::mob crossbow in the main hand via populate; the enchant roll is in Ctx::enchant +Pillager.getArmPose port crates/horizon-client/src/mobs/illager.rs::arm_pose +Pillager.getSlot missing minor: the pillager's five spare slots (ids 300 and up, used by /item) are not modelled +Pillager.pickUpItem missing player: during a raid a pillager does not pick up a white banner lying on the ground +Pillager.registerGoals port crates/horizon-client/src/mobs/illager.rs::register_pillager +Pillager.wantsItem missing player: the raid banner pickup check is missing (see pickUpItem) +PiglinBrute.createAttributes skip:data attributes come from entity-types.json +PiglinBrute.customServerAiStep inline crates/horizon-client/src/mobs/piglin.rs::register_brute gap: minor: the brain tick is replaced by the goals set in register_brute; the activity update is in PiglinBruteAi rows +PiglinBrute.finalizeSpawn inline crates/horizon-client/src/mobs/finalize.rs::Ctx::mob golden axe in the main hand via populate; initMemories is missing +PiglinBrute.hurtServer inline crates/horizon-client/src/mobs/piglin.rs::register_brute gap: minor: retaliation is the HurtByTarget goal; no PiglinAi.maybeRetaliate for non-piglin attackers +PiglinBrute.wantsToPickUp missing player: a piglin brute does not pick up a golden axe lying on the ground; piglin_pickups only serves piglins +PiglinBruteAi.createIdleLookBehaviors inline crates/horizon-client/src/mobs/piglin.rs::register_brute gap: minor: looks at players only (LookAtPlayer); no look at piglins, random look or do-nothing pause +PiglinBruteAi.createIdleMovementBehaviors inline crates/horizon-client/src/mobs/piglin.rs::register_brute gap: minor: strolls anywhere; no visits to piglins and no return to its home point +PiglinBruteAi.findNearestValidAttackTarget inline crates/horizon-client/src/mobs/piglin.rs::register_brute gap: minor: targets nearest player or wither skeleton through the target goals; no anger memory first +PiglinBruteAi.initCoreActivity inline crates/horizon-client/src/mobs/piglin.rs::register_brute gap: minor: no door opening and no stop-anger-when-target-dead rule; panic-free float only +PiglinBruteAi.initFightActivity inline crates/horizon-client/src/mobs/piglin.rs::register_brute melee goal at priority 2; attacks as the MeleeAttack goal does +PiglinBruteAi.initIdleActivity inline crates/horizon-client/src/mobs/piglin.rs::register_brute idle behaviour as goals: stroll, look at player, targets +PiglinBruteAi.initMemories missing minor: no home point is remembered, so the brute never returns to where it spawned +PiglinBruteAi.isNearestValidAttackTarget missing minor: the brute never drops a target for a nearer one; target goals keep theirs until it dies or is lost +PiglinBruteAi.maybePlayActivitySound missing player: a piglin brute never growls while it fights; the angry sound is not in Horizon +PiglinBruteAi.playActivitySound missing player: no angry sound when a piglin brute starts a fight (entity.piglin_brute.angry is not played) +PiglinBruteAi.setAngerTarget missing minor: no 600-tick anger memory; the target goals keep the target instead +PiglinBruteAi.updateActivity inline crates/horizon-client/src/mobs/piglin.rs::register_brute gap: minor: no fight and idle activity switch; the melee goal sets the aggressive flag +StartAdmiringItemIfSeen.create inline crates/horizon-client/src/game/piglins.rs::Game::piglin_pickups gap: player: a piglin takes a wanted item and admires it in hand; it does not stop to admire one it only sees on the ground +StopAdmiringIfItemTooFarAway.create missing minor: no admire-on-the-ground phase, so no stop when the wanted item moves away +StopAdmiringIfTiredOfTryingToReachItem.create missing minor: no time limit on walking to an admired item; the piglin walks to the item as GoToWantedItem does +Parched.getArrow inline crates/horizon-client/src/mobs/undead.rs::perform_ranged_attack Parched arrows carry weakness for 600 ticks +Stray.checkStraySpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::Stray arm: powder snow skipped, sky must be visible +Stray.getArrow inline crates/horizon-client/src/mobs/undead.rs::perform_ranged_attack Stray arrows carry slowness for 600 ticks +CaveSpider.getVehicleAttachmentPoint port crates/horizon-client/src/mobs/riders.rs::vehicle_attachment gap: minor: rides at 0 height; vanilla uses 0.21875 when the vehicle is not wider +VillagerDataHolder.finalizeVillagerType inline crates/horizon-client/src/mobs/finalize.rs::Ctx::villager_type +Abilities.apply port crates/horizon-client/src/game/remote.rs::Game::apply_remote_abilities gap: minor: invulnerable, flying, may fly and flying speed only; instabuild, may build and walk speed are not applied +Abilities.pack skip:java record copy for the abilities packet; the flags are read in remote.rs +PlayerSkin.with missing minor: skin patches (body, cape, elytra and model overrides) are not applied on top of a profile skin +PlayerSkin.Patch.create skip:java record factory for the patch; the patch itself is missing (see PlayerSkin.with) +SpectralArrow.doPostHurtEffects missing player: a spectral arrow shot from a bow or crossbow does not make its target glow; release_bow and crossbow use no effect +SpectralArrow.tick missing player: a spectral arrow in flight leaves no yellow effect trail +Fireball.getSlot missing minor: the fireball's item slot (its displayed fire charge) is not modelled +Fireball.readAdditionalSaveData missing unsure: fireball_from_nbt reads no Item key, so the displayed item is not restored +Fireball.setItem missing minor: the displayed item of a fireball is not set +Fireball.shouldRenderAtSqrDistance skip:ui render distance rule for the first two ticks; Horizon draws the projectile as its own pass +LargeFireball.onHit port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit LargeFireball arm: explosion of the given power, then the projectile is gone +LargeFireball.onHitEntity port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit LargeFireball arm: 6 damage to the entity +SmallFireball.onHitEntity port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit SmallFireball arm: ignites for 5 seconds and deals 5 damage +AbstractThrownPotion.affectEntitiesAround missing player: a splash water potion does not hurt water-sensitive mobs or put out burning entities nearby +AbstractThrownPotion.calculateHorizontalHurtKnockbackDirection missing minor: the splash hurt pushes nothing away from the potion's position +AbstractThrownPotion.douseFire missing player: a splash water potion does not put out fire blocks, candles or campfires around where it lands +AbstractThrownPotion.onHitBlock missing player: a water splash that lands on a block does not douse fire, candles or campfires next to it +Snowball.getParticle port crates/horizon-client/src/game/creatures.rs::Game::item_burst snowball item burst on break +Snowball.handleEntityEvent port crates/horizon-client/src/game/creatures.rs::Game::thrown_break_event break event 3 for thrown items +Snowball.onHit inline crates/horizon-client/src/game/creatures.rs::Game::projectile_hit Snowball arm: 3 damage to blazes, then the break burst +ThrowableItemProjectile.readAdditionalSaveData missing minor: the thrown item is not read back from the save +Activity.equals skip:java value equality of a named activity; no Horizon counterpart needed +ModelAndTexture.codec skip:data codec for the variant model and texture asset; Horizon reads variant data from files +MoonBrightnessCheck.test inline crates/horizon-client/src/mobs/finalize.rs::VariantRegistry::select Condition::Moon arm: moon brightness of the phase against the range +PriorityProvider.select port crates/horizon-client/src/mobs/finalize.rs::VariantRegistry::select highest priority entries whose conditions hold, one picked at random +SpawnConditions.bootstrap skip:data registry bootstrap of spawn condition codecs; Horizon reads variant conditions from files +SpawnContext.create inline crates/horizon-client/src/mobs/finalize.rs::VariantRegistry::select the level and position passed to select; no separate context object +variant/StructureCheck.test inline crates/horizon-client/src/mobs/finalize.rs::VariantRegistry::select Condition::Structure arm: structures with a piece at the position +AbstractMinecartContainer.applyNaturalSlowdown inline crates/horizon-client/src/game/minecarts.rs::Game::natural_slowdown gap: minor: plain cart friction 0.96; the container's 0.98 plus 0.001 per empty redstone level is missing +AbstractMinecartContainer.createMenu inline crates/horizon-client/src/game/minecarts.rs::Game::interact_cart gap: minor: opens the chest for anyone; no refusal of a loot-table cart for spectators +AbstractMinecartContainer.remove inline crates/horizon-client/src/game/minecarts.rs::Game::hurt_cart contents spill when the cart breaks, if entity_drops is on +MinecartChest.interact inline crates/horizon-client/src/game/minecarts.rs::Game::interact_cart chest arm: opens the cart's items and plays the chest sound +MinecartCommandBlock.activateMinecart inline crates/horizon-client/src/game/minecarts.rs::Game::activate_cart gap: minor: no command ever runs; the cart holds no command since its editor is missing +MinecartCommandBlock.defineSynchedData missing minor: the command text and last output are not synced; no command cart holds a command +MinecartCommandBlock.interact missing player: a creative player right-clicking a command minecart gets no command editor; vanilla opens it +MinecartCommandBlock.onSyncedDataUpdated missing minor: no synced command text to load into the cart +MinecartCommandBlock.readAdditionalSaveData inline crates/horizon-client/src/game/minecarts.rs::Minecart::from_nbt gap: minor: the saved Command text is not read +MinecartCommandBlock.MinecartCommandBase.createCommandSourceStack missing minor: no command runs from a cart, so no source stack is built +MinecartCommandBlock.MinecartCommandBase.onUpdated missing minor: no command text changes to push to the cart +MinecartFurnace.addAdditionalSaveData port crates/horizon-client/src/game/minecarts.rs::Minecart::to_nbt +MinecartFurnace.addFuel inline crates/horizon-client/src/game/minecarts.rs::Game::interact_cart furnace arm: coal or charcoal adds 3600 ticks of fuel up to 32000 +MinecartFurnace.applyNaturalSlowdown port crates/horizon-client/src/game/minecarts.rs::Game::natural_slowdown +MinecartFurnace.calculateNewPushAlong inline crates/horizon-client/src/game/minecarts.rs::Game::natural_slowdown the push projected on the velocity, inside natural_slowdown +MinecartFurnace.getDefaultDisplayBlockState port crates/horizon-client/src/game/minecarts.rs::Game::display_state lit furnace while it has fuel +MinecartFurnace.getMaxSpeed port crates/horizon-client/src/game/minecarts.rs::Game::max_speed half speed on land, three quarters of 0.2 in water +MinecartFurnace.interact inline crates/horizon-client/src/game/minecarts.rs::Game::interact_cart furnace arm +MinecartFurnace.readAdditionalSaveData port crates/horizon-client/src/game/minecarts.rs::Minecart::from_nbt +MinecartFurnace.tick inline crates/horizon-client/src/game/minecarts.rs::Game::tick_cart furnace variant: fuel counts down, push clears at zero, smoke puffs +MinecartSpawner missing player: spawner minecarts do not exist in Horizon: no cart kind, so a spawner minecart item does nothing on a rail +NewMinecartBehavior skip:other experimental minecart_improvements physics; Horizon always runs the old behaviour in minecarts.rs and writes that pack disabled in save.rs +NewMinecartBehavior.TrackIteration skip:other part of the experimental minecart_improvements physics; see NewMinecartBehavior +FeatureFlagRegistry skip:data name table of the feature packs (functions.rs FEATURE_PACKS); codecs are not needed +FeatureFlagRegistry.Builder skip:java builder for the flag registry +FeatureFlagSet skip:java bit set of feature flags; Horizon only tracks the built-in packs by name +FeatureFlags skip:server message text for the server's pack feature check +AbstractCraftingMenu.addCraftingGridSlots skip:ui slot positions of the crafting grid; menu.rs keeps the vanilla slot indices +AbstractCraftingMenu.addResultSlot skip:ui result slot position; menu.rs keeps the vanilla slot index +AbstractCraftingMenu.handlePlacement port crates/horizon-client/src/menu/place.rs::Menu::place_recipe recipe placement from the book +ChestMenu.addChestGrid skip:ui slot grid layout of the chest screen, drawn by storage.rs +ChestMenu.quickMoveStack port crates/horizon-client/src/game/storage.rs::Game::container_quick_move +ChestMenu.sixRows skip:ui constructor shorthand for the six-row chest layout +ChestMenu.threeRows skip:ui constructor shorthand for the three-row chest layout +ContainerLevelAccess.execute skip:java level access wrapper for menus; Horizon's menus hold the game state directly +DataSlot.checkAndClearUpdateFlag skip:java dirty flag for data slot syncing; Horizon sends menu data as it changes +FurnaceFuelSlot.mayPlace inline crates/horizon-client/src/menu.rs::Menu::may_place fuel slot: only items that burn (is_fuel) +ItemCombinerMenuSlotDefinition.Builder.build skip:java builder for anvil and smithing slot definitions +ItemCombinerMenuSlotDefinition.Builder.withResultSlot skip:java builder for anvil and smithing slot definitions +ItemCombinerMenuSlotDefinition.Builder.withSlot skip:java builder for anvil and smithing slot definitions +PlayerEnderChestContainer.fromSlots skip:java builds the ender chest from saved slots; Horizon keeps ender_items +PlayerEnderChestContainer.startOpen port crates/horizon-client/src/game/storage.rs::Game::open_ender_chest +PlayerEnderChestContainer.stillValid inline crates/horizon-client/src/game/storage.rs::Game::tick_container_reach closes the chest when the player walks away +PlayerEnderChestContainer.stopOpen port crates/horizon-client/src/game/storage.rs::Game::close_ender_chest +PlayerEnderChestContainer.storeAsSlots skip:java saves the ender chest as slots; Horizon writes ender_items +RecipeCraftingHolder.setRecipeUsed missing minor: the last recipe used by a crafting block is not remembered +RemoteSlot.Synchronized.matches skip:java compares a slot with its last synced copy; Horizon sends the slot when it changes +ResultContainer.isEmpty port crates/horizon-client/src/menu.rs::Menu::has_item the result slot is empty when it has no item +ShulkerBoxMenu.quickMoveStack port crates/horizon-client/src/game/storage.rs::Game::container_quick_move +TransientCraftingContainer.fillStackedContents port crates/horizon-client/src/menu/place.rs::Menu::stacked_contents +TransientCraftingContainer.getItem skip:java slot read of a menu-owned grid; Horizon reads menu slots directly +TransientCraftingContainer.isEmpty skip:java emptiness of a menu-owned grid; Horizon reads menu slots directly +TransientCraftingContainer.removeItem skip:java slot removal of a menu-owned grid; Horizon takes from menu slots directly +TransientCraftingContainer.setItem skip:java slot write of a menu-owned grid; Horizon writes menu slots directly +AdventureModePredicate missing unsure: no can_place_on or can_break list check found in the client +ArrayItemProvider.findNext inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +ArrayItemProvider.next inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +ArrayItemProvider.peek inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +ArrayItemProvider.setPosition inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +ArrayItemProvider.Cycled.computeNextItem inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +ArrayItemProvider.Cycled.findNext inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +BottleItem.use port crates/horizon-client/src/game/potions.rs::Game::use_potion_item glass bottle arm +CreativeModeTab.buildContents port crates/horizon-client/src/creative.rs::Creative::items +CreativeModeTab.getIconItem inline crates/horizon-client/src/creative.rs::Creative::tab_headers tab header carries the icon +CreativeModeTab.shouldDisplay inline crates/horizon-client/src/creative.rs::Creative::tab_headers tabs listed from the data file +CreativeModeTab.Builder.build skip:java builder for tab properties +CreativeModeTab.ItemDisplayBuilder.accept skip:data tab contents are read from the creative data file +CreativeModeTab.ItemDisplayParameters.needsUpdate skip:java cache check for tab contents +CreativeModeTab.Output.accept skip:data tab contents are read from the creative data file +CreativeModeTab.Output.acceptAll skip:data tab contents are read from the creative data file +CreativeModeTabs.buildAllTabContents port crates/horizon-client/src/creative.rs::Creative::from_json tabs and their items built from the data +CreativeModeTabs.copperBlockFamilies skip:data copper family list is in the data file +CreativeModeTabs.generateEnchantmentBookTypesAllLevels skip:data creative contents come from the data file +CreativeModeTabs.generateEnchantmentBookTypesOnlyMaxLevel skip:data creative contents come from the data file +CreativeModeTabs.generateFireworksAllDurations skip:data creative contents come from the data file +CreativeModeTabs.generateInstrumentTypes skip:data creative contents come from the data file +CreativeModeTabs.generateOminousBottles skip:data creative contents come from the data file +CreativeModeTabs.generatePotionEffectTypes skip:data creative contents come from the data file +CreativeModeTabs.generatePresetPaintings skip:data creative contents come from the data file +CreativeModeTabs.generateSuspiciousStews skip:data creative contents come from the data file +CreativeModeTabs.registerColoredItems skip:data creative contents come from the data file +CreativeModeTabs.tryRebuildTabContents port crates/horizon-client/src/creative.rs::Creative::items contents rebuilt on demand +DebugStickItem missing player: the debug stick does nothing in creative; no block state cycling or marker +DoubleHighBlockItem.placeBlock missing unsure: no code found that places the upper half of a tall block +DyeItem.interactLivingEntity missing unsure: no dye use on mobs (sheep, collars) found in the client +DyeItem.tryApplyToSign inline crates/horizon-client/src/game/sign_edit.rs::Game::use_sign_applicator dye colours the sign text +EggItem.asProjectile port crates/horizon-client/src/game/creatures.rs::Game::throw_item egg kinds in throw_item +EggItem.use inline crates/horizon-client/src/game/creatures.rs::Game::throw_item egg throw +EnderpearlItem.use inline crates/horizon-client/src/game/creatures.rs::Game::throw_item ender pearl throw +ExperienceBottleItem.asProjectile port crates/horizon-client/src/game/potions.rs::Game::thrown_kind thrown bottle entity type +ExperienceBottleItem.createDispenseConfig port crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile dispensed bottle flight +ExperienceBottleItem.use inline crates/horizon-client/src/game/potions.rs::Game::use_potion_item thrown bottle path +FireChargeItem.asProjectile port crates/horizon-client/src/game/dispenser.rs::Game::shoot_projectile_item fire charge becomes a small fireball +FireChargeItem.createDispenseConfig port crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile dispensed fire charge flight +FireChargeItem.useOn missing unsure: no fire charge use on blocks found in the client +GameMasterBlockItem.getPlacementState port crates/horizon-client/src/game/block_items.rs::Game::apply_item_block_state default state for the placed block +GlowInkSacItem.tryApplyToSign inline crates/horizon-client/src/game/sign_edit.rs::Game::use_sign_applicator glow ink on a sign +HangingSignItem.canPlace missing unsure: no hanging sign placement check found +InkSacItem.tryApplyToSign inline crates/horizon-client/src/game/sign_edit.rs::Game::use_sign_applicator ink on a sign +InstrumentItem.create skip:java builds the horn stack +InstrumentItem.getInstrument skip:data instrument read from the item data +InstrumentItem.getUseDuration port crates/horizon-client/src/game/item_draw.rs::use_duration goat horn use duration 72000 +InstrumentItem.play missing player: using a goat horn plays no sound; Horizon has no horn playback +InstrumentItem.use missing player: using a goat horn plays no sound and does not start the horn cooldown +Instruments.bootstrap skip:data registry bootstrap of the instrument list +Instruments.register skip:data registry bootstrap of the instrument list +ItemFrameItem.mayPlace port crates/horizon-client/src/game/frames.rs::Game::place_item_frame +ItemProvider.cycle inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot shows its items in turn +ItemProvider.of inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +ItemProvider.FallbackItemProvider.findNext inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +ItemProvider.FallbackItemProvider.next inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +ItemProvider.FallbackItemProvider.peek inline crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost slot item list +JukeboxSongs skip:data registry of jukebox songs; song data is read from files +KnowledgeBookItem.use missing minor: using a knowledge book (recipe unlock) does nothing in Horizon +LingeringPotionItem.createPotion port crates/horizon-client/src/game/potions.rs::thrown_kind lingering entity type +LingeringPotionItem.use inline crates/horizon-client/src/game/potions.rs::Game::use_potion_item lingering potion throw +NameTagItem.interactLivingEntity missing unsure: name tag use on a mob is only forwarded on remote games (remote_mob_use.rs); no local rename found +PlaceOnWaterBlockItem.use inline crates/horizon-client/src/game/block_items.rs::Game::apply_placed_item placed by generic block placement; the water rule is not checked here diff --git a/tools/port_map/classes-02.tsv b/tools/port_map/classes-02.tsv new file mode 100644 index 0000000000..4d8b2eada4 --- /dev/null +++ b/tools/port_map/classes-02.tsv @@ -0,0 +1,158 @@ +PlayerHeadItem.getName missing minor: a head whose profile has a name should read "'s Head"; Horizon shows the plain item name +ShearsItem.createToolProperties skip:data tool rules (cobweb speed, shears breaking-speed tags) come from item data; items.rs reads minecraft:tool +ShearsItem.mineBlock port crates/horizon-client/src/game.rs::Game::mine_block_with_held shears wear one damage_per_block on any block but fire +ShearsItem.useOn missing unsure: SHEARS_TRIM is a NeoForge ability that no block in the pinned sources registers; pumpkin carving is a separate path (game/pumpkin.rs) +SnowballItem.asProjectile port crates/horizon-client/src/game/dispenser.rs::Game::shoot_projectile_item a dispensed snowball or egg is shot with the dispenser's own config +SnowballItem.use port crates/horizon-client/src/game/creatures.rs::Game::throw_item throw at 1.5 power, throw sound, use cooldown, one consumed +SplashPotionItem.createPotion port crates/horizon-client/src/game/potions.rs::Game::launch_thrown the thrown splash potion entity is launched with the stack +SplashPotionItem.use port crates/horizon-client/src/game/potions.rs::Game::use_potion_item splash throw sound, throw at -20 degrees, one consumed +ThrowablePotionItem.createDispenseConfig port crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile splash, lingering and XP bottle: uncertainty 3.0 and power 1.375 +ThrowablePotionItem.use port crates/horizon-client/src/game/potions.rs::Game::use_potion_item throw at -20 degrees, one consumed +StandingAndWallBlockItem.getPlacementState port crates/horizon-world/src/level/place.rs::Sim::placement standing or wall state by nearest looking direction, with survival and unobstructed checks +StandingAndWallBlockItem.registerBlocks skip:data wall block to item bindings come from the generated item table (level/data.rs) +SwingAnimationType.byName skip:data swing names are data; Horizon hardcodes spear swings (spear.rs) and reads no swing_animation component +ToolMaterial.applyCommonProperties skip:data material numbers come from item data (items.rs), not built in code +ToolMaterial.applySwordProperties skip:data sword properties come from item data (items.rs) +ToolMaterial.applyToolProperties skip:data tool properties come from item data (items.rs) +ToolMaterial.createSwordAttributes skip:data sword attribute modifiers come from item data (items.rs) +ToolMaterial.createToolAttributes skip:data tool attribute modifiers come from item data (items.rs) +TippedArrowItem.getDefaultInstance skip:data creative entries come from the exported creative list (creative.rs), not a default stack built in code +TippedArrowItem.getName port crates/horizon-client/src/game/commands.rs::Game::stack_display_name the hover name takes the potion's name key for a tipped arrow +BlockTransformers.axe skip:data axe rules are built in code; Horizon loads block transforms from data (block_transform.rs) +BlockTransformers.axeStrippables skip:data stripping table is data; Horizon loads it from block_transform.rs +BlockTransformers.bootstrap skip:data registry bootstrap; transforms are read from data +CustomData.equals skip:java +CustomData.set inline crates/horizon-client/src/game/commands/items.rs::edit_components gap: minor: does not remove the component when the tag ends up empty +CustomData.update inline crates/horizon-client/src/game/commands/items.rs::edit_components gap: minor: does not remove the component when the tag ends up empty +CustomModelData.getSafe port crates/horizon-assets/src/item_model.rs::custom_data_at index bounds check for flags, floats, strings and colors +DeathProtection.applyEffects port crates/horizon-client/src/game/totem.rs::Game::apply_totem totem use: clears effects, then the totem's regeneration, absorption and fire resistance +GrowableMutableContainer.replaceSlotItems inline crates/horizon-items/src/bundle.rs::Contents::try_insert gap: minor: the loop that adds slots while items remain is not ported; bundles insert one stack at a time +ItemAttributeModifiers.compute inline crates/horizon-world/src/attributes/mod.rs::Instance::value item modifiers are added to the attribute instance and summed by operation; slot filtering is in the equipment code +ItemAttributeModifiers.forEach inline crates/horizon-client/src/item_tooltip.rs::attribute_lines tooltip lines per slot group +ItemAttributeModifiers.withModifierAdded missing minor: the loot set_attributes function (SetAttributesFunction) is not ported; no loot path adds a modifier to a stack +ItemAttributeModifiers.Builder.add skip:java +ItemAttributeModifiers.Entry.matches inline crates/horizon-world/src/attributes/mod.rs::Instance::add_or_replace_permanent modifier identity by id within one attribute +OminousBottleAmplifier.addToTooltip missing player: an ominous bottle's tooltip shows no Bad Omen line for its level; Horizon's potion tooltip covers potion items only +SimpleMutableContainer.modifySlots port crates/horizon-client/src/game/commands/items.rs::Game::cmd_item_modify each slot is visited and set +SimpleMutableContainer.replaceSlotItems port crates/horizon-client/src/game/commands/items.rs::Game::cmd_item /item replace fills slots from the item list +SulfurCubeContent.addToTooltip missing minor: Horizon never writes sulfur_cube_content, so no item shows the absorbed block line +SuspiciousStewEffects.addToTooltip missing player: in creative, a suspicious stew's tooltip should list its effects; Horizon's stack tooltip shows none +TypedEntityData.addToTooltip missing player: in Peaceful, a hostile mob's spawn egg should show a red Peaceful line; Horizon shows only the OP-only warning +TypedEntityData.equals skip:java +TypedEntityData.stripId skip:other the stored entity data keeps its id; block_entity_item.rs reads it from the tag +WritableBookContent.getPages missing minor: the filtered page text (chat filter) is not ported; pages always show raw text +PlaySoundConsumeEffect.apply port crates/horizon-world/src/consume_effect.rs::ConsumeEffect::apply the play_sound arm +RemoveStatusEffectsConsumeEffect.apply port crates/horizon-world/src/consume_effect.rs::ConsumeEffect::apply the remove_status arm +AbstractCookingRecipe.cookingStreamCodec skip:java +AbstractCookingRecipe.display port crates/horizon-client/src/recipe_book.rs::Book::build furnace display (Display::Furnace) for each cooking recipe +BannerDuplicateRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble copies the patterns of the banner onto a blank one +BannerDuplicateRecipe.getRemainingItems port crates/horizon-client/src/crafting.rs::Recipes::remainders a patterned banner stays as one copy +BannerDuplicateRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches +BlastingRecipe.recipeBookCategory skip:data book category comes from the recipe JSON (Recipes::load keeps recipes.meta) +BrewingInput.getItem skip:java +BrewingRecipe.matches port crates/horizon-client/src/brewing.rs::Brewing::find input and reagent both tested +CraftingRecipe.recipeBookCategory skip:java +DecoratedPotRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble +DecoratedPotRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches four sherds or bricks in the four pattern cells +DyeRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble +DyeRecipe.display port crates/horizon-client/src/crafting.rs::Special::book_displays +DyeRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches +FireworkRocketRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble +FireworkRocketRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches +FireworkStarFadeRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble +FireworkStarFadeRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches +FireworkStarRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble +FireworkStarRecipe.findShape inline crates/horizon-client/src/crafting.rs::Special::assemble the shape is looked up from the shapes table while assembling +FireworkStarRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches +ImbueRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble tipped arrows +ImbueRecipe.createPlacementInfo port crates/horizon-client/src/crafting.rs::Recipes::placement +ImbueRecipe.display port crates/horizon-client/src/crafting.rs::Special::book_displays +ImbueRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches +MapExtendingRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble +MapExtendingRecipe.findFilledMap inline crates/horizon-client/src/crafting.rs::Special::matches the filled map must have scale under 4 +MapExtendingRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches +RecipeCache.compute skip:java +RecipeCache.insert skip:java +RecipeCache.moveEntryToFront skip:java +RecipeCache.validateRecipeManager skip:java +RecipeCache.Entry.matches skip:java +RecipeHolder.equals skip:java +RecipeInput.isEmpty skip:java +RecipeMap.create skip:java +RecipeMap.getRecipesFor port crates/horizon-client/src/crafting.rs::Recipes::find_with the recipes that match the input, in order +RecipePropertySet.create skip:java +SelectableRecipe.noRecipeCodec skip:java +SelectableRecipe.SingleInputSet.acceptsInput port crates/horizon-client/src/stations.rs::StationRecipes::stonecutter any stonecutting recipe takes the input +SelectableRecipe.SingleInputSet.selectByInput port crates/horizon-client/src/stations.rs::StationRecipes::stonecutter the stonecutting recipes that take the input +ShapedRecipe.display port crates/horizon-client/src/recipe_book.rs::Book::build shaped grid display +ShapelessRecipe.display port crates/horizon-client/src/recipe_book.rs::Book::build shapeless display +ShapelessRecipe.matches port crates/horizon-client/src/crafting.rs::Recipes::recipe_matches +ShieldDecorationRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble +ShieldDecorationRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches +SingleItemRecipe.placementInfo port crates/horizon-client/src/crafting.rs::Recipes::placement +SingleItemRecipe.simpleMapCodec skip:java +SingleItemRecipe.simpleStreamCodec skip:java +SingleRecipeInput.getItem skip:java +SmeltingRecipe.recipeBookCategory skip:data book category comes from the recipe JSON (Recipes::load keeps recipes.meta) +SmithingRecipe.matches port crates/horizon-client/src/stations.rs::StationRecipes::smithing_result template, base and addition tested together; the result is shown only when they match +SmithingRecipeInput.getItem skip:java +SmithingRecipeInput.isEmpty skip:java +StonecutterRecipe.display port crates/horizon-client/src/stations.rs::StationRecipes::stonecutter stonecutting recipe with its input and result +TransmuteResult.fromTemplate inline crates/horizon-client/src/crafting.rs::Template::create the transmute result is kept as the template +TransmuteResult.resolve port crates/horizon-client/src/crafting.rs::Template::over item from the input when the template has none; count and components from the template +DisplayContentsFactory.ForStacks.forStack skip:java +FurnaceRecipeDisplay.isEnabled skip:data feature flags: Horizon's recipe set is the pinned pack's, with no flags to check +ShapedCraftingRecipeDisplay.isEnabled skip:data feature flags: Horizon's recipe set is the pinned pack's, with no flags to check +ShapelessCraftingRecipeDisplay.isEnabled skip:data feature flags: Horizon's recipe set is the pinned pack's, with no flags to check +SlotDisplays.bootstrap skip:data registry bootstrap +ConditionalEffect.codec skip:java +ConditionalEffect.matches inline crates/horizon-client/src/game/enchant_effects.rs::Game::post_attack the requirements check is repeated per hook: post_attack, post_attack_bursts, thorns_damage, bow_projectile_ignite +ItemEnchantments.addToTooltip port crates/horizon-client/src/game/stations.rs::Game::stack_tooltip_lines enchantment lines; stored enchantments for books +ItemEnchantments.equals skip:java +ItemEnchantments.getTagOrEmpty skip:java +TargetedConditionalEffect.equipmentDropsCodec skip:java +TargetedConditionalEffect.matches port crates/horizon-world/src/loot.rs::LootData::equipment_drops equipment_drops (Looting) checks the requirements per enchanted slot +AllOf.codec skip:java +AllOf.EntityEffects.apply inline crates/horizon-client/src/game/enchant_effects.rs::Game::apply_piercing_effect gap: minor: nested all_of is walked only for the post-piercing hook +AllOf.LocationBasedEffects.onChangedBlock inline crates/horizon-client/src/game/enchant_effects.rs::Game::location_changed_effects gap: minor: only the replace_disk effect is handled; the nested list is not walked +AllOf.LocationBasedEffects.onDeactivated inline crates/horizon-client/src/game/enchant_effects.rs::Game::location_changed_effects gap: minor: only the replace_disk effect is handled; the nested list is not walked +AllOf.ValueEffects.process port crates/horizon-items/src/enchant.rs::Enchantments::modify_value_with each value effect in turn +ApplyEntityImpulse.apply port crates/horizon-client/src/game/enchant_effects.rs::Game::apply_piercing_effect impulse along the look direction on the post-piercing hook +ApplyExhaustion.apply port crates/horizon-client/src/game/enchant_effects.rs::Game::apply_piercing_effect food exhaustion on the post-piercing hook +DamageEntity.apply port crates/horizon-client/src/game/enchant_effects.rs::Game::thorns_damage thorns damage, random between min and max +ExplodeEffect.apply port crates/horizon-client/src/game/enchant_effects.rs::Game::post_attack_bursts wind burst at the player's feet +ExplodeEffect.getDamageSource inline crates/horizon-client/src/game/enchant_effects.rs::Game::post_attack_bursts gap: minor: the burst builds no damage source; it knocks back and triggers blocks only +PlaySoundEffect.apply port crates/horizon-client/src/game/enchant_effects.rs::Game::apply_piercing_effect sound by level, heard by all +RemoveBinomial.process port crates/horizon-items/src/enchant.rs::apply_value_effect the binomial drop count for unbreaking +ReplaceBlock.apply missing minor: no enchantment in the pinned data uses replace_block +RunFunction.apply missing minor: no enchantment in the pinned data uses run_function +ScaleExponentially.process missing minor: no enchantment in the pinned data uses the exponential value effect +SetBlockProperties.apply missing minor: no enchantment in the pinned data uses set_block_properties +SpawnParticlesEffect.apply port crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed gap: minor: soul speed's particles are hardcoded, not read from the effect data +SpawnParticlesEffect.PositionSource.getCoordinate inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed gap: minor: the particle position is hardcoded +SpawnParticlesEffect.VelocitySource.getVelocity inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed gap: minor: the particle velocity is hardcoded +SummonEntityEffect.apply port crates/horizon-client/src/game/trident.rs::Game::channel_lightning gap: minor: only the lightning bolt of channeling; other summon_entity types are not ported +EnchantmentProviderTypes.bootstrap skip:data registry bootstrap +EnchantmentsByCost.enchant port crates/horizon-world/src/loot.rs::LootData::select_enchantments EnchantmentHelper.selectEnchantment with a cost budget +SingleEnchantment.enchant port crates/horizon-world/src/loot.rs::enchant one enchantment at a level +VanillaEnchantmentProviders.bootstrap skip:data registry bootstrap +ArmorMaterials.makeDefense skip:data armour defence values come from item data +ArmorTrim.addToTooltip port crates/horizon-client/src/game/stations.rs::Game::stack_tooltip_lines upgrade title, trim pattern and material lines +TrimMaterials.bootstrap skip:data registry bootstrap +TrimMaterials.register skip:data trim materials are read from data +TrimPatterns.bootstrap skip:data registry bootstrap +TrimPatterns.register skip:data trim patterns are read from data +CompositeSlotSource.createCodec skip:java +CompositeSlotSource.group missing minor: composite slot groups (loot-style term lists) are not ported; /item takes one slot name +CountingModifier.accept port crates/horizon-client/src/game/commands/items.rs::Game::cmd_item_modify counts each slot set, limited to the max stack size +RangeSlotSource.provide port crates/horizon-client/src/game/commands/items.rs::Game::slots_named gap: minor: slots by name pattern only, no slot_range argument +SlotCollection.concat inline crates/horizon-client/src/game/commands/items.rs::Game::slots_named the slots of several names become one list +SlotCollection.of port crates/horizon-client/src/game/commands/items.rs::Game::slot_get +SlotCollection.FlatMapped.modifySlots inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item_modify per-target slot loop +SlotCollection.FlatMapped.replaceSlotItems inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item per-target slot loop +SlotSelector.FilteringSelector.trySelectSlot inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item_modify gap: minor: only the non-empty filter (SlotSelector.NON_EMPTY_SLOTS); predicate filters are not ported +SlotSelector.TrackingSelector.filter missing minor: slot limit selectors are not ported +SlotSelector.TrackingSelector.trySelectSlot missing minor: slot limit selectors are not ported +SlotSources.bootstrap skip:data registry bootstrap +TransformedSlotSource.commonFields skip:java +TradeCost.toItemCost port crates/horizon-client/src/merchant.rs::cost count plus the extra cost, clamped to the max stack size diff --git a/tools/port_map/classes-03.tsv b/tools/port_map/classes-03.tsv new file mode 100644 index 0000000000..55ec0933a1 --- /dev/null +++ b/tools/port_map/classes-03.tsv @@ -0,0 +1,316 @@ +TradeRebalanceVillagerTrades skip:data rebalanced villager trade definitions are data; Horizon reads trade_set JSON (merchant.rs Trades::load) +TradeSets skip:data registry bootstrap; trade sets come from the trade_set data (merchant.rs Trades::load) +VillagerTrades skip:data trade definitions are data; read from data/minecraft/trade_set and villager_trade (merchant.rs Trades::load) +BlockCollisions. port crates/horizon-world/src/block_collision_scan.rs::BlockCollisionScan::new scan set-up over the box +BlockCollisions.computeNext port crates/horizon-world/src/block_collision_scan.rs::BlockCollisionScan::find_next next block shape along the box +BlockCollisions.getChunk inline crates/horizon-world/src/block_collision_scan.rs::BlockCollisionScan::find_next chunk fetch through collision_chunk inside find_next +CardinalLighting.byFace port crates/horizon-client/src/mesher/lighting.rs::cardinal per-face directional shade; nether flag handled in the same fn +ClipContext.getFluidShape inline crates/horizon-world/src/world_ray.rs::clip_world pick_fluid decides canPick; the shape is clipped in clip_world +ColorMapColorUtil.get port crates/horizon-client/src/biome.rs::sample colormap lookup by temperature and downfall +EntityBasedExplosionDamageCalculator.getBlockExplosionResistance missing unsure: source-entity overrides (wither skull, minecart TNT, primed TNT keeping nether portals) are not read; explosion_hit_block uses the block's own resistance +ExplosionDamageCalculator.getEntityDamageAmount inline crates/horizon-client/src/game/explosion.rs::Game::explosion_hurt_entities damage formula inline; the player copy is in explosion_hurt_player +LevelSettings.copy skip:java immutable record copy; Horizon keeps level fields on its own struct +LevelSettings.parse port crates/horizon-client/src/save.rs::Level::read reads difficulty, lock, allowCommands and game type from level.dat +LevelSettings.withAllowCommands skip:java immutable record builder +LevelSettings.withDataConfiguration skip:java immutable record builder +LevelSettings.withDifficulty skip:java immutable record builder +LevelSettings.withDifficultyLock skip:java immutable record builder +LevelSettings.withGameType skip:java immutable record builder +PathNavigationRegion skip:other pathfinding reads the live level (mobs/path.rs); no region snapshot is built +SimpleExplosionDamageCalculator.getBlockExplosionResistance inline crates/horizon-client/src/game/wind.rs::Game::wind_positions gap: minor: only the wind charge tag stops the rays; enchantment immune_blocks are not read +SimpleExplosionDamageCalculator.getKnockbackMultiplier inline crates/horizon-client/src/game/wind.rs::Game::wind_push_player creative-flying zero is here; the enchantment multiplier is in post_attack_bursts (enchant_effects.rs) +TicketStorage.activateAllDeactivatedTickets skip:other no deactivated-ticket state; ticket_levels.rs recomputes from players and forced chunks +TicketStorage.addTicket missing unsure: portal and teleport tickets are not modelled; ticket_levels.rs counts only players and forced chunks +TicketStorage.addTicketWithRadius missing unsure: radius tickets (portal, teleport) are not modelled; ticket_levels.rs counts only players and forced chunks +TicketStorage.canTicketExpire skip:other ticket expiry is not modelled; no timed tickets in ticket_levels.rs +TicketStorage.deactivateTicketsOnClosing skip:other chunk-unload bookkeeping; no per-ticket state +TicketStorage.forEachTicket skip:other iteration over stored tickets; ticket_levels.rs walks its own sources +TicketStorage.fromPacked skip:other saved tickets are not read; Horizon does not save chunk tickets +TicketStorage.getAllChunksWithTicketThat skip:other query over stored tickets; ticket_levels.rs answers per chunk +TicketStorage.getLowestTicket port crates/horizon-client/src/ticket_levels.rs::Map::get gap: minor: lowest level from player and forced sources only +TicketStorage.getOrCreateTickets skip:java per-chunk cache of tickets +TicketStorage.getTicketDebugString skip:debug debug text +TicketStorage.getTicketLevelAt port crates/horizon-client/src/ticket_levels.rs::Map::get gap: minor: levels from player and forced sources only +TicketStorage.isTicketSameTypeAndLevel skip:other compares stored tickets; Horizon stores none, levels come from ticket_levels.rs +TicketStorage.packTickets skip:other saves stored tickets; Horizon saves forced chunks as their own file (save.rs save_forced_chunks) +TicketStorage.purgeStaleTickets skip:other drops expired tickets; ticket_levels.rs has no timed tickets +TicketStorage.removeTicket inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_forceload gap: minor: only the forced-chunk path; portal and teleport tickets are not modelled +TicketStorage.removeTicketIf skip:other predicate removal over stored tickets; forced chunks are changed by cmd_forceload +TicketStorage.removeTicketWithRadius missing unsure: radius tickets (portal, teleport) are not modelled +TicketStorage.replaceTicketLevelOfType skip:other re-levels stored tickets; ticket_levels.rs computes levels from positions +TicketStorage.shouldKeepDimensionActive missing unsure: whether a dimension stays ticking because of forced chunks was not checked +TicketStorage.updateChunkForced inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_forceload the per-chunk add or remove of a forced chunk +TicketStorage.updateForcedChunks inline crates/horizon-client/src/save.rs::SaveDir::load_forced_chunks gap: minor: restores the saved forced set on load; no forced-chunk packet is sent here +BiomeSpecialEffects.Builder.build skip:java builder for biome effects; Horizon reads the effects from biome data (biome.rs) +CheckerboardColumnBiomeSource.getNoiseBiome port crates/horizon-world/src/worldgen/biome_source.rs::CheckerboardBiomeSource::noise_biome checkerboard column pick +FeatureSorter.buildFeaturesPerStep port crates/horizon-world/src/worldgen/feature_sort.rs::build_features_per_step +MobSpawnSettings.Builder skip:java builder for spawn data; Horizon reads spawn costs and entries from biome data (biome.rs, spawn_selection.rs) +MultiNoiseBiomeSourceParameterList skip:data knownPresets is the datagen preset table; Horizon reads biome-parameters copied by horizon-prepare +MultiNoiseBiomeSourceParameterList.Preset skip:data preset biome lists are datagen output; read from biome-parameters data +MultiNoiseBiomeSourceParameterLists skip:data bootstrap of the datagen parameter lists; Horizon reads biome-parameters +OverworldBiomeBuilder skip:data builds the overworld parameter tree at datagen time; Horizon reads biome-parameters JSON +TheEndBiomeSource.collectPossibleBiomes skip:java the five End biomes are fields of EndBiomeSource; no collection step +TheEndBiomeSource.create skip:java factory; the End source is built from the dimension data +TheEndBiomeSource.getNoiseBiome port crates/horizon-world/src/worldgen/biome_source.rs::EndBiomeSource::noise_biome central circle, then erosion bands +AbstractFurnaceBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +AbstractFurnaceBlock.createFurnaceTicker port crates/horizon-client/src/game/block_entity_ticks.rs::Game::tick_furnace furnace ticker +AbstractFurnaceBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +AmethystBlock missing player: an arrow or other projectile that hits an amethyst block plays no chime (vanilla plays block.amethyst_block.chime) +AmethystClusterBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +AmethystClusterBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +AmethystClusterBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged state from block data +AmethystClusterBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +AmethystClusterBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +AmethystClusterBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +AttachedStemBlock.getCloneItemStack missing unsure: middle-click pick of an attached stem; pick_item.rs was not checked for block-to-item picks +AttachedStemBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +AttachedStemBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +AzaleaBlock.isBonemealSuccess port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_success +AzaleaBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +AzaleaBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +BambooSaplingBlock.growBamboo port crates/horizon-world/src/level/tick.rs::Sim::bamboo_grow sets the stalk above with small leaves +BambooSaplingBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +BambooSaplingBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::bamboo_sapling_grow +BambooSaplingBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +BarrelBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +BarrelBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +BarrelBlock.tick port crates/horizon-client/src/game/block_entity_anims.rs::Game::barrel_openers_changed gap: minor: recheckOpen is the open-viewer count, checked where the lid state is set +BarrelBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +BarrierBlock.canPlaceLiquid port crates/horizon-world/src/level/fluids.rs::Sim::can_place_liquid +BarrierBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +BarrierBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +BarrierBlock.pickupBlock port crates/horizon-world/src/level/fluids.rs::Sim::pickup_block gap: minor: vanilla lets only a creative player pick it up; that rule is not checked here +BarrierBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +BaseCoralWallFanBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +BaseCoralWallFanBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +BaseCoralWallFanBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +BaseCoralWallFanBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +BaseCoralWallFanBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +BaseEntityBlock.getMenuProvider port crates/horizon-world/src/level/interact.rs::Sim::use_without_item menu opened from the block's use action +BaseEntityBlock.triggerEvent port crates/horizon-world/src/level/pistons.rs::Sim::trigger_event +BaseTorchBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +BeetrootBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::crop_random_tick +BigDripleafStemBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +BigDripleafStemBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +BigDripleafStemBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +BigDripleafStemBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +BigDripleafStemBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +BigDripleafStemBlock.place port crates/horizon-world/src/level/bonemeal.rs::Sim::dripleaf_stem_place places the stem with waterlogging and facing +BigDripleafStemBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::big_dripleaf_tick +BigDripleafStemBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +BlastFurnaceBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick lit blast furnace smoke and flame +BlastFurnaceBlock.openContainer port crates/horizon-world/src/level/interact.rs::Sim::use_without_item opens the blast furnace menu +BonemealableFeaturePlacerBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Behaviours::place_feature places the configured feature +BrewingStandBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick brewing bubbles +BrewingStandBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +BrewingStandBlock.getTicker port crates/horizon-client/src/brewing.rs::BrewingStand::brew the stand's brewing step +BuddingAmethystBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::budding_amethyst_tick +BushBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +CactusFlowerBlock.mayPlaceOn port crates/horizon-world/src/level/survive.rs::Sim::may_place_on +CakeBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +CakeBlock.useItemOn port crates/horizon-world/src/level/interact.rs::Behaviours::use_item_on +CakeBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +CalibratedSculkSensorBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +CalibratedSculkSensorBlock.getSignal port crates/horizon-world/src/level/sculk.rs::Behaviours::calibrated_back_signal +CalibratedSculkSensorBlock.getTicker port crates/horizon-world/src/level/sculk.rs::Sim::sculk_sensor_tick +CalibratedSculkSensorBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +CarpetBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +CartographyTableBlock.getMenuProvider port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +CartographyTableBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +CaveVines.hasGlowBerries port crates/horizon-world/src/level/interact.rs::Sim::use_without_item berries check on the pick branch +CaveVinesBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +CaveVinesBlock.getGrowIntoState port crates/horizon-world/src/level/tick.rs::Sim::growing_plant_grow_into_state 11% berries roll +CaveVinesPlantBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +CeilingHangingSignBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +CeilingHangingSignBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +CeilingHangingSignBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +CeilingHangingSignBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +CeilingHangingSignBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +CeilingHangingSignBlock.shouldTryToChainAnotherHangingSign inline crates/horizon-world/src/level/place.rs::Sim::placement_class chain check sits in the ceiling sign arm +CeilingHangingSignBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +CeilingHangingSignBlock.useItemOn port crates/horizon-world/src/level/interact.rs::Behaviours::use_item_on +CocoaBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +CocoaBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +CocoaBlock.getShape skip:data block shape is a per-state table from the vanilla data +CocoaBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +CocoaBlock.isRandomlyTicking port crates/horizon-world/src/level/mod.rs::Behaviours::is_randomly_ticking +CocoaBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +CocoaBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +CocoaBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block +CocoaBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +ColorCollection skip:data registry helper for colour families of blocks and items; the variants are data in horizon-items +ConduitBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +ConduitBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +ConduitBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +CoralBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +CoralBlock.scanForWater port crates/horizon-world/src/level/shapes.rs::Sim::coral_scan_for_water +CoralBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block +CoralBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +CoralFanBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block +CoralFanBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +CoralPlantBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block +CoralPlantBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +CoralWallFanBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block +CoralWallFanBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +CrossCollisionBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +CrossCollisionBlock.makeShapes skip:java builds the VoxelShape arrays; shapes are data +CrossCollisionBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +CrossCollisionBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +CryingObsidianBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick crying tear particles +DropExperienceBlock.spawnAfterBreak port crates/horizon-world/src/level/removal.rs::break_experience +EnchantingTableBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick book and rune particles +EnchantingTableBlock.getMenuProvider port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +EnchantingTableBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +EndRodBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick +EndRodBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +EnderChestBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick +EnderChestBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +EnderChestBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +EnderChestBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +EnderChestBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +EnderChestBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block +EnderChestBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +FenceGateBlock.connectsToDirection port crates/horizon-world/src/level/shapes.rs::Sim::fence_connects_to +FenceGateBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +FenceGateBlock.getBlockSupportShape skip:data block shape is a per-state table from the vanilla data +FenceGateBlock.getCollisionShape skip:data block shape is a per-state table from the vanilla data +FenceGateBlock.getOcclusionShape skip:data block shape is a per-state table from the vanilla data +FenceGateBlock.getShape skip:data block shape is a per-state table from the vanilla data +FenceGateBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +FenceGateBlock.isPathfindable skip:data path types come from the per-state path data (mobs/path.rs) +FenceGateBlock.neighborChanged port crates/horizon-world/src/level/redstone.rs::Sim::gate_neighbor_changed +FenceGateBlock.onExplosionHit missing player: an explosion (TNT, creeper) does not open a closed, unpowered fence gate; vanilla toggles it open +FenceGateBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +FenceGateBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +FireflyBushBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +FlowerBedBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced +FlowerBedBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +FlowerBedBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +FlowerBedBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +FlowerBlock.makeEffectList skip:data suspicious stew effects are item data (stew_effects component) +FrogspawnBlock.mayPlaceOn port crates/horizon-world/src/level/survive.rs::Sim::frogspawn_may_place_on +FrogspawnBlock.spawnTadpoles port crates/horizon-client/src/game.rs::Game::process_level_events handles the hatch block event: 2 to 5 tadpoles +FrogspawnBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block +FrogspawnBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +FrostedIceBlock.fewerNeigboursThan port crates/horizon-world/src/level/hooks.rs::Sim::frosted_ice_fewer_neighbours_than +FrostedIceBlock.neighborChanged port crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed +FrostedIceBlock.slightlyMelt port crates/horizon-world/src/level/tick.rs::Sim::frosted_slightly_melt +FrostedIceBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::frosted_ice_tick +FurnaceBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick lit furnace smoke and flame +FurnaceBlock.openContainer port crates/horizon-world/src/level/interact.rs::Sim::use_without_item opens the furnace menu +GlowLichenBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +GrindstoneBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +GrindstoneBlock.getMenuProvider port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +GrindstoneBlock.makeShapes skip:java builds the VoxelShape arrays; shapes are data +GrindstoneBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +GrindstoneBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +HalfTransparentBlock.skipRendering port crates/horizon-world/src/block_render.rs::FaceVisibility::skips neighbour faces of the same block +HangingRootsBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +HangingRootsBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +HangingRootsBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +HangingRootsBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +HangingSignBlock.getAttachmentPoint inline crates/horizon-world/src/level/place.rs::Sim::placement_class attach point is the wall or ceiling arm of placement_class +HeavyCoreBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +HeavyCoreBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +HeavyCoreBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +HopperBlock.checkPoweredState port crates/horizon-world/src/level/redstone.rs::Sim::hopper_check_powered +HopperBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +HopperBlock.entityInside inline crates/horizon-client/src/game/machines.rs::Game::suck_item_entities gap: minor: the hopper pulls item entities every tick, not on contact +HopperBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +HopperBlock.makeShapes skip:java builds the VoxelShape arrays; shapes are data +HopperBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place +HopperBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +HopperBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +HorizontalDirectionalBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +HugeMushroomBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +HugeMushroomBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +HugeMushroomBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +HugeMushroomBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +HugeMushroomBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +KelpBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +LadderBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +LadderBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +LadderBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +LadderBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +LadderBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +LadderBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +LanternBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +LanternBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +LanternBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +LanternBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +LanternBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +LeafLitterBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced +LeafLitterBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +LeafLitterBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +LeafLitterBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +LilyPadBlock.entityInside missing player: a boat that drives over a lily pad does not break it; vanilla destroys it +LilyPadBlock.mayPlaceOn port crates/horizon-world/src/level/survive.rs::Sim::may_place_on +MagmaBlock.stepOn missing player: standing on magma does not hurt (vanilla deals 1 hot-floor damage unless sneaking) +MangrovePropaguleBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +MangrovePropaguleBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +MangrovePropaguleBlock.createNewHangingPropagule inline crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal hanging state set when a propagule grows down +MangrovePropaguleBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +MangrovePropaguleBlock.getShape skip:data block shape is a per-state table from the vanilla data +MangrovePropaguleBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +MangrovePropaguleBlock.isBonemealSuccess port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_success +MangrovePropaguleBlock.isFullyGrown inline crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid age 4 check in the propagule arm +MangrovePropaguleBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +MangrovePropaguleBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block +MangrovePropaguleBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +MangroveRootsBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state +MangroveRootsBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +MangroveRootsBlock.skipRendering port crates/horizon-world/src/block_render.rs::FaceVisibility::skips +MangroveRootsBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +MyceliumBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick mycelium spore particles +NetherFungusBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +NetherFungusBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +NetherWartBlock.isRandomlyTicking port crates/horizon-world/src/level/mod.rs::Behaviours::is_randomly_ticking +NetherWartBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block +NetherrackBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid netherrack beside nylium +NetherrackBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +NyliumBlock.canBeNylium port crates/horizon-world/src/level/tick.rs::Sim::can_be_nylium +NyliumBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +NyliumBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +NyliumBlock.place inline crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal fungus placement folded into the bonemeal step +NyliumBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block +ObserverBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal +ObserverBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +ObserverBlock.getSignal port crates/horizon-world/src/level/redstone.rs::Sim::signal +ObserverBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place +ObserverBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +ObserverBlock.startSignal port crates/horizon-world/src/level/redstone.rs::Sim::observer_start_signal +ObserverBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block +ObserverBlock.updateNeighborsInFront port crates/horizon-world/src/level/redstone.rs::Sim::observer_update_in_front +ObserverBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +PipeBlock skip:java makeShapes builds the VoxelShape arrays; shapes are data +PlainSignBlock skip:java constant accessor for the attach point; sign placement picks standing or wall signs in place.rs +PoweredRailBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +PoweredRailBlock.findPoweredRailSignal port crates/horizon-world/src/level/rails.rs::Sim::powered_rail_find +PoweredRailBlock.isSameRailWithPower port crates/horizon-world/src/level/rails.rs::Sim::powered_rail_same_with_power +PoweredRailBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +PoweredRailBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +PoweredRailBlock.updateState port crates/horizon-world/src/level/rails.rs::Sim::powered_rail_update +PressurePlateBlock.getSignalStrength port crates/horizon-world/src/level/redstone.rs::Sim::plate_signal_strength +PressurePlateBlock.setSignalForState port crates/horizon-world/src/level/redstone.rs::Sim::plate_signal_for_state +RedstoneLampBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +RedstoneLampBlock.neighborChanged port crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed +RedstoneLampBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block +RedstoneTorchBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick redstone torch smoke +RedstoneTorchBlock.getDirectSignal port crates/horizon-world/src/level/redstone.rs::Sim::direct_signal +RedstoneTorchBlock.getSignal port crates/horizon-world/src/level/redstone.rs::Sim::signal +RedstoneTorchBlock.isToggledTooFrequently port crates/horizon-world/src/level/redstone.rs::Sim::torch_toggled_too_frequently +RedstoneTorchBlock.neighborChanged port crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed +RedstoneTorchBlock.notifyNeighbors port crates/horizon-world/src/level/redstone.rs::Sim::torch_notify_neighbors +RedstoneTorchBlock.tick port crates/horizon-world/src/level/redstone.rs::Sim::torch_tick +RedstoneWallTorchBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick +RedstoneWallTorchBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +RedstoneWallTorchBlock.getSignal port crates/horizon-world/src/level/redstone.rs::Sim::signal +RedstoneWallTorchBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +RedstoneWallTorchBlock.hasNeighborSignal port crates/horizon-world/src/level/redstone.rs::Sim::torch_has_neighbor_signal +RedstoneWallTorchBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +RedstoneWallTorchBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +RepeaterBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +RepeaterBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +RepeaterBlock.shouldRedstoneWireConnectTo port crates/horizon-world/src/level/redstone.rs::Sim::should_wire_connect_to +RepeaterBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +RepeaterBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item diff --git a/tools/port_map/classes-04.tsv b/tools/port_map/classes-04.tsv new file mode 100644 index 0000000000..0bd056d722 --- /dev/null +++ b/tools/port_map/classes-04.tsv @@ -0,0 +1,231 @@ +RodBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror rod facing mirrored through the state table +RodBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate rod facing turned through the state table +RootedDirtBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid needs air below +RotatedPillarBlock.rotatePillar port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate axis swap is in the rotate table +SandBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick desert sand ambient sound +SeagrassBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +SeagrassBlock.mayPlaceOn port crates/horizon-world/src/level/survive.rs::Sim::may_place_on +SeagrassBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +SeagrassBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +SegmentableBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced leaf litter and flower bed segments +SegmentableBlock.getShapeCalculator port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape shape per state from the table +SegmentableBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +ShelfMushroomBlock.bounceOn port crates/horizon-world/src/block_landing.rs::BlockLandingRegistry::fall_on shelf bounce flag of the landing rule +ShelfMushroomBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +ShelfMushroomBlock.createBlockStateDefinition skip:data states come from the generated block state table +ShelfMushroomBlock.getShape port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape shape per state from the table +ShelfMushroomBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +ShelfMushroomBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +ShelfMushroomBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +ShelfMushroomBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +SimpleWaterloggedBlock.pickupBlock port crates/horizon-world/src/level/fluids.rs::Sim::pickup_block +SimpleWaterloggedBlock.placeLiquid port crates/horizon-world/src/level/fluids.rs::Sim::place_liquid +SkullBlock.createBlockStateDefinition skip:data states come from the generated block state table +SkullBlock.getShape port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape shape per state from the table +SkullBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +SkullBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +SkullBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +SlabBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced +SlabBlock.canPlaceLiquid port crates/horizon-world/src/level/fluids.rs::Sim::can_place_liquid +SlabBlock.createBlockStateDefinition skip:data states come from the generated block state table +SlabBlock.getFluidState port crates/horizon-world/src/fluid.rs::FluidStateTable::get +SlabBlock.getShape port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape shape per state from the table +SlabBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +SlabBlock.isPathfindable skip:data per-state path type is exported state data (StateData.path) +SlabBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +SlimeBlock.fallOn port crates/horizon-world/src/block_landing.rs::BlockLandingRegistry::fall_on slime landing rule +SmallDripleafBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +SmallDripleafBlock.createBlockStateDefinition skip:data states come from the generated block state table +SmallDripleafBlock.getFluidState port crates/horizon-world/src/fluid.rs::FluidStateTable::get +SmallDripleafBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +SmallDripleafBlock.mayPlaceOn port crates/horizon-world/src/level/survive.rs::Sim::may_place_on +SmallDripleafBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +SmallDripleafBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +SmallDripleafBlock.setPlacedBy port crates/horizon-world/src/level/place.rs::Sim::set_placed_by +SmallDripleafBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +SmithingTableBlock.getMenuProvider inline crates/horizon-world/src/level/interact.rs::Sim::use_without_item station menu is opened by the use arm +SmithingTableBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +SmokerBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick lit smoker smoke +SmokerBlock.openContainer inline crates/horizon-world/src/level/interact.rs::Sim::use_without_item furnace-family arm, which the smoker shares +SnowyBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +SnowyBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +SoulFireBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +SporeBlossomBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick +SporeBlossomBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +SporeBlossomBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +SpreadingSnowyBlock.canPropagate inline crates/horizon-world/src/level/tick.rs::Sim::spreading_snowy_tick spread test folded into the tick +SpreadingSnowyBlock.canStayAlive inline crates/horizon-world/src/level/tick.rs::Sim::spreading_snowy_tick survival test folded into the tick +SpreadingSnowyBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::spreading_snowy_tick +StandingSignBlock.createBlockStateDefinition skip:data states come from the generated block state table +StandingSignBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +StandingSignBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +StandingSignBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +StandingSignBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +StemBlock.getCloneItemStack missing unsure: minor: middle-click on a stem may not return its seed item; no stem case found in the pick code +StemBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +StemBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +StemBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::stem_random_tick +StonecutterBlock.getMenuProvider inline crates/horizon-world/src/level/interact.rs::Sim::use_without_item station menu opened by the use arm +StonecutterBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +StonecutterBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +SugarCaneBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block growth arm of the random tick +SugarCaneBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block +SugarCaneBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +SuspiciousEffectHolder.tryGet skip:data the effect comes from the item's stew effects data (item_data.rs) +TallSeagrassBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +TallSeagrassBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +TallSeagrassBlock.mayPlaceOn port crates/horizon-world/src/level/survive.rs::Sim::may_place_on +TestBlock missing minor: game-test block for operators; Horizon keeps only its NBT +TestInstanceBlock missing minor: game-test instance block; Horizon keeps only its NBT +TintedParticleLeavesBlock.spawnFallingLeavesParticle port crates/horizon-client/src/animate_tick.rs::block_animate_tick falling tinted leaf particle +TorchBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick torch flame +TorchflowerCropBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block growth arm +TrapDoorBlock.createBlockStateDefinition skip:data states come from the generated block state table +TrapDoorBlock.getFluidState port crates/horizon-world/src/fluid.rs::FluidStateTable::get +TrapDoorBlock.getShape port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape shape per state from the table +TrapDoorBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +TrapDoorBlock.isPathfindable skip:data per-state path type is exported state data (StateData.path) +TrapDoorBlock.neighborChanged port crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed redstone update arm for trapdoors +TrapDoorBlock.onExplosionHit missing player: a wind burst or explosion that could open a trapdoor leaves it shut; the explosion code has no trapdoor case +TrapDoorBlock.playSound port crates/horizon-world/src/level/redstone.rs::Sim::trapdoor_sound +TrapDoorBlock.toggle inline crates/horizon-world/src/level/interact.rs::Sim::use_without_item the trapdoor arm cycles the open state +TrapDoorBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +TrapDoorBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item +TrappedChestBlock.getDirectSignal port crates/horizon-world/src/level/redstone.rs::Sim::state_direct_signal +WallBannerBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +WallBannerBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +WallBannerBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +WallBannerBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +WallHangingSignBlock.canAttachTo inline crates/horizon-world/src/level/place.rs::Sim::wall_hanging_sign_can_place attach test folded into the placement check +WallHangingSignBlock.canPlace port crates/horizon-world/src/level/place.rs::Sim::wall_hanging_sign_can_place +WallHangingSignBlock.createBlockStateDefinition skip:data states come from the generated block state table +WallHangingSignBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +WallHangingSignBlock.isHittingEditableSide missing unsure: player: a hanging sign clicked on its side may not chain onto a wall hanging sign +WallHangingSignBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +WallHangingSignBlock.shouldTryToChainAnotherHangingSign missing unsure: player: a second hanging sign used on a wall sign may not chain on +WallHangingSignBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +WallHangingSignBlock.useItemOn port crates/horizon-world/src/level/interact.rs::Behaviours::use_item_on gap: player: chaining a hanging sign onto a wall hanging sign is not ported here +WallSignBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +WallSignBlock.createBlockStateDefinition skip:data states come from the generated block state table +WallSignBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +WallSignBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +WallSignBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +WallSkullBlock.createBlockStateDefinition skip:data states come from the generated block state table +WallSkullBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +WallSkullBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +WallTorchBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick wall torch flame +WallTorchBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +WallTorchBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +WallTorchBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +WallTorchBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +WaterloggedTransparentBlock.getFluidState port crates/horizon-world/src/fluid.rs::FluidStateTable::get +WaterloggedTransparentBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class +WaterloggedTransparentBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +WeatheringCopperChestBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block oxidation through the weathering interface branch +WeatheringCopperCollection skip:data block family registry; oxidation links come from the block data +WeatheringCopperCollection.ByState skip:data helper over the oxidation stages of the block family registry +WeatheringCopperDoorBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block oxidation through the weathering interface branch +WeightedPressurePlateBlock.getSignalStrength port crates/horizon-world/src/level/redstone.rs::Sim::plate_signal_strength +WetSpongeBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick wet sponge drip +WetSpongeBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place wet sponge drying arm +BannerPatternLayers.removeLast inline crates/horizon-client/src/cauldron.rs::interact banner wash pops the last pattern in its arm +BannerPatternLayers.Builder skip:java builder for banner pattern layers +BannerPatterns skip:data registry bootstrap +BarrelBlockEntity.loadAdditional port crates/horizon-client-core/src/block_entity_nbt.rs::canonical gap: minor: canonical NBT form only, no load step of its own +BarrelBlockEntity.recheckOpen missing unsure: player: a barrel lid may stay open after a player walks away; only golem openers are rechecked on a timer +BarrelBlockEntity.saveAdditional port crates/horizon-client-core/src/block_entity_nbt.rs::canonical gap: minor: canonical NBT form only +BarrelBlockEntity.startOpen port crates/horizon-client/src/game/block_entity_anims.rs::Game::barrel_openers_changed +BarrelBlockEntity.stopOpen port crates/horizon-client/src/game/block_entity_anims.rs::Game::barrel_openers_changed +BarrelBlockEntity.updateBlockState inline crates/horizon-client/src/game/block_entity_anims.rs::Game::barrel_openers_changed sets the open property when the count crosses zero +BlockEntityTicker skip:java ticker combinator; Horizon ticks each block entity directly +BlockEntityType.getBlockEntity skip:java factory that makes the block entity object for a state +BlockEntityTypes skip:data registry +BoundingBoxRenderable.RenderableBox skip:debug test box outline renderer +CopperGolemStatueBlockEntity.createStatue port crates/horizon-client/src/game/copper_golems.rs::Game::golem_turn_to_statue gap: minor: the golem's custom name is not copied onto the statue +CopperGolemStatueBlockEntity.getItem port crates/horizon-client/src/game/block_items.rs::Game::picked_stack statue pose on the picked item +CopperGolemStatueBlockEntity.initCopperGolem port crates/horizon-client/src/game/copper_golems.rs::Game::use_golem_statue axe on an unaffected statue brings the golem back +DecoratedPotPatterns skip:data bootstrap and asset registration +EnchantingTableBlockEntity.bookAnimationTick port crates/horizon-client/src/block_entity_anim.rs::EnchantingBook::tick +ListBackedContainer skip:java slot helpers over a plain list +PotDecorations.addSideDetailsToTooltip port crates/horizon-client/src/item_tooltip.rs::TooltipDisplay::detail_lines sherd lines of the decorated pot tooltip +PotDecorations.allBrick skip:data constant brick decoration set +PotentSulfurBlockEntity.applyNauseaEffect missing player: standing in potent sulfur gas gives no nausea, since the geyser never erupts +PotentSulfurBlockEntity.canBeReachedByNoxiousGas missing player: gas from a potent sulfur geyser never reaches players +PotentSulfurBlockEntity.findNoxiousGasSourceBlock missing minor: helper of the geyser gas, not ported +PotentSulfurBlockEntity.getNearbyLivingEntities missing minor: helper of the geyser gas, not ported +PotentSulfurBlockEntity.getUnobstructedBlockCount missing minor: helper of the geyser gas, not ported +PotentSulfurBlockEntity.geyserPositional missing player: no potent sulfur geyser eruption, so no geyser plume or sound +PotentSulfurBlockEntity.haveLineOfSight missing minor: helper of the geyser gas, not ported +PotentSulfurBlockEntity.isGeyserPassableBlock missing minor: helper of the geyser, not ported +PotentSulfurBlockEntity.loadAdditional missing minor: the eruption timer is not loaded; Horizon keeps only the countdown in NBT +PotentSulfurBlockEntity.setLevel missing minor: the eruption timer is not started on load +PotentSulfurBlockEntity.spawnGeyserParticle missing player: no geyser particles over a potent sulfur block +PotentSulfurBlockEntity.spawnNoxiousGasCloudParticle missing player: no noxious gas cloud particles over a potent sulfur block +RandomizableContainerBlockEntity.applyImplicitComponents missing minor: a chest item does not hand its loot table back to the block +RandomizableContainerBlockEntity.canOpen missing unsure: minor: the spectator and loot-table open checks were not found +RandomizableContainerBlockEntity.collectImplicitComponents missing minor: a container does not save its loot table onto the item it drops +RandomizableContainerBlockEntity.createMenu port crates/horizon-client/src/game.rs::Game::open_menu gap: minor: the spectator and loot-table checks are not ported here +SculkSensorBlockEntity.loadAdditional port crates/horizon-client/src/sculk.rs::SculkEntity::load +SculkSensorBlockEntity.saveAdditional port crates/horizon-client/src/sculk.rs::SculkEntity::to_nbt +SculkSensorBlockEntity.VibrationUser.canReceiveVibration port crates/horizon-client/src/sculk.rs::valid_vibration +SculkSensorBlockEntity.VibrationUser.onReceiveVibration port crates/horizon-client/src/sculk.rs::Data::schedule +TestBlockEntity missing minor: game-test block entity; reset, trigger and logging are not ported, NBT only +TestInstanceBlockEntity missing minor: game-test structure tooling; export and chunk forcing are not ported +TrialSpawnerConfig.calculateTargetSimultaneousMobs port crates/horizon-client/src/mobs/trial_spawner.rs::TrialConfig::target_simultaneous_mobs +TrialSpawnerConfig.calculateTargetTotalMobs port crates/horizon-client/src/mobs/trial_spawner.rs::TrialConfig::target_total_mobs +TrialSpawnerConfig.withSpawning skip:java builder-style setter on the config record +TrialSpawnerConfig.Builder skip:java builder for the trial config +TrialSpawnerConfigs skip:data registry bootstrap and equipment spawn data +SolidDebugger skip:debug debug dump of a block state +StateDefinition skip:java codecs and state builders; states come from the generated block state table +StateDefinition.Builder skip:java state builder +StateDefinition.StateCollection skip:data neighbour tables of the state graph are generated data +BlockInWorld skip:java accessor over the level's state and block entity +BlockPattern.matches port crates/horizon-client/src/game/golems.rs::Game::find_golem golem head and body patterns only, not a generic pattern matcher +BlockPattern.translateAndRotate inline crates/horizon-client/src/game/golems.rs::Game::find_golem pattern placement folded into golem search +BlockPattern.BlockPatternMatch skip:java match result accessor +BlockPatternBuilder skip:java builder for block patterns +BlockStatePredicate skip:java predicate builder +BooleanProperty skip:java property accessor +ChestType skip:java enum helper +EnumProperty skip:java property factory and hashing +IntegerProperty skip:java property accessor +BlockMatcher skip:java block scan helpers; Horizon scans with direct loops +BoxBlockMatcher skip:java block scan helpers; Horizon scans with direct loops +FilteredSectionCache skip:java section cache for block scans +OrderedBlockMatcher skip:java block scan helpers; Horizon scans with direct loops +BulkSectionAccess skip:java section cache for bulk writes +ChunkGenerators skip:data bootstrap +ChunkSource.hasChunk port crates/horizon-client/src/game.rs::GameLevel::is_loaded +GlobalPalette skip:java palette storage; Horizon's chunk.rs keeps block ids in plain arrays +HashMapPalette skip:java palette storage; Horizon's chunk.rs keeps block ids in plain arrays +ImposterProtoChunk skip:other wrapper so the vanilla status pipeline can read a loaded chunk; streaming keeps its own protos +LinearPalette skip:java palette storage; Horizon's chunk.rs keeps block ids in plain arrays +PalettedContainerFactory skip:java factory for palette storage; Horizon's chunk.rs keeps block ids in plain arrays +SingleValuePalette skip:java palette storage; Horizon's chunk.rs keeps block ids in plain arrays +UpgradeData missing unsure: minor: block upgrades of old chunks on load are not ported +ChunkDependencies.getRadiusOf inline crates/horizon-client/src/streaming.rs::Ranges::margins fixed margins instead of a per-status radius +ChunkPyramid.Builder skip:java builder for the status pyramid; Horizon uses fixed margins +ChunkStatusTasks.buildTerrain inline crates/horizon-world/src/worldgen/generator.rs::Pipeline::generate_with_context terrain pass +ChunkStatusTasks.collectPossibleBiomes inline crates/horizon-world/src/worldgen/generator.rs::Pipeline::biome_chunk biome set folded into the biome pass +ChunkStatusTasks.full skip:other turns the proto into the finished chunk; the generator returns the finished chunk +ChunkStatusTasks.generateBiomes port crates/horizon-world/src/worldgen/generator.rs::Pipeline::biome_chunk +ChunkStatusTasks.generateFeatures port crates/horizon-world/src/worldgen/generator_decoration.rs::Decoration::decorate +ChunkStatusTasks.generateSpawn port crates/horizon-world/src/worldgen/spawn_finder.rs::find_spawn_position +ChunkStatusTasks.generateStructureReferences port crates/horizon-world/src/worldgen/generator_structures.rs::StructureState::references +ChunkStatusTasks.generateStructureStarts port crates/horizon-world/src/worldgen/generator_structures.rs::StructureState::starts_at +ChunkStatusTasks.initializeLight port crates/horizon-client/src/light.rs::light_chunk +ChunkStatusTasks.isLighted skip:other status check; streaming tracks which chunks are lit +ChunkStep.apply inline crates/horizon-client/src/streaming.rs::Streaming::update stage advance +ChunkStep.completeChunkGeneration inline crates/horizon-client/src/streaming.rs::Streaming::update final stage hand-over +ChunkStep.getAccumulatedRadiusOf inline crates/horizon-client/src/streaming.rs::Ranges::margins fixed margins +ChunkStep.Builder skip:java builder for chunk steps +ChunkIOErrorReporter.createMisplacedChunkReport skip:other report text for a chunk saved at another position; Horizon logs its own load error +RecreatingSimpleRegionStorage skip:java close: Rust closes the file on drop +RegionFileStorage.cache port crates/horizon-client/src/save.rs::cached_region +RegionFileStorage.close skip:java files close on drop +RegionFileStorage.flush port crates/horizon-world/src/region.rs::RegionFile::sync +RegionFileStorage.getRegionFile port crates/horizon-client/src/save.rs::cached_region +RegionFileStorage.read port crates/horizon-world/src/region.rs::RegionFile::read +RegionFileStorage.scanChunk port crates/horizon-client/src/save.rs::read_region_record +RegionFileStorage.write port crates/horizon-world/src/region.rs::RegionFile::write diff --git a/tools/port_map/classes-05.tsv b/tools/port_map/classes-05.tsv new file mode 100644 index 0000000000..fb8f3a2840 --- /dev/null +++ b/tools/port_map/classes-05.tsv @@ -0,0 +1,227 @@ +RegionFileVersion.configure missing minor: region-file-compression is read from server.properties (horizon-server-files properties.rs) but nothing applies it; chunk saves always write zlib (save.rs compress_records) +RegionFileVersion.register skip:data static version table; Horizon's codecs are the Compression enum (compression.rs) +RegionFileVersion.wrap port crates/horizon-world/src/compression.rs::Compression::encode whole-buffer codec; output side (input side is Compression::decode) +RegionStorageInfo.withTypeSuffix port crates/horizon-client/src/save.rs::Folder::name folder name per region kind; only the fixed region, entities and poi folders exist, none is suffixed +EntityLookup.add skip:java index insert; Horizon keeps entities in per-kind Vecs (game.rs) and a uuid map (game/persist.rs) +EntityLookup.getEntities port crates/horizon-client/src/mobs/seen_grid.rs::SeenGrid::candidates gap: minor: returns mob candidates only (only mobs are indexed); callers test the boxes exactly +EntityLookup.getEntity skip:java lookup by id or uuid over Horizon's per-kind lists and the uuid map +EntityLookup.remove skip:java index removal; Horizon removes the entity from its per-kind list +EntitySection skip:java section container; Horizon has no section index, only the mob grid (mobs/seen_grid.rs) +EntitySectionStorage skip:java spatial section index; Horizon has no section storage, the mob grid is its only spatial index +EntityTickList skip:java ticking list; Horizon ticks its per-kind Vecs in order (game.rs) +LevelEntityGetterAdapter skip:java adapter over the section storage; no equivalent needed +PersistentEntitySectionManager.addEntity skip:java section and visibility bookkeeping; Horizon spawns entities into its per-kind Vecs (game.rs) +PersistentEntitySectionManager.addEntityUuid skip:java uuid set kept in persist.uuids (game/persist.rs) +PersistentEntitySectionManager.addLegacyChunkEntities port crates/horizon-client/src/game/persist.rs::Game::spawn_loaded_entities saved chunk entity tags are spawned here once the chunk's entities arrive +PersistentEntitySectionManager.addWorldGenChunkEntities port crates/horizon-client/src/game/persist.rs::Game::spawn_loaded_entities worldgen entities arrive as saved tags and are spawned the same way +PersistentEntitySectionManager.canPositionTick port crates/horizon-client/src/ticket_levels.rs::Levels::entity_ticking_at a position ticks entities when its chunk is entity ticking +PersistentEntitySectionManager.dumpSections skip:debug section dump for debugging +PersistentEntitySectionManager.ensureChunkQueuedForLoad skip:other async load queue; Horizon's streaming hands the saved entity tags over with the chunk +PersistentEntitySectionManager.gatherStats skip:debug statistics string +PersistentEntitySectionManager.getAllChunksToSave inline crates/horizon-client/src/game/persist.rs::Game::save_entities the resident chunk keys that save_entities writes +PersistentEntitySectionManager.processChunkUnload port crates/horizon-client/src/game/persist.rs::Game::unload_entities saves and removes the entities of an unloaded chunk +PersistentEntitySectionManager.processUnloads inline crates/horizon-client/src/game/persist.rs::Game::unload_entities the batch of unloaded chunks handed over by the tick +PersistentEntitySectionManager.requestChunkLoad port crates/horizon-client/src/save.rs::SaveDir::load_entities reads the saved entity record of the chunk; the chunk load itself is streaming +PersistentEntitySectionManager.saveAll port crates/horizon-client/src/game/persist.rs::Game::save_entities the waiting form (quit path) writes every resident chunk and flushes +PersistentEntitySectionManager.storeChunkSections port crates/horizon-client/src/game/persist.rs::Game::entity_chunks the saved entity tags of a chunk; with remove the entities leave the world +PersistentEntitySectionManager.updateChunkStatus skip:other visibility per chunk status; Horizon derives entity ticking from ticket levels (ticket_levels.rs) instead of storing it +PersistentEntitySectionManager.Callback.onMove skip:java section membership change; Horizon has no sections +PersistentEntitySectionManager.Callback.onRemove port crates/horizon-client/src/game/persist.rs::Game::prune_entity_memory gap: minor: removal is batched; dead ids are dropped from persist memory on save, not per removal +PersistentEntitySectionManager.Callback.updateStatus skip:other visibility changes are chunk ticket levels in Horizon (ticket_levels.rs) +TransientEntitySectionManager skip:java sections for entities that are not saved; Horizon keeps them in its per-kind lists (game.rs) +TransientEntitySectionManager.Callback skip:java section callbacks of the transient manager; no sections in Horizon +DynamicGameEventListener.ifChunkExists port crates/horizon-client/src/game/sculk.rs::Game::sculk_listen a sensor in an unloaded chunk has no entity, so the listener is dropped +DynamicGameEventListener.move skip:other listeners are fixed block positions in Horizon; sculk blocks do not move +DynamicGameEventListener.remove port crates/horizon-client/src/game/sculk.rs::Game::sculk_sync_index drops the listener's index entry when its block is gone +EntityPositionSource.getId skip:java accessor; Horizon's listeners hold the source as a Who (sculk.rs Info) +EntityPositionSource.getPosition port crates/horizon-client/src/mobs/warden.rs::listen gap: minor: covers the warden's eye-height listener only; no other position source is used +EntityPositionSource.getUuid skip:java accessor; Horizon holds the source as a Who +EntityPositionSource.resolveEntity skip:other the source is a Who held by the listener, so there is no entity lookup to port +EuclideanGameEventListenerRegistry.getPostableListenerPosition inline crates/horizon-client/src/game/sculk.rs::Game::post_game_event the in-range distance test that picks the listeners +EuclideanGameEventListenerRegistry.register port crates/horizon-client/src/game/sculk.rs::Game::sculk_scan_chunk a sculk block found in a loaded chunk enters the index +EuclideanGameEventListenerRegistry.sendDebugInfo skip:debug debug rendering of listeners +EuclideanGameEventListenerRegistry.unregister port crates/horizon-client/src/game/sculk.rs::Game::sculk_sync_index index entries of removed sculk blocks are dropped +EuclideanGameEventListenerRegistry.visitInRangeListeners port crates/horizon-client/src/game/sculk.rs::Game::post_game_event walks the index for listeners in range, then the wardens +VibrationInfo.getEntity port crates/horizon-client/src/game/sculk.rs::Game::sculk_receive the source is held as a Who and read there +VibrationInfo.getProjectileOwner port crates/horizon-client/src/game/sculk.rs::Game::sculk_receive the projectile owner is captured when posted (info.owner) and read there +VibrationSelector.addCandidate port crates/horizon-client/src/sculk.rs::Selector::add_candidate +VibrationSelector.chosenCandidate port crates/horizon-client/src/sculk.rs::Selector::chosen_candidate +VibrationSelector.shouldReplaceVibration port crates/horizon-client/src/sculk.rs::Selector::should_replace +GameRuleCategory.register skip:data static category table; Horizon's rule categories are static strings (gamerules.rs) +GameRuleMap.equals skip:java value equality of the map +GameRuleMap.of port crates/horizon-client-core/src/gamerules.rs::GameRules::get no stored map: a rule that was never set reads its table default +GameRuleMap.setFromIf skip:other no caller in the pinned sources (only withOther uses it) +GameRuleMap.withOther skip:other no caller in the pinned sources +BlendingData missing player: in worlds upgraded from before 1.18 the old/new chunk border is not blended (datafix/fixes_c.rs drops blending_data), so a seam can show +BlendingData.Packed.validateArraySize skip:java codec size check +AllOfPredicate.test port crates/horizon-world/src/worldgen/block_predicate.rs::BlockPredicate::test_inner the All arm +AnyOfPredicate.test port crates/horizon-world/src/worldgen/block_predicate.rs::BlockPredicate::test_inner the Any arm +CombiningPredicate.codec skip:java codec +HasSturdyFacePredicate.test port crates/horizon-world/src/worldgen/block_predicate.rs::BlockPredicate::test_inner the HasSturdyFace arm +HeightRangePredicate.test port crates/horizon-world/src/worldgen/block_predicate.rs::BlockPredicate::test_inner the HeightRange arm +StateTestingPredicate.stateTestingCodec skip:java codec +VolumeMatchPredicate.test port crates/horizon-world/src/worldgen/block_predicate.rs::BlockPredicate::test_inner the Volume arm +CanyonWorldCarver.carve port crates/horizon-world/src/worldgen/carver/canyon.rs::CanyonCarver::carve +CanyonWorldCarver.doCarve inline crates/horizon-world/src/worldgen/carver/canyon.rs::CanyonCarver::carve the per-tunnel walk inside carve +CanyonWorldCarver.initWidthFactors inline crates/horizon-world/src/worldgen/carver/canyon.rs::CanyonCarver::carve the width factors are built at the start of carve +CanyonWorldCarver.shouldSkip inline crates/horizon-world/src/worldgen/carver/canyon.rs::CanyonCarver::carve the width-factor test in the carve_ellipsoid closure +CanyonWorldCarver.updateVerticalRadius inline crates/horizon-world/src/worldgen/carver/canyon.rs::CanyonCarver::carve center-weighted vertical factor times the random 0.75 to 1.0 scale +CaveWorldCarver.carve port crates/horizon-world/src/worldgen/carver.rs::CaveCarver::carve +CaveWorldCarver.createRoom inline crates/horizon-world/src/worldgen/carver.rs::CaveCarver::carve the room branch of carve, which calls CaveRun::ellipsoid +CaveWorldCarver.createTunnel port crates/horizon-world/src/worldgen/carver.rs::CaveRun::tunnel +CaveWorldCarver.getThickness inline crates/horizon-world/src/worldgen/carver.rs::CaveCarver::carve thickness sample and the weird thickness bias +CaveWorldCarver.shouldSkip inline crates/horizon-world/src/worldgen/carver.rs::CaveRun::ellipsoid the floor and unit sphere test in the skip closure +WorldCarverTypes.bootstrap skip:data registry bootstrap; carvers are read from JSON (carver.rs, canyon.rs) +DensityBuffer.copyFrom skip:java buffers are plain slices; the samplers copy slices directly +DensityBufferPool skip:java buffer pool; Horizon's scratch Vecs in SamplerContext (density/mod.rs) play that role +DensityFunction.axesFrom inline crates/horizon-core/src/density/graph/mod.rs::DensityGraph::domain_axes the axis bit (1 << axis) is set inline in the Gradient and Slice arms +DensityFunction.pow port crates/horizon-world/src/worldgen/density.rs::Loader::decode the minecraft:pow token builds a Power node +DensityFunctionCompiler.optimizeAndCompile port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::get_sampler optimize, then compile_raw, cached per function id +DensityFunctionCompiler.prepareCache port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::prepare cache arm: compile the input, bind its range and axes +DensityFunctionCompiler.reuseOrPrepareCache inline crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::prepare the prepared-map lookup in the cache arm +DensityFunctions.ceil port crates/horizon-world/src/worldgen/density.rs::Loader::decode minecraft:ceil token; multiple defaults to 1 +DensityFunctions.floor port crates/horizon-world/src/worldgen/density.rs::Loader::decode minecraft:floor token; multiple defaults to 1 +DensityFunctions.lerp port crates/horizon-world/src/worldgen/density.rs::Loader::decode minecraft:lerp token +DensityFunctions.mappedNoise skip:data bootstrap helper that composes noise and remap; Horizon reads the finished graph from data +DensityFunctions.noise port crates/horizon-world/src/worldgen/density.rs::Loader::decode minecraft:noise token +DensityFunctions.remap skip:data bootstrap composition of clamp and arithmetic; Horizon reads the finished graph from data +DensityFunctions.round port crates/horizon-world/src/worldgen/density.rs::Loader::decode floor, round, ceil and truncate share one token arm +DensityFunctions.truncate port crates/horizon-world/src/worldgen/density.rs::Loader::decode minecraft:truncate token in the same arm as floor, round and ceil +DensityFunctions.HolderHolder.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children the Reference arm rewrites its input only +DfRewriteRule.SliceUniformAxes.getExistingRemovedAxes inline crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::slice_uniform_axes the walk over existing slice nodes +DfRewriteRule.SliceUniformAxes.removeAxes inline crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::slice_uniform_axes the remove mask and the X, Z, Y slice insertion +DfRewriteRule.SliceUniformAxes.rewrite port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::slice_uniform_axes +ScopedDensityBuffer.close port crates/horizon-core/src/density/mod.rs::SamplerContext::with_buffer the scratch buffer goes back when the scope ends +ScopedDensityBuffer.restore skip:java reuse of a pooled buffer's size and flags; scratch Vecs are simply reused +DistanceToPointFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw DistanceToPoint arm +DistanceToPointFunction.Sampler.sampleValue port crates/horizon-core/src/density/terrain_noise.rs::DistanceToPointSampler::sample_value +DistanceToPointFunction.Sampler.sampleVolume port crates/horizon-core/src/density/mod.rs::visit the trait default sample_volume, which visits each position +EndIslandFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw EndIslands arm, islands from the seeded context +EndIslandFunction.getHeightValue port crates/horizon-core/src/density/end_islands.rs::EndIslandSampler::height +EndIslandFunction.Sampler.sampleValue port crates/horizon-core/src/density/end_islands.rs::EndIslandSampler::sample_value +EndIslandFunction.Sampler.sampleVolume port crates/horizon-core/src/density/end_islands.rs::EndIslandSampler::sample_volume +GradientFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Gradient arm builds a GradientSampler +GradientFunction.validate port crates/horizon-world/src/worldgen/density.rs::Loader::decode the gradient arm rejects invalid coordinates +GradientFunction.ClampedSampler.compute port crates/horizon-core/src/density/mod.rs::GradientSampler::compute clamped tiling +GradientFunction.MirroredRepeatSampler.compute port crates/horizon-core/src/density/mod.rs::GradientSampler::compute mirrored repeat tiling +GradientFunction.RepeatSampler.compute port crates/horizon-core/src/density/mod.rs::GradientSampler::compute repeat tiling +NoiseFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Noise arm; nonzero shifts select the shifted sampler +NoiseFunction.domainAxes port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::domain_axes +NoiseFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +NoiseFunction.Sampler.sampleValue port crates/horizon-core/src/density/mod.rs::NoiseSampler::sample_value +NoiseFunction.Sampler.sampleVolume port crates/horizon-core/src/density/mod.rs::NoiseSampler::sample_volume +NoiseFunction.ShiftedXyzSampler.sampleValue port crates/horizon-core/src/density/mod.rs::ShiftedNoiseSampler::sample_value shift_y present +NoiseFunction.ShiftedXyzSampler.sampleVolume port crates/horizon-core/src/density/mod.rs::ShiftedNoiseSampler::sample_volume shift_y present +NoiseFunction.ShiftedXzSampler.sampleValue port crates/horizon-core/src/density/mod.rs::ShiftedNoiseSampler::sample_value shift_y absent +NoiseFunction.ShiftedXzSampler.sampleVolume port crates/horizon-core/src/density/mod.rs::ShiftedNoiseSampler::sample_volume shift_y absent +ShiftNoiseFunction.Shift.compileSampler port crates/horizon-core/src/density/terrain_noise.rs::compile_shift_noise +ShiftNoiseFunction.ShiftA.compileSampler port crates/horizon-core/src/density/terrain_noise.rs::compile_shift_noise +ShiftNoiseFunction.ShiftB.compileSampler port crates/horizon-core/src/density/terrain_noise.rs::compile_shift_noise ShiftB gets ShiftBSampler +SimpleDensityFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw WorldField arm (blend alpha and offset, end islands) +SimpleDensityFunction.domainAxes port crates/horizon-core/src/density/world_fields.rs::WorldField::domain_axes +SimpleDensityFunction.range port crates/horizon-core/src/density/world_fields.rs::WorldField::range +BinaryFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_binary constant operands become unary ops +BinaryFunction.range port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::range Binary arm +BinaryFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +BinaryFunction.warnNonIntersecting skip:debug log-only warning when min or max ranges never meet; Horizon returns the surviving side silently in compile_binary +BinaryFunction.AddSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_value BinaryOp::Add +BinaryFunction.AddSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_volume BinaryOp::Add +BinaryFunction.ConstAddSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value UnaryOp::Add with a constant +BinaryFunction.ConstAddSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::Add with a constant +BinaryFunction.ConstDivSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value UnaryOp::DivideInto (constant over input) +BinaryFunction.ConstDivSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::DivideInto +BinaryFunction.ConstMaxSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value UnaryOp::Maximum +BinaryFunction.ConstMaxSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::Maximum +BinaryFunction.ConstMinSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value UnaryOp::Minimum +BinaryFunction.ConstMinSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::Minimum +BinaryFunction.ConstMulSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value UnaryOp::Multiply (a constant divisor becomes 1/c, as in vanilla) +BinaryFunction.ConstMulSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::Multiply +BinaryFunction.ConstSubSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value UnaryOp::SubtractFrom (constant minus input) +BinaryFunction.ConstSubSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::SubtractFrom +BinaryFunction.DivSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_value BinaryOp::Divide; a zero left gives zero +BinaryFunction.DivSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_volume BinaryOp::Divide +BinaryFunction.MaxSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_value BinaryOp::Maximum with the right-max shortcut +BinaryFunction.MaxSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_volume BinaryOp::Maximum +BinaryFunction.MinSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_value BinaryOp::Minimum with the right-min shortcut +BinaryFunction.MinSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_volume BinaryOp::Minimum +BinaryFunction.MulSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_value BinaryOp::Multiply; a zero left gives zero +BinaryFunction.MulSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_volume BinaryOp::Multiply +BinaryFunction.SubSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_value BinaryOp::Subtract +BinaryFunction.SubSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::BinarySampler::sample_volume BinaryOp::Subtract +BlendDensityFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +BlendDensityFunction.Sampler.sampleValue port crates/horizon-core/src/density/world_fields.rs::BlendDensitySampler::sample_value +BlendDensityFunction.Sampler.sampleVolume port crates/horizon-core/src/density/world_fields.rs::BlendDensitySampler::sample_volume +CacheFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children the cache node itself is prepared in DensityCompiler::prepare +ClampFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Clamp arm: a UnarySampler with UnaryOp::Clamp +ClampFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +ClampFunction.validate port crates/horizon-world/src/worldgen/density.rs::Loader::decode the clamp arm rejects max below min +ClampFunction.Sampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value UnaryOp::Clamp +ClampFunction.Sampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::Clamp +FindTopSurfaceFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Surface arm, built by compile_surface_search +FindTopSurfaceFunction.range port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::range Surface arm +FindTopSurfaceFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +FindTopSurfaceFunction.Sampler.findSurfaceFrom port crates/horizon-core/src/density/surface.rs::FindTopSurfaceSampler::find_surface_from +FindTopSurfaceFunction.Sampler.sampleValue port crates/horizon-core/src/density/surface.rs::FindTopSurfaceSampler::sample_value +FindTopSurfaceFunction.Sampler.sampleVolume port crates/horizon-core/src/density/surface.rs::FindTopSurfaceSampler::sample_volume +InterpolatedFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Interpolated arm +InterpolatedFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +InterpolatedFunction.Sampler.fillCell port crates/horizon-core/src/density/interpolation.rs::InterpolatedSampler::fill_cell +InterpolatedFunction.Sampler.sampleValue port crates/horizon-core/src/density/interpolation.rs::InterpolatedSampler::sample_value +InterpolatedFunction.Sampler.sampleVolume port crates/horizon-core/src/density/interpolation.rs::InterpolatedSampler::sample_volume +InterpolatedFunction.Sampler.sampleWithBlockStep port crates/horizon-core/src/density/interpolation.rs::InterpolatedSampler::sample_block_volume +InterpolatedFunction.Sampler.sampleWithNonBlockStep inline crates/horizon-core/src/density/interpolation.rs::InterpolatedSampler::sample_volume the sparse and arbitrary-step branches of sample_volume +IntervalSelectFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw IntervalSelect arm +IntervalSelectFunction.domainAxes port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::domain_axes +IntervalSelectFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +IntervalSelectFunction.validate port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::insert the branch and threshold counts are checked when the node is inserted +IntervalSelectFunction.Sampler.sampleValue port crates/horizon-core/src/density/selection.rs::IntervalSelectSampler::sample_value +IntervalSelectFunction.Sampler.sampleVolume port crates/horizon-core/src/density/selection.rs::IntervalSelectSampler::sample_volume +IntervalSelectFunction.Sampler.selectSamplerIndex port crates/horizon-core/src/density/selection.rs::IntervalSelectSampler::select +IntervalSelectFunction.SingleThresholdSampler.sampleValue inline crates/horizon-core/src/density/selection.rs::IntervalSelectSampler::select no one-threshold class; the generic select covers it +IntervalSelectFunction.SingleThresholdSampler.sampleVolume inline crates/horizon-core/src/density/selection.rs::IntervalSelectSampler::sample_volume no one-threshold class; the generic path covers it +densityfunction/op/LerpFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Lerp arm; constant ends become DensityValue::Constant +densityfunction/op/LerpFunction.domainAxes port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::domain_axes +densityfunction/op/LerpFunction.range port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::range Lerp arm +densityfunction/op/LerpFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +LerpFunction.ConstFirstSampler.sampleValue port crates/horizon-core/src/density/selection.rs::DensityValue::sample_value constant first end +LerpFunction.ConstFirstSampler.sampleVolume port crates/horizon-core/src/density/selection.rs::DensityValue::with_volume constant first end +LerpFunction.ConstSecondSampler.sampleValue port crates/horizon-core/src/density/selection.rs::DensityValue::sample_value constant second end +LerpFunction.ConstSecondSampler.sampleVolume port crates/horizon-core/src/density/selection.rs::DensityValue::with_volume constant second end +LerpFunction.Sampler.sampleValue port crates/horizon-core/src/density/selection.rs::LerpSampler::sample_value +LerpFunction.Sampler.sampleVolume port crates/horizon-core/src/density/selection.rs::LerpSampler::sample_volume +PowFunction.compileConstExponent port crates/horizon-core/src/density/power.rs::compile_constant_exponent 0.5, 1, 2, 3 and negatives get their own ops +PowFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Power arm +PowFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +PowFunction.ConstBaseSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value UnaryOp::PowerBase +PowFunction.ConstBaseSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::PowerBase +PowFunction.ConstExponentSampler.sampleValue port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value UnaryOp::PowerExponent or the specialised ops +PowFunction.ConstExponentSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::PowerExponent or the specialised ops +PowFunction.Sampler.sampleValue port crates/horizon-core/src/density/power.rs::PowerSampler::sample_value +PowFunction.Sampler.sampleVolume port crates/horizon-core/src/density/power.rs::PowerSampler::sample_volume +RangeChoiceFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Choice arm; constant branches become UnaryOp::ConstantRangeChoice +RangeChoiceFunction.domainAxes port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::domain_axes +RangeChoiceFunction.range port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::range Choice arm +RangeChoiceFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +RangeChoiceFunction.ConstSampler.choose port crates/horizon-core/src/density/arithmetic.rs::UnaryOp::apply UnaryOp::ConstantRangeChoice +RangeChoiceFunction.ConstSampler.sampleVolume port crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume UnaryOp::ConstantRangeChoice +RangeChoiceFunction.Sampler.sampleValue port crates/horizon-core/src/density/selection.rs::RangeChoiceSampler::sample_value +RangeChoiceFunction.Sampler.sampleVolume port crates/horizon-core/src/density/selection.rs::RangeChoiceSampler::sample_volume +RoundFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Round arm; a multiple of 1 skips the multiply +RoundFunction.range port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::range Round arm +RoundFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +RoundFunction.roundToInteger port crates/horizon-core/src/density/rounding.rs::RoundMode::apply the multiple-of-1 case +RoundFunction.IntegerMultipleSampler.sampleValue port crates/horizon-core/src/density/rounding.rs::RoundSampler::sample_value multiple absent +RoundFunction.IntegerMultipleSampler.sampleVolume port crates/horizon-core/src/density/rounding.rs::RoundSampler::sample_volume multiple absent +RoundFunction.Sampler.apply port crates/horizon-core/src/density/rounding.rs::RoundMode::apply +RoundFunction.Sampler.sampleValue port crates/horizon-core/src/density/rounding.rs::RoundSampler::sample_value +RoundFunction.Sampler.sampleVolume port crates/horizon-core/src/density/rounding.rs::RoundSampler::sample_volume +SliceFunction.compileSampler port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw Slice arm; an X and a Z slice fuse into one Xz sampler +SliceFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +SliceFunction.XSampler.sampleVolume port crates/horizon-core/src/density/slice.rs::SliceSampler::sample_volume SliceAxes::X +SliceFunction.XzSampler.sampleVolume port crates/horizon-core/src/density/slice.rs::SliceSampler::sample_volume SliceAxes::Xz +SliceFunction.YSampler.sampleVolume port crates/horizon-core/src/density/slice.rs::SliceSampler::sample_volume SliceAxes::Y +SliceFunction.ZSampler.sampleVolume port crates/horizon-core/src/density/slice.rs::SliceSampler::sample_volume SliceAxes::Z +SplineFunction.domainAxes port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::domain_axes +SplineFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children spline coordinates are children too (GraphNode::children_mut) +SplineFunction.BufferSplineInput.close port crates/horizon-core/src/density/spline.rs::SplineSampler::sample_volume coordinate buffers go back to context.scratch when sample_volume ends +SplineFunction.BufferSplineInput.sampleCoordinate port crates/horizon-core/src/density/spline.rs::SplineSampler::sample_volume each coordinate buffer is filled on first use diff --git a/tools/port_map/classes-06.tsv b/tools/port_map/classes-06.tsv new file mode 100644 index 0000000000..ce49b393af --- /dev/null +++ b/tools/port_map/classes-06.tsv @@ -0,0 +1,188 @@ +SplineFunction.PointSplineInput.sampleCoordinate inline crates/horizon-core/src/density/spline.rs::SplineSampler::sample_value per-point coordinate lookup in the sample closure +SplineFunction.Sampler.sampleValue port crates/horizon-core/src/density/spline.rs::SplineSampler::sample_value +SplineFunction.Sampler.sampleVolume port crates/horizon-core/src/density/spline.rs::SplineSampler::sample_volume +UnaryFunction.compileSampler inline crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::compile_raw unary arm of compile_raw +UnaryFunction.range port crates/horizon-core/src/density/graph/mod.rs::DensityGraph::range unary interval arm +UnaryFunction.rewriteChildren port crates/horizon-core/src/density/graph/compiler.rs::DensityCompiler::rewrite_children +UnaryFunction.AbsSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler; Abs is a UnaryOp arm +UnaryFunction.CubeSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler +UnaryFunction.LeakyReLUSampler.sampleValue inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value LeakyRelu arm of UnaryOp::apply +UnaryFunction.LeakyReLUSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler +UnaryFunction.LogSampler.sampleValue inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_value Log arm of UnaryOp::apply +UnaryFunction.LogSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler +UnaryFunction.NegateSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler +UnaryFunction.ReciprocalSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler +UnaryFunction.SignSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler +UnaryFunction.SqrtSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler +UnaryFunction.SquareSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler +UnaryFunction.SqueezeSampler.apply inline crates/horizon-core/src/density/arithmetic.rs::UnaryOp::apply Squeeze arm +UnaryFunction.SqueezeSampler.sampleVolume inline crates/horizon-core/src/density/arithmetic.rs::UnarySampler::sample_volume one generic unary sampler +AbstractHugeMushroomFeature.getTreeHeight inline crates/horizon-world/src/worldgen/vegetation.rs::HugeMushroomFeature::place height roll at the start of place +AbstractHugeMushroomFeature.isValidPosition inline crates/horizon-world/src/worldgen/vegetation.rs::HugeMushroomFeature::place base and clearance checks +AbstractHugeMushroomFeature.place port crates/horizon-world/src/worldgen/vegetation.rs::HugeMushroomFeature::place +AbstractHugeMushroomFeature.placeMushroomBlock port crates/horizon-world/src/worldgen/vegetation.rs::HugeMushroomFeature::place_block +AbstractHugeMushroomFeature.placeTrunk inline crates/horizon-world/src/worldgen/vegetation.rs::HugeMushroomFeature::place stem loop +AbstractOreFeature.canPlaceOre inline crates/horizon-world/src/worldgen/ore_feature.rs::OreFeature::do_place target rule test and air check in the block loop +AbstractOreFeature.checkNeighbors inline crates/horizon-world/src/worldgen/ore_feature.rs::OreFeature::do_place six-neighbour air scan +AbstractOreFeature.makeCodec skip:java codec builder +AbstractOreFeature.shouldSkipAirCheck port crates/horizon-world/src/worldgen/ore_feature.rs::skip_air_check +CoralClawFeature.place port crates/horizon-world/src/worldgen/vegetation.rs::CoralFeature::place +CoralTreeFeature.place port crates/horizon-world/src/worldgen/vegetation.rs::CoralFeature::place +CuboidPlacement.modify port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner Cuboid arm +FeatureCountTracker skip:debug feature count debug log, no game behaviour +HugeBrownMushroomFeature.makeCap inline crates/horizon-world/src/worldgen/vegetation.rs::HugeMushroomFeature::place non-red cap branch +HugeRedMushroomFeature.getTreeRadiusForHeight port crates/horizon-world/src/worldgen/vegetation.rs::HugeMushroomFeature::radius_for_height +HugeRedMushroomFeature.makeCap inline crates/horizon-world/src/worldgen/vegetation.rs::HugeMushroomFeature::place red cap loop +ProjectedRandomPatchySquare.place port crates/horizon-world/src/worldgen/projected.rs::ProjectedPatchySquareFeature::place +RandomBooleanSelectorFeature.getSubFeatures port crates/horizon-world/src/worldgen/live.rs::nested_features gap: minor: walks nested feature names from the JSON, not feature objects +RandomBooleanSelectorFeature.place port crates/horizon-world/src/worldgen/feature_graph.rs::FeatureDefinition::evaluate BooleanChoice arm +RandomNeighborSpreadFeature.place port crates/horizon-world/src/worldgen/feature_graph.rs::FeatureDefinition::evaluate NeighborSpread arm +RandomSelectorFeature.getSubFeatures port crates/horizon-world/src/worldgen/live.rs::nested_features gap: minor: walks nested feature names from the JSON, not feature objects +RandomSelectorFeature.place port crates/horizon-world/src/worldgen/feature_graph.rs::FeatureDefinition::evaluate RandomChoice arm +SequenceFeature.getSubFeatures port crates/horizon-world/src/worldgen/live.rs::nested_features gap: minor: walks nested feature names from the JSON, not feature objects +SequenceFeature.place port crates/horizon-world/src/worldgen/feature_graph.rs::FeatureDefinition::evaluate Sequence arm +SimpleRandomSelectorFeature.getSubFeatures port crates/horizon-world/src/worldgen/live.rs::nested_features gap: minor: walks nested feature names from the JSON, not feature objects +SimpleRandomSelectorFeature.place port crates/horizon-world/src/worldgen/feature_graph.rs::FeatureDefinition::evaluate RandomChoice arm (simple selector decodes there) +SingleBlockPillarFeature.place port crates/horizon-world/src/worldgen/feature_graph.rs::FeatureDefinition::evaluate Pillar arm +VoidStartPlatformFeature.place port crates/horizon-world/src/worldgen/feature.rs::BasicFeature::place VoidStartPlatform arm +WeightedRandomSelectorFeature.getSubFeatures port crates/horizon-world/src/worldgen/live.rs::nested_features gap: minor: walks nested feature names from the JSON, not feature objects +WeightedRandomSelectorFeature.place port crates/horizon-world/src/worldgen/feature_graph.rs::FeatureDefinition::evaluate WeightedChoice arm +ThreeLayersFeatureSize.getSizeAtHeight port crates/horizon-world/src/worldgen/tree.rs::FeatureSize::size_at_height ThreeLayers arm +AcaciaFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage Acacia arm +AcaciaFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip Acacia arm +BlobFoliagePlacer.blobParts skip:java codec part builder +BlobFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage Blob arm +BlobFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip Blob arm +BushFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage Bush arm +BushFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip Bush arm +CherryFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage Cherry arm +CherryFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip Cherry arm +DarkOakFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage DarkOak arm +DarkOakFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip DarkOak arm +DarkOakFoliagePlacer.shouldSkipLocationSigned port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip_signed +FancyFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage Fancy arm +FancyFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip Fancy arm +MegaJungleFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage MegaJungle arm +MegaJungleFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip MegaJungle arm +MegaPineFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage MegaPine arm +MegaPineFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip MegaPine arm +PineFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage Pine arm +PineFoliagePlacer.foliageRadius port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::foliage_radius +PineFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip Pine arm +PoplarFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage Poplar arm +PoplarFoliagePlacer.getCornerBlocksToCutForRhombusShape port crates/horizon-world/src/worldgen/tree/foliage.rs::Poplar::corner_cut +PoplarFoliagePlacer.isLeftLowerCornerOrRightTopCorner inline crates/horizon-world/src/worldgen/tree/foliage.rs::Poplar::corner_cut corner tests inlined in corner_cut +PoplarFoliagePlacer.isLeftTopCornerOrRightLowerCorner inline crates/horizon-world/src/worldgen/tree/foliage.rs::Poplar::corner_cut corner tests inlined in corner_cut +PoplarFoliagePlacer.isWithinRhombusShape port crates/horizon-world/src/worldgen/tree/foliage.rs::Poplar::within +PoplarFoliagePlacer.placeLeavesRow port crates/horizon-world/src/worldgen/tree/foliage.rs::Poplar::row +PoplarFoliagePlacer.replaceLeavesWithLog port crates/horizon-world/src/worldgen/tree/foliage.rs::Poplar::replace_with_log +PoplarFoliagePlacer.shouldRowBePartialRhombusShape port crates/horizon-world/src/worldgen/tree/foliage.rs::Poplar::partial_row +PoplarFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::Poplar::skip +PoplarFoliagePlacer.tryPlaceLog inline crates/horizon-world/src/worldgen/tree/foliage.rs::Poplar::replace_with_log log set inside the replace loop +RandomSpreadFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage RandomSpread arm +SpruceFoliagePlacer.createFoliage port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::create_foliage Spruce arm +SpruceFoliagePlacer.shouldSkipLocation port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip Spruce arm +MangroveRootPlacer.canPlaceRoot port crates/horizon-world/src/worldgen/tree.rs::RootPlacer::can_place_root +MangroveRootPlacer.placeRoot port crates/horizon-world/src/worldgen/tree.rs::RootPlacer::place_root +MangroveRootPlacer.placeRoots port crates/horizon-world/src/worldgen/tree.rs::RootPlacer::place_roots +MangroveRootPlacer.potentialRootPositions port crates/horizon-world/src/worldgen/tree.rs::RootPlacer::potential +MangroveRootPlacer.simulateRoots port crates/horizon-world/src/worldgen/tree.rs::RootPlacer::simulate +CopyPropertiesProvider.getState port crates/horizon-world/src/worldgen/state_provider.rs::BlockStateProvider::get_optional_state CopyProperties arm +DualNoiseProvider.getSlowNoiseValue inline crates/horizon-world/src/worldgen/state_provider/noise.rs::NoiseProvider::sample slow noise sampled in the Dual arm +DualNoiseProvider.getState port crates/horizon-world/src/worldgen/state_provider/noise.rs::NoiseProvider::sample Dual arm +NoiseBasedStateProvider.getNoiseValue inline crates/horizon-world/src/worldgen/state_provider/noise.rs::NoiseProvider::sample fast noise sample shared by all noise kinds +NoiseBasedStateProvider.noiseCodec skip:java codec builder +NoiseThresholdProvider.getState port crates/horizon-world/src/worldgen/state_provider/noise.rs::NoiseProvider::sample Threshold arm +RandomBlockProvider.getState port crates/horizon-world/src/worldgen/state_provider.rs::BlockStateProvider::get_optional_state RandomBlock arm +RandomizedIntStateProvider.findProperty inline crates/horizon-world/src/worldgen/state_provider.rs::BlockStateProvider::get_optional_state property lookup in the RandomizedInt arm +RandomizedIntStateProvider.getState port crates/horizon-world/src/worldgen/state_provider.rs::BlockStateProvider::get_optional_state RandomizedInt arm +RotatedBlockProvider.getState port crates/horizon-world/src/worldgen/state_provider.rs::BlockStateProvider::get_optional_state Rotated arm +RuleBasedStateProvider.builder skip:java builder API; Horizon decodes rule_based JSON +RuleBasedStateProvider.getOptionalState port crates/horizon-world/src/worldgen/state_provider.rs::BlockStateProvider::get_optional_state RuleBased arm +RuleBasedStateProvider.getState port crates/horizon-world/src/worldgen/state_provider.rs::BlockStateProvider::get_optional_state RuleBased arm +RuleBasedStateProvider.ifTrueThenProvide skip:java builder API; Horizon decodes rule_based JSON +RuleBasedStateProvider.Builder skip:java builder API; Horizon decodes rule_based JSON +AlterGroundDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place AlterGround arm +AlterGroundDecorator.placeBlockAt inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::circle single set inside the circle loop +AlterGroundDecorator.placeCircle port crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::circle +AttachedToLeavesDecorator.hasRequiredEmptyBlocks inline crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place required_empty check in AttachedToLeaves arm +AttachedToLeavesDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place AttachedToLeaves arm +AttachedToLogsDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place AttachedToLogs arm +CocoaDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place Cocoa arm +CreakingHeartDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place CreakingHeart arm +LeaveVineDecorator.addHangingVine port crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::place_vine +LeaveVineDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place LeaveVine arm +PaleMossDecorator.addMossHanger port crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::moss_hanger +PaleMossDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place PaleMoss arm +PlaceOnGroundDecorator.attemptToPlaceBlockAbove inline crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place PlaceOnGround arm +PlaceOnGroundDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place PlaceOnGround arm +ShelfMushroomDecorator.hasHorizontallyAdjacentShelfMushroom port crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::adjacent_mushroom +ShelfMushroomDecorator.isBlockReplaceableWithShelfMushroom port crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::mushroom_replaceable +ShelfMushroomDecorator.isFallenLog inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::shelf_mushroom fallen-log test (all logs at one y) +ShelfMushroomDecorator.isWithinDecoratableHeight inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::shelf_mushroom height offset 1 to 4 test +ShelfMushroomDecorator.perpendicularToFallenLog inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::shelf_mushroom fallen-log facings +ShelfMushroomDecorator.pickTwoPerpendicularDirections inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::shelf_mushroom standing-tree facings +ShelfMushroomDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::shelf_mushroom +ShelfMushroomDecorator.placeMushroom port crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::place_mushroom +ShelfMushroomDecorator.placeOnFallenLog inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::shelf_mushroom fallen-log branch +ShelfMushroomDecorator.placeOnStandingTree inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::shelf_mushroom standing-tree branch +ShelfMushroomDecorator.tryPlaceMushroomOnFallenTree inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::shelf_mushroom fallen-log branch test and place +ShelfMushroomDecorator.tryPlaceMushroomOnStandingTree inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::shelf_mushroom standing-tree branch test and place +TrunkVineDecorator.place port crates/horizon-world/src/worldgen/tree/decorator.rs::TreeDecorator::place TrunkVine arm +BendingTrunkPlacer.placeTrunk port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::place_trunk Bending arm +CherryTrunkPlacer.generateBranch inline crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::cherry branch loop in cherry +CherryTrunkPlacer.placeTrunk port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::cherry +DarkOakTrunkPlacer.placeTrunk port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::dark_oak +FancyTrunkPlacer.getLogAxis port crates/horizon-world/src/worldgen/tree/trunk.rs::log_axis +FancyTrunkPlacer.getSteps inline crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::make_limb step count inside make_limb +FancyTrunkPlacer.makeBranches inline crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::fancy branch loop at the end of fancy +FancyTrunkPlacer.makeLimb port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::make_limb +FancyTrunkPlacer.treeShape port crates/horizon-world/src/worldgen/tree/trunk.rs::tree_shape +FancyTrunkPlacer.trimBranches port crates/horizon-world/src/worldgen/tree/trunk.rs::trim_branches +ForkingTrunkPlacer.placeTrunk port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::forking +GiantTrunkPlacer.placeLogIfFreeWithOffset port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::place_log_if_free gap: minor: the offset is added by giant before this call +GiantTrunkPlacer.placeTrunk port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::giant +MegaJungleTrunkPlacer.placeTrunk port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::place_trunk MegaJungle arm +PoplarTrunkPlacer.getShuffledBranchDirections inline crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::place_trunk shuffle of horizontal directions in the Poplar arm +PoplarTrunkPlacer.placeTrunk port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::place_trunk Poplar arm +StraightTrunkPlacer.placeTrunk port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::place_trunk Straight arm +UpwardsBranchingTrunkPlacer.placeBranch port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::upwards_branch +UpwardsBranchingTrunkPlacer.placeTrunk port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::place_trunk UpwardsBranching arm +UpwardsBranchingTrunkPlacer.validTreePos port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::valid_tree_pos +FlatLayerInfo.heightLimited missing minor: no per-layer clamp to the max height; flat settings only cap the total at the build height +FlatLayerInfo.toString skip:debug debug text +FlatLevelGeneratorPresets.Bootstrap skip:data registry bootstrap of the preset list +BiasedToBottomHeight.sample port crates/horizon-world/src/worldgen/height_provider.rs::HeightProvider::sample BiasedToBottom arm +TrapezoidHeight.of skip:java factory helper; Horizon decodes the trapezoid JSON +TrapezoidHeight.sample port crates/horizon-world/src/worldgen/height_provider.rs::HeightProvider::sample Trapezoid arm +TrapezoidHeight.toString skip:debug debug text +UniformHeight.sample port crates/horizon-world/src/worldgen/height_provider.rs::HeightProvider::sample Uniform arm +VeryBiasedToBottomHeight.sample port crates/horizon-world/src/worldgen/height_provider.rs::HeightProvider::sample VeryBiasedToBottom arm +MaterialRules skip:data rule builders for the vanilla surface-rule bootstrap; Horizon reads the material rule JSON +BiomeCondition.canNeverMatch inline crates/horizon-world/src/worldgen/material_condition.rs::Compiled::compile Biome arm, constant false when biomes are disjoint +BiomeCondition.compile port crates/horizon-world/src/worldgen/material_condition.rs::Compiled::compile Biome arm +BiomeCondition.willAlwaysMatch inline crates/horizon-world/src/worldgen/material_condition.rs::Compiled::compile Biome arm, constant true when biomes are a subset +NoiseThresholdCondition.compile port crates/horizon-world/src/worldgen/material_condition.rs::Compiled::compile Noise arm +NotCondition.compile port crates/horizon-world/src/worldgen/material_condition.rs::Compiled::compile Not arm +StoneDepthCondition.compile port crates/horizon-world/src/worldgen/material_condition.rs::Compiled::compile keeps the StoneDepth config; the depth test is Compiled::test +VerticalGradientCondition.compile port crates/horizon-world/src/worldgen/material_condition.rs::Compiled::compile Gradient arm +ConditionRule.compile port crates/horizon-world/src/worldgen/material_condition/rule.rs::CompiledRule::compile Condition arm +OreVeinRule.compile port crates/horizon-world/src/worldgen/material_condition/rule.rs::CompiledRule::compile OreVein arm +OreVeinRule.VeinType.create skip:data factory the bootstrap uses for vein rules; Horizon reads the ore vein JSON +SequenceRule.compile port crates/horizon-world/src/worldgen/material_condition/rule.rs::CompiledRule::compile Sequence arm +BiomeFilter.shouldPlace port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner Biome arm +CountOnEveryLayerPlacement.findOnGroundYPosition port crates/horizon-world/src/worldgen/placement/layer.rs::ground +CountOnEveryLayerPlacement.isEmpty inline crates/horizon-world/src/level/feature_world.rs::LevelFeatureWorld::layer_block empty layer test (air, water, lava) +CountOnEveryLayerPlacement.modify port crates/horizon-world/src/worldgen/placement/layer.rs::modify +EnvironmentScanPlacement.modify port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner EnvironmentScan arm +EnvironmentScanPlacement.scanningFor skip:java factory helper; Horizon decodes the environment_scan JSON +FixedPlacement.isSameChunk inline crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner chunk test in the Fixed arm +FixedPlacement.modify port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner Fixed arm +HeightmapPlacement.modify port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner Heightmap arm +InSquarePlacement.modify port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner InSquare arm +NoiseBasedCountPlacement.count port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::repetitions NoiseBasedCount arm +NoiseThresholdCountPlacement.count port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::repetitions NoiseThresholdCount arm +OffsetPlacement.modify port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner Offset arm +OffsetPlacement.of skip:java factory helper; Horizon decodes the offset JSON +RandomlySelectedPlacement.modify port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner RandomlySelected arm +RarityFilter.shouldPlace port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner Rarity arm diff --git a/tools/port_map/classes-07.tsv b/tools/port_map/classes-07.tsv new file mode 100644 index 0000000000..bf84bbb12c --- /dev/null +++ b/tools/port_map/classes-07.tsv @@ -0,0 +1,189 @@ +RepeatingPlacement.modify port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner repeat arm for Count and the noise counts emits the count +SurfaceRelativeThresholdFilter.shouldPlace port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner SurfaceRelative arm: y between heightmap plus min and max +SurfaceWaterDepthFilter.shouldPlace port crates/horizon-world/src/worldgen/placement.rs::PlacementModifier::modify_inner SurfaceWaterDepth arm +WorldPreset skip:data data-pack preset record; bundled world_presets carry the dimensions and generator.rs builds one dimension by name +WorldPresets.fromSettings skip:data names a preset from its dimensions; Horizon loads presets by name +WorldPresets.getNormalOverworld port crates/horizon-world/src/worldgen/generator.rs::OverworldGenerator::new builds the normal preset's overworld directly; no LevelStem object +WorldPresets.Bootstrap skip:data datagen bootstrap that registers the presets +SinglePieceStructure.findGenerationPoint port crates/horizon-world/src/worldgen/structure/start.rs::try_generate DesertPyramid/JungleTemple arm: valid biome, sea-level check, stub on the surface +SinglePieceStructure.generatePieces port crates/horizon-world/src/worldgen/structure/start.rs::try_generate same arm builds the piece at the chunk corner +structure/StructureCheck.canCreateStructure port crates/horizon-world/src/worldgen/structure/start.rs::try_generate no start when the arm returns None +structure/StructureCheck.checkStart missing player: an explorer map (skip_existing_chunks, default on) can point at a structure the player already reached; the reference check is not ported +structure/StructureCheck.checkStructureInfo missing player: requireUnreferenced is ignored, so explorer maps do not skip known structures (see checkStart) +structure/StructureCheck.incrementReference missing player: no reference counts per chunk, so explorer maps do not skip structures already reached +structure/StructureCheck.loadStructures skip:other reads saved starts for the storage cache; Horizon places saved starts with use_saved_start +structure/StructureCheck.onStructureLoad missing player: starts loaded in a chunk do not record references (see incrementReference) +structure/StructureCheck.storeFullResults skip:other cache write for the storage check +structure/StructureCheck.tryLoadFromStorage skip:other chunk NBT scan for the storage cache; Horizon places saved starts with use_saved_start +PiecesContainer.isInsidePiece inline crates/horizon-world/src/worldgen/generator_decoration.rs::World::add_entity start-piece box test that feeds the mob finalize hook +PiecesContainer.load port crates/horizon-world/src/worldgen/generator_structures.rs::StructureState::use_saved_start gap: minor: only jigsaw pool pieces are read back; legacy piece types are not +PiecesContainer.save port crates/horizon-world/src/worldgen/structure/start.rs::StructureStart::save writes the Children list of the start +StructurePieceSerializationContext skip:java record bundling server services for piece NBT; Horizon passes its resources explicitly +StructurePiecesBuilder.moveBelowSeaLevel inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::generate sea-level drop of the pieces; stronghold::generate has the same step +StructurePiecesBuilder.offsetPiecesVertically inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::generate moves every piece by the same dy +AbstractSpreadingStructurePlacement.applyAdditionalChunkRestrictions inline crates/horizon-world/src/worldgen/structure/start.rs::StructureRegistry::structure_chunk frequency reduction step of the chunk test +AbstractSpreadingStructurePlacement.applyInteractionsWithOtherStructures inline crates/horizon-world/src/worldgen/structure/start.rs::StructureRegistry::structure_chunk exclusion zone and avoid checks +AbstractSpreadingStructurePlacement.isStructureChunk port crates/horizon-world/src/worldgen/structure/start.rs::StructureRegistry::is_structure_chunk +AbstractSpreadingStructurePlacement.legacyArbitrarySaltProbabilityReducer port crates/horizon-world/src/worldgen/structure/start.rs::reduce Legacy2 arm +AbstractSpreadingStructurePlacement.legacyPillagerOutpostReducer port crates/horizon-world/src/worldgen/structure/start.rs::reduce Legacy1 arm +AbstractSpreadingStructurePlacement.legacyProbabilityReducerWithDouble port crates/horizon-world/src/worldgen/structure/start.rs::reduce Legacy3 arm +AbstractSpreadingStructurePlacement.probabilityReducer port crates/horizon-world/src/worldgen/structure/start.rs::reduce Default arm +ConcentricRingsStructurePlacement.isPlacementChunk port crates/horizon-world/src/worldgen/structure/start.rs::StructureRegistry::structure_chunk ConcentricRings arm: chunk must be in the set's ring positions +DimensionOriginStructurePlacement missing minor: dimension_origin placement decodes as Unsupported in start.rs, so such a structure never places; no bundled structure file in the repo uses it +RandomSpreadType.evaluate inline crates/horizon-world/src/worldgen/structure/start.rs::StructureRegistry::potential_chunk spread closure: linear, or triangular as the mean of two draws +StructurePlacements skip:data registers placement codecs; Horizon decodes placement type names in start.rs +FeaturePoolElement.getBoundingBox port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::bounding_box Feature arm, zero size +FeaturePoolElement.getShuffledJigsawBlocks port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::shuffled_jigsaws Feature arm: one rollable empty jigsaw +JigsawJunction.equals skip:java value equality +JigsawJunction.hashCode skip:java hashing for Java collections +JigsawJunction.toString skip:debug debug text +LegacySinglePoolElement.getSettings port crates/horizon-world/src/worldgen/structure/jigsaw.rs::place_element Single arm, legacy branch: structure-block ignore is replaced by structure and air ignore after jigsaw replacement +ListPoolElement.getBoundingBox port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::bounding_box List arm, skips empty elements +ListPoolElement.getShuffledJigsawBlocks port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::shuffled_jigsaws List arm: first element only +ListPoolElement.getSize port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::size List arm: per-axis maximum +ListPoolElement.place port crates/horizon-world/src/worldgen/structure/jigsaw.rs::place_element List arm +ListPoolElement.setProjectionOnEachElement port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::decode children take the list's projection while decoding +ListPoolElement.toString skip:debug debug text +StructurePoolElement.getProjection port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::projection +StructurePoolElement.legacy port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::decode legacy_single_pool_element kind +StructurePoolElement.single port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::decode single_pool_element kind +StructureTemplatePool. port crates/horizon-world/src/worldgen/structure/jigsaw.rs::Pools::get builds each Pool from its raw templates +StructureTemplatePool.getMaxSize port crates/horizon-world/src/worldgen/structure/jigsaw.rs::Pool::max_size +StructureTemplatePool.getRandomTemplate port crates/horizon-world/src/worldgen/structure/jigsaw.rs::Pool::random_template +PoolAliasBinding.direct port crates/horizon-world/src/worldgen/structure/start.rs::resolve_aliases minecraft:direct arm +PoolAliasBinding.random port crates/horizon-world/src/worldgen/structure/start.rs::resolve_aliases minecraft:random arm: weighted pick +PoolAliasBindings skip:data datagen bootstrap that registers alias targets as pools +RandomGroupPoolAlias.allTargets skip:data lists alias targets for bootstrap registration +RandomGroupPoolAlias.forEachResolved port crates/horizon-world/src/worldgen/structure/start.rs::resolve_aliases minecraft:random_group arm +BuriedTreasureStructure.generatePieces port crates/horizon-world/src/worldgen/structure/start.rs::try_generate BuriedTreasure arm +EndCityStructure.findGenerationPoint port crates/horizon-world/src/worldgen/structure/start.rs::try_generate EndCity arm: biome column and height checks +EndCityStructure.generatePieces port crates/horizon-world/src/worldgen/structure/start.rs::try_generate EndCity arm calls end_city::pieces +IglooStructure.findGenerationPoint port crates/horizon-world/src/worldgen/structure/start.rs::try_generate Igloo arm +IglooStructure.generatePieces port crates/horizon-world/src/worldgen/structure/start.rs::try_generate Igloo arm +JigsawStructure.findGenerationPoint port crates/horizon-world/src/worldgen/structure/start.rs::try_generate Jigsaw arm: start height, checks, assembly +JigsawStructure.verifyRange skip:java codec validation of the size limit +NetherFortressPieces.findAndCreateBridgePieceFactory port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create Kind dispatch to the bridge piece +NetherFortressPieces.PieceWeight.doPlace inline crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::generate_piece placement check: max is 0 or count below max +NetherFortressPieces.PieceWeight.isValid inline crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::generate_piece entry is dropped once count reaches max +OceanMonumentPieces.FitDoubleXRoom.create inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleX branch of the layout chain +OceanMonumentPieces.FitDoubleXRoom.fits inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleX branch condition +OceanMonumentPieces.FitDoubleXYRoom.create inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleXY branch of the layout chain +OceanMonumentPieces.FitDoubleXYRoom.fits inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleXY branch condition +OceanMonumentPieces.FitDoubleYRoom.create inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleY branch of the layout chain +OceanMonumentPieces.FitDoubleYRoom.fits inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleY branch condition +OceanMonumentPieces.FitDoubleYZRoom.create inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleYZ branch of the layout chain +OceanMonumentPieces.FitDoubleYZRoom.fits inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleYZ branch condition +OceanMonumentPieces.FitDoubleZRoom.create inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleZ branch of the layout chain +OceanMonumentPieces.FitDoubleZRoom.fits inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new DoubleZ branch condition +OceanMonumentPieces.FitSimpleTopRoom.fits inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new SimpleTop branch: no openings +OceanMonumentPieces.OceanMonumentCoreRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room Core arm +OceanMonumentPieces.OceanMonumentDoubleXRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room DoubleX arm +OceanMonumentPieces.OceanMonumentDoubleXYRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room DoubleXY arm +OceanMonumentPieces.OceanMonumentDoubleYRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room DoubleY arm +OceanMonumentPieces.OceanMonumentDoubleYZRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room DoubleYZ arm +OceanMonumentPieces.OceanMonumentDoubleZRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room DoubleZ arm +OceanMonumentPieces.OceanMonumentEntryRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room Entry arm +OceanMonumentPieces.OceanMonumentPenthouse.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room Penthouse arm +OceanMonumentPieces.OceanMonumentSimpleRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room Simple arm +OceanMonumentPieces.OceanMonumentSimpleTopRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room SimpleTop arm +OceanMonumentPieces.OceanMonumentWingRoom.postProcess port crates/horizon-world/src/worldgen/structure/monument.rs::room Wing arms +OceanMonumentPieces.RoomDefinition.countOpenings inline crates/horizon-world/src/worldgen/structure/monument.rs::room count of open sides inside the Simple arm +OceanMonumentPieces.RoomDefinition.findSource port crates/horizon-world/src/worldgen/structure/monument.rs::Graph::find_source +OceanMonumentPieces.RoomDefinition.setConnection port crates/horizon-world/src/worldgen/structure/monument.rs::Graph::connect +OceanMonumentPieces.RoomDefinition.updateOpenings port crates/horizon-world/src/worldgen/structure/monument.rs::Graph::update_openings +OceanRuinStructure.findGenerationPoint port crates/horizon-world/src/worldgen/structure/start.rs::try_generate OceanRuin arm +OceanRuinStructure.generatePieces port crates/horizon-world/src/worldgen/structure/start.rs::try_generate OceanRuin arm +ShipwreckStructure.findGenerationPoint port crates/horizon-world/src/worldgen/structure/start.rs::try_generate Shipwreck arm: world surface when beached +ShipwreckStructure.generatePieces port crates/horizon-world/src/worldgen/structure/start.rs::try_generate Shipwreck arm, including the too-big height +SwampHutStructure.generatePieces port crates/horizon-world/src/worldgen/structure/start.rs::try_generate SwampHut arm +AllOfRuleTest.test port crates/horizon-world/src/worldgen/rule_test.rs::RuleTest::test All arm +AnyOfRuleTest.test port crates/horizon-world/src/worldgen/rule_test.rs::RuleTest::test Any arm +AxisAlignedLinearPosTest.test port crates/horizon-world/src/worldgen/structure/processor.rs::PosRule::test Linear arm with an axis +BlockAgeProcessor.getRandomBlock inline crates/horizon-world/src/worldgen/structure/processor.rs::block_age random pick inside the mossy branch +BlockAgeProcessor.getRandomFacingStairs inline crates/horizon-world/src/worldgen/structure/processor.rs::block_age random stairs with facing, inside the stairs branch +BlockAgeProcessor.maybeReplaceFullStoneBlock inline crates/horizon-world/src/worldgen/structure/processor.rs::block_age full stone branch +BlockAgeProcessor.maybeReplaceSlab inline crates/horizon-world/src/worldgen/structure/processor.rs::block_age slab branch +BlockAgeProcessor.maybeReplaceStairs inline crates/horizon-world/src/worldgen/structure/processor.rs::block_age stairs branch +BlockAgeProcessor.maybeReplaceWall inline crates/horizon-world/src/worldgen/structure/processor.rs::block_age wall branch +BlockAgeProcessor.processBlock port crates/horizon-world/src/worldgen/structure/processor.rs::process_block BlockAge arm calls block_age +BlockIgnoreProcessor.processBlock port crates/horizon-world/src/worldgen/structure/processor.rs::process_block BlockIgnore arm +BlockRotProcessor.processBlock port crates/horizon-world/src/worldgen/structure/processor.rs::process_block BlockRot arm +CappedProcessor.finalizeProcessing inline crates/horizon-world/src/worldgen/structure/processor.rs::process_infos post-pass over the Capped processors, after the block loop +GravityProcessor.processBlock port crates/horizon-world/src/worldgen/structure/processor.rs::process_block Gravity arm +HeightMatchTest.test port crates/horizon-world/src/worldgen/rule_test.rs::RuleTest::test Height arm +LavaSubmergedBlockProcessor.processBlock port crates/horizon-world/src/worldgen/structure/processor.rs::process_block LavaSubmerged arm +LinearPosTest.test port crates/horizon-world/src/worldgen/structure/processor.rs::PosRule::test Linear arm +NotRuleTest.test port crates/horizon-world/src/worldgen/rule_test.rs::RuleTest::test Not arm +ProcessorRule.test port crates/horizon-world/src/worldgen/structure/processor.rs::process_block Rule arm: input, location and position tests +ProtectedBlockProcessor.processBlock port crates/horizon-world/src/worldgen/structure/processor.rs::process_block Protected arm +RandomBlockMatchTest.test port crates/horizon-world/src/worldgen/rule_test.rs::RuleTest::test RandomStates arm +RandomBlockStateMatchTest.test port crates/horizon-world/src/worldgen/rule_test.rs::RuleTest::test RandomState arm +RuleProcessor.processBlock port crates/horizon-world/src/worldgen/structure/processor.rs::process_block Rule arm +RuleTest.either skip:java builder over allOf, anyOf and not; bundled data holds the expanded form +DirectoryTemplateSource skip:data loads templates from a datapack folder; Horizon reads its prepared template bundle (structure.rs StructureResources::template) +ResourceManagerTemplateSource skip:data loads templates through the resource manager; Horizon reads its prepared template bundle +TemplatePathFactory skip:data builds file paths for template loading; Horizon reads a prepared bundle +TemplateSource skip:data template file loading and SNBT reading; Horizon reads a prepared bundle +AppendLoot.apply port crates/horizon-world/src/worldgen/structure/processor.rs::Modifier::apply AppendLoot arm +AppendStatic.apply port crates/horizon-world/src/worldgen/structure/processor.rs::Modifier::apply AppendStatic arm +GradientNoise.wrap port crates/horizon-core/src/noise/mod.rs::wrap Far Lands off +GradientNoise.Gradient.dot port crates/horizon-core/src/noise/mod.rs::dot 3D dot of a gradient +GradientNoise.Gradient.dotXz inline crates/horizon-core/src/noise/perlin.rs::SmearedPerlinNoise::get2 2D dot: get2 samples the 3D corner dots at y 0; no separate dotXz +LegacyFbmInitializer.createForLegacyNetherBiome port crates/horizon-core/src/noise/normal.rs::create_legacy_fbm +NoiseUtils.biasTowardsExtreme skip:other no caller in the vanilla 26.3 sources +NoiseUtils.parityNoiseOctaveConfigString skip:debug debug text +BlockLightEngine.checkNode inline crates/horizon-client/src/light.rs::relight_layer neighbour checks in both passes +BlockLightEngine.getEmission port crates/horizon-client/src/light.rs::LightTables::emission +BlockLightEngine.propagateDecrease port crates/horizon-client/src/light.rs::relight_layer decrease pass, sky false +BlockLightEngine.propagateIncrease port crates/horizon-client/src/light.rs::relight_layer increase pass, sky false +BlockLightEngine.propagateLightSources inline crates/horizon-client/src/light.rs::relight_layer emission seeds for cleared cells +BlockLightSectionStorage.getLightValue port crates/horizon-client/src/light.rs::level +DataLayerStorageMap skip:java cache of light data layers keyed by section +DynamicGraphMinFixedPoint.checkEdge inline crates/horizon-client/src/light.rs::relight_layer edge test inside the passes +DynamicGraphMinFixedPoint.checkNeighbor inline crates/horizon-client/src/light.rs::relight_layer neighbour test inside the passes +DynamicGraphMinFixedPoint.removeFromQueue skip:java queue bookkeeping; the passes never remove queued entries +DynamicGraphMinFixedPoint.removeIf skip:java queue bookkeeping +DynamicGraphMinFixedPoint.runUpdates port crates/horizon-client/src/light.rs::relight_layer runs the decrease and increase passes +LayerLightSectionStorage skip:other per-section light data bookkeeping; Horizon keeps light in chunk sections (light.rs apply_lighting, light_chunk) +LayerLightSectionStorage.SectionState skip:other section state flags of the light storage; no Horizon equivalent needed +LeveledPriorityQueue skip:java priority queue of light levels; relight_layer runs two FIFO passes +SkyLightEngine.addSourcesAbove inline crates/horizon-client/src/light.rs::relight_region column sources loop +SkyLightEngine.checkNode inline crates/horizon-client/src/light.rs::relight_layer neighbour checks in the sky passes +SkyLightEngine.countEmptySectionsBelowIfAtBorder skip:java shortcut over empty sections; Horizon relights a region directly +SkyLightEngine.crossedSectionEdge port crates/horizon-client/src/light.rs::LightChanges::class edge flags of a changed cell +SkyLightEngine.getChunkSources skip:java per-chunk source cache; lowest_sky_source computes each column on demand +SkyLightEngine.getLowestSourceY port crates/horizon-client/src/light.rs::lowest_sky_source +SkyLightEngine.propagateDecrease port crates/horizon-client/src/light.rs::relight_layer decrease pass, sky true +SkyLightEngine.propagateFromEmptySections inline crates/horizon-client/src/light.rs::relight_region sky seeds below the source in each column +SkyLightEngine.propagateIncrease port crates/horizon-client/src/light.rs::relight_layer increase pass, sky true +SkyLightEngine.propagateLightSources inline crates/horizon-client/src/light.rs::relight_layer sky sources inside the cleared area +SkyLightEngine.removeSourcesBelow inline crates/horizon-client/src/light.rs::relight_region column sources loop +SkyLightEngine.setLightEnabled skip:other light is computed when a chunk loads (light_chunk); no per-chunk enable flag +SkyLightSectionStorage skip:other sky data layer bookkeeping per section; Horizon keeps light in chunk sections +SkyLightSectionStorage.SkyDataLayerStorageMap skip:java copy of sky data layers; collection plumbing +SpatialLongSet.InternalMap skip:java hash set internals for section positions +NodeEvaluator.getNode port crates/horizon-client/src/mobs/path.rs::Finder::node +NodeEvaluator.getPathType port crates/horizon-client/src/mobs/path.rs::Finder::path_type +NodeEvaluator.getTargetNodeAt port crates/horizon-client/src/mobs/path.rs::Finder::node target lookup is the same arena lookup +NodeEvaluator.prepare port crates/horizon-client/src/mobs/path.rs::Finder::new per-query state +PathTypeCache.compute port crates/horizon-client/src/mobs/path.rs::Finder::compute_path_type +PathTypeCache.get port crates/horizon-client/src/mobs/path.rs::Finder::cached +PathTypeCache.getOrCompute port crates/horizon-client/src/mobs/path.rs::Finder::cached +PathTypeCache.invalidate skip:java the cache lives for one query, so nothing is invalidated +SwimNodeEvaluator.findAcceptedNode port crates/horizon-client/src/mobs/path.rs::Finder::swim_accepted +SwimNodeEvaluator.getCachedBlockType port crates/horizon-client/src/mobs/path.rs::Finder::cached +SwimNodeEvaluator.getNeighbors port crates/horizon-client/src/mobs/path.rs::Finder::swim_neighbors +SwimNodeEvaluator.getPathTypeOfMob port crates/horizon-client/src/mobs/path.rs::Finder::path_type_of_mob +SwimNodeEvaluator.getStart port crates/horizon-client/src/mobs/path.rs::Finder::start Swim arm +SwimNodeEvaluator.hasMalus inline crates/horizon-client/src/mobs/path.rs::Finder::swim_neighbors diagonal check on both sides +CollectingNeighborUpdater.addAndRun port crates/horizon-world/src/level/update.rs::Sim::add_and_run +CollectingNeighborUpdater.neighborChanged port crates/horizon-world/src/level/update.rs::Sim::neighbor_changed_at Simple update +CollectingNeighborUpdater.runUpdates port crates/horizon-world/src/level/update.rs::Sim::run_updates +CollectingNeighborUpdater.shapeUpdate port crates/horizon-world/src/level/update.rs::Sim::neighbor_shape_changed +CollectingNeighborUpdater.updateNeighborsAtExceptFromFacing port crates/horizon-world/src/level/update.rs::Sim::update_neighbors_at_except +CollectingNeighborUpdater.FullNeighborUpdate.runNext port crates/horizon-world/src/level/update.rs::Update::next Full arm +CollectingNeighborUpdater.MultiNeighborUpdate.forEachUpdatedPos inline crates/horizon-world/src/level/update.rs::Update::next Multi arm steps the index and skips the facing side +CollectingNeighborUpdater.MultiNeighborUpdate.runNext port crates/horizon-world/src/level/update.rs::Update::next Multi arm +CollectingNeighborUpdater.ShapeUpdate.runNext port crates/horizon-world/src/level/update.rs::Update::next Shape arm +CollectingNeighborUpdater.SimpleNeighborUpdate.runNext port crates/horizon-world/src/level/update.rs::Update::next Simple arm +ExperimentalRedstoneUtils.initialOrientation skip:other returns null unless the Redstone Experiments feature flag is on; Horizon has no such flag diff --git a/tools/port_map/classes-08.tsv b/tools/port_map/classes-08.tsv new file mode 100644 index 0000000000..47b6a61972 --- /dev/null +++ b/tools/port_map/classes-08.tsv @@ -0,0 +1,154 @@ +ExperimentalRedstoneWireEvaluator missing minor: only worlds with the Redstone Experiments flag use this evaluator; Horizon has no experimental wire evaluator +InstantNeighborUpdater skip:other never instantiated in vanilla 26.3; Level always uses CollectingNeighborUpdater +NeighborUpdater.executeShapeUpdate port crates/horizon-world/src/level/update.rs::Sim::execute Shape arm: wire skip flag, then update_shape and update_or_destroy +NeighborUpdater.executeUpdate port crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed per-block neighbour change dispatch +NeighborUpdater.updateNeighborsAtExceptFromFacing port crates/horizon-world/src/level/update.rs::Sim::update_neighbors_at_except +Orientation. skip:java constructor of the experimental redstone orientation value +Orientation.generateContext missing minor: builds the orientation lookup of the experimental wire evaluator only +Orientation.generateIndex skip:java index arithmetic for the orientation table +Orientation.toString skip:debug debug string +Orientation.withFrontAdjustSideBias missing minor: experimental redstone orientation transform; only the experimental evaluator uses it +Orientation.withFrontPreserveUp missing minor: experimental redstone orientation transform; only the experimental evaluator uses it +RedstoneWireEvaluator.getIncomingWireSignal port crates/horizon-world/src/level/redstone.rs::Sim::wire_incoming_signal +RedstoneWireEvaluator.getWireSignal port crates/horizon-world/src/level/redstone.rs::Sim::wire_power_of +SavedDataType skip:java equals over the saved-data type record; Horizon keys saved data by name +WanderingTraderData.setSpawnChance inline crates/horizon-client/src/game/wandering_trader.rs::Game::tick_wandering_trader field write of spawn_chance inside the tick; no setter +WanderingTraderData.setSpawnDelay inline crates/horizon-client/src/game/wandering_trader.rs::Game::tick_wandering_trader field write of spawn_delay inside the tick; no setter +MapBanner.fromWorld missing player: using a map on a placed banner adds no banner marker to the map (MapItem.useOn toggle is not ported) +MapBanner.getDecoration missing player: banner markers would show the dye colour icon; maps.rs has no banner decoration types +MapBanner.getId missing minor: key string of a banner marker, only used by the marker feature +MapDecorationTypes skip:data registry of map decoration types; maps.rs keeps its own DECORATION_TYPES table +MapId.addToTooltip missing player: map tooltips show no 'Map #id', 'Locked' or advanced scale lines +MapIndex.getNextMapId port crates/horizon-client/src/game/maps.rs::Game::next_map_id +DerivedLevelData skip:debug crash report category +LevelData skip:debug crash report category +LevelStorageSource.createAccess port crates/horizon-client/src/save.rs::SessionLock::acquire gap: minor: takes the folder's session lock; there is no separate access object +LevelStorageSource.createDefault skip:java factory for the source with its symlink validator +LevelStorageSource.findLevelCandidates inline crates/horizon-client/src/title.rs::list_worlds folders with a readable level.dat +LevelStorageSource.getFileModificationTime inline crates/horizon-client/src/title.rs::list_worlds level.dat mtime, used when LastPlayed is absent +LevelStorageSource.getLevelDataAndDimensions port crates/horizon-client/src/save.rs::Level::read gap: minor: reads level.dat; dimension and world-gen registry setup happen elsewhere +LevelStorageSource.isNewLevelIdAcceptable port crates/horizon-client/src/title.rs::Title::target_folder gap: minor: sanitises the name and picks a free folder instead of rejecting it +LevelStorageSource.levelExists inline crates/horizon-client/src/title.rs::Title::target_folder folder-exists check +LevelStorageSource.loadLevelSummaries port crates/horizon-client/src/title.rs::list_worlds +LevelStorageSource.makeLevelSummary inline crates/horizon-client/src/title.rs::list_worlds per-folder name, version, mode and last played +LevelStorageSource.parseFeatureFlagsFromSummary missing unsure: list_worlds does not read enabled_features, so experimental worlds are not flagged +LevelStorageSource.parseValidator skip:other symlink allow-list (allowed_symlinks.txt); Horizon does not read that file +LevelStorageSource.readDataConfig missing minor: the world's own enabled datapacks and features are not read back from level.dat +LevelSummary.backupStatus missing player: opening a world from an older version gives no 'back up first' prompt; the upgrade runs straight away (world_loading.rs) +LevelSummary.compareTo inline crates/horizon-client/src/title.rs::list_worlds newest first by last played +LevelSummary.createInfo missing unsure: row status text (locked, needs conversion) is not carried in WorldEntry +LevelSummary.getLevelName inline crates/horizon-client/src/title.rs::list_worlds WorldEntry.name +LevelSummary.getWorldVersionName inline crates/horizon-client/src/title.rs::list_worlds WorldEntry.version from Version.Name +LevelSummary.isDisabled missing unsure: list_worlds drops folders whose level.dat does not read instead of showing them disabled +LevelVersion.parse inline crates/horizon-client/src/title.rs::list_worlds reads Version.Name only +PrimaryLevelData.createTag port crates/horizon-client/src/save.rs::Level::write +PrimaryLevelData.setModdedInfo inline crates/horizon-client/src/save.rs::Level::write gap: minor: writes WasModded 0 and the brand list; there is no modded save state +PrimaryLevelData.stringCollectionToTag inline crates/horizon-client/src/save.rs::Level::write ServerBrands and DataPacks lists +PrimaryLevelData.writeLastPlayed inline crates/horizon-client/src/save.rs::Level::write LastPlayed +PrimaryLevelData.writeVersionHistory inline crates/horizon-client/src/save.rs::Level::write version_history +PrimaryLevelData.writeVersionTag inline crates/horizon-client/src/save.rs::Level::write Version compound +ServerLevelData skip:debug crash report category +TagValueInput skip:java NBT read helpers; Horizon reads Tag values directly +TagValueInput.CompoundListWrapper skip:java NBT list wrapper; Horizon reads Tag values directly +TagValueInput.ListWrapper skip:java NBT list wrapper; Horizon reads Tag values directly +TagValueInput.TypedListWrapper skip:java NBT list wrapper; Horizon reads Tag values directly +TagValueOutput skip:java NBT write helpers; Horizon writes Tag values directly +TagValueOutput.ListWrapper skip:java NBT write wrapper +TagValueOutput.TypedListWrapper skip:java NBT write wrapper +WorldData skip:debug crash report category +BuiltInLootTables.register skip:data registers built-in loot table keys; Horizon reads tables from data files +ContainerComponentManipulator missing minor: set_contents and modify_contents, which no vanilla loot table uses +ContainerComponentManipulator.ComponentSlotCollection missing minor: slot helpers of set_contents and modify_contents, which no vanilla loot table uses +FloatRangePredicate.Line.createPredicate missing minor: float range with number-provider bounds; Horizon does not evaluate these bounds +FloatRangePredicate.Line.test missing minor: float range check with number-provider bounds +FloatRangePredicate.Line.validate skip:data validation at datapack load +IntLimit.createLimiter port crates/horizon-world/src/loot.rs::Context::run gap: minor: limit_count arm reads literal min and max only +IntLimit.validate skip:data validation at datapack load +IntRangePredicate.Line.createPredicate inline crates/horizon-world/src/loot.rs::int_bounds gap: minor: literal bounds only; number-provider bounds are not evaluated +IntRangePredicate.Line.test port crates/horizon-world/src/loot.rs::int_bounds gap: minor: literal bounds only +IntRangePredicate.Line.validate skip:data validation at datapack load +LootContextArg.ArgCodecBuilder skip:java codec builders for context arguments +LootContextArg.Getter.get skip:java codec getter for a context argument +LootDataType skip:data validation of loot data at datapack load +LootParams.addDynamicDrops port crates/horizon-client/src/game/spill.rs::Pending::dynamic gap: minor: decorated pot sherds only; shulker contents are attached by Pending::attach +LootParams.Builder skip:java builder for loot params +Validatable skip:data validation at datapack load +ValidationContext skip:data validation at datapack load +ValidationContextSource skip:data validation at datapack load +AlternativesEntry.alternatives skip:java builder +AlternativesEntry.compose port crates/horizon-world/src/loot.rs::Context::expand alternatives arm +AlternativesEntry.validate skip:data validation at datapack load +ComposableEntryContainer skip:java container combinators; Horizon expands entries in Context::expand +CompositeEntryBase.createCodec skip:java codec +CompositeEntryBase.validate skip:data validation at datapack load +DynamicLoot.dynamicEntry skip:java builder +EntryGroup.compose port crates/horizon-world/src/loot.rs::Context::expand group arm +ExpandableContainerBase.expandRaw inline crates/horizon-world/src/loot.rs::Context::expand expansion of raw entries +ExpandableContainerBase.expandableFields skip:java codec fields +NestedLootTable.addExpandedEntries inline crates/horizon-world/src/loot.rs::Context::expand loot_table arm +NestedLootTable.lootTableReference skip:java codec factory +SequentialEntry.compose port crates/horizon-world/src/loot.rs::Context::expand sequence arm +SlotLoot.createItemStack missing minor: slot entries (copies of container slot items); no vanilla loot table uses one +UniformContainerBase.uniformFields skip:java codec fields +UniformContainerBase.DummyBuilder.build skip:java builder +UniformContainerBase.EntryBase.getWeight inline crates/horizon-world/src/loot.rs::Context::add_random_item weight with luck, in add_random_item +ApplyBonusCount.addBonusBinomialDistributionCount skip:java builder +ApplyBonusCount.addUniformBonusCount skip:java builder +ApplyBonusCount.run port crates/horizon-items/src/items.rs::LootContext::modifiers apply_bonus arm +ApplyBonusCount.BinomialWithBonusCount.calculateNewCount inline crates/horizon-items/src/items.rs::LootContext::modifiers binomial_with_bonus_count formula +ApplyBonusCount.OreDrops.calculateNewCount inline crates/horizon-items/src/items.rs::LootContext::modifiers ore_drops formula +ApplyBonusCount.UniformBonusCount.calculateNewCount inline crates/horizon-items/src/items.rs::LootContext::modifiers uniform_bonus_count formula +ApplyExplosionDecay.run port crates/horizon-items/src/items.rs::LootContext::modifiers explosion_decay arm +CopyBlockState.run port crates/horizon-items/src/items.rs::LootContext::modifiers copy_state arm; copies the listed properties +CopyBlockState.Builder skip:java builder (build, copy) +CopyComponentsFunction.run port crates/horizon-client/src/game/spill.rs::Pending::record gap: minor: copies only what spill.rs keeps for block entities (shulker, banner, names, heads, bees) +CopyComponentsFunction.Builder skip:java builder (build, exclude, include) +CopyCustomDataFunction.run missing minor: no vanilla loot table or item modifier uses copy_custom_data +CopyCustomDataFunction.copyData skip:java builder factory +CopyCustomDataFunction.Builder.copy skip:java builder +CopyCustomDataFunction.CopyOperation.apply missing minor: part of copy_custom_data, which is not ported +CopyNameFunction.run missing minor: no vanilla loot table uses copy_name +EnchantedCountIncreaseFunction.run port crates/horizon-world/src/loot.rs::Context::run enchanted_count_increase arm +EnchantedCountIncreaseFunction.Builder.build skip:java builder +FillPlayerHead.run missing minor: no vanilla loot table uses fill_player_head +FilteredFunction.run port crates/horizon-world/src/loot.rs::Context::run filtered arm +FilteredFunction.Builder.build skip:java builder +FunctionUserBuilder skip:java builder interface (apply, buildFunction) +LimitCount.run port crates/horizon-world/src/loot.rs::Context::run gap: minor: limit_count arm reads literal min and max only +LootItemConditionalFunction.apply port crates/horizon-world/src/loot.rs::Context::modify condition check, then run +LootItemConditionalFunction.commonFields skip:java codec fields +LootItemFunction.decorate port crates/horizon-world/src/loot.rs::Context::modify function applied to a drop +ModifyContainerContents.run missing minor: no vanilla loot table uses modify_contents +SequenceFunction.compose port crates/horizon-world/src/loot.rs::Context::run sequence arm +SetAttributesFunction missing minor: set_attributes is not ported; no vanilla loot table uses it +SetAttributesFunction.ModifierBuilder.build skip:java builder +SetBannerPatternFunction.run missing minor: banner pattern layers from loot are not set; no vanilla loot table uses set_banner_pattern +SetBannerPatternFunction.Builder skip:java builder +SetBookCoverFunction.apply missing minor: no vanilla loot table uses set_book_cover +SetComponentsFunction.run port crates/horizon-client/src/game/commands/items.rs::Game::apply_item_modifier gap: player: only /item modify runs it; loot tables (trial chamber copper trim) do not apply the components +SetComponentsFunction.setComponent skip:java builder factory +SetContainerContents.run missing minor: no vanilla loot table uses set_contents +SetContainerContents.Builder skip:java builder +SetContainerLootTable missing minor: no vanilla loot table uses set_loot_table +SetCustomDataFunction.run port crates/horizon-client/src/game/commands/items.rs::Game::apply_item_modifier gap: minor: only /item modify runs it; no loot table uses set_custom_data +SetCustomModelDataFunction.apply missing minor: custom_model_data changes from loot; no vanilla loot table uses it +SetCustomModelDataFunction.run missing minor: custom_model_data changes from loot; no vanilla loot table uses it +SetCustomModelDataFunction.validate skip:data validation at datapack load +SetEnchantmentsFunction.run port crates/horizon-world/src/loot.rs::Context::run set_enchantments arm +SetEnchantmentsFunction.validate skip:data validation at datapack load +SetEnchantmentsFunction.Builder skip:java builder +SetFireworkExplosionFunction.apply missing minor: no vanilla loot table uses set_firework_explosion +SetFireworksFunction.apply missing minor: no vanilla loot table uses set_fireworks +SetInstrumentFunction.run port crates/horizon-world/src/loot.rs::Context::run gap: player: only draws the random option; the goat horn's instrument component is never set (pillager outpost horns) +SetItemCountFunction.run port crates/horizon-world/src/loot.rs::Context::run set_count arm +SetItemCountFunction.setCount skip:java builder factory +SetItemDamageFunction.run port crates/horizon-world/src/loot.rs::Context::run set_damage arm +SetItemDamageFunction.setDamage skip:java builder factory +SetLoreFunction.run port crates/horizon-client/src/game/commands/items.rs::Game::apply_item_modifier gap: minor: only /item modify runs set_lore; no loot table uses it +SetLoreFunction.updateLore inline crates/horizon-client/src/game/commands/items.rs::Game::apply_item_modifier set_lore arm, list_operation +SetLoreFunction.getReferencedContextParams skip:data validation of referenced context params +SetLoreFunction.Builder skip:java builder +SetOminousBottleAmplifierFunction.run port crates/horizon-world/src/loot.rs::Context::run set_ominous_bottle_amplifier arm +SetRandomDyesFunction.run port crates/horizon-client/src/merchant.rs::Trades::apply_modifier set_random_dyes arm +SetRandomPotionFunction.run port crates/horizon-client/src/merchant.rs::Trades::apply_modifier set_random_potion arm +SetStewEffectFunction.run port crates/horizon-world/src/loot.rs::Context::run set_stew_effect arm diff --git a/tools/port_map/classes-09.tsv b/tools/port_map/classes-09.tsv new file mode 100644 index 0000000000..87ee323875 --- /dev/null +++ b/tools/port_map/classes-09.tsv @@ -0,0 +1,156 @@ +SetStewEffectFunction.Builder skip:java builder for the stew effect list; Horizon keeps the effects in loot.rs Context::run (set_stew_effect) +SetWritableBookPagesFunction missing minor: no vanilla loot table uses set_writable_book_pages; Horizon's loot run has no arm for it +SetWrittenBookPagesFunction missing minor: no vanilla loot table uses set_written_book_pages; Horizon's loot run has no arm for it +SmeltItemFunction.run port crates/horizon-world/src/loot.rs::Context::run furnace_smelt arm: smelting recipe, use_input_count and stack cap +SmeltItemFunction.smelted skip:java static builder factory +ToggleTooltips missing minor: no vanilla loot table uses toggle_tooltips; Horizon's loot run has no arm for it +LootContextParamSets skip:data loot parameter set registry and validation; Horizon reads its loot tables +AllOfCondition.allOf skip:java static builder +AllOfCondition.combine inline crates/horizon-items/src/items.rs::LootContext::condition all_of arm: every term must hold +AnyOfCondition.combine inline crates/horizon-items/src/items.rs::LootContext::condition any_of arm: one term must hold +AnyOfCondition.Builder skip:java builder (or) +BonusLevelTableCondition.bonusLevelFlatChance skip:java static builder +BonusLevelTableCondition.test port crates/horizon-items/src/items.rs::LootContext::condition table_bonus arm: chance by enchantment level, capped at the last entry +CompositeLootItemCondition skip:java codec only +CompositeLootItemCondition.Builder skip:java builder (addTerm) +ConditionUserBuilder skip:java builder interface for loot conditions +DamageSourceCondition.test port crates/horizon-world/src/loot.rs::Context::condition damage_source_properties arm (mob death loot) +EnchantmentActiveCheck.test inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed the soul speed modifiers switch on and off here; the ENCHANTMENT_ACTIVE flag itself is not read as a condition +EntityHasScoreCondition.hasScore missing minor: no vanilla loot table uses entity_scores +EntityHasScoreCondition.test missing minor: no vanilla loot table uses entity_scores +EntityHasScoreCondition.validate skip:data datapack validation +EnvironmentAttributeCheck.createCodec skip:java codec +EnvironmentAttributeCheck.test missing minor: no vanilla data uses environment_attribute_check +ExplosionCondition.test port crates/horizon-items/src/items.rs::LootContext::condition survives_explosion arm: 1 / radius under an explosion (block loot; only block tables use it) +FloatValueCheck.validate skip:data datapack validation +IntValueCheck.validate skip:data datapack validation +InvertedLootItemCondition.invert skip:java static builder +InvertedLootItemCondition.test port crates/horizon-world/src/loot.rs::Context::condition inverted arm +LocationCheck.checkLocation skip:java static builder +LocationCheck.test port crates/horizon-world/src/loot.rs::Context::condition gap: minor: biome lists only; block predicate and offsetY are not read here, and block loot (items.rs) passes location_check unconditionally +LootItemCondition.Builder skip:java builders (and, or) +LootItemEntityPropertyCondition.hasProperties skip:java static builder +LootItemEntityPropertyCondition.test port crates/horizon-world/src/loot.rs::Context::condition entity_properties arm (mob loot); the block-loot arm covers only a bare 'this' check +LootItemRandomChanceCondition.test port crates/horizon-world/src/loot.rs::Context::condition random_chance arm +LootItemRandomChanceWithEnchantedBonusCondition.randomChanceAndLootingBoost skip:java static builder +LootItemRandomChanceWithEnchantedBonusCondition.test port crates/horizon-world/src/loot.rs::Context::condition random_chance_with_enchanted_bonus arm: living attacker's enchantment level +LootPredicates skip:data registry bootstrap; loot conditions are matched in loot.rs and items.rs +MatchBlock.blockMatches skip:java static builder +MatchBlock.getReferencedContextParams skip:data validation metadata for context params +MatchBlock.test port crates/horizon-items/src/items.rs::LootContext::condition match_block arm: block id, state and property ranges; gap: minor: the block-entity part of the predicate is not read +MatchTool.test port crates/horizon-items/src/items.rs::LootContext::condition match_tool arm; gap: minor: item ids, tags and enchantment levels only, other ItemPredicate parts are not read +TimeCheck missing minor: time_check is used by no vanilla data file (datapacks only) +WeatherCheck.test port crates/horizon-client/src/game/trident.rs::Game::channel_lightning channeling's thunder check (thundering only); gap: minor: raining is not read, channeling only uses thundering +NbtProviders skip:data registry bootstrap +AggregateProvider skip:java codec only +BinaryProvider.mapCodec skip:java codec +BinaryProvider.validate skip:data datapack validation +ConditionalProvider.selectValue port crates/horizon-client/src/game/commands/compute.rs::Game::provider_select conditional arm: on_true or on_false by the condition +DispatcherProvider.mapCodec skip:java codec +DispatcherProvider.selectValue port crates/horizon-client/src/game/commands/compute.rs::Game::provider_select number_dispatcher arm: first case whose condition holds, else default +DispatcherProvider.validate skip:data datapack validation +DispatcherProvider.Case.codec skip:java codec +DispatcherProvider.Case.validate skip:data datapack validation +DistributionProvider.mapCodec skip:java codec +DistributionProvider.validate skip:data datapack validation +EnvironmentAttributeProvider skip:java codec only +PowerProvider.mapCodec skip:java codec +PowerProvider.validate skip:data datapack validation +RangeProvider.mapCodec skip:java codec +RangeProvider.validate skip:data datapack validation +StoredNumberAccess missing minor: storage-backed numbers; no vanilla data uses storage providers +UnaryProvider skip:java codec only +floats/Average.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float avg arm +floats/ContextFloatProviders skip:data registry bootstrap +floats/Difference.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float sub arm +floats/EnchantmentLevelProvider.getFloatUnsafe port crates/horizon-items/src/enchant.rs::Enchantments::condition random_chance arm: chance at the enchantment level; gap: minor: only the random_chance use is read +floats/EnvironmentAttributeValue missing minor: no vanilla data uses environment_attribute providers +floats/Length.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float length arm +floats/Maximum.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float max arm +floats/Minimum.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float min arm +floats/Modulus.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float mod arm +floats/Product.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float mul arm +floats/Quotient.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float div arm +ResolvableFloat.getFromItem port crates/horizon-client/src/providers.rs::Providers::speed furnace fuel speed multiplier (COOKING_FUEL); gap: minor: only the furnace fuel component is read +ResolvableFloat.wrap skip:java codec helper +floats/StorageValue missing minor: no vanilla data uses storage providers +floats/Sum.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float add arm +floats/Truncate.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float truncate arm +floats/UniformGenerator.getFloatUnsafe port crates/horizon-world/src/loot.rs::Context::float uniform arm: min + random * (max - min) +floats/WeightedListValue.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_select weighted list pick (the default branch) +ints/Average.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int avg arm +ints/BinomialDistributionGenerator.getIntUnsafe port crates/horizon-world/src/loot.rs::Context::int binomial arm +ints/BinomialDistributionGenerator.validate skip:data datapack validation +ints/ContextIntProviders.bootstrap skip:data registry bootstrap +ints/ContextIntProviders.compostable port crates/horizon-client/src/game/composter.rs::Game::compostable the item's compostable layer provider; evaluated when the composter takes it (Game::composter_add) +ints/ContextIntProviders.cooking port crates/horizon-client/src/providers.rs::Providers::burn_time fuel burn times (the cooking/time_* providers) +ints/Difference.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int sub arm +ints/EnvironmentAttributeValue missing minor: no vanilla data uses environment_attribute providers +ints/FloorModulus.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int floor_mod arm +ints/FloorQuotient.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int floor_div arm +ints/Maximum.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int max arm +ints/Minimum.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int min arm +ints/Modulus.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int mod arm +ints/Product.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int mul arm +ints/Quotient.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int div arm +ResolvableInt.getFromItem port crates/horizon-client/src/providers.rs::Providers::burn_time furnace fuel burn time (COOKING_FUEL); gap: minor: only the furnace fuel component is read +ResolvableInt.wrap skip:java codec helper +ints/ScoreboardValue.getIntUnsafe missing minor: no vanilla data uses score providers +ints/ScoreboardValue.getScoreInfo missing minor: no vanilla data uses score providers +ints/ScoreboardValue.validate skip:data datapack validation +ints/StorageValue missing minor: no vanilla data uses storage providers +ints/Sum.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int add arm +ints/UniformGenerator.getIntUnsafe port crates/horizon-world/src/loot.rs::Context::int uniform arm: inclusive random int between min and max +ints/WeightedListValue.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_select weighted list pick (the default branch) +ScoreboardNameProviders skip:data registry bootstrap +FunctionCallback.handle port crates/horizon-client/src/game/functions.rs::Game::run_function_by_id runs the function by id when its time comes +FunctionTagCallback.handle inline crates/horizon-client/src/game/functions.rs::Game::tick_functions tag branch: runs every function in the tag +TimerQueue.createComparator inline crates/horizon-client/src/game/functions.rs::Game::tick_functions ordering by (time, seq) in the due-event search +TimerQueue.pack port crates/horizon-client/src/game/functions.rs::Game::save_schedule writes data/minecraft/scheduled_events.dat sorted by (time, seq) +TimerQueue.remove port crates/horizon-client/src/game/functions.rs::Game::cmd_schedule schedule clear arm +TimerQueue.schedule port crates/horizon-client/src/game/functions.rs::Game::cmd_schedule schedule function arm +TimerQueue.tick port crates/horizon-client/src/game/functions.rs::Game::tick_functions runs due events in (time, seq) order, then the tick tag +TimerQueue.Event.Packed.codec skip:java codec +TimerQueue.Packed.codec skip:java codec +ContentValidationException.getMessage skip:java exception message getter +DirectoryValidator.validateDirectory skip:server server check of symlinks in world and resource folders (allowed_symlinks.txt) +DirectoryValidator.validateSymlink skip:server server check of symlinks in world and resource folders (allowed_symlinks.txt) +PathAllowList.getForFileSystem skip:server server file allow-list +PathAllowList.readPlain skip:server server file allow-list +PathAllowList.ConfigEntry.parse skip:server server file allow-list +BlockHitResult.hitBorder missing minor: BlockHit has no world-border flag; vanilla projectiles check it on a border hit +BlockHitResult.withDirection skip:java copy helper (Rust struct update) +BlockHitResult.withPosition skip:java copy helper (Rust struct update) +HitResult.distanceTo missing minor: only the fishing bobber's block-hit bounce uses it; Horizon's bobber does not bounce back on a block hit +Vec2.add skip:other 2D vector maths; Horizon has no Vec2 type and keeps 2D maths in its callers +Vec2.distanceToSqr skip:other 2D vector maths; Horizon has no Vec2 type and keeps 2D maths in its callers +Vec2.dot skip:other 2D vector maths; Horizon has no Vec2 type and keeps 2D maths in its callers +Vec2.length skip:other 2D vector maths; Horizon has no Vec2 type and keeps 2D maths in its callers +Vec2.lengthSquared skip:other 2D vector maths; Horizon has no Vec2 type and keeps 2D maths in its callers +Vec2.normalized skip:other 2D vector maths; Horizon has no Vec2 type and keeps 2D maths in its callers +Vec2.rotate skip:other 2D vector maths; Horizon has no Vec2 type and keeps 2D maths in its callers +Vec2.scale skip:other 2D vector maths; Horizon has no Vec2 type and keeps 2D maths in its callers +Vec2.equals skip:java Object equality; Rust derives it +Vec2.hashCode skip:java Object hashing; Rust derives it +ArrayVoxelShape.getCoords port crates/horizon-core/src/geometry/shape.rs::VoxelShape::coordinates +BitSetDiscreteVoxelShape. port crates/horizon-core/src/geometry/shape.rs::VoxelShape::from_cube gap: minor: takes the occupancy from the caller; there is no empty-grid constructor +BitSetDiscreteVoxelShape.clearZStrip port crates/horizon-core/src/geometry/shape.rs::clear_strip +BitSetDiscreteVoxelShape.fillUpdateBounds inline crates/horizon-core/src/geometry/shape.rs::VoxelShape::build bounds computed from the filled grid +BitSetDiscreteVoxelShape.forAllBoxes port crates/horizon-core/src/geometry/shape.rs::VoxelShape::decompose +BitSetDiscreteVoxelShape.getIndex port crates/horizon-core/src/geometry/shape.rs::index +BitSetDiscreteVoxelShape.isInterior skip:other no vanilla caller outside its own class +BitSetDiscreteVoxelShape.isXZRectangleFull inline crates/horizon-core/src/geometry/shape.rs::VoxelShape::decompose the greedy x-z merge inside decompose +BitSetDiscreteVoxelShape.isZStripFull port crates/horizon-core/src/geometry/shape.rs::strip_full +BitSetDiscreteVoxelShape.join port crates/horizon-core/src/geometry/shape/create.rs::VoxelShape::join +BitSetDiscreteVoxelShape.withFilledBounds port crates/horizon-core/src/geometry/shape.rs::VoxelShape::with_discrete_bounds +CubeVoxelShape.findIndex port crates/horizon-core/src/geometry/shape.rs::VoxelShape::find_index +DiscreteCubeMerger.forMergedIndexes port crates/horizon-core/src/geometry/shape/join.rs::merge +IdenticalMerger.forMergedIndexes port crates/horizon-core/src/geometry/shape/join.rs::merge +IndirectMerger. skip:java list wrapper constructor +IndirectMerger.forMergedIndexes port crates/horizon-core/src/geometry/shape/join.rs::merge +IndirectMerger.getList skip:java list accessor +MinecartCollisionContext missing player: vanilla lets a rail cart pass through the block under its rail and the slope block of an ascending rail (MinecartCollisionContext); minecarts.rs move_cart has no such exclusion, so carts may bump on those blocks (read from code, not checked in game) +NonOverlappingMerger.forMergedIndexes port crates/horizon-core/src/geometry/shape/join.rs::disjoint +NonOverlappingMerger.forNonSwappedIndexes port crates/horizon-core/src/geometry/shape/join.rs::disjoint +NonOverlappingMerger.getDouble skip:java list accessor +PositionCollisionContext.isAbove port crates/horizon-world/src/collision_context.rs::CollisionContext::is_above diff --git a/tools/port_map/classes-10.tsv b/tools/port_map/classes-10.tsv new file mode 100644 index 0000000000..0fb9cf7a5a --- /dev/null +++ b/tools/port_map/classes-10.tsv @@ -0,0 +1,18 @@ +SubShape.clampToShape inline crates/horizon-core/src/geometry/shape/face.rs::VoxelShape::calculate_face clamps the parent's discrete extrema to the slice (face.rs:43-46) +SubShape.fill skip:other write path on a read-only slice; vanilla calls fill only on fresh BitSetDiscreteVoxelShape (Shapes.java:25, DiscreteVoxelShape.java:43), never on a SubShape; Horizon shapes are immutable +SubShape.isFull inline crates/horizon-core/src/geometry/shape/face.rs::VoxelShape::calculate_face slice occupancy is copied from the parent plane when the face is built (face.rs:61-68) +PlayerScores.getOrCreate port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::score_mut creates the score and tracks the holder (holder_order.insert) +PlayerScores.listScores port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::holder_scores one holder's (objective, value) pairs +ScoreboardSaveData.setData inline crates/horizon-client/src/game/scoreboard.rs::Game::save_scoreboard gap: minor: no in-memory save-data copy and no equality/dirty check; the file is rewritten from board.pack() on each save +LevelChunkTicks.count port crates/horizon-core/src/ticks/mod.rs::ChunkTicks::count +LevelChunkTicks.pack port crates/horizon-core/src/ticks/mod.rs::ChunkTicks::pack +LevelChunkTicks.poll port crates/horizon-core/src/ticks/mod.rs::ChunkTicks::poll +LevelChunkTicks.removeIf port crates/horizon-core/src/ticks/mod.rs::ChunkTicks::remove_if +LevelChunkTicks.scheduleUnchecked inline crates/horizon-core/src/ticks/mod.rs::ChunkTicks::schedule gap: minor: push only, no duplicate check (the unpack path is in ChunkTicks::unpack); the onTickAdded deadline update is in LevelTicks::schedule +LevelChunkTicks.unpack port crates/horizon-core/src/ticks/mod.rs::ChunkTicks::unpack gap: minor: the deadline refresh is in LevelTicks::unpack_chunk +AttributeTrack.bakeSampler inline crates/horizon-client/src/environment.rs::Environment::load_dimension gap: minor: builds the day timeline's tracks only (keyframes, easing, multiply); the clock holder is implicit +AttributeTrack.createCodec skip:java codec; Horizon parses the timeline JSON in Environment::load_dimension and patrol.rs GameplayAttributes::load +AttributeTrack.createCodecWithModifier skip:java codec; same parsing as createCodec +AttributeTrack.validatePeriod skip:data load-time check of keyframes against period_ticks; Horizon reads the vanilla timeline files without it +AttributeTrackSampler.applyTimeBased port crates/horizon-client/src/environment.rs::Environment::value gap: minor: multiply and override only; star_brightness 'maximum' reads as override (same value at base 0); booleans are in patrol.rs BoolTrack or hardcoded per consumer; no per-tick cache +Timelines.bootstrap skip:data registers the timelines in code; Horizon reads data/minecraft/timeline/*.json, and the few day-cycle numbers it needs are in level/timeline.rs diff --git a/tools/port_map/client-001.tsv b/tools/port_map/client-001.tsv new file mode 100644 index 0000000000..e938a4dfe9 --- /dev/null +++ b/tools/port_map/client-001.tsv @@ -0,0 +1,117 @@ +AccountProfileKeyPairManager.prepareKeyPair skip:service Mojang profile key pair for chat signing; Horizon fetches none +AccountProfileKeyPairManager.shouldRefreshKeyPair skip:service profile key refresh timer; no chat signing key pair +AccountProfileKeyPairManager.readOrFetchProfileKeyPair skip:service profile key pair fetch; no chat signing +AccountProfileKeyPairManager.readProfileKeyPair skip:service reads the cached profile key pair file; no chat signing +AccountProfileKeyPairManager.writeProfileKeyPair skip:service writes the cached profile key pair file; no chat signing +AccountProfileKeyPairManager.fetchProfileKeyPair skip:service fetches a profile key pair from the Mojang API +AccountProfileKeyPairManager.parsePublicKey skip:service parses a profile public key from the Mojang API +CacheSlot.compute skip:java memo slot for a computed value; Horizon has no per-context cache slot +ChunkBatchSizeCalculator.onBatchFinished missing minor: Horizon answers every ChunkBatchFinished with a fixed 64 chunks per tick (remote.rs), so the server's batch pace is never tuned to batch timing +ClientAdvancements.setSelectedTab port crates/horizon-client/src/game/advancements_screen.rs::Game::advancements_mouse gap: minor: the tab change is local; the openedTab packet to the server is not sent +ClientAdvancements.setListener skip:ui listener plumbing for the advancements screen; Horizon's screen reads its state each frame +ClientAdvancements.get port crates/horizon-client/src/advancements.rs::Tree::get returns the advancement by id; holder wrapper is not needed +ClientChunkCache.isValidChunk skip:other chunks are stored by position, so there is no ring slot to check +ClientChunkCache.drop port crates/horizon-client/src/streaming.rs::Streaming::forget gap: minor: no ChunkEvent unload hook; block entities and renderer columns are cleared in Game::handle_packet +ClientChunkCache.getChunk skip:java lookup of a loaded chunk; Horizon reads world.chunks directly +ClientChunkCache.replaceBiomes missing minor: ChunksBiomes (id 13) has no arm in Game::handle_packet, so server biome changes (e.g. /fillbiome) do not reach the client until the chunk is resent +ClientChunkCache.flipUpdateTrackingSets skip:other double-buffered change lists for LevelRenderer; Horizon hands changed columns to the renderer directly +ClientChunkCache.Storage.getIndex skip:java ring-buffer index into the view range; Horizon keys chunks by position +ClientChunkCache.Storage.replace port crates/horizon-client/src/game/remote_chunks.rs::Game::remote_chunk gap: minor: old chunk unload is in Streaming::forget, not here +ClientChunkCache.Storage.drop port crates/horizon-client/src/streaming.rs::Streaming::forget forgets the chunk at the index +ClientChunkCache.Storage.onSectionEmptinessChanged skip:java empty-section sets for the renderer's culling; Horizon's mesher works from block data +ClientChunkCache.Storage.onChunkRemoved skip:java empty-section bookkeeping when a chunk leaves; see Storage.onSectionEmptinessChanged +ClientChunkCache.Storage.onChunkAdded skip:java empty-section bookkeeping when a chunk arrives; see Storage.onSectionEmptinessChanged +ClientChunkCache.Storage.refreshEmptySections skip:java empty-section bookkeeping; see Storage.onSectionEmptinessChanged +ClientChunkCache.Storage.markSectionEmpty skip:java empty-section set update; see Storage.onSectionEmptinessChanged +ClientChunkCache.Storage.markSectionNotEmpty skip:java empty-section set update; see Storage.onSectionEmptinessChanged +ClientChunkCache.Storage.dumpChunks skip:debug writes a chunk list file for debugging +ClientCommonPacketListenerImpl.onPacketError missing minor: Horizon logs an undecodable packet and carries on (Event::DecodeError) instead of disconnecting with a report +ClientCommonPacketListenerImpl.createDisconnectionInfo skip:debug builds the disconnect crash report and its file +ClientCommonPacketListenerImpl.storeDisconnectionReport skip:debug writes the disconnect report file under debug/ +ClientCommonPacketListenerImpl.shouldHandleMessage inline crates/horizon-client/src/net.rs::receive_play minor: receive_play returns at Transfer, so nothing else is read after a transfer starts +ClientCommonPacketListenerImpl.handleKeepAlive port crates/horizon-client/src/net.rs::receive_play gap: minor: the keep-alive reply is sent at once; vanilla defers it while the game is frozen at poll events +ClientCommonPacketListenerImpl.handleCustomPayload port crates/horizon-client/src/net.rs::receive_brand gap: minor: reads the brand only; other custom payloads go to the game and are ignored +ClientCommonPacketListenerImpl.handleResourcePackPush port crates/horizon-client/src/game/server_packs.rs::Game::handle_pack_push +ClientCommonPacketListenerImpl.handleResourcePackPop port crates/horizon-client/src/game/server_packs.rs::Game::handle_pack_pop +ClientCommonPacketListenerImpl.handlePostEffects missing player: a server's screen post effect (ClientboundPostEffectsPacket, set by /posteffect and on join) is not drawn on remote servers; Horizon's /posteffect only works in its own game +ClientCommonPacketListenerImpl.preparePackPrompt port crates/horizon-client/src/game/server_packs.rs::PackPrompt::texts builds the server prompt line from the header and the server's prompt +ClientCommonPacketListenerImpl.handleStoreCookie port crates/horizon-client/src/net.rs::receive_play stores the cookie for a later transfer or cookie request +ClientCommonPacketListenerImpl.handleCustomReportDetails skip:debug extra lines for crash reports only +ClientCommonPacketListenerImpl.showDialog port crates/horizon-client/src/game/dialogs.rs::Game::show_play_dialog shows a direct or registry dialog +ClientCommonPacketListenerImpl.clearDialog port crates/horizon-client/src/game/dialogs.rs::Game::clear_dialog closes the dialog screen +ClientCommonPacketListenerImpl.sendDeferredPackets missing minor: no deferred-packet queue; keep-alive and other packets are sent at once +ClientCommonPacketListenerImpl.onDisconnect port crates/horizon-client/src/game/remote.rs::Game::leave_server shows the disconnect reason on the multiplayer screen; no telemetry +ClientCommonPacketListenerImpl.fillListenerSpecificCrashDetails skip:debug crash report details only +ClientCommonPacketListenerImpl.createDisconnectScreen port crates/horizon-client/src/game/remote.rs::Game::leave_server disconnected view with the reason (Screen::Disconnected) +ClientCommonPacketListenerImpl.sendWhen missing minor: no deferred sends; packets go out at once (see handleKeepAlive) +ClientCommonPacketListenerImpl.addOrUpdatePackPrompt port crates/horizon-client/src/game/server_packs.rs::Game::handle_pack_push one prompt holds every pack pushed before it is answered +ClientCommonPacketListenerImpl.PackConfirmScreen. port crates/horizon-client/src/game/server_packs.rs::Game::handle_pack_push creates the prompt with its pending requests +ClientCommonPacketListenerImpl.PackConfirmScreen.update port crates/horizon-client/src/game/server_packs.rs::Game::handle_pack_push adds the new request to the open prompt +ClientConfigurationPacketListenerImpl.handleRegistryData port crates/horizon-client/src/net.rs::configure registry entries are collected in configuration +ClientConfigurationPacketListenerImpl.handleUpdateTags missing minor: UpdateTags is not read in configure (net.rs); Horizon uses its built-in tags, which match vanilla unless a datapack changes them +ClientConfigurationPacketListenerImpl.handleSelectKnownPacks port crates/horizon-client/src/net.rs::configure gap: minor: always answers with no known packs, so the server sends every registry entry +ClientConfigurationPacketListenerImpl.runWithResources skip:data resource loading for known packs; Horizon reads its data from tables +ClientConfigurationPacketListenerImpl.handleCodeOfConduct port crates/horizon-client/src/net.rs::configure gap: player: accepts the server's code of conduct at once; the player never sees its text or can decline +ClientConfigurationPacketListenerImpl.handleConfigurationFinished port crates/horizon-client/src/net.rs::configure gap: minor: no registry filter for an integrated server (see filterRegistries) +ClientConfigurationPacketListenerImpl.filterRegistries skip:data Horizon's registries come from its own data; nothing to filter +ClientDebugSubscriber.requestedSubscriptions missing minor: Horizon never requests debug subscriptions; vanilla asks for server tick time while the F3+2 chart is on +ClientDebugSubscriber.tick missing minor: no subscriptions to purge +ClientDebugSubscriber.onSubscriptionsChanged missing minor: no ServerboundDebugSubscriptionRequest is sent +ClientDebugSubscriber.initializeSubscriptions skip:java creates empty value maps per subscription +ClientDebugSubscriber.getValueMap skip:java map accessor for debug values +ClientDebugSubscriber.getValue skip:java value accessor for debug values +ClientDebugSubscriber.pushEvent missing minor: server debug events are dropped +ClientDebugSubscriber.updateMap missing minor: server debug updates are dropped +ClientDebugSubscriber.forEachValue skip:java iterates debug values +ClientDebugSubscriber.dropChunk missing minor: no server debug values to drop with a chunk +ClientDebugSubscriber.dropEntity missing minor: no server debug values to drop with an entity +ClientDebugSubscriber.ValueMap.getValue skip:java value accessor +ClientDebugSubscriber.ValueMap.apply missing minor: applies server debug updates, which Horizon never receives +ClientDebugSubscriber.ValueMap.forEach skip:java iteration helper +ClientDebugSubscriber.ValueMaps.purgeExpired missing minor: expiry of server debug values; none are received +ClientDebugSubscriber.ValueMaps.dropChunkAndBlocks missing minor: drops server debug values per chunk; none are received +ClientDebugSubscriber.ValueWrapper.hasExpired skip:java timestamp check on a debug value wrapper +ClientExplosionTracker.track inline crates/horizon-client/src/particles.rs::ParticleEngine::explosion gap: minor: the debris is thrown at once instead of queued for later ticks +ClientExplosionTracker.addParticle port crates/horizon-client/src/particles.rs::ParticleEngine::explosion_blocks gap: minor: always the default poof and smoke list; the explode packet's own block particle list is not read +ClientHandshakePacketListenerImpl.switchState inline crates/horizon-client/src/net.rs::run login steps run in packet order in run(); no state machine +ClientHandshakePacketListenerImpl.handleHello port crates/horizon-client/src/net.rs::run gap: minor: the session join and server check are in auth.rs (join_server); the encryption setup is in run +ClientHandshakePacketListenerImpl.setEncryption port crates/horizon-client/src/net.rs::run enables the connection cipher +ClientHandshakePacketListenerImpl.handleLoginFinished port crates/horizon-client/src/net.rs::run login finished; play starts after configuration +ClientHandshakePacketListenerImpl.onDisconnect port crates/horizon-client/src/net.rs::run a login disconnect returns the reason to the connect screen +ClientHandshakePacketListenerImpl.handleCompression port crates/horizon-client/src/net.rs::run sets the compression threshold +ClientHandshakePacketListenerImpl.handleCustomQuery port crates/horizon-client/src/net.rs::run answers an unknown custom query with no data +ClientHandshakePacketListenerImpl.fillListenerSpecificCrashDetails skip:debug crash report details only +ClientLevel.handleBlockChangedAck port crates/horizon-client/src/game/remote.rs::Game::end_predictions settles the predicted block actions up to the sequence +ClientLevel.onBlockEntityAdded port crates/horizon-client/src/game.rs::Game::entity_geometry off-screen block entities (beacon, end gateway, structure and test blocks) are drawn without a view test +ClientLevel.syncBlockState port crates/horizon-client/src/game/remote.rs::Game::end_predictions gap: minor: writes the server's state but does not move the player out when the block would sit on them (absSnapTo) +ClientLevel.setBlock port crates/horizon-client/src/game/remote.rs::Game::set_block gap: minor: the prediction bookkeeping is in Game::server_block +ClientLevel. port crates/horizon-client/src/game/remote.rs::Game::enter_remote_dimension gap: minor: the environment layers are applied in Game::render +ClientLevel.addEnvironmentAttributeLayers port crates/horizon-client/src/game.rs::Game::render sky colour and sky light move during a lightning flash +ClientLevel.pollLightUpdates skip:other light is applied when it arrives or relit at once (light.rs relight); there is no queue to poll +ClientLevel.getPrecipitationAt port crates/horizon-client/src/game.rs::GameLevel::precipitation gap: minor: no check that the chunk is loaded; vanilla answers none for an unloaded column +ClientLevel.removeBlockBreakingProgress inline crates/horizon-client/src/game.rs::Game::destroy_block_progress minor: the 400-tick expiry runs on every update, not every 20 ticks +ClientLevel.removeProgress inline crates/horizon-client/src/game.rs::Game::destroy_block_progress cracks are keyed by breaker id, so there are no per-position sets to tidy +ClientLevel.shouldTickDeath missing minor: death animations tick for every remote mob held; no simulation-distance check +ClientLevel.tickNonPassenger port crates/horizon-client/src/game/remote.rs::Game::tick_remote_entities ticks each remote entity +ClientLevel.tickPassenger port crates/horizon-client/src/game/passengers.rs::Game::position_riders gap: minor: passengers are placed on their vehicle; no per-passenger tick +ClientLevel.update skip:other runs the light queue each tick; Horizon relights when changes arrive +ClientLevel.unload inline crates/horizon-client/src/game/remote.rs::Game::handle_packet minor: the ForgetLevelChunk arm clears block entities and renderer columns; light and entity ticking are not toggled +ClientLevel.onChunkLoaded inline crates/horizon-client/src/game/remote_chunks.rs::Game::remote_chunk minor: chunks are meshed on arrival; no tint cache to clear and no entity-ticking gate +ClientLevel.removeEntity port crates/horizon-client/src/game/remote.rs::Game::remove_entity removes the entity and its passengers +ClientLevel.getPushableEntities missing minor: the client does not push the local player with nearby mobs or boats; the server's position correction moves it +ClientLevel.disconnect port crates/horizon-client/src/game/remote.rs::Game::leave_server gap: minor: no presence update +ClientLevel.spawnParticle port crates/horizon-client/src/animate_tick.rs::try_spawn_drip drip particle from a block shape (doAnimateTick) +ClientLevel.spawnFluidParticle port crates/horizon-client/src/animate_tick.rs::try_spawn_drip drip particle over a solid top (doAnimateTick) +ClientLevel.fillReportDetails skip:debug crash report details only +ClientLevel.playPlayerSound skip:other no caller in the 26.3 client sources +ClientLevel.playSound missing player: distance-delayed sounds (over 10 blocks, sqrt(distance)/40 s) play at once, so far fireworks and similar sounds arrive early; audio.rs has no delay +ClientLevel.getRelativeTickSpeed skip:other no caller in the 26.3 client sources +ClientLevel.destroyBlockProgress port crates/horizon-client/src/game.rs::Game::destroy_block_progress sets or clears another player's crack on a block +ClientLevel.addAlwaysVisibleParticle port crates/horizon-client/src/particles.rs::ParticleEngine::add_with always-show flag set +ClientLevel.doAddParticle port crates/horizon-client/src/particles.rs::ParticleEngine::add_with drops particles beyond 32 blocks and at minimal particle level +ClientLevel.getSkyFlashTime inline crates/horizon-client/src/game.rs::Game::render the flash check with the hideLightningFlashes option +ClientLevel.calculateBlockTint port crates/horizon-client/src/mesher.rs::MeshTables::terrain_particle_tint gap: minor: returns none for grass-block tints +ClientLevel.playBreakingSound port crates/horizon-client/src/game/level_particles.rs::Game::breaking_block_effects the block's hit sound while digging +ClientLevel.getClientLeafTintColor port crates/horizon-client/src/animate_tick.rs::AnimateContext::foliage leaf tint for tinted leaves particles; the caller is TintedParticleLeavesBlock +ClientLevel.EntityCallbacks.onTrackingStart inline crates/horizon-client/src/game/remote.rs::Game::spawn_entity minor: ender dragon parts are placed by the dragon module, with no tracked parts list +ClientLevel.EntityCallbacks.onTrackingEnd port crates/horizon-client/src/game/remote.rs::Game::remove_entity minor: detaches passengers and removes the dragon's parts; the leave-level event is not posted diff --git a/tools/port_map/client-002.tsv b/tools/port_map/client-002.tsv new file mode 100644 index 0000000000..5df69cd315 --- /dev/null +++ b/tools/port_map/client-002.tsv @@ -0,0 +1,147 @@ +ClientPacketListener. port crates/horizon-client/src/game/remote.rs::Game::join_server gap: minor: clears the chat on join; vanilla restores chat state kept across a reconfiguration +ClientPacketListener.clearCacheSlots skip:java weak-reference cache slots; Horizon has no such cache +ClientPacketListener.handleAddEntity inline crates/horizon-client/src/game/remote.rs::Game::handle_packet AddEntity arm: spawn_entity, then object or projectile spawn, then motion +ClientPacketListener.createEntityFromPacket inline crates/horizon-client/src/game/remote.rs::Game::handle_packet gap: minor: the player spawns without checking that its profile is known +ClientPacketListener.handleSetEntityData port crates/horizon-client/src/game/remote.rs::Game::handle_packet gap: minor: only the data indexes Horizon reads are applied (flags, names, sleep, crouch, mob fields) +ClientPacketListener.handleEntityPositionSync port crates/horizon-client/src/game/remote.rs::Game::move_entity absolute move with the packet rotation and on-ground flag +ClientPacketListener.handleTeleportEntity inline crates/horizon-client/src/game/remote.rs::Game::handle_packet TeleportEntity arm works out the relative position, then move_entity; gap: minor: a teleport of the player's former vehicle is not applied to the player +ClientPacketListener.handleTickingState port crates/horizon-client/src/game/remote.rs::Game::handle_packet TickingState arm: clock rate and frozen flag +ClientPacketListener.handleTickingStep port crates/horizon-client/src/game/remote.rs::Game::handle_packet TickingStep arm: frozen ticks to run +ClientPacketListener.handleMoveEntity port crates/horizon-client/src/game/remote.rs::Game::move_entity relative move and rotation from the Pos, PosRot and Rot packets +ClientPacketListener.handleMinecartAlongTrack missing player: the server's rail steps for a minecart are dropped, so a cart on a dedicated server follows the client's own rail physics and can drift from where the server has it +ClientPacketListener.handleRemoveEntities port crates/horizon-client/src/game/remote.rs::Game::remove_entity gap: minor: a removed vehicle carrying the player is not remembered for a later teleport +ClientPacketListener.setValuesFromPositionPacket port crates/horizon-client/src/game/remote.rs::Game::remote_teleport gap: minor: the entity case (TeleportEntity) is in the handle_packet arm, not here +ClientPacketListener.handleRotatePlayer port crates/horizon-client/src/game/remote.rs::Game::handle_packet PlayerRotation arm; gap: minor: no ServerboundMovePlayerPacket.Rot reply is sent +ClientPacketListener.handleChunkBlocksUpdate port crates/horizon-client/src/game/remote.rs::Game::handle_packet SectionBlocksUpdate arm: each change through the block update +ClientPacketListener.handleChunksBiomes missing player: biome updates for loaded chunks are dropped, so grass, leaves and water keep the old biome colour +ClientPacketListener.enableChunkLight inline crates/horizon-client/src/streaming.rs::Streaming::insert_remote gap: minor: the air-only section status is not set; every section of the 3x3 columns is marked dirty +ClientPacketListener.handleForgetLevelChunk port crates/horizon-client/src/game/remote.rs::Game::handle_packet ForgetLevelChunk arm: Streaming::forget, block entities and renderer columns dropped +ClientPacketListener.queueLightRemoval inline crates/horizon-client/src/streaming.rs::Streaming::forget gap: minor: the column's light state is dropped with the column; no per-section light clearing +ClientPacketListener.handleBlockUpdate port crates/horizon-client/src/game/remote.rs::Game::server_block +ClientPacketListener.handleConfigurationStart port crates/horizon-client/src/net.rs::receive_play gap: player: no reconfiguring screen is shown and the chat state is not stored +ClientPacketListener.handleSystemChat port crates/horizon-client/src/game/remote.rs::Game::handle_packet SystemChat arm: overlay to the action bar, otherwise the chat +ClientPacketListener.handlePlayerChat port crates/horizon-client/src/game/remote.rs::Game::handle_packet PlayerChat arm; gap: minor: no signature, chat index or validity checks +ClientPacketListener.handleDisguisedChat port crates/horizon-client/src/game/remote.rs::Game::handle_packet DisguisedChat arm +ClientPacketListener.handleDeleteChat missing player: a chat line the server deletes (moderation) stays on screen +ClientPacketListener.handleAnimate port crates/horizon-client/src/game/remote.rs::Game::remote_animate action 0 stops sleeping, 1 and 2 emit crit and enchanted hit particles +ClientPacketListener.handleHurtAnimation port crates/horizon-client/src/game/remote.rs::Game::handle_packet HurtAnimation arm: own player's hurt tilt, or a mob's hurt time +ClientPacketListener.handleSetSpawn port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetDefaultSpawnPosition arm; gap: minor: only an overworld spawn is kept, other dimensions are dropped +ClientPacketListener.handleAddTransientBlockPacket missing minor: the transient block shown while a falling block lands is not drawn; the block update still arrives +ClientPacketListener.findTotem missing player: the totem of undying pop-up over the screen is not shown when death protection triggers; Horizon has no item activation +ClientPacketListener.handleSetExperience port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetExperience arm +ClientPacketListener.determineLevelLoadingReason port crates/horizon-client/src/game.rs::Game::loading_reason gap: player: only the in-frame game picks a reason; a dedicated server join or dimension change shows no level loading screen +ClientPacketListener.handleOpenScreen port crates/horizon-client/src/game/remote.rs::Game::remote_open_screen +ClientPacketListener.handleSetCursorItem port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetCursorItem arm; gap: minor: sets the carried stack even while the creative screen is open +ClientPacketListener.handleSetPlayerInventory port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetPlayerInventory arm: slots 0-35, armour 36-39, off hand 40 +ClientPacketListener.handleContainerContent port crates/horizon-client/src/game/remote.rs::Game::handle_packet ContainerSetContent arm: each slot, then the carried stack +ClientPacketListener.handleBlockEntityData port crates/horizon-client/src/game/remote.rs::Game::remote_block_entity_data +ClientPacketListener.handleContainerSetData port crates/horizon-client/src/game/remote.rs::Game::handle_packet ContainerSetData arms (lectern, furnace, crafter, brewing); station containers go to remote_stations.rs::Game::remote_station_data +ClientPacketListener.handleContainerClose port crates/horizon-client/src/game/remote.rs::Game::handle_packet ContainerClose arm: closes the inventory screen; gap: minor: a server close while the creative screen is open is ignored +ClientPacketListener.handleBlockEvent port crates/horizon-client/src/game/remote.rs::Game::handle_packet BlockEvent arm: client block event, then the block entity event +ClientPacketListener.handleGameEvent port crates/horizon-client/src/game/remote.rs::Game::handle_packet GameEvent arm; gap: player: no handling for 0 (no spawn point message), 6 (arrow hit sound), 9 (puffer fish sting), 12 (limited crafting); 5 (demo) and 13 (chunk load start) are minor +ClientPacketListener.openDemoIntroScreen skip:other demo mode only (GameEvent 5); Horizon has no demo mode +ClientPacketListener.startWaitingForNewLevel missing player: no level loading screen after a dimension change or respawn on a dedicated server; a join shows only the 'Loading terrain' status text +ClientPacketListener.handleMapItemData port crates/horizon-client/src/game/maps.rs::Game::remote_map_item_data +ClientPacketListener.handleUpdateAdvancementsPacket port crates/horizon-client/src/game/advancements.rs::Game::remote_advancements UpdateAdvancements arm passes the added icons +ClientPacketListener.handleSelectAdvancementsTab missing minor: the server's advancement tab choice is ignored +ClientPacketListener.handleCommands port crates/horizon-client/src/game/remote.rs::Game::handle_packet Commands arm: command tree from the packet, merged with Horizon's own +ClientPacketListener.handleStopSoundEvent port crates/horizon-client/src/game/remote.rs::Game::handle_packet StopSound arm: audio stop_sounds +ClientPacketListener.handleUpdateRecipes skip:data Horizon reads recipes from its own tables; the server's list is not read +ClientPacketListener.handleLookAt missing player: a server command that turns the camera (/faceplayer, /tp with facing) does not turn this player's view +ClientPacketListener.handleTagQueryPacket skip:debug answer to a debug NBT query; Horizon sends none +ClientPacketListener.handleAwardStats port crates/horizon-client/src/game/remote.rs::Game::handle_packet AwardStats arm: known stat keys into the stats values +ClientPacketListener.handleRecipeBookAdd port crates/horizon-client/src/game/recipe_book.rs::Game::remote_recipe_book_add +ClientPacketListener.handleRecipeBookRemove port crates/horizon-client/src/game/recipe_book.rs::Game::remote_recipe_book_remove +ClientPacketListener.handleRecipeBookSettings port crates/horizon-client/src/game/recipe_book.rs::Game::remote_recipe_book_settings +ClientPacketListener.refreshRecipeBook inline crates/horizon-client/src/game/recipe_book.rs::Game::remote_recipe_book_add the collections rebuild lazily through the client version bump +ClientPacketListener.updateTags skip:data the server's tag payload is not read; tags come from Horizon's own data (net.rs configure ignores UpdateTags) +ClientPacketListener.handleUpdateTags skip:data the server's tag payload is not read; tags come from Horizon's own data (net.rs configure ignores UpdateTags) +ClientPacketListener.handleChangeDifficulty port crates/horizon-client/src/game/remote.rs::Game::handle_packet ChangeDifficulty arm +ClientPacketListener.handleSetCamera missing player: a spectator's view does not attach to the entity it spectates (for example /spectate) +ClientPacketListener.handleInitializeBorder port crates/horizon-client/src/game/remote.rs::Game::handle_packet InitializeBorder arm: centre, size or lerp, absolute max, warnings +ClientPacketListener.handleSetBorderCenter port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetBorderCenter arm +ClientPacketListener.handleSetBorderLerpSize port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetBorderLerpSize arm +ClientPacketListener.handleSetBorderSize port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetBorderSize arm +ClientPacketListener.handleSetBorderWarningDistance port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetBorderWarningDistance arm +ClientPacketListener.handleSetBorderWarningDelay port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetBorderWarningDelay arm +ClientPacketListener.handleTitlesClear port crates/horizon-client/src/game/remote.rs::Game::handle_packet ClearTitles arm: clear, optionally reset the times +ClientPacketListener.handleCustomChatCompletions missing minor: server-sent custom chat completions are not used +ClientPacketListener.setTitleText port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetTitleText arm +ClientPacketListener.setSubtitleText port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetSubtitleText arm +ClientPacketListener.setTitlesAnimation port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetTitlesAnimation arm +ClientPacketListener.handleTabListCustomisation port crates/horizon-client/src/game/remote.rs::Game::handle_packet TabList arm: empty text clears the header or footer +ClientPacketListener.handleRemoveMobEffect port crates/horizon-client/src/game/remote.rs::Game::handle_packet RemoveMobEffect arm; gap: minor: only the player's own effects are removed +ClientPacketListener.handlePlayerInfoRemove port crates/horizon-client/src/game/remote.rs::Game::handle_packet PlayerInfoRemove arm +ClientPacketListener.handlePlayerInfoUpdate port crates/horizon-client/src/game/remote.rs::Game::handle_packet PlayerInfoUpdate arm: new entries, then listing, latency, name, hat, list order and game mode for tab players +ClientPacketListener.applyPlayerInfoUpdate inline crates/horizon-client/src/game/remote.rs::Game::handle_packet gap: minor: the own player's game-mode update does not change the local mode; INITIALIZE_CHAT is ignored +ClientPacketListener.initializeChatSession missing minor: other players' chat keys are not kept or checked, so no signature check is made on their messages +ClientPacketListener.enforcesSecureChat missing minor: the Login flag enforces_secure_chat is not read +ClientPacketListener.handleGameRuleValues port crates/horizon-client/src/game/lan_screen.rs::Game::remote_world_options_rules +ClientPacketListener.handleSoundEntityEvent missing unsure: no SoundEntity arm; a sound attached to an entity would be silent unless mob_sounds.rs plays it +ClientPacketListener.handleAddObjective port crates/horizon-client/src/game/remote.rs::Game::remote_objective add, remove and update of an objective +ClientPacketListener.handleSetScore port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetScore arm +ClientPacketListener.handleResetScore port crates/horizon-client/src/game/remote.rs::Game::handle_packet ResetScore arm +ClientPacketListener.handleSetDisplayObjective port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetDisplayObjective arm: display slot names +ClientPacketListener.handleSetPlayerTeamPacket port crates/horizon-client/src/game/remote.rs::Game::remote_team +ClientPacketListener.tryAddParticle skip:other the try/catch that drops one particle that fails to add; Horizon's particle ids are typed, so nothing is caught +ClientPacketListener.handlePlaceRecipe port crates/horizon-client/src/game/recipe_book.rs::Game::remote_ghost_recipe +ClientPacketListener.handleLightUpdatePacket port crates/horizon-client/src/game/remote_chunks.rs::Game::remote_light_update +ClientPacketListener.handleMerchantOffers port crates/horizon-client/src/game/remote.rs::Game::remote_merchant_offers +ClientPacketListener.handleSetChunkCacheRadius port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetChunkCacheRadius arm: streaming radius, columns out of range dropped +ClientPacketListener.handleSetSimulationDistance missing minor: the server's simulation distance is not read +ClientPacketListener.handleSetChunkCacheCenter port crates/horizon-client/src/game/remote.rs::Game::handle_packet SetChunkCacheCenter arm +ClientPacketListener.handleBlockChangedAck port crates/horizon-client/src/game/remote.rs::Game::end_predictions +ClientPacketListener.handleBundlePacket inline crates/horizon-client/src/net.rs::receive_play bundle delimiters are dropped; the packets inside arrive as a stream and are handled one by one, in order +ClientPacketListener.handleProjectilePowerPacket missing minor: the projectile's acceleration power is not read (ProjectilePower has no arm) +ClientPacketListener.handleChunkBatchFinished port crates/horizon-client/src/game/remote.rs::Game::handle_packet ChunkBatchFinished arm; gap: minor: answers with a fixed 64 chunks per tick, not a measured rate +ClientPacketListener.handleTestInstanceBlockStatus skip:debug status for the test instance block editor (developer blocks); Horizon has no editor +ClientPacketListener.handleDebugChunkValue skip:debug debug overlay values from the server +ClientPacketListener.handleDebugBlockValue skip:debug debug overlay values from the server +ClientPacketListener.handleDebugEntityValue skip:debug debug overlay values from the server +ClientPacketListener.handleDebugEvent skip:debug debug overlay events from the server +ClientPacketListener.handleGameTestHighlightPos skip:debug game test block highlight (developer tool) +ClientPacketListener.isAcceptingMessages inline crates/horizon-client/src/game/remote.rs::Game::send_chat gap: minor: only the chat abilities are checked; no connection-open check +ClientPacketListener.getPlayerInfo skip:java a map lookup by UUID; Horizon keeps the player list in remote.tab_players and remote.players +ClientPacketListener.getPlayerInfoIgnoreCase missing unsure: a case-insensitive name lookup for profiles (LocalPlayerResolver); Horizon has none +ClientPacketListener.markMessageAsProcessed missing minor: no secure-chat acknowledgement tracking; Horizon's chat is unsigned +ClientPacketListener.sendChatAcknowledgement missing minor: no ChatAck is sent; Horizon's chat is unsigned +ClientPacketListener.sendCommand port crates/horizon-client/src/game/remote.rs::Game::send_chat gap: player: commands are sent unsigned, including ones with signable arguments such as /msg +ClientPacketListener.verifyCommand missing minor: no parse or permission check before a command is sent, so the confirmation screens never show +ClientPacketListener.isValidCommand missing minor: only used by verifyCommand, which is missing +ClientPacketListener.openSendConfirmationWindow missing minor: the confirmation screen before a command with problems is not shown +ClientPacketListener.openCommandSendConfirmationWindow missing minor: the same confirmation screen for click-to-run commands is not shown +ClientPacketListener.openSignedCommandSendConfirmationWindow missing minor: no confirmation screen for a command that needs a signature +ClientPacketListener.broadcastClientInformation port crates/horizon-client/src/game/remote.rs::Game::tick_remote_level gap: minor: only view distance and chat visibility are sent, and only when they change +ClientPacketListener.notifyPlayerLoaded port crates/horizon-client/src/game/remote.rs::Game::tick_remote_level gap: minor: gated on the player's column being lit, not on its sections being compiled +ClientPacketListener.setKeyPair missing player: no chat key pair is ever set, so chat and commands stay unsigned +ClientRegistryLayer.createRegistryAccess skip:data registry layers come from Horizon's own tables +ClientSuggestionProvider.getOnlinePlayerNames port crates/horizon-client/src/game/remote.rs::Game::remote_online_names sorted names of the server's player list +ClientSuggestionProvider.getCustomTabSuggestions missing minor: server-sent custom completions are not used +ClientSuggestionProvider.getSelectedEntities missing minor: no suggestion for the entity under the crosshair +ClientSuggestionProvider.suggestRegistryElements port crates/horizon-command/src/args.rs::suggest_resources registry ids come from Horizon's registry tables +ClientSuggestionProvider.getRelevantCoordinates port crates/horizon-command/src/args.rs::suggest_coordinates the block under the crosshair for block positions +ClientSuggestionProvider.getAbsoluteCoordinates port crates/horizon-command/src/args.rs::suggest_coordinates gap: minor: the exact hit point is not offered; Vec3 arguments only get ~ +ClientSuggestionProvider.modifyCustomCompletions missing minor: server-sent custom completions are not stored +KnownPacksManager.trySelectingPacks skip:data Horizon answers SelectKnownPacks with no packs, so the server sends full registries (net.rs configure) +LegacyServerPinger.channelActive skip:other legacy pre-1.7 server-list ping, not used by this client +LegacyServerPinger.channelRead0 skip:other legacy pre-1.7 server-list ping, not used by this client +LevelLoadTracker.startClientLoad inline crates/horizon-client/src/game/remote.rs::Game::tick_remote_level gap: player: no loading screen state; the wait is only for the player's column to be lit +LevelLoadTracker.isLevelReady inline crates/horizon-client/src/game/remote.rs::Game::tick_remote_level gap: minor: readiness is the player's column being lit; vanilla's close delay is not kept +LevelLoadTracker.getPlayerCompiledSectionCallback inline crates/horizon-client/src/game/remote.rs::Game::tick_remote_level gap: minor: the gate is the column being lit, not the section being compiled +LevelLoadTracker.WaitingForPlayerChunk.isReady inline crates/horizon-client/src/game/remote.rs::Game::tick_remote_level gap: minor: no timeout and no spectator or dead exemption +LevelLoadTracker.WaitingForServer.loadingPacketsReceived missing minor: the server's level-chunks-start event (GameEvent 13) is ignored +MultiPlayerGameMode.startPrediction port crates/horizon-client/src/game/remote.rs::Game::next_sequence gap: minor: only the sequence number; each caller builds and sends its own packet +MultiPlayerGameMode.ensureHasSentCarriedItem inline crates/horizon-client/src/game/remote.rs::Game::remote_send_state the SetCarriedItem check, made on every send +MultiPlayerGameMode.performUseItemOn port crates/horizon-client/src/game/remote.rs::Game::remote_use_block gap: minor: no world-border check, and UseItemOn always sends world_border false +MultiPlayerGameMode.createPlayer port crates/horizon-client/src/game/remote.rs::Game::remote_teleport gap: minor: the player is created on the first position packet, not at login +MultiPlayerGameMode.handleCreativeModeItemAdd inline crates/horizon-client/src/game/remote.rs::Game::remote_send_state gap: minor: the creative inventory is sent as a per-tick diff, with no feature-flag check +MultiPlayerGameMode.handleCreativeModeItemDrop port crates/horizon-client/src/game/remote.rs::Game::remote_creative_drop gap: minor: no check that another container screen is open +MultiPlayerGameMode.getDestroyStage port crates/horizon-client/src/first_person.rs::Destroying::stage +RegistryDataCollector.appendContents skip:data registry data for the configuration handshake; Horizon reads its own tables +RegistryDataCollector.appendTags skip:data registry tags for the configuration handshake; Horizon reads its own tables +RegistryDataCollector.loadNewElementsAndTags skip:data registry loading for the configuration handshake; Horizon reads its own tables +RegistryDataCollector.addCrashDetails skip:debug crash report details +RegistryDataCollector.loadOnlyTags skip:data registry tags for the configuration handshake; Horizon reads its own tables +RegistryDataCollector.updateComponents skip:data data component registry; Horizon reads its own tables +RegistryDataCollector.collectGameRegistries skip:data registry collection for the configuration handshake; Horizon reads its own tables +RegistryDataCollector.ContentsCollector.append skip:data registry contents collector; Horizon reads its own tables diff --git a/tools/port_map/client-003.tsv b/tools/port_map/client-003.tsv new file mode 100644 index 0000000000..b0e3f9d2f6 --- /dev/null +++ b/tools/port_map/client-003.tsv @@ -0,0 +1,150 @@ +ServerData.write port crates/horizon-protocol/src/servers.rs::ServerEntry::to_tag gap: minor: no acceptedCodeOfConduct entry (Horizon never stores code of conduct acceptance) +ServerData.read port crates/horizon-protocol/src/servers.rs::ServerEntry::from_tag gap: minor: acceptedCodeOfConduct is not read +ServerData.copyNameIconFrom inline crates/horizon-client/src/multiplayer.rs::Screen::click the Edit save writes name, ip and pack setting and keeps the icon +ServerData.copyFrom inline crates/horizon-client/src/multiplayer.rs::Screen::click Edit opens the form pre-filled from the entry (name, ip, pack setting) +ServerData.validateIcon port crates/horizon-client/src/multiplayer.rs::icon_pixels gap: minor: only 64x64 PNGs are kept; vanilla keeps any PNG up to 1024x1024 in servers.dat +ServerList.get missing minor: Direct Connect never looks up a saved entry by address, so its saved pack setting and icon are not used +ServerList.unhide missing minor: no hidden-server list (vanilla keeps up to 16 direct-join entries) +ServerList.remove port crates/horizon-client/src/multiplayer.rs::Screen::click Delete confirm removes the selected row and saves +ServerList.add port crates/horizon-client/src/multiplayer.rs::Screen::click gap: minor: direct-join entries are not kept hidden (hidden=true) +ServerList.swap port crates/horizon-client/src/multiplayer.rs::Screen::click arrow click on a row swaps it with its neighbour and saves +ServerList.set missing minor: no write-back of one saved entry by name and address (only the code-of-conduct save uses it) +ServerStatusPinger.pingServer port crates/horizon-client/src/multiplayer.rs::ping_all gap: minor: no unknown-host message; one thread per server with a 5 s timeout +ServerStatusPinger.onPingFailed port crates/horizon-client/src/multiplayer.rs::ping_all failed ping stores Ping::Failed, shown as can't-connect in the row +ServerStatusPinger.pingLegacyServer missing player: a server that answers only the old legacy ping shows as incompatible with its version and player count in vanilla; Horizon shows it as unreachable +ServerStatusPinger.tick skip:other Horizon pings on plain threads, so there are no connections to tick +ServerStatusPinger.removeAll skip:other pings are not cancelled on screen close; threads end on their own timeout +SessionSearchTrees.rebuildAfterLanguageChange skip:java creative search reads names at query time, so there are no trees to rebuild +SessionSearchTrees.getTooltipLines inline crates/horizon-client/src/creative.rs::Creative::from_json search lines come pre-stripped from the export and are read here +SessionSearchTrees.updateCreativeTags inline crates/horizon-client/src/creative.rs::Creative::from_json tag index comes from the export's tag lists +SessionSearchTrees.updateCreativeTooltips inline crates/horizon-client/src/creative.rs::Creative::from_json name index comes from the export's tooltip lines +ChatAbilities.selectVisibleMessages port crates/horizon-client/src/chat.rs::ChatAbilities::shows source filter: client lines always, player and server lines per the abilities +ChatAbilities.Builder.build inline crates/horizon-client/src/chat.rs::ChatAbilities::new restrictions come from the Chat option alone; new() maps the visibility to them +ChatListener.tick missing player: the Chat Delay option does nothing; lines show at once instead of after the delay +ChatListener.setMessageDelay missing player: no delayed-message queue, so Chat Delay has no effect +ChatListener.flushQueue missing player: no queue to flush (part of Chat Delay) +ChatListener.removeFromDelayedMessageQueue missing minor: only matters while a delayed message is queued +ChatListener.handleMessage missing player: nothing is held back, so Chat Delay is ignored +ChatListener.handlePlayerChatMessage port crates/horizon-client/src/game/remote.rs::Game::handle_packet gap: player: no Only Show Secure Chat filter, no server filter-mask text, no block or friend-only hiding of chat lines +ChatListener.handleChatMessageError missing player: a signed chat line that fails validation shows no red chat.validation_error line; Horizon checks no chat signatures (rare on most servers) +ChatListener.handleDisguisedChatMessage port crates/horizon-client/src/game/remote.rs::Game::handle_packet gap: minor: no narration and no chat log entry +ChatListener.showMessageToPlayer port crates/horizon-client/src/game/remote.rs::Game::chat_line gap: player: trust, block, friend-only and secure-only checks are not run before showing +ChatListener.evaluateTrustLevel missing player: no secure, modified or not-secure marking, so unsigned lines show with no Not Secure bar and Only Show Secure Chat does nothing +ChatListener.logPlayerMessage skip:service feeds the chat log that chat reports read +ChatListener.logSystemMessage skip:service feeds the chat log that chat reports read +ChatListener.handleSystemMessage port crates/horizon-client/src/game/remote.rs::Game::handle_packet gap: minor: hide-matched-names and friend filtering are not applied to system lines +ChatListener.guessChatUUID skip:service social lookup of a name in a system line, used only for blocking and hiding +ChatListener.isSenderLocalPlayer missing minor: only feeds the trust level check +ChatLog.codec skip:java serializer of the chat log +ChatLog.loggedChatEvents skip:service chat log for chat reports +ChatLog.push skip:service chat log for chat reports +ChatLog.lookup skip:service chat log for chat reports +ChatTrustLevel.evaluate missing player: no Not Secure or Modified marking on chat lines, and Only Show Secure Chat does nothing (see createTag) +ChatTrustLevel.isModified missing minor: only feeds the Modified marking +ChatTrustLevel.containsModifiedStyle missing minor: only feeds the Modified marking +ChatTrustLevel.createTag missing player: no grey Not Secure bar or Modified tag on player chat lines +GuiMessage.splitLines port crates/horizon-client/src/chat.rs::Chat::add_lines gap: minor: no width reserved for a tag icon; Horizon draws no icon tags +GuiMessageTag.chatModified missing minor: no Modified tag text or icon (needs the trust check) +GuiMessageTag.Icon.extractRenderState missing minor: the chat_modified icon is never drawn +LoggedChatMessage.Player.toContentComponent skip:service chat report view of a logged line +LoggedChatMessage.Player.toNarrationComponent skip:service chat report view of a logged line +LoggedChatMessage.Player.toHeadingComponent skip:service chat report view of a logged line +LoggedChatMessage.Player.getTimeComponent skip:service chat report view of a logged line +AbuseReportSender.Services.send skip:service chat and player reporting +AbuseReportSender.Services.getErrorDescription skip:service chat and player reporting +BanReason.byId skip:service chat and player reporting +ChatReport.toggleReported skip:service chat and player reporting +ChatReport.copy skip:service chat and player reporting +ChatReport.Builder.hasContent skip:service chat and player reporting +ChatReport.Builder.checkBuildable skip:service chat and player reporting +ChatReport.Builder.build skip:service chat and player reporting +ChatReport.Builder.buildEvidence skip:service chat and player reporting +ChatReport.Builder.buildReportedChatMessage skip:service chat and player reporting +ChatReportContextBuilder.collectAllContext skip:service chat and player reporting +ChatReportContextBuilder.acceptContext skip:service chat and player reporting +ChatReportContextBuilder.Collector.accept skip:service chat and player reporting +ChatReportContextBuilder.Collector.isComplete skip:service chat and player reporting +NameReport.copy skip:service chat and player reporting +NameReport.Builder.checkBuildable skip:service chat and player reporting +NameReport.Builder.build skip:service chat and player reporting +ReportEnvironment.thirdPartyServerInfo skip:service chat and player reporting +ReportEnvironment.realmInfo skip:service chat and player reporting +ReportEnvironment.getClientVersion skip:service chat and player reporting +ReportReason.getIncompatibleCategories skip:service chat and player reporting +ReportingContext.create skip:service chat and player reporting +ReportingContext.draftReportHandled skip:service chat and player reporting +ReportingContext.hasDraftReportFor skip:service chat and player reporting +SkinReport.copy skip:service chat and player reporting +SkinReport.Builder.hasContent skip:service chat and player reporting +SkinReport.Builder.checkBuildable skip:service chat and player reporting +SkinReport.Builder.build skip:service chat and player reporting +BlockStatePredictionHandler.updateKnownServerState port crates/horizon-client/src/game/remote.rs::Game::server_block a predicted block keeps its predicted state; the server state is remembered +BlockStatePredictionHandler.startPredicting port crates/horizon-client/src/game/remote.rs::Game::predict_break gap: minor: the sequence number is passed in by the caller instead of counted here +AddressCheck.createFromService missing minor: no block-list suppliers are loaded, so no address is refused on connect +ServerAddress.isValidAddress port crates/horizon-protocol/src/servers.rs::parse_address gap: minor: no IDN check on the host name +ServerAddress.equals skip:java value equality; Horizon compares the strings +ServerNameResolver.resolveAddress port crates/horizon-protocol/src/resolver.rs::resolve gap: minor: no block-list check on the resolved address +ServerRedirectHandler.createDnsSrvRedirectHandler port crates/horizon-protocol/src/resolver.rs::srv_redirect SRV lookup of _minecraft._tcp on port 25565 +AbstractBoatRenderer.submit port crates/horizon-client/src/game/boats.rs::Game::boat_geometry gap: player: no bubble-column wobble (bubbleAngle tilt); the rest of the transform, hurt tilt and paddles are drawn +AbstractBoatRenderer.extractRenderState inline crates/horizon-client/src/game/boats.rs::Game::boat_geometry gap: player: bubbleAngle is not read, so boats in bubble columns do not wobble +AbstractCubeMobRenderer.downscaleSlightly port crates/horizon-client/src/mobs/cube.rs::scale slime only: the 0.999 scale and small lift +AbstractCubeMobRenderer.applySizeAndSquish port crates/horizon-client/src/mobs/cube.rs::scale size and squish scale +AbstractCubeMobRenderer.extractRenderState port crates/horizon-client/src/mobs/cube.rs::scale squish and size are read from the mob at draw time +AbstractHoglinRenderer.extractRenderState port crates/horizon-client/src/mobs/piglin.rs::hoglin_anim the headbutt count (anim_ticks) drives the head pose +AbstractHorseRenderer.extractRenderState port crates/horizon-client/src/mobs/equine.rs::anim eat, stand and mouth poses; saddle and armour come from render.rs layers +AbstractMinecartRenderer.submit port crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry gap: player: no hurt tilt when a cart is hit (hurt_time is not read here); minor: no per-cart offset jitter +AbstractMinecartRenderer.newRender missing minor: experimental minecart movement (new render path) is not implemented +AbstractMinecartRenderer.oldRender port crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry rail-based draw is the path Horizon has +AbstractMinecartRenderer.newExtractState missing minor: experimental minecart movement (new render path) is not implemented +AbstractMinecartRenderer.oldExtractState port crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry rail front and back points are taken from the cart's rail position +AbstractMinecartRenderer.getRenderOffset missing minor: only the new render path offsets the cart +AbstractSkeletonRenderer.extractRenderState port crates/horizon-client/src/mobs/anim.rs::setup gap: player: a skeleton's freeze-conversion shake (its isShaking) is not drawn; the aggressive and bow flags are read in setup +AbstractSkeletonRenderer.getArmPose port crates/horizon-client/src/mobs/anim.rs::setup bow-and-arrow arm pose when aggressive with a bow +AbstractZombieRenderer.extractRenderState port crates/horizon-client/src/mobs/anim.rs::setup aggressive arms (zombie arm pose) +AbstractZombieRenderer.isShaking port crates/horizon-client/src/mobs/render.rs::shaking converting zombies and husks shake +AgeableMobRenderer.submit port crates/horizon-client/src/mobs/render.rs::body baby or adult model choice +AllayRenderer.extractRenderState port crates/horizon-client/src/mobs/allay.rs::animate dance, spin and hold clocks +ArmadilloRenderer.extractRenderState port crates/horizon-client/src/mobs/armadillo.rs::animate roll-out, roll-up and peek states; hiding_in_shell covers the shell flag +ArmorModelSet.get skip:java container lookup of baked models +ArmorModelSet.map skip:java container of baked models +ArmorModelSet.putFrom skip:java container of baked models +ArmorModelSet.bake skip:data models are loaded from Horizon's assets, not baked from layer locations +ArmorStandRenderer.extractRenderState port crates/horizon-client/src/game/armor_stands.rs::Game::armor_stand_geometry wiggle, small, marker and poses are read there +ArmorStandRenderer.submit port crates/horizon-client/src/game/armor_stands.rs::Game::armor_stand_geometry small or big model +ArmorStandRenderer.setupRotations port crates/horizon-client/src/game/armor_stands.rs::Game::armor_stand_geometry the wiggle turn after a hit +ArmorStandRenderer.getRenderType port crates/horizon-client/src/game/armor_stands.rs::Game::armor_stand_geometry gap: minor: the translucent marker variant is not split out +ArrowRenderer.submit inline crates/horizon-client/src/game/creatures.rs::Game::mob_geometry the arrow model is drawn here, turned by its yaw and pitch +ArrowRenderer.extractRenderState inline crates/horizon-client/src/game/creatures.rs::Game::mob_geometry arrow yaw and pitch are read here +AxolotlRenderer.extractRenderState port crates/horizon-client/src/mobs/axolotl.rs::Animation::tick in-water, on-ground, moving and playing-dead factors are updated here; poses in body_anim::axolotl +BatRenderer.extractRenderState port crates/horizon-client/src/mobs/bat.rs::setup_animation_states flying and resting states; resting() is the flag +BeeRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::bee stinger, wings on the ground, angry and roll read there +BreezeRenderer.extractRenderState port crates/horizon-client/src/mobs/breeze.rs::anim idle, shoot, slide, inhale and long-jump states +CamelRenderer.createCamelSaddleLayer port crates/horizon-client/src/mobs/render.rs::layers camel uses the equine saddle branch +CamelRenderer.extractAdditionalState port crates/horizon-client/src/mobs/camel.rs::animation_states sit, stand, idle and dash states; the dash cooldown is in dash() +CatRenderer.extractRenderState port crates/horizon-client/src/mobs/tame.rs::anim gap: player: sitting and roll poses are drawn; lie-down (lieDownAmount) and relaxed poses are not, so a cat never lies down +ChickenRenderer.bakeModels skip:data models are loaded from Horizon's assets +ChickenRenderer.submit port crates/horizon-client/src/mobs/render.rs::body adult, baby and cold model choice +ChickenRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body texture by variant and age +ChickenRenderer.extractRenderState port crates/horizon-client/src/mobs/anim.rs::setup wing flap and flap speed; the variant is read in render.rs::body +CodRenderer.setupRotations missing player: a cod out of water is not turned onto its side and in water does not sway; salmon and tropical fish share this +CopperGolemRenderer.extractRenderState port crates/horizon-client/src/mobs/copper_golem.rs::anim chest and idle animation states; the weathering texture comes from texture() +CowRenderer.bakeModels skip:data models are loaded from Horizon's assets +CowRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body variant texture, baby texture for babies +CowRenderer.extractRenderState port crates/horizon-client/src/mobs/render.rs::body variant is read there +CowRenderer.submit port crates/horizon-client/src/mobs/render.rs::body model per variant and age +CreakingRenderer.extractRenderState port crates/horizon-client/src/mobs/creaking.rs::animate attack, invulnerability and death clocks +CreeperRenderer.extractRenderState port crates/horizon-client/src/overlay.rs::creeper_progress white flash from swelling; the charged layer is in render.rs (energy swirl) +CushionRenderer.extractRenderState port crates/horizon-client/src/game/cushions.rs::Game::cushion_geometry yaw and colour texture are read there +CushionRenderer.submit port crates/horizon-client/src/game/cushions.rs::Game::cushion_geometry turned to its facing and drawn +DisplayRenderer.getSkyLightLevel port crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry brightness override +DisplayRenderer.getBlockLightLevel port crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry brightness override +DisplayRenderer.getShadowRadius missing minor: display shadows are not drawn (shadow_radius is stored and not read) +DisplayRenderer.getShadowStrength missing minor: display shadows are not drawn (shadow_strength is stored and not read) +DisplayRenderer.submit port crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry gap: player: only text displays are drawn; block and item displays are not +DisplayRenderer.calculateOrientation port crates/horizon-client/src/text_display.rs::TextDisplayData::matrix billboard orientation (fixed, horizontal, vertical, center) +DisplayRenderer.extractRenderState port crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry interpolation progress and the entity yaw and pitch are read there +DisplayRenderer.BlockDisplayRenderer.extractRenderState missing player: block display entities are invisible; the remote path tracks only text displays +DisplayRenderer.ItemDisplayRenderer.extractRenderState missing player: item display entities are invisible +DisplayRenderer.ItemDisplayRenderer.submitInner missing player: item displays draw nothing +DisplayRenderer.TextDisplayRenderer.extractRenderState port crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry render state comes from the text display's metadata +DisplayRenderer.TextDisplayRenderer.submitInner port crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry text, background and opacity diff --git a/tools/port_map/client-004.tsv b/tools/port_map/client-004.tsv new file mode 100644 index 0000000000..59b1df1f50 --- /dev/null +++ b/tools/port_map/client-004.tsv @@ -0,0 +1,149 @@ +DolphinRenderer.extractRenderState port crates/horizon-client/src/game/mob_blocks.rs::Game::held_item_geometry gap: minor: isMoving (dolphin motion flag) is not read +DragonFireballRenderer.submit port crates/horizon-client/src/game/dragon.rs::Game::dragon_fireball_geometry +DragonFireballRenderer.buildQuad inline crates/horizon-client/src/game/dragon.rs::Game::dragon_fireball_geometry quad built inline +DragonFireballRenderer.vertex inline crates/horizon-client/src/game/dragon.rs::Game::dragon_fireball_geometry vertex built inline +EndCrystalRenderer.submit port crates/horizon-client/src/game/end.rs::Game::end_crystal_geometry model at scale 2 and the beam to the target +EndCrystalRenderer.extractRenderState inline crates/horizon-client/src/game/end.rs::Game::end_crystal_geometry age, show-bottom and beam target read from the crystal there +EndCrystalRenderer.shouldRender inline crates/horizon-client/src/game/end.rs::Game::end_crystal_geometry gap: minor: no frustum test; crystals within 256 blocks always draw, beam included +EnderDragonRenderer.submit port crates/horizon-client/src/game/dragon.rs::Game::dragon_geometry +EnderDragonRenderer.submitRays port crates/horizon-client/src/game/dragon.rs::Game::dragon_rays +EnderDragonRenderer.submitCrystalBeams port crates/horizon-client/src/game/dragon.rs::Game::crystal_beam +EnderDragonRenderer.extractRenderState inline crates/horizon-client/src/game/dragon.rs::Game::dragon_geometry flap, death fade, hurt overlay, nearest-crystal beam and egg distance read there +EndermanRenderer.getRenderOffset missing player: an angry enderman does not jitter sideways (vanilla random offset each frame); no such offset in the mob draw path +EndermanRenderer.extractRenderState inline crates/horizon-client/src/mobs/enderman.rs::anim gap: minor: carried block is drawn by game/mob_blocks.rs, not from this pose +EntityRenderDispatcher.getRenderer skip:other renderer lookup; Horizon dispatches by mob kind in game.rs::Game::entity_geometry and game/creatures.rs::Game::mob_geometry +EntityRenderDispatcher.getAvatarRenderer port crates/horizon-client/src/mobs/render.rs::body slim or wide arm model by skin +EntityRenderDispatcher.shouldRender skip:other frustum culling only; Horizon culls with its own entity culler +EntityRenderDispatcher.extractEntity skip:other render-state wrapper with a crash report; Horizon has no separate extraction stage +EntityRenderDispatcher.fillRendererDetails skip:debug crash report detail +EntityRenderDispatcher.onResourceManagerReload skip:data renderer rebuild on resource reload; Horizon loads its assets from files +EntityRenderer.getBlockLightLevel port crates/horizon-client/src/game/creatures.rs::Game::mob_geometry burning mobs drawn at full light (game/creatures.rs::Game::mob_geometry); other entities use world light +EntityRenderer.submit inline crates/horizon-client/src/game.rs::Game::entity_geometry leash ropes and name tags drawn in their own passes from there +EntityRenderer.shouldShowName port crates/horizon-client/src/game/names.rs::Game::name_tags named mob under the crosshair +EntityRenderer.submitNameDisplay port crates/horizon-client/src/game/names.rs::Game::name_tag_geometry +EntityRenderer.createRenderState skip:java allocates the render-state object +EntityRenderer.extractRenderState inline crates/horizon-client/src/game.rs::Game::entity_geometry positions, invisibility, leash state, outline colour and light read there +EntityRenderer.finalizeRenderState inline crates/horizon-client/src/game.rs::Game::entity_geometry shadow pass after the entities +EntityRenderer.extractShadowPiece port crates/horizon-client/src/shadows.rs::shadow block-top shadow pieces with fall-off +EntityRenderer.getServerSideEntity skip:other no callers in vanilla (dead code) +EntityRenderers.createEntityRenderers skip:data renderer registration table +EntityRenderers.createAvatarRenderers skip:data renderer registration table +EntityRenderers.validateRegistrations skip:debug startup check that every type has a renderer +EvokerFangsRenderer.submit port crates/horizon-client/src/game/fangs.rs::Game::fangs_geometry bite progress and facing +EvokerFangsRenderer.extractRenderState inline crates/horizon-client/src/game/fangs.rs::Game::fangs_geometry yaw and bite progress read there +ExperienceOrbRenderer.submit inline crates/horizon-client/src/game.rs::Game::entity_geometry orb billboards with the pulsing colour, in the entity pass +ExperienceOrbRenderer.vertex inline crates/horizon-client/src/game.rs::Game::entity_geometry vertex built inline +FallingBlockRenderer.shouldRender missing minor: a falling block inside an identical block (vanilla hides it that tick) is still drawn; no such check in game.rs or falling_block.rs +FallingBlockRenderer.submit inline crates/horizon-client/src/game.rs::Game::entity_geometry falling block pass with its baked block model +FallingBlockRenderer.extractRenderState inline crates/horizon-client/src/game.rs::Game::entity_geometry position, state, biome tint and light read there +FireworkEntityRenderer.extractRenderState port crates/horizon-client/src/game/fireworks.rs::Game::rocket_geometry angled rocket keeps its direction +FishingHookRenderer.shouldRender inline crates/horizon-client/src/game/fishing.rs::Game::fishing_geometry hook drawn only with an owner +FishingHookRenderer.submit port crates/horizon-client/src/game/fishing.rs::Game::fishing_geometry hook quad and line +FishingHookRenderer.vertex inline crates/horizon-client/src/game/fishing.rs::Game::fishing_geometry hook quad vertex +FishingHookRenderer.stringVertex inline crates/horizon-client/src/game/fishing.rs::Game::fishing_line line strip vertices +FishingHookRenderer.extractRenderState port crates/horizon-client/src/game/fishing.rs::Game::rod_hand_position line origin from the rod hand +FoxRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::fox crouch, sleep, sit, faceplant, pounce and head roll +FrogRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::frog jump, croak, tongue and swim animations; climate texture in mobs/render.rs::body +GiantMobRenderer.extractRenderState port crates/horizon-client/src/mobs/anim.rs::humanoid gap: minor: giant model comes from the species table (giant#main), not a separate renderer +GlowSquidRenderer.getBlockLightLevel port crates/horizon-client/src/game/creatures.rs::Game::mob_geometry gap: player: glow squid dark-tick light is not read, so its body never brightens in the dark +GoatRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::goat horns and ramming head pitch +GuardianRenderer.shouldRender skip:other frustum culling of the beam only; the guardian body is culled by the entity culler +GuardianRenderer.getPosition inline crates/horizon-client/src/mobs/guardian.rs::beam_geometry target and eye positions +GuardianRenderer.submit port crates/horizon-client/src/mobs/guardian.rs::beam_geometry beam to the attack target +GuardianRenderer.vertex inline crates/horizon-client/src/mobs/guardian.rs::beam_geometry vertex built inline +GuardianRenderer.extractRenderState port crates/horizon-client/src/mobs/guardian.rs::beam beam attack scale and time +HappyGhastRenderer.getBoundingBoxForCulling skip:other culling box only +HappyGhastRenderer.extractRenderState port crates/horizon-client/src/mobs/happy_ghast.rs::wearing_harness gap: player: the body item (harness) is not drawn on the ghast; only the harnessed state is read +HoglinRenderer.isShaking port crates/horizon-client/src/mobs/render.rs::shaking converting hoglin shakes +HorseRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body variant and baby texture +HorseRenderer.extractRenderState port crates/horizon-client/src/mobs/equine.rs::markings variant, markings and armour layer +HumanoidMobRenderer.getArmPose port crates/horizon-client/src/mobs/anim.rs::pose_arm spear and empty arm poses +HumanoidMobRenderer.usesSpearPose inline crates/horizon-client/src/mobs/anim.rs::pose_arm spear pose check folded into the arm pose +HumanoidMobRenderer.extractRenderState port crates/horizon-client/src/mobs/anim.rs::humanoid arm poses for both arms +HumanoidMobRenderer.extractHumanoidRenderState port crates/horizon-client/src/mobs/anim.rs::humanoid crouch, swim, use-item hand, elytra and armour state +HumanoidMobRenderer.getEquipmentIfRenderable port crates/horizon-client/src/mobs/render.rs::humanoid_armor armour slot shown only when renderable +IllagerRenderer.extractRenderState port crates/horizon-client/src/mobs/illager.rs::holding_crossbow crossbow charge and aggressive pose +IllusionerRenderer.extractRenderState port crates/horizon-client/src/mobs/illusioner.rs::offsets mirror-image offsets; casting flag +IronGolemRenderer.extractRenderState port crates/horizon-client/src/mobs/golem.rs::client_tick gap: player: the offered poppy is not drawn in the golem hand and cracks from damage are not drawn +IronGolemRenderer.setupRotations missing player: an iron golem does not tilt side to side as it walks (IronGolemRenderer.setupRotations); only arm and leg swing is ported in mobs/golem.rs +ItemEntityRenderer.extractRenderState port crates/horizon-client/src/game/item_draw.rs::Game::dropped_item_geometry bob offset, spin and spread +ItemEntityRenderer.renderMultipleFromCount port crates/horizon-client/src/item_render.rs::cluster_poses copies of a stack laid out by depth +ItemFrameRenderer.submit port crates/horizon-client/src/game/frames.rs::Game::frame_geometry frame model, item or map +ItemFrameRenderer.getRenderOffset inline crates/horizon-client/src/game/frames.rs::Game::frame_geometry offset in front of the wall +ItemFrameRenderer.extractRenderState inline crates/horizon-client/src/game/frames.rs::Game::frame_geometry direction, item, rotation, glow and map read there +LeashKnotRenderer.submit port crates/horizon-client/src/game/leads.rs::Game::leash_geometry knot drawn with the rope pass +LightningBoltRenderer.quad inline crates/horizon-client/src/gfx/lightning.rs::geometry quad built inline +LivingEntityRenderer.getBoundingBoxForCulling skip:other culling box only (dragon-head inflate) +LivingEntityRenderer.isEntityUpsideDown port crates/horizon-client/src/mobs/render.rs::base_transform_at upside-down flip for Dinnerbone and Grumm +LivingEntityRenderer.isUpsideDownName inline crates/horizon-client/src/mobs/render.rs::base_transform_at name check folded into the flip +LivingEntityRenderer.extractNameTags port crates/horizon-client/src/game/names.rs::Game::name_tags gap: minor: unsure whether the name-tag distance attributes are read +LivingEntityRenderer.solveBodyRot missing minor: unsure: no rider body-rotation clamp (85 degrees, 50 degree lag) found; mob AI turn limits only +LlamaRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body variant and baby texture +LlamaRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::llama chest flag; decor layers in mobs/render.rs::layers +LlamaSpitRenderer.submit port crates/horizon-client/src/game/creatures.rs::Game::mob_geometry gap: minor: drawn as a camera-facing sprite, not the spit model with its rotation +LlamaSpitRenderer.extractRenderState inline crates/horizon-client/src/game/creatures.rs::Game::mob_geometry rotation read there +MobRenderer.checkMagicName inline crates/horizon-client/src/mobs/render.rs::body gap: player: Toast is handled; a sheep named jeb_ does not get rainbow wool +MushroomCowRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body variant and baby texture +MushroomCowRenderer.extractRenderState port crates/horizon-client/src/mobs/render.rs::body gap: player: the mushroom block on the back is not drawn +NautilusRenderer.extractRenderState port crates/horizon-client/src/mobs/render.rs::layers saddle and armour layers +OcelotRenderer.extractRenderState missing player: an ocelot keeps the standing pose when crouching or sprinting; no ocelot pose code in mobs/ +OminousItemSpawnerRenderer.extractRenderState inline crates/horizon-client/src/game/ominous_item_spawner.rs::Game::ominous_item_spawner_geometry item and age read there +OminousItemSpawnerRenderer.submit port crates/horizon-client/src/game/ominous_item_spawner.rs::Game::ominous_item_spawner_geometry spinning item cluster, scaled in during the first 50 ticks +PaintingRenderer.submit port crates/horizon-client/src/game/paintings.rs::Game::painting_geometry +PaintingRenderer.extractRenderState inline crates/horizon-client/src/game/paintings.rs::Game::painting_geometry per-segment light read there +PaintingRenderer.renderPainting port crates/horizon-client/src/game/paintings.rs::Game::painting_geometry front, back and edge quads +PaintingRenderer.vertex inline crates/horizon-client/src/game/paintings.rs::Game::painting_geometry vertex built inline +PandaRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::panda sit, lie, sneeze and roll states +ParrotRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::parrot pose and flap angle +PhantomRenderer.extractRenderState port crates/horizon-client/src/mobs/phantom.rs::anim gap: player: phantom size is not read, so phantoms never grow +PhantomRenderer.scale missing player: phantoms are never drawn larger (vanilla scale 1 + 0.15 x size); no phantom size in mobs/render.rs::species_scale +PhantomRenderer.setupRotations port crates/horizon-client/src/mobs/phantom.rs::anim gap: minor: body pitch (xRot) is not applied; wings and tail are +PigRenderer.bakeModels skip:data model baking; Horizon loads model data from files +PigRenderer.submit port crates/horizon-client/src/mobs/render.rs::body cold or normal model by variant +PigRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body variant and baby texture +PigRenderer.extractRenderState port crates/horizon-client/src/mobs/render.rs::layers saddle layer and variant +PiglinRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body brute, baby and zombified texture +PiglinRenderer.extractRenderState port crates/horizon-client/src/mobs/anim.rs::humanoid brute flag, arm pose and crossbow charge +PiglinRenderer.isShaking port crates/horizon-client/src/mobs/render.rs::shaking converting piglin shakes +PolarBearRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::polar_bear standing scale +PufferfishRenderer.submit port crates/horizon-client/src/mobs/render.rs::body small, medium or big model by size +PufferfishRenderer.setupRotations missing player: a pufferfish does not bob up and down (cos bob, 0.08 blocks); no such offset in mobs/anim.rs, body_anim.rs or aquatic.rs +RabbitRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::rabbit hop and idle head-tilt animations; Toast name in mobs/render.rs::body +RavagerRenderer.extractRenderState port crates/horizon-client/src/mobs/ravager.rs::anim stun, bite and roar clocks +SalmonRenderer.setupRotations missing player: salmon do not wiggle their body while swimming or flop on land (SalmonRenderer.setupRotations); no 4.3 amplitude or flop pose in mobs/ +SheepRenderer.extractRenderState port crates/horizon-client/src/mobs/render.rs::sheep_wool_rgb gap: player: sheep named jeb_ do not cycle their wool colour; head eat and sheared state read elsewhere +ShulkerBulletRenderer.submit port crates/horizon-client/src/game/creatures.rs::Game::mob_geometry gap: minor: drawn as a spark sprite, not the spinning two-layer model +ShulkerBulletRenderer.extractRenderState inline crates/horizon-client/src/game/creatures.rs::Game::mob_geometry rotation read there +ShulkerRenderer.shouldRender skip:other frustum culling only +ShulkerRenderer.getTextureLocation port crates/horizon-client/src/mobs/shulker.rs::texture colour texture +ShulkerRenderer.extractRenderState port crates/horizon-client/src/mobs/shulker.rs::rotations peek and head angles +SnifferRenderer.extractRenderState port crates/horizon-client/src/mobs/sniffer.rs::anim searching state and animation clocks +SnowGolemRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::snow_golem gap: player: a carved pumpkin on the head is not drawn; the pumpkin flag is not read +SquidRenderer.extractRenderState port crates/horizon-client/src/mobs/aquatic.rs::squid_ai_step tentacle cycle clock +SquidRenderer.setupRotations missing player: a squid does not tilt its body while swimming (xBodyRot, zBodyRot); only the vector rotation for ink exists in mobs/aquatic.rs +StriderRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body cold texture when suffocating +StriderRenderer.getShadowRadius missing minor: a baby strider keeps the full shadow (shadows.rs::shadow_params takes the species only) +StriderRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::strider saddle, suffocating and ridden states +StriderRenderer.isShaking port crates/horizon-client/src/mobs/render.rs::shaking suffocating strider shakes +SulfurCubeRenderer.submit port crates/horizon-client/src/mobs/sulfur_cube.rs::body normal or small model +SulfurCubeRenderer.extractRenderState port crates/horizon-client/src/game/sulfur_cubes.rs::Game::sulfur_cube_body_geometry fuse flash, contained block and worn head +SulfurCubeRenderer.applySizeAndSquish port crates/horizon-client/src/mobs/sulfur_cube.rs::scale size and squish scale +ThrownItemRenderer.submit port crates/horizon-client/src/game/creatures.rs::Game::mob_geometry thrown item sprite at its scale +ThrownItemRenderer.extractRenderState inline crates/horizon-client/src/game/creatures.rs::Game::mob_geometry item sprite read there +ThrownTridentRenderer.submit port crates/horizon-client/src/game/trident.rs::Game::trident_geometry gap: minor: foil glint is not drawn on a thrown trident +ThrownTridentRenderer.extractRenderState inline crates/horizon-client/src/game/trident.rs::Game::trident_geometry yaw, pitch and foil read there +TippableArrowRenderer.extractRenderState missing player: a tipped arrow looks like a plain arrow; no potion colour on arrows in game/creatures.rs or projectiles.rs +TntMinecartRenderer.submitMinecartContents port crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry TNT block swell and white flash +TntMinecartRenderer.submitWhiteSolidBlock inline crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry white overlay built inline +TntMinecartRenderer.extractRenderState inline crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry fuse read from the cart +TntRenderer.submit port crates/horizon-client/src/game/tnt.rs::Game::tnt_geometry swell and white flash +TntRenderer.getSwellAmount inline crates/horizon-client/src/game/tnt.rs::Game::tnt_geometry swell curve folded in +TntRenderer.isLit inline crates/horizon-client/src/game/tnt.rs::Game::tnt_geometry flash every 5 ticks +TntRenderer.extractRenderState inline crates/horizon-client/src/game/tnt.rs::Game::tnt_geometry fuse read there +TropicalFishRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body small or large texture +TropicalFishRenderer.extractRenderState port crates/horizon-client/src/mobs/render.rs::body_color pattern colours +TropicalFishRenderer.submit port crates/horizon-client/src/mobs/render.rs::body small or large model +TropicalFishRenderer.setupRotations missing player: a tropical fish does not wiggle its body while swimming or flop on land (see SalmonRenderer.setupRotations); no 4.3 amplitude in mobs/ +TurtleRenderer.getShadowRadius missing minor: a baby turtle keeps the full shadow (shadows.rs::shadow_params takes the species only) +TurtleRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::turtle on-land, laying and egg states +VexRenderer.extractRenderState port crates/horizon-client/src/mobs/vex.rs::anim charging pose; held item in mobs/render.rs::hand_base diff --git a/tools/port_map/client-005.tsv b/tools/port_map/client-005.tsv new file mode 100644 index 0000000000..208e695dac --- /dev/null +++ b/tools/port_map/client-005.tsv @@ -0,0 +1,149 @@ +VillagerRenderer.getShadowRadius missing minor: baby villagers keep the full shadow radius; shadows.rs shadow_params takes no baby flag +VillagerRenderer.extractRenderState port crates/horizon-client/src/mobs/villager.rs::anim unhappy head shake read here; gap: player: no held item is read (Horizon villagers carry none) +WanderingTraderRenderer.extractRenderState port crates/horizon-client/src/mobs/render.rs::render_with the trader's unhappy counter is never ticked in vanilla (only Villager ticks it), so nothing is read +WardenRenderer. port crates/horizon-client/src/mobs/warden.rs::emissive_layers the four emissive layers: bioluminescent, pulsating spots, tendrils, heart +WardenRenderer.extractRenderState port crates/horizon-client/src/mobs/warden.rs::anim tendril and heart clocks and the animation states (roar, sniff, emerge, dig, attack, sonic boom) +WindChargeRenderer.submit port crates/horizon-client/src/game/wind.rs::Game::wind_charge_geometry gap: player: the wind texture does not scroll (no xOffset), the charge is drawn with a fixed texture +WitchRenderer.extractRenderState port crates/horizon-client/src/mobs/witch.rs::anim nose wobble; gap: player: the held potion is not drawn (see WitchItemLayer.applyTranslation) +WitherBossRenderer.getTextureLocation port crates/horizon-client/src/mobs/wither.rs::texture invulnerable blink texture +WitherBossRenderer.extractRenderState port crates/horizon-client/src/mobs/wither.rs::anim invulnerable clock feeds the texture and scale; the powered glow is render.rs::energy_swirl +WitherSkullRenderer.createSkullLayer skip:data the skull model is baked into entity-models.json +WitherSkullRenderer.submit port crates/horizon-client/src/game/creatures.rs::Game::mob_geometry gap: player: the skull is drawn as a flat sprite (Projectile::item_sprite), not the 3D skull model +WitherSkullRenderer.extractRenderState port crates/horizon-client/src/projectiles.rs::Projectile::item_sprite the dangerous (blue) skull texture is chosen here; gap: player: skull turns are not drawn (sprite only) +WolfRenderer.getModelTint port crates/horizon-client/src/mobs/tame.rs::tint wet shade as a vertex colour +WolfRenderer.extractRenderState port crates/horizon-client/src/mobs/tame.rs::anim tail angle, head roll and shake body roll; texture, collar and armour are read at draw +ZombieNautilusRenderer.bakeModels skip:data models are baked into entity-models.json +ZombieNautilusRenderer.submit port crates/horizon-client/src/mobs/render.rs::body model chosen by variant +ZombieNautilusRenderer.getTextureLocation port crates/horizon-client/src/mobs/render.rs::body texture by variant +ZombieNautilusRenderer.extractRenderState port crates/horizon-client/src/mobs/render.rs::layers saddle and armour layers +ZombieVillagerRenderer.extractRenderState port crates/horizon-client/src/mobs/render.rs::shaking converting shake; type and profession are in layers, aggressive arms in anim +ZombieVillagerRenderer.isShaking port crates/horizon-client/src/mobs/render.rs::shaking converting zombie villagers shake +BlockDecorationLayer.submit missing player: a block placed on a copper golem's antenna is not drawn; Horizon reads the antenna slot only as the flower +BreezeEyesLayer.submit port crates/horizon-client/src/mobs/render.rs::layers breeze eyes layer, full bright +BreezeWindLayer.submit port crates/horizon-client/src/mobs/render.rs::layers gap: player: the wind layer does not scroll (no texture offset, as noted in layers) +CapeLayer.hasLayer missing player: capes are never drawn; skin cape textures are downloaded (skins.rs) but no draw reads them +CapeLayer.submit missing player: capes are never drawn (see CapeLayer.hasLayer) +CarriedBlockLayer.submit port crates/horizon-client/src/game/mob_blocks.rs::Game::mob_block_geometry the enderman's carried block +CatCollarLayer.submit port crates/horizon-client/src/mobs/render.rs::layers dyed collar +CrossedArmsItemLayer.submit missing player: a villager or wandering trader never shows an item in its folded arms (Horizon villagers carry nothing and the villager model has no right_arm for the hand lookup) +CrossedArmsItemLayer.applyTranslation missing player: no crossed-arms item placement (see CrossedArmsItemLayer.submit) +CustomHeadLayer.submit missing player: a skull or head in the head slot (player, zombie, creeper, dragon heads) is not drawn on players or mobs; only the snow golem pumpkin and mooshroom mushrooms are placed +CustomHeadLayer.resolveSkullRenderType missing player: player-head skins on worn heads are never resolved (no worn heads are drawn, see CustomHeadLayer.submit) +Deadmau5EarsLayer.submit missing minor: only a player named deadmau5 gets ears; the name tag lift for them is missing too +DolphinCarryingItemLayer.submit missing player: a dolphin carrying an item shows nothing (the dolphin model has no arm bones for the hand lookup) +DrownedOuterLayer.submit port crates/horizon-client/src/mobs/render.rs::layers drowned outer layer, adult and baby +EnergySwirlLayer.submit port crates/horizon-client/src/mobs/render.rs::energy_swirl charged creeper and powered wither swirl, drawn in render_visible +EquipmentLayerRenderer.renderLayers port crates/horizon-client/src/game/armor_stands.rs::Game::armor_layers gap: player: the leather overlay (layer 1 of the equipment asset) is never drawn, only layer 0 +EquipmentLayerRenderer.getColorForLayer port crates/horizon-client/src/game/armor_stands.rs::Game::armor_layers gap: player: a dyed leather piece is drawn in the undyed brown; the stack's dyed colour is not read +EquipmentLayerRenderer.TrimTextureKey.createTextureWithNoPalette port crates/horizon-client/src/trims.rs::TrimTexture::generate a trim with no palette is used as its own texture +EyesLayer.submit port crates/horizon-client/src/mobs/render.rs::layers spider, enderman and phantom eyes +FoxHeldItemLayer.submit missing player: a fox's food in its mouth (berries, fish) is not drawn; the fox model has no arm bones for the hand lookup +HorseMarkingLayer.submit port crates/horizon-client/src/mobs/render.rs::layers horse markings layer +HumanoidArmorLayer.shouldRender port crates/horizon-client/src/mobs/render.rs::humanoid_armor slot read from the item id suffix (_helmet and so on); players use Game::armor_layers +HumanoidArmorLayer.submit port crates/horizon-client/src/mobs/render.rs::humanoid_armor armour for mobs; players use Game::armor_layers +HumanoidArmorLayer.renderArmorPiece port crates/horizon-client/src/mobs/render.rs::humanoid_armor one armour piece per slot with its layer model +HumanoidArmorLayer.getArmorModel port crates/horizon-client/src/mobs/render.rs::humanoid_armor armour model is the body's #helmet, #chestplate, #leggings or #boots part +IronGolemCrackinessLayer.submit port crates/horizon-client/src/mobs/render.rs::layers crack levels via golem::crackiness +IronGolemFlowerLayer.submit missing player: an iron golem offering a poppy raises its arm (golem anim) but no poppy is drawn in it +ItemInHandLayer.submit port crates/horizon-client/src/game/mob_blocks.rs::Game::held_item_geometry both hands through the item pipeline +ItemInHandLayer.useBabyOffset port crates/horizon-client/src/mobs/render.rs::hand_base baby hand offset +LivingEntityEmissiveLayer.submit port crates/horizon-client/src/mobs/render.rs::layers emissive layers drawn full bright; warden alpha is in warden::emissive_layers +LlamaDecorLayer.submit port crates/horizon-client/src/mobs/llama.rs::decor carpet, or the trader llama's blanket +LlamaDecorLayer.renderEquipment port crates/horizon-client/src/mobs/llama.rs::decor same decor model and texture +MushroomCowMushroomLayer.submitMushroomBlock port crates/horizon-client/src/game/mob_blocks.rs::Game::worn_block_geometry mushrooms on an adult mooshroom +PandaHoldsItemLayer.submit port crates/horizon-client/src/mobs/panda.rs::item_offset item in the mouth of a sitting panda, with the eating bob +ParrotOnShoulderLayer.submit missing player: a tamed parrot on a player's shoulder is not drawn +ParrotOnShoulderLayer.submitOnShoulder missing player: a parrot on a player's shoulder is not drawn (see ParrotOnShoulderLayer.submit) +PlayerItemInHandLayer.submitArmWithItem port crates/horizon-client/src/game/mob_blocks.rs::Game::held_item_geometry gap: player: the spyglass raised to the eye is not drawn (see renderItemHeldToEye) +PlayerItemInHandLayer.renderItemHeldToEye missing player: another player using a spyglass does not hold it at the eye in third person; Horizon has no held-on-head state +RenderLayer.coloredCutoutModelCopyLayerRender port crates/horizon-client/src/mobs/render.rs::render_visible coloured cutout layers drawn in render_visible +RenderLayer.renderColoredCutoutModel port crates/horizon-client/src/mobs/render.rs::render_visible coloured cutout layers drawn in render_visible +RopesLayer.submit missing player: a harnessed happy ghast that holds a leash shows no rope model on its body +SheepWoolLayer.submit port crates/horizon-client/src/mobs/render.rs::layers gap: minor: the outline-only pass for an invisible glowing sheep is not drawn +SheepWoolUndercoatLayer.submit port crates/horizon-client/src/mobs/render.rs::layers gap: player: a sheep named jeb_ gets no undercoat while its wool is white (vanilla always draws it for jeb_) +SimpleEquipmentLayer.submit port crates/horizon-client/src/mobs/render.rs::layers pig, strider and nautilus saddles and the happy ghast harness +SkeletonClothingLayer.submit port crates/horizon-client/src/mobs/render.rs::layers stray and bogged overlay +SlimeOuterLayer.submit port crates/horizon-client/src/mobs/render.rs::layers translucent outer slime cube +SnowGolemHeadLayer.submit port crates/horizon-client/src/game/mob_blocks.rs::Game::worn_block_geometry carved pumpkin on the snow golem +SpinAttackEffectLayer.submit port crates/horizon-client/src/game/perspective.rs::Game::spin_attack_geometry riptide spin model +StuckInBodyLayer.submitStuckItem missing player: arrows stuck in a player's body are not drawn (Horizon keeps no stuck-arrow count) +StuckInBodyLayer.submit missing player: arrows stuck in a player's body are not drawn (see submitStuckItem) +SulfurCubeInnerLayer.submit port crates/horizon-client/src/mobs/render.rs::layers gap: player: a block swallowed by a sulfur cube is not drawn inside the shell (sulfur_cube::inner returns nothing when the cube has a body) +TropicalFishPatternLayer.submit port crates/horizon-client/src/mobs/render.rs::layers pattern on the small or large model, tinted +VillagerProfessionLayer.submit port crates/horizon-client/src/mobs/render.rs::layers type, profession and level layers; hat masks applied in render_visible +VillagerProfessionLayer.getIdentifier port crates/horizon-client/src/mobs/render.rs::layers texture paths built per type, profession and level +VillagerProfessionLayer.getHatData port crates/horizon-client/src/mobs/villager_hats.rs::VillagerHats::load hat metadata read from the resources +WingsLayer.submit port crates/horizon-client/src/game/perspective.rs::Game::player_elytra_geometry gap: minor: the wings texture comes from the equipment asset; a skin's own elytra or cape texture is not used +WingsLayer.getPlayerElytraTexture port crates/horizon-client/src/game/perspective.rs::Game::player_elytra_geometry gap: minor: a skin elytra or cape texture is never chosen +WitchItemLayer.applyTranslation missing player: a witch's potion (drunk or about to be thrown) is not drawn; the witch model has no right_arm for the hand lookup +WolfArmorLayer.submit port crates/horizon-client/src/mobs/render.rs::layers gap: minor: the armour texture is fixed to the armadillo scute; the equipment asset is not read +WolfArmorLayer.maybeRenderCracks port crates/horizon-client/src/mobs/render.rs::layers cracks at three damage levels +WolfCollarLayer.submit port crates/horizon-client/src/mobs/render.rs::layers dyed collar +AvatarRenderer. port crates/horizon-client/src/game/creatures.rs::Game::mob_geometry gap: player: no cape, ears, worn head, parrot or stuck-arrow layer; armour, held items, wings and spin are drawn +AvatarRenderer.getRenderOffset port crates/horizon-client/src/game/perspective.rs::Game::tick_player_model gap: minor: other players are not lowered 0.125 when sneaking; only the local model is +AvatarRenderer.submitNameDisplay port crates/horizon-client/src/game/names.rs::Game::name_tag_geometry gap: minor: the 10 unit lift for ears is not applied (no ears) +AvatarRenderer.extractRenderState port crates/horizon-client/src/mobs.rs::Mob::tick_avatar_pose glide count, wings and swim read here; gap: player: arrow and stinger counts, parrots, cape, ears and the spyglass use are not read +AvatarRenderer.shouldShowName port crates/horizon-client/src/game/names.rs::Game::name_tags name tags for players and named mobs +AvatarRenderer.extractFlightData port crates/horizon-client/src/mobs/render.rs::base_transform_at glide time drives the tilt; gap: player: the body does not turn toward the flight direction (no flying yaw) +AvatarRenderer.extractCapeState missing player: capes are never drawn, so the cape flap and lean are never used +AvatarRenderer.renderRightHand port crates/horizon-client/src/game.rs::Game::hand_geometry first-person arm; gap: minor: no 0.1 radian arm tilt or sleeve toggle in hand_geometry +AvatarRenderer.renderLeftHand port crates/horizon-client/src/game.rs::Game::hand_geometry first-person arm; gap: minor: no 0.1 radian arm tilt or sleeve toggle in hand_geometry +AvatarRenderer.renderHand port crates/horizon-client/src/game.rs::Game::hand_geometry first-person arm; gap: minor: no 0.1 radian arm tilt or sleeve toggle in hand_geometry +AvatarRenderer.isEntityUpsideDown port crates/horizon-client/src/mobs/render.rs::base_transform_at Dinnerbone and Grumm flip; gap: minor: the cape-shown check is missing and the flip applies to every mob +ArmedEntityRenderState.getArmPose port crates/horizon-client/src/mobs/anim.rs::setup arm poses are chosen per species and hand inside setup +ArmedEntityRenderState.extractArmedEntityRenderState port crates/horizon-client/src/mobs/anim.rs::state swing and main arm; the held items are read in Game::held_item_geometry +AvatarRenderState.fallFlyingScale port crates/horizon-client/src/mobs/render.rs::base_transform_at glide scale: ticks squared over 100, clamped to 1 +EnderDragonRenderState.getHeadPartYOffset port crates/horizon-client/src/game/dragon.rs::Game::dragon_geometry sitting, landing and the part 6 cases +EntityRenderState.fillCrashReportCategory skip:debug crash report detail only +HumanoidRenderState.ticksUsingItem port crates/horizon-client/src/game/mob_blocks.rs::Game::held_item_geometry use time for the hand that is using an item +ItemClusterRenderState.extractItemGroupRenderState port crates/horizon-client/src/item_render.rs::rendered_amount rendered count of the stack; the seed is taken in cluster_poses +SheepRenderState.getWoolColor port crates/horizon-client/src/mobs/render.rs::sheep_wool_rgb gap: player: a jeb_ sheep does not cycle through the rainbow (ColorLerper) +BabyModelTransform.apply skip:data baby meshes are baked into entity-models.json +HumanoidModel.createMesh skip:data humanoid mesh is baked into entity-models.json +HumanoidModel.createArmorMeshSet skip:data armour meshes are baked into entity-models.json +HumanoidModel.createBaseArmorMesh skip:data armour mesh is baked into entity-models.json +HumanoidModel.createBabyArmorMesh skip:data baby armour mesh is baked into entity-models.json +HumanoidModel.setupAttackAnimation port crates/horizon-client/src/mobs/anim.rs::humanoid body sway and the swinging arm +Model.resetPose port crates/horizon-client/src/mobs/anim.rs::Poses::reset resets every part to its rest pose +QuadrupedModel.createBodyMesh skip:data quadruped mesh is baked into entity-models.json +QuadrupedModel.createLegs skip:data leg meshes are baked into entity-models.json +BatModel.createBodyLayer skip:data bat model is baked into entity-models.json +AllayModel.createBodyLayer skip:data allay model is baked into entity-models.json +AllayModel.translateToHand port crates/horizon-client/src/mobs/render.rs::held_item_transform gap: player: the allay's own hand transform (0.7 scale, forearm offset) is not applied; the humanoid hand offset is used +AdultArmadilloModel.createBodyLayer skip:data armadillo model is baked into entity-models.json +BabyArmadilloModel.createBodyLayer skip:data baby armadillo model is baked into entity-models.json +AdultAxolotlModel.setupAnim port crates/horizon-client/src/mobs/body_anim.rs::axolotl the axolotl pose dispatch +AdultAxolotlModel.setupLayStillOnGroundAnimation port crates/horizon-client/src/mobs/body_anim.rs::axolotl_lay lay-still pose on the ground +AdultAxolotlModel.setupGroundCrawlingAnimation port crates/horizon-client/src/mobs/body_anim.rs::axolotl_crawl ground crawl pose +AdultAxolotlModel.setupWaterHoveringAnimation port crates/horizon-client/src/mobs/body_anim.rs::axolotl_hover hovering in water +AdultAxolotlModel.setupSwimmingAnimation port crates/horizon-client/src/mobs/body_anim.rs::axolotl_swim swimming pose +AdultAxolotlModel.setupPlayDeadAnimation port crates/horizon-client/src/mobs/body_anim.rs::axolotl_dead playing dead +AdultAxolotlModel.applyMirrorLegRotations port crates/horizon-client/src/mobs/body_anim.rs::axolotl mirrored leg rotations, inline in axolotl +AdultBeeModel.bobUpAndDown port crates/horizon-client/src/mobs/body_anim.rs::bee adult antennae wiggle (not for babies) +BeeModel.bobUpAndDown port crates/horizon-client/src/mobs/body_anim.rs::bee bone, bob and leg swing +BeeStingerModel.createBodyLayer skip:data stinger model is baked into entity-models.json +AdultCamelModel.createBodyMesh skip:data camel mesh is baked into entity-models.json +BabyCamelModel.createBodyLayer skip:data baby camel model is baked into entity-models.json +CamelModel.applyHeadRotation port crates/horizon-client/src/mobs/camel.rs::anim head clamp and the jump cooldown lift +CamelSaddleModel.createSaddleLayer skip:data saddle model is baked into entity-models.json +AdultChickenModel.setupAnim port crates/horizon-client/src/mobs/anim.rs::head head pitch and yaw from the state +AdultChickenModel.createBaseChickenModel skip:data chicken mesh is baked into entity-models.json +BabyChickenModel.createBodyLayer skip:data baby chicken model is baked into entity-models.json +ChickenModel.setupAnim port crates/horizon-client/src/mobs/anim.rs::setup leg swing and wing flap +ColdChickenModel.createBodyLayer skip:data cold chicken model is baked into entity-models.json +BabyCowModel.createBodyLayer skip:data baby cow model is baked into entity-models.json +ColdCowModel.createBodyLayer skip:data cold cow model is baked into entity-models.json +CowModel.createBaseCowModel skip:data cow mesh is baked into entity-models.json +WarmCowModel.createBodyLayer skip:data warm cow model is baked into entity-models.json +BabyDolphinModel.createBodyLayer skip:data baby dolphin model is baked into entity-models.json +DolphinModel.createBodyLayer skip:data dolphin model is baked into entity-models.json +DolphinModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim body turn and, while moving, the tail and fin swing; gap: minor: moving is read from horizontal velocity, not the isMoving flag +AbstractEquineModel.createBodyMesh skip:data equine mesh is baked into entity-models.json +AbstractEquineModel.animateHeadPartsPlacement port crates/horizon-client/src/mobs/equine.rs::anim head part placement while eating and standing +BabyDonkeyModel.setupAnim port crates/horizon-client/src/mobs/equine.rs::anim baby donkey head pitch of -30 degrees and head part pose +BabyDonkeyModel.offsetLegPositionWhenStanding port crates/horizon-client/src/mobs/equine.rs::anim hind leg heights when standing +BabyDonkeyModel.animateHeadPartsPlacement port crates/horizon-client/src/mobs/equine.rs::anim baby donkey head part placement +BabyHorseModel.createBabyMesh skip:data baby horse mesh is baked into entity-models.json +BabyHorseModel.animateHeadPartsPlacement port crates/horizon-client/src/mobs/equine.rs::anim baby horse head part placement +DonkeyModel.createBodyLayer skip:data donkey model is baked into entity-models.json +DonkeyModel.setupAnim port crates/horizon-client/src/mobs/equine.rs::anim donkey pose diff --git a/tools/port_map/client-006.tsv b/tools/port_map/client-006.tsv new file mode 100644 index 0000000000..dcc7b88bef --- /dev/null +++ b/tools/port_map/client-006.tsv @@ -0,0 +1,150 @@ +EquineSaddleModel.createSaddleLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +EquineSaddleModel.setupAnim missing player: a saddled horse with nobody riding it shows its reins (left and right saddle lines); vanilla hides them until it is ridden (render.rs draws the whole #saddle layer) +AdultFelineModel.createBodyMesh skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabyFelineModel.createBabyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CodModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CodModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim aquatic setup arm for this species (fin/tail swing by age) +PufferfishBigModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim aquatic setup arm for this species (fin/tail swing by age) +PufferfishMidModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim aquatic setup arm for this species (fin/tail swing by age) +PufferfishSmallModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PufferfishSmallModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim aquatic setup arm for this species (fin/tail swing by age) +SalmonModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +SalmonModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim aquatic setup arm for this species (fin/tail swing by age) +TropicalFishLargeModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +TropicalFishLargeModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim aquatic setup arm for this species (fin/tail swing by age) +TropicalFishSmallModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +TropicalFishSmallModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim aquatic setup arm for this species (fin/tail swing by age) +AdultFoxModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +AdultFoxModel.setSittingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +AdultFoxModel.setSleepingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +AdultFoxModel.setWalkingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +AdultFoxModel.setCrouchingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +AdultFoxModel.setPouncingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +BabyFoxModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabyFoxModel.setSittingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +BabyFoxModel.setSleepingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +BabyFoxModel.setWalkingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +BabyFoxModel.setCrouchingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +FoxModel.setSleepingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +FoxModel.setWalkingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +FoxModel.setCrouchingPose port crates/horizon-client/src/mobs/body_anim.rs::fox fox pose branch (adult and baby) in the one fox pose function +FrogModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +TadpoleModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +TadpoleModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim aquatic setup arm for this species (fin/tail swing by age) +HappyGhastHarnessModel.createHarnessLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +HappyGhastModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabyGoatModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +GoatModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +GoatModel.setupAnim port crates/horizon-client/src/mobs/body_anim.rs::goat horn visibility and ramming head pitch +CopperGolemModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CopperGolemModel.createRunningPoseBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CopperGolemModel.createSittingPoseBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CopperGolemModel.createStarPoseBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CopperGolemModel.translateToHand missing player: an item a copper golem holds sits at the humanoid hand offset; vanilla turns it (IDLE) or scales it to 0.55 with its own grip (mobs/render.rs hand_base has only the vex case) +CopperGolemModel.translateToHead skip:other only CustomHeadLayer calls it, and a copper golem has no head slot (its only extra slot is the antenna) +CopperGolemModel.applyBlockOnAntennaTransform missing player: a block or flower on the copper golem antenna is not drawn; vanilla draws it with BlockDecorationLayer (nothing in mobs/render.rs draws the antenna item) +IronGolemModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +SnowGolemModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +LlamaSpitModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +NautilusArmorModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +NautilusModel.createBodyMesh skip:data geometry baked into entity-models.json, which Horizon loads as a resource +NautilusModel.createBabyBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +NautilusModel.applyBodyRotation inline crates/horizon-client/src/mobs/nautilus.rs::anim body yaw/pitch clamped to 10 degrees, inside the nautilus setupAnim +NautilusSaddleModel.createSaddleLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabyPandaModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PandaModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +ParrotModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +ParrotModel.prepare port crates/horizon-client/src/mobs/body_anim.rs::parrot sitting, flying and party leg/body offsets +ParrotModel.getPose inline crates/horizon-client/src/mobs/body_anim.rs::anim pose chosen where the parrot is animated: party, sitting, flying (not on ground), standing +BabyPigModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +ColdPigModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PigModel.createBasePigModel skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabyPolarBearModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PolarBearModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +AdultRabbitModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabyRabbitModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabySheepModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +SheepFurModel.createFurLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +SheepFurModel.setupAnim port crates/horizon-client/src/mobs/anim.rs::setup Anim::Sheep arm: head lowered while eating grass +SheepModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +SheepModel.setupAnim port crates/horizon-client/src/mobs/anim.rs::setup Anim::Sheep arm: head lowered while eating grass +SnifferModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +SniffletModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabySquidModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +SquidModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +SquidModel.setupAnim port crates/horizon-client/src/mobs/aquatic.rs::anim aquatic setup arm for this species (fin/tail swing by age) +AdultTurtleModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabyTurtleModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +TurtleModel.setupAnim port crates/horizon-client/src/mobs/body_anim.rs::turtle walk swing on land, paddling in water, egg belly +AdultWolfModel.setSittingPose port crates/horizon-client/src/mobs/tame.rs::anim adult upper body raised when sitting +AdultWolfModel.shakeOffWater port crates/horizon-client/src/mobs/tame.rs::anim shake roll of head, body and tail (body_roll) +BabyWolfModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabyWolfModel.shakeOffWater port crates/horizon-client/src/mobs/tame.rs::anim shake roll of head, body and tail (body_roll) +BabyWolfModel.setSittingPose port crates/horizon-client/src/mobs/tame.rs::anim baby sitting pose: body xRot minus pi/2 +WolfModel.setSittingPose port crates/horizon-client/src/mobs/tame.rs::anim sitting pose, adult and baby +EvokerFangsModel.setupAnim port crates/horizon-client/src/game/fangs.rs::Game::fangs_geometry jaws, base sink and pre-scale +SpearAnimations.thirdPersonAttackHand missing player: a spear stab shows the whack swing on other players and mobs; the arm does not thrust (STAB arm pose absent, only WHACK in mobs/anim.rs) +SpearAnimations.firstPersonAttack missing player: when you stab with a spear your own hand does not thrust forward; the default swing plays instead (first_person.rs has no STAB pose) +SpearAnimations.UseParams.fromKineticWeapon port crates/horizon-client/src/spear.rs::UseParams::of the charge timeline and sway values +SpinAttackEffectModel.createLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +EntityModelSet.bakeLayer skip:data baking a layer; Horizon loads each layer from entity-models.json via load_model +LayerDefinitions.createRoots skip:data layer registry; Horizon names layers by string key (minecraft:x#main) +ModelLayers.register skip:data layer id registry; Horizon uses string keys +ModelLayers.registerArmorSet skip:data layer id registry; Horizon uses string keys +ModelPart.storePose skip:other no callers in vanilla; a snapshot of the current pose that nothing reads +ModelPart.loadPose port crates/horizon-client/src/mobs.rs::Part::parse reads the pose from the model data; the reset per frame is Poses::reset +ModelPart.getChild port crates/horizon-client/src/block_entity_render.rs::ModelPart::child named child lookup (Option instead of a throw) +ModelPart.compile port crates/horizon-client/src/mobs/render.rs::draw_normals emits each polygon with the posed transform; skipDraw gating +ModelPart.offsetRotation port crates/horizon-client/src/mobs/keyframes.rs::Animation::apply Target::Rotation channel adds to xRot/yRot/zRot +ModelPart.offsetScale port crates/horizon-client/src/mobs/keyframes.rs::Animation::apply Target::Scale channel adds to the scale +ModelPart.getAllParts inline crates/horizon-client/src/mobs/anim.rs::Poses::reset the part list Model.resetPose walks; Poses::reset refills the whole name map instead +ModelPart.createPartLookup inline crates/horizon-client/src/mobs/anim.rs::Poses::reset gap: minor: duplicate part names keep the last part here, the first in vanilla (the name map is built by the same walk) +ModelPart.addAllChildren inline crates/horizon-client/src/mobs/anim.rs::Poses::reset gap: minor: duplicate part names keep the last part here, the first in vanilla (the name map is built by the same walk) +ModelPart.Cube. skip:data geometry baked into entity-models.json, which Horizon loads as a resource (cube faces are precomputed polygons) +ModelPart.Cube.compile port crates/horizon-client/src/mobs/render.rs::draw_normals six faces emitted from the posed part (polygons in Horizon) +ModelPart.Polygon. skip:data geometry baked into entity-models.json, which Horizon loads as a resource (polygons are precomputed) +ModelPart.Vertex.remap skip:data UV normalisation done when entity-models.json is generated +PartPose.offsetAndRotation skip:data pose builder; baked poses come from entity-models.json +PartPose.translated skip:data pose builder; baked poses come from entity-models.json +PartPose.withScale skip:data pose builder; baked poses come from entity-models.json +PartPose.scaled skip:data pose builder; baked poses come from entity-models.json +CubeDefinition.bake skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CubeDeformation.extend skip:java builder value for cube inflation, folded into the baked geometry +CubeListBuilder.addBox skip:data geometry baked into entity-models.json, which Horizon loads as a resource +LayerDefinition.bakeRoot skip:data geometry baked into entity-models.json, which Horizon loads as a resource +MeshTransformer.scaling skip:data baby scaling applied when entity-models.json is generated +PartDefinition.addOrReplaceChild skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PartDefinition.clearRecursively skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PartDefinition.clearChild skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PartDefinition.retainPartsAndChildren skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PartDefinition.retainExactParts skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PartDefinition.bake skip:data geometry baked into entity-models.json, which Horizon loads as a resource +PartDefinition.transformed skip:data geometry baked into entity-models.json, which Horizon loads as a resource +UVPair.pack skip:java packs two texture coordinates for the builder; Horizon stores normalised UVs +BlazeModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BreezeModel.createBaseMesh skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BreezeModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BreezeModel.createWindLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BreezeModel.createEyesLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CreakingModel.createMesh skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CreakingModel.createEyesLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CreeperModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +CreeperModel.setupAnim port crates/horizon-client/src/mobs/anim.rs::setup Anim::Creeper arm: head and legs +EnderDragonModel. skip:data geometry baked into entity-models.json, which Horizon loads as a resource +EnderDragonModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +EnderDragonModel.poseLimbs inline crates/horizon-client/src/game/dragon.rs::Game::dragon_geometry leg and foot pitches inside the dragon pose +EndermanModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +EndermiteModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +GhastModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +GuardianModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +GuardianModel.setupSpikes inline crates/horizon-client/src/mobs/guardian.rs::anim spike offsets and positions, inside the guardian setupAnim +GuardianModel.getSpikeOffset inline crates/horizon-client/src/mobs/guardian.rs::anim spike offsets and positions, inside the guardian setupAnim +GuardianModel.getSpikeY inline crates/horizon-client/src/mobs/guardian.rs::anim spike offsets and positions, inside the guardian setupAnim +BabyHoglinModel.animateHeadbutt inline crates/horizon-client/src/mobs/piglin.rs::hoglin_anim baby head lift in the headbutt; adult head lerp is in the same fn +BabyHoglinModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +HoglinModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +IllagerModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +AbstractPiglinModel.removeEars skip:data geometry baked into entity-models.json, which Horizon loads as a resource (ears are part of the baked piglin head) +AbstractPiglinModel.addHead skip:data geometry baked into entity-models.json, which Horizon loads as a resource +AdultPiglinModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource +BabyPiglinModel.createBodyLayer skip:data geometry baked into entity-models.json, which Horizon loads as a resource diff --git a/tools/port_map/client-007.tsv b/tools/port_map/client-007.tsv new file mode 100644 index 0000000000..97f9962223 --- /dev/null +++ b/tools/port_map/client-007.tsv @@ -0,0 +1,149 @@ +PiglinModel.setupAttackAnimation port crates/horizon-client/src/mobs/anim.rs::humanoid melee swing branch via weapon_swing, which piglin_pose sets +PiglinModel.holdWeaponHigh port crates/horizon-client/src/mobs/piglin.rs::piglin_pose holdWeaponHigh arm pose when the swing is at rest +ZombifiedPiglinModel.setupAttackAnimation port crates/horizon-client/src/mobs/anim.rs::humanoid zombie arm raise via the zombie flag (Anim::ZombifiedPiglin) +RavagerModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +ShulkerModel.createShellMesh skip:data layer baked into entity-models.json, loaded by key +ShulkerModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +SilverfishModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +BoggedModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +BoggedModel.setupAnim missing player: a sheared bogged keeps its mushrooms on its head; Bogged shearing sets sheared but nothing hides the mushrooms part +SkeletonModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +SkeletonModel.createDefaultSkeletonMesh skip:data layer baked into entity-models.json, loaded by key +SkeletonModel.createSingleModelDualBodyLayer skip:data layer baked into entity-models.json, loaded by key +SkeletonModel.setupAnim port crates/horizon-client/src/mobs/anim.rs::setup skeleton melee pose in the Skeleton branch of setup +SkeletonModel.translateToHand port crates/horizon-client/src/mobs/render.rs::hand_base held item hand offset for skeletons (plus or minus one pixel) +MagmaCubeModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +SlimeModel.createOuterBodyLayer skip:data layer baked into entity-models.json, loaded by key +SlimeModel.createInnerBodyLayer skip:data layer baked into entity-models.json, loaded by key +SmallSulfurCubeModel.createOuterBodyLayer skip:data layer baked into entity-models.json, loaded by key +SmallSulfurCubeModel.createInnerBodyLayer skip:data layer baked into entity-models.json, loaded by key +SulfurCubeModel.createOuterBodyLayer skip:data layer baked into entity-models.json, loaded by key +SulfurCubeModel.createInnerBodyLayer skip:data layer baked into entity-models.json, loaded by key +SpiderModel.createSpiderBodyLayer skip:data layer baked into entity-models.json, loaded by key +SpiderModel.setupAnim port crates/horizon-client/src/mobs/anim.rs::setup Spider branch of setup: four leg pairs swing and step +AdultStriderModel.customAnimations port crates/horizon-client/src/mobs/body_anim.rs::strider adult bristles on z, body and leg bob, shared with the baby +BabyStriderModel.customAnimations port crates/horizon-client/src/mobs/body_anim.rs::strider baby bristles on x, body and leg bob, ridden flag not used +VexModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +VexModel.setArmsCharging inline crates/horizon-client/src/mobs/vex.rs::anim charging arm pose inside the flag branch of vex anim +WardenModel.createBioluminescentLayer skip:data layer baked into entity-models.json, loaded by key +WardenModel.createPulsatingSpotsLayer skip:data layer baked into entity-models.json, loaded by key +WardenModel.animateHeadLookTarget inline crates/horizon-client/src/mobs/warden.rs::anim head yaw and pitch set first in warden anim +WardenModel.animateIdlePose inline crates/horizon-client/src/mobs/warden.rs::anim head and body wobble in warden anim +WardenModel.animateWalk inline crates/horizon-client/src/mobs/warden.rs::anim walk swing, arm and body tilt in warden anim +WardenModel.resetArmPoses inline crates/horizon-client/src/mobs/warden.rs::anim arm rest positions reset in warden anim +WardenModel.animateTendrils inline crates/horizon-client/src/mobs/warden.rs::anim tendril sway from the tendril animation value +WitchModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +WitherBossModel.setupHeadRotation inline crates/horizon-client/src/mobs/wither.rs::anim side head yaw minus body yaw in wither anim +BabyZombieModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +BabyZombieVillagerModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +BabyZombieVillagerModel.createNoHatLayer skip:data layer baked into entity-models.json, loaded by key +DrownedModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +DrownedModel.setupAttackAnimation port crates/horizon-client/src/mobs/anim.rs::humanoid zombie arm raise via the zombie flag (Anim::Drowned) +GiantZombieModel.setupAttackAnimation port crates/horizon-client/src/mobs/anim.rs::humanoid giant is Humanoid with zombie_arms, same zombie arm raise +ZombieVillagerModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +ZombieVillagerModel.createNoHatLayer skip:data layer baked into entity-models.json, loaded by key +ZombieVillagerModel.createArmorLayerSet skip:data layer baked into entity-models.json, loaded by key +ZombieVillagerModel.createBaseArmorMesh skip:data layer baked into entity-models.json, loaded by key +ZombieVillagerModel.setupAttackAnimation port crates/horizon-client/src/mobs/anim.rs::humanoid zombie arm raise via the zombie flag (Anim::ZombieVillager) +BabyVillagerModel.createBodyModel skip:data layer baked into entity-models.json, loaded by key +VillagerModel.createBodyModel skip:data layer baked into entity-models.json, loaded by key +ArmorStandArmorModel.createArmorLayerSet skip:data layer baked into entity-models.json, loaded by key +ArmorStandArmorModel.createBaseMesh skip:data layer baked into entity-models.json, loaded by key +ArmorStandModel.translateToHand port crates/horizon-client/src/game/armor_stands.rs::Game::armor_stand_items held items drawn from the posed arms even when ShowArms hides them +BannerFlagModel.createFlagLayer skip:data layer baked into entity-models.json, loaded by key +BannerModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +BellModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +AbstractBoatModel.setupAnim port crates/horizon-client/src/game/boats.rs::Game::boat_geometry paddle pose from rowing time, in the boat draw +BoatModel.createBoatModel skip:data layer baked into entity-models.json, loaded by key +BoatModel.createChestBoatModel skip:data layer baked into entity-models.json, loaded by key +BoatModel.createWaterPatch skip:data layer baked into entity-models.json, loaded by key +RaftModel.createRaftModel skip:data bamboo raft is boat/bamboo#main in entity-models.json +RaftModel.createChestRaftModel skip:data bamboo chest raft is chest_boat/bamboo#main in entity-models.json +BookModel.State.forAnimation port crates/horizon-client/src/block_entity_anim.rs::EnchantingBook::render openness and page flips computed in EnchantingBook::render +MinecartModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +ChestModel.createSingleBodyLayer skip:data layer baked into entity-models.json, loaded by key +ChestModel.createDoubleBodyRightLayer skip:data layer baked into entity-models.json, loaded by key +ChestModel.createDoubleBodyLeftLayer skip:data layer baked into entity-models.json, loaded by key +CushionModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +ElytraModel.createLayer skip:data layer baked into entity-models.json, loaded by key +ElytraModel.setupAnim port crates/horizon-client/src/game/perspective.rs::Game::player_elytra_geometry wing angles, crouch drop and mirrored right wing +ShieldModel.createLayer skip:data layer baked into entity-models.json, loaded by key +LeashKnotModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +ArrowModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +ArrowModel.setupAnim missing player: an arrow that lands in a block does not wobble for 7 ticks; arrows.rs sets shake but the arrow draw has no roll +ShulkerBulletModel.createBodyLayer missing player: shulker bullets draw as a flat spark sprite, not the tumbling 3D orb; the baked shulker_bullet model is never loaded +ShulkerBulletModel.setupAnim missing player: the bullet's spin and facing toward its flight are absent, since no model is drawn +TridentModel.createLayer skip:data layer baked into entity-models.json, loaded by key +WindChargeModel.createBodyLayer skip:data layer baked into entity-models.json, loaded by key +WindChargeModel.setupAnim port crates/horizon-client/src/game/wind.rs::Game::wind_charge_geometry wind and wind_charge parts spin in opposite directions +DragonHeadModel.createHeadLayer skip:data layer baked into entity-models.json, loaded by key +DragonHeadModel.setupAnim inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one jaw animation in the skull branch of render_one +PiglinHeadModel.setupAnim inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one ear flap in the skull branch of render_one +SkullModel.createHeadModel skip:data layer baked into entity-models.json, loaded by key +SkullModel.createHumanoidHeadLayer skip:data layer baked into entity-models.json, loaded by key +SkullModel.setupAnim inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one head yaw and pitch in the skull branch of render_one +PlayerCapeModel.createCapeLayer missing player: capes never draw; skins.rs downloads the cape texture but nothing draws the cape layer +PlayerCapeModel.setupAnim missing player: the cape does not lean or flap with movement, since no cape is drawn +PlayerEarsModel.createEarsLayer missing minor: extra ears (showExtraEars accounts) are not drawn +PlayerModel.createMesh skip:data layer baked into entity-models.json, loaded by key +PlayerModel.createArmorMeshSet skip:data layer baked into entity-models.json, loaded by key +PlayerModel.setupAnim port crates/horizon-client/src/mobs/anim.rs::setup gap: player: hat, jacket, sleeve and pant toggles (modelPart options) are not read, so a hidden layer still draws; humanoid pose is in the Humanoid branch +PlayerModel.translateToHand port crates/horizon-client/src/mobs/render.rs::hand_base gap: minor: slim-arm 0.5 pixel hand offset not applied +BlockModelLighter.prepareQuadAmbientOcclusion port crates/horizon-client/src/mesher/lighting.rs::ambient_occlusion per-vertex AO brightness and smooth light from the corner samples +BlockModelLighter.prepareQuadFlat port crates/horizon-client/src/mesher/lighting.rs::flat light only; the directional colour is applied in Mesher::face +BlockModelLighter.prepareQuadShape port crates/horizon-client/src/mesher/lighting.rs::shape face bounds, partial and cubic flags +BlockModelLighter.getDirectionalBrightness port crates/horizon-client/src/mesher/lighting.rs::cardinal face shade; the shade direction override is read in Mesher::face +BlockModelLighter.Cache.getLightCoords port crates/horizon-client/src/mesher/lighting.rs::coords packed light for a position +BlockModelLighter.Cache.getShadeBrightness port crates/horizon-client/src/mesher.rs::MeshTables::new shade per block state (Info.shade), not cached per position +BlockModelRenderState.clear skip:java reuse of a pooled render state object +BlockModelRenderState.tintLayers port crates/horizon-client/src/game/minecarts.rs::Game::block_model_vertices gap: minor: the caller tints every face with one sprite tint, not per tint index +BlockModelRenderState.setupModel port crates/horizon-client/src/game/minecarts.rs::Game::block_model_vertices model parts with the transform matrix +BlockModelRenderState.submit port crates/horizon-client/src/game/minecarts.rs::Game::block_model_vertices faces written to the vertex list +BlockModelRenderState.submitModel port crates/horizon-client/src/game/minecarts.rs::Game::block_model_vertices faces written to the vertex list +BlockModelRenderState.scratchRandomSource port crates/horizon-client/src/game/minecarts.rs::Game::block_model_vertices seeded variant choice through collect_seeded +BlockModelResolver.update port crates/horizon-client/src/game/minecarts.rs::Game::block_model_vertices resolves the seeded model for a block state; light is passed in by the caller +BlockModelSet.createFallbackModel port crates/horizon-client/src/mesher.rs::MeshTables::new per-state tint sources (Info.tints) and the plain model +BuiltInBlockModels.special port crates/horizon-client/src/block_entity_render.rs::classify special model name to Kind +BuiltInBlockModels.createMobHead port crates/horizon-client/src/block_entity_render.rs::classify Skull kind for mob heads +BuiltInBlockModels.createMobWallHead port crates/horizon-client/src/block_entity_render.rs::classify Skull kind for wall mob heads +BuiltInBlockModels.createMobHeads port crates/horizon-client/src/block_entity_render.rs::classify Skull kind for the head set +BuiltInBlockModels.createPlayerHead port crates/horizon-client/src/block_entity_render.rs::classify Skull kind for player heads +BuiltInBlockModels.createPlayerWallHead port crates/horizon-client/src/block_entity_render.rs::classify Skull kind for wall player heads +BuiltInBlockModels.createBanner port crates/horizon-client/src/block_entity_render.rs::classify Banner kind +BuiltInBlockModels.createWallBanner port crates/horizon-client/src/block_entity_render.rs::classify Banner kind for wall banners +BuiltInBlockModels.createDyedShulkerBox port crates/horizon-client/src/block_entity_render.rs::classify Shulker kind for dyed boxes +BuiltInBlockModels.createChest port crates/horizon-client/src/block_entity_render.rs::classify Chest kind +BuiltInBlockModels.createSingletonChest port crates/horizon-client/src/block_entity_render.rs::classify Chest kind, single chest +BuiltInBlockModels.createXmasChest port crates/horizon-client/src/block_entity_render.rs::classify Chest kind with the Christmas texture +BuiltInBlockModels.createCopperGolem port crates/horizon-client/src/block_entity_render.rs::classify CopperGolem kind +BuiltInBlockModels.createFlowerBedModel missing minor: the 0.25 block shift for a flower bed shown by a copper golem is not applied +BuiltInBlockModels.createEnchantingTable port crates/horizon-client/src/block_entity_render.rs::classify EnchantingTable kind +BuiltInBlockModels.specialModelWithPropertyDispatch port crates/horizon-client/src/block_entity_render.rs::classify property dispatch by block state values +BuiltInBlockModels.Builder.put skip:java builder map of special models +FluidModel.Unbaked.bake port crates/horizon-client/src/mesher.rs::MeshTables::new gap: minor: water and lava layers hard-wired; translucency picked in fluid tesselate +FluidModel.Unbaked.getAndValidateMaterial port crates/horizon-client/src/mesher.rs::MeshTables::new sprite lookup through layer(); no separate atlas check +FluidRenderer.isFaceOccludedByState port crates/horizon-client/src/mesher/fluid.rs::occluded_by fluid face occlusion by the neighbour's face shape +FluidRenderer.shouldRenderFace port crates/horizon-client/src/mesher/fluid.rs::tesselate render_up, render_down and side checks, neighbour-same-fluid and self occlusion +FluidRenderer.addFace inline crates/horizon-client/src/mesher/fluid.rs::tesselate quad closure in tesselate +FluidRenderer.calculateAverageHeight inline crates/horizon-client/src/mesher/fluid.rs::tesselate average closure in tesselate +FluidRenderer.addWeightedHeight inline crates/horizon-client/src/mesher/fluid.rs::tesselate weighted sum inside the average closure +FluidRenderer.vertex inline crates/horizon-client/src/mesher/fluid.rs::tesselate vertex built in the quad closure +FluidStateModelSet.bake port crates/horizon-client/src/mesher.rs::MeshTables::new fluid layers and tints baked per fluid +LoadedBlockModels.bake port crates/horizon-assets/src/bake.rs::bake_block_model bakes each block model; the loop over the set is the caller's +ModelBlockRenderer.tesselateBlock port crates/horizon-client/src/mesher.rs::Mesher::mesh per block: fluids, models, smooth-light choice +ModelBlockRenderer.configureTintCache port crates/horizon-client/src/mesher.rs::Tints::new per section tint tables +ModelBlockRenderer.resetTintCache inline crates/horizon-client/src/mesher.rs::Tints::new tables are built fresh per section, no reset +ModelBlockRenderer.tesselateAmbientOcclusion port crates/horizon-client/src/mesher.rs::Mesher::face AO branch of face +ModelBlockRenderer.tesselateFlat port crates/horizon-client/src/mesher.rs::Mesher::face flat branch of face +ModelBlockRenderer.shouldRenderFace port crates/horizon-world/src/block_render.rs::FaceVisibility::should_render_face per face visibility, called from Mesher::mesh +ModelBlockRenderer.putQuadWithTint port crates/horizon-client/src/mesher.rs::Mesher::face tinted vertex write +ModelBlockRenderer.getTintColor port crates/horizon-client/src/mesher.rs::Mesher::tint tint source index to colour +ModelBlockRenderer.computeTintColor port crates/horizon-client/src/mesher.rs::Tints::get biome colour average over the blend radius +MovingBlockRenderState.getBlockTint port crates/horizon-client/src/game/moving_pistons.rs::Game::moving_block_vertices gap: minor: tint is the particle colour at the source position, not the biome tint at the moved spot +MovingBlockRenderState.getBlockState port crates/horizon-client/src/game/moving_pistons.rs::Game::moving_piston_blocks moved block and piston base +SelectBlockModel.update missing minor: display-context switch (copper golem hand, flower bed) is not applied; block items use the normal transform +SelectBlockModel.Unbaked.bake missing minor: same display-context switch, not baked +SelectBlockModel.UnbakedSwitch.bake missing minor: same display-context switch, not baked +BlockStateModelDispatcher.instantiate port crates/horizon-assets/src/blockstate/bind.rs::BlockStateDefinition::bind variant selection per block state +BlockStateModelDispatcher.MultiPartDefinition.instantiate port crates/horizon-assets/src/blockstate/bind.rs::BlockStateDefinition::bind multipart selectors bound per state +BlockStateModelDispatcher.SimpleModelSelectors.instantiate port crates/horizon-assets/src/blockstate/bind.rs::BlockStateDefinition::bind variant predicates bound per state diff --git a/tools/port_map/client-008.tsv b/tools/port_map/client-008.tsv new file mode 100644 index 0000000000..91155f9222 --- /dev/null +++ b/tools/port_map/client-008.tsv @@ -0,0 +1,115 @@ +Variant.SimpleModelState.asModelState port crates/horizon-assets/src/bake/transform.rs::BlockModelTransform::new quarter turns from x, y, z plus the uvlock flag, prepared once per rotation +Variant.SimpleModelState.withX skip:java copy-with setter of an immutable record; Variant::decode reads x, y, z directly +Variant.SimpleModelState.withY skip:java copy-with setter of an immutable record; Variant::decode reads x, y, z directly +Variant.SimpleModelState.withZ skip:java copy-with setter of an immutable record; Variant::decode reads x, y, z directly +Variant.SimpleModelState.withUvLock skip:java copy-with setter of an immutable record; Variant::decode reads uvlock directly +VariantMutator.then skip:java composes JSON field setters; Variant::decode reads all fields in one pass +VariantSelector.predicate port crates/horizon-assets/src/blockstate/bind.rs::variant_predicate parses the property clauses; matching against states is in BlockStateDefinition::bind +WeightedVariants.computeMaterialFlags skip:java OR of per-model render flags; Horizon keeps translucency per face (BakedFace in models/catalog.rs) +WeightedVariants.Unbaked.bake inline crates/horizon-client/src/models/catalog.rs::StateModels::load each weighted variant baked once with its transform; the choice is kept in CompiledChoices +WeightedVariants.Unbaked.resolveDependencies inline crates/horizon-client/src/models/catalog.rs::StateModels::load model names gathered from all definitions before the block library loads +CombinedCondition.instantiate port crates/horizon-assets/src/blockstate/bind.rs::BoundCondition::bind AND and OR branches; the test itself is BoundCondition::matches +KeyValueCondition.instantiate port crates/horizon-assets/src/blockstate/bind.rs::BoundCondition::bind property lookup per key and the properties branch +KeyValueCondition.Term.parse port crates/horizon-assets/src/blockstate.rs::Condition::decode reads the leading "!" as negation +KeyValueCondition.Terms.parse port crates/horizon-assets/src/blockstate.rs::Condition::decode splits on "|"; empty terms are rejected +KeyValueCondition.Terms.instantiate port crates/horizon-assets/src/blockstate/bind.rs::BoundCondition::bind each term is checked against the states directly, not via allowed-value lists; same result +KeyValueCondition.Terms.getValueOrThrow port crates/horizon-assets/src/blockstate/bind.rs::PropertyValues::parse an unknown value is an error in both +MultiPartModel.collectParts port crates/horizon-assets/src/blockstate/choice.rs::CompiledChoices::for_each one nextLong seed for the root, reset before each selected part +MultiPartModel.SharedBakedState.getFirstModel port crates/horizon-assets/src/blockstate/choice.rs::CompiledChoices::particle_variant first selector's variant, whether or not it applies +MultiPartModel.SharedBakedState.computeMaterialFlags skip:java same OR of render flags as WeightedVariants; translucency is kept per face +MultiPartModel.SharedBakedState.selectModels port crates/horizon-assets/src/blockstate/bind.rs::BlockStateDefinition::bind selected selector indices per state, computed once at load +MultiPartModel.Unbaked.visualEqualityGroup skip:java key for sharing identical visual states; Horizon keys block states by id +MultiPartModel.Unbaked.resolveDependencies inline crates/horizon-client/src/models/catalog.rs::StateModels::load model names gathered from every multipart selector +MultiPartModel.Unbaked.bake inline crates/horizon-client/src/models/catalog.rs::StateModels::load shared bake per definition; per-state selection stored in states +Selector.instantiate port crates/horizon-assets/src/blockstate/bind.rs::BlockStateDefinition::bind a selector without a condition applies to every state +BlockStateModelWrapper.update port crates/horizon-client/src/game/minecarts.rs::Game::block_model_vertices gap: player: no tint layers, so carried or cart blocks with tint sources (grass, leaves) are drawn untinted +BlockStateModelWrapper.updateTints missing player: carried and cart blocks with tint sources (grass, leaves, water) show untinted; Horizon's block_model_vertices has no tint step +BlockStateModelWrapper.Unbaked.bake skip:java builds the wrapper object; Horizon passes the transform straight to block_model_vertices +CompositeBlockModel.update missing player: only block display entities reach this and Horizon draws none, so a special block (chest and similar) shown that way never appears +CompositeBlockModel.Unbaked.bake skip:java builds the composite object; no separate step in Horizon +ConditionalBlockModel.update inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one Christmas chest texture chosen in the chest branch by extended_christmas() +ConditionalBlockModel.Unbaked.bake skip:java builds the conditional object; the chest branch picks the texture when drawing +SpecialBlockModelWrapper.Unbaked.bake skip:data special models come from exported model data in BlockEntityRenderer::load; no baking step +AbstractEndPortalRenderer.submitSpecial port crates/horizon-client/src/block_entity_render.rs::portal_cube gap: minor: no outline pass (outlineColor) for the portal faces +AbstractSignRenderer.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one front and back text drawn in the sign branch via sign_text_calls +AbstractSignRenderer.isOutlineVisible port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: minor: only the 16-block distance is tested; spyglass scoping in first person does not turn the outline on +AbstractSignRenderer.extractRenderState inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: minor: the text filtering option is not read; filtered sign lines are not shown +BannerRenderer.extractRenderState inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one flutter phase from position, game time and partial tick; standing or wall placement +BannerRenderer.bannerModel inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one wall or standing banner model chosen in the banner branch +BannerRenderer.flagModel inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one wall or standing flag model chosen in the banner branch +BannerRenderer.submitSpecial port crates/horizon-client/src/item_render/special.rs::submit banner as a special item model: base, flag and pattern layers +BannerRenderer.submitBanner port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one base model, flag and pattern layers; the crumbling overlay is drawn by render_breaking +BannerRenderer.submitPatternLayer inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one loop over up to 16 pattern layers in the banner branch +BannerRenderer.modelTransformation inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one translate, turn by angle, scale in the banner branch +BeaconRenderer.extractRenderState inline crates/horizon-client/src/game/beacon.rs::Game::beacon_geometry animation time and beam radius scale computed there +BeaconRenderer.extract inline crates/horizon-client/src/game/beacon.rs::Game::beacon_geometry same time and scale rules, from distance and scoping +BeaconRenderer.renderQuad port crates/horizon-client/src/block_entity_render.rs::beam solid beam and glow quads per section +BeaconRenderer.addVertex port crates/horizon-client/src/block_entity_render.rs::beam vertex with colour and full light, emitted by beam's inner emit +BellRenderer.extractRenderState inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one bell ticks and shake direction read in the bell branch +BellRenderer.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: player: no crack overlay while a bell is broken (render_breaking draws only banners and chests) +BlockEntityRenderDispatcher.getRenderer port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::kind renderer chosen from the block state +BlockEntityRenderDispatcher.tryExtractRenderState port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render gap: minor: distance cull only (256 for gateways); no frustum, type-validity or off-screen test here +BlockEntityRenderDispatcher.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one draws each kind; no crash report wrapper +BlockEntityRenderDispatcher.onResourceManagerReload missing minor: block entity models load once in BlockEntityRenderer::load; no reload path found +BlockEntityRenderers.createEntityRenderers skip:data renderer registration table; Horizon's classify table replaces it +BlockEntityWithBoundingBoxRenderer.extractRenderState missing player: game-master players (creative or op) see no structure box, invisible-block or structure-void markers +BlockEntityWithBoundingBoxRenderer.extract missing player: game-master players (creative or op) see no structure box, invisible-block or structure-void markers +BlockEntityWithBoundingBoxRenderer.submit missing player: game-master players (creative or op) see no structure box, invisible-block or structure-void markers +BlockEntityWithBoundingBoxRenderer.renderInvisibleBlocks missing player: game-master players (creative or op) see no barrier, light or air block markers +BlockEntityWithBoundingBoxRenderer.renderStructureVoids missing player: game-master players (creative or op) see no structure void markers +BrightnessCombiner.acceptDouble inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one brighter of the two chest halves per channel, in the chest branch +BrushableBlockRenderer.extractRenderState missing player: brushing a suspicious block shows no item on its face; the hit side and dust stage are not read +BrushableBlockRenderer.submit missing player: the brushed item never slides out of the block while it is brushed +BrushableBlockRenderer.translations missing player: the per-side slide offset for each dust stage is not computed +CampfireRenderer.extractRenderState inline crates/horizon-client/src/game/campfire.rs::Game::campfire_geometry facing and per-slot item seed read there +CampfireRenderer.submit port crates/horizon-client/src/game/campfire.rs::Game::campfire_geometry items on the four sides, drawn within 64 blocks +ChestRenderer.createModelTransformation inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one chest turn about its centre in the chest branch +ConduitRenderer.createEyeLayer skip:data model layer; Horizon loads the conduit parts from exported model data in BlockEntityRenderer::load +ConduitRenderer.createWindLayer skip:data model layer; Horizon loads the conduit parts from exported model data in BlockEntityRenderer::load +ConduitRenderer.createShellLayer skip:data model layer; Horizon loads the conduit parts from exported model data in BlockEntityRenderer::load +ConduitRenderer.createCageLayer skip:data model layer; Horizon loads the conduit parts from exported model data in BlockEntityRenderer::load +ConduitRenderer.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: player: no crack overlay on the conduit (render_breaking draws only banners and chests) +CopperGolemStatueBlockRenderer.extractRenderState inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one facing, pose and oxidation texture read in the golem branch +CopperGolemStatueBlockRenderer.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: player: no crack overlay on the statue (render_breaking draws only banners and chests) +CopperGolemStatueBlockRenderer.createModelTransformation inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one rotation from the opposite facing in the golem branch +DecoratedPotRenderer.createBaseLayer skip:data pot base model; Horizon loads it from exported model data in BlockEntityRenderer::load +DecoratedPotRenderer.createSidesLayer skip:data pot side models; Horizon loads them from exported model data in BlockEntityRenderer::load +DecoratedPotRenderer.getSideSprite inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one sherd pattern texture per side from pot_patterns, blank side otherwise +DecoratedPotRenderer.createModelTransformation inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one pot turn about its centre in the decorated pot branch +DecoratedPotRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents decorated pot part extents for the item model bounds +HangingSignRenderer.extractRenderState inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: minor: the text filtering option is not read +LecternRenderer.extractRenderState inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one book drawn only when has_book is set, turned by facing +LecternRenderer.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: player: no crack overlay on the lectern book (render_breaking draws only banners and chests) +PistonHeadRenderer.submit port crates/horizon-client/src/game/moving_pistons.rs::Game::moving_piston_geometry head and base blocks drawn at their offsets +PistonHeadRenderer.createMovingBlock port crates/horizon-client/src/game/moving_pistons.rs::Game::moving_block_vertices gap: minor: the tint is the particle sprite colour, not the block's tint sources +ShelfRenderer.extractRenderState missing player: items placed on shelf blocks are not kept for drawing; only the comparator signal is handled (game/machines.rs::shelf_signal) +ShelfRenderer.submit missing player: items on shelves are invisible +ShelfRenderer.submitItem missing player: shelf item slots, offsets and scale are not drawn +ShulkerBoxRenderer.extractRenderState inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one facing, colour texture and lid progress read in the shulker branch +ShulkerBoxRenderer.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: player: no crack overlay on shulker boxes (render_breaking draws only banners and chests) +ShulkerBoxRenderer.createModelTransform inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one facing transform in the shulker branch +ShulkerBoxRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents shulker lid pose extents for the item model bounds +SkullBlockRenderer.createModel skip:data skull models; Horizon loads them from exported model data in BlockEntityRenderer::load +SkullBlockRenderer.extractRenderState inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one type, wall facing and turn read from the skull kind; player skin via scene.skull +SkullBlockRenderer.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: player: no crack overlay on skulls and heads (render_breaking draws only banners and chests) +SkullBlockRenderer.submitSkull port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: player: no crack overlay on skulls and heads (render_breaking draws only banners and chests) +SkullBlockRenderer.resolveSkullRenderType inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one player head skin via scene.skull, otherwise the type's texture +SkullBlockRenderer.createWallTransformation inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one wall skull offset and turn in the skull branch +SkullBlockRenderer.createGroundTransformation inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one ground skull turn by segment in the skull branch +SpawnerRenderer.extractRenderState port crates/horizon-client/src/mobs/spawner.rs::display_pose spin and scale of the display mob +SpawnerRenderer.submit port crates/horizon-client/src/game/spawner.rs::Game::spawner_geometry display mob at the spawner's centre with spin, tilt and scale +StandingSignRenderer.extractRenderState inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one gap: minor: the text filtering option is not read +StandingSignRenderer.createTransformations inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one front and back text transforms per attachment +TestInstanceRenderer.extractRenderState missing player: creative players placing a test instance block see no beacon beam, box or error markers +TestInstanceRenderer.submit missing player: creative players placing a test instance block see no beacon beam or box +TestInstanceRenderer.submitErrorMarker missing player: red error markers and their text are not drawn +TestInstanceRenderer.shouldRenderOffScreen missing minor: culling policy of a renderer Horizon lacks +TestInstanceRenderer.shouldRender missing minor: culling policy of a renderer Horizon lacks +TheEndGatewayRenderer.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one beam and visible cube faces in the end gateway branch +TheEndPortalRenderer.submit port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one portal slab in the end portal branch +TrialSpawnerRenderer.extractRenderState port crates/horizon-client/src/mobs/spawner.rs::display_pose same spin and scale rule as spawners +TrialSpawnerRenderer.submit port crates/horizon-client/src/game/spawner.rs::Game::spawner_geometry trial spawner display mob drawn in the same pass +VaultRenderer.extractRenderState inline crates/horizon-client/src/game/vault.rs::Game::vault_geometry display item and spin read there +VaultRenderer.submit port crates/horizon-client/src/game/vault.rs::Game::vault_geometry item cluster at the vault centre with spin +BlockEntityRenderState.extractBase skip:java copies position, state, light and crack progress; Horizon passes these as arguments +BlockEntityRenderState.fillCrashReportCategory skip:debug crash report detail diff --git a/tools/port_map/client2-001.tsv b/tools/port_map/client2-001.tsv new file mode 100644 index 0000000000..b50fbbd9f9 --- /dev/null +++ b/tools/port_map/client2-001.tsv @@ -0,0 +1,150 @@ +CloudRenderer.getSizeForCloudDistance skip:java GPU ring-buffer capacity for the cloud face buffer; Horizon sizes its vertex buffer from the built mesh +CloudRenderer.packCellData inline crates/horizon-client/src/clouds.rs::Mesh::prepare cell presence is a bool test on the texture grid; no packed long +CloudRenderer.isNorthEmpty inline crates/horizon-client/src/clouds.rs::Mesh::prepare neighbour-empty flags passed to face_mask in N,E,S,W order +CloudRenderer.isEastEmpty inline crates/horizon-client/src/clouds.rs::Mesh::prepare neighbour-empty flags passed to face_mask in N,E,S,W order +CloudRenderer.isSouthEmpty inline crates/horizon-client/src/clouds.rs::Mesh::prepare neighbour-empty flags passed to face_mask in N,E,S,W order +CloudRenderer.isWestEmpty inline crates/horizon-client/src/clouds.rs::Mesh::prepare neighbour-empty flags passed to face_mask in N,E,S,W order +CloudRenderer.render port crates/horizon-client/src/gfx.rs::Renderer::draw_frame gap: minor: one cloud pipeline; fancy vs flat is chosen by the mesh fast flag, not by pipeline +CloudRenderer.renderOit missing player: with Improved transparency on, clouds keep the classic blend; no OIT pass exists and the option is only saved +CloudRenderer.buildMesh port crates/horizon-client/src/clouds.rs::Mesh::prepare cell ring walk via cells(radius), nearest first +CloudRenderer.tryBuildCell inline crates/horizon-client/src/clouds.rs::Mesh::prepare skip empty cells, then build the cell's faces +CloudRenderer.encodeFace inline crates/horizon-client/src/clouds.rs::Mesh::prepare 3-byte face records replaced by vertex quads +CloudRenderer.buildExtrudedCell port crates/horizon-client/src/clouds.rs::face_mask top, bottom and side visibility; quads emitted in Mesh::prepare via faces() +CubeMap.initializeVertices port crates/horizon-client/src/gfx/panorama.rs::Panorama::new 24-corner cube; Horizon draws 36 vertices from the shader in the pipeline built here +DebugCrosshairRenderer. skip:debug F3 3D axis gizmo at the view; Horizon has no debug crosshair +DynamicGpuData.reset skip:other GPU per-frame ring reset; wgpu staging in Horizon's Uploads belt +DynamicGpuData.close skip:other GPU buffer release; Rust drops the wgpu buffers +DynamicGpuData.writeTransform skip:other per-draw uniform staging; Horizon writes its uniforms directly with queue writes +DynamicGpuData.writeChunkSections port crates/horizon-client/src/gfx.rs::section_instance gap: player: section fade-in (vanilla default 0.75 s) is not sent; Horizon writes a fixed fourth value +DynamicGpuData.writeChunkSectionsInstanced port crates/horizon-client/src/gfx.rs::section_instance gap: player: section fade-in (vanilla default 0.75 s) is not sent; same instance record as writeChunkSections +DynamicGpuData.writeChunkSectionCommands port crates/horizon-client/src/gfx.rs::Renderer::draw_frame IndexedDraw records built and uploaded into the indirect buffer +DynamicGpuData.ChunkSectionInfo.write port crates/horizon-client/src/gfx.rs::section_instance gap: player: packs origin only; the fade-in visibility float (default 0.75 s) is not in the record +DynamicGpuData.IndexedDraw.write skip:other indirect draw args; Horizon keeps them as [u32;5] words uploaded in one copy +DynamicGpuData.TerrainTransform.write skip:other terrain UBO layout; Horizon's terrain pass uses its own uniforms +DynamicGpuData.Transform.write skip:other std140 layout of the transform UBO; wgpu bindings in Horizon +DynamicGpuDataStorageMapped.endFrame skip:other GPU ring buffer frame bookkeeping +DynamicGpuDataStorageMapped.resizeBuffers skip:other GPU buffer growth; Horizon grows its wgpu buffers when a draw needs more space +DynamicGpuDataStorageMapped.writeData skip:other GPU staging ring for mapped buffers +DynamicGpuDataStorageMapped.writeDataBatched skip:other GPU staging ring for mapped buffers +DynamicGpuDataStorageMapped.writeDataBatchedMultiple skip:other GPU staging ring for mapped buffers +DynamicGpuDataStorageMapped.close skip:other GPU buffer release +DynamicGpuDataStorageNonMapped.endFrame skip:other GPU staging bookkeeping; Horizon uses queue writes for uploads +DynamicGpuDataStorageNonMapped.writeData skip:other GPU staging for non-mapped buffers +DynamicGpuDataStorageNonMapped.resizeBuffers skip:other GPU buffer growth +DynamicGpuDataStorageNonMapped.writeDataBatched skip:other GPU staging for non-mapped buffers +DynamicGpuDataStorageNonMapped.writeDataBatchedMultiple skip:other GPU staging for non-mapped buffers +DynamicGpuDataStorageNonMapped.close skip:other GPU buffer release +EndFlashState.calculateIntensity port crates/horizon-client/src/game/end.rs::EndFlash::tick intensity sine over offset..offset+duration, computed on each tick +EndFlashState.flashStartedThisTick inline crates/horizon-client/src/game/end.rs::EndFlash::tick sound_in is armed on the same rising edge (intensity above 0, previous at or below 0) +FaceInfo.Extent.select skip:data corner selection; Horizon's unit-cube corner table PORTAL_FACES in block_entity_render.rs has the same order +FaceInfo.VertexInfo.select skip:data corner selection; Horizon's unit-cube corner table PORTAL_FACES in block_entity_render.rs has the same order +FirstPersonHandsAndItemsRenderer.calculateMapTilt inline crates/horizon-client/src/game/maps.rs::Game::two_handed_map_geometry tilt computed inline with the calculateMapTilt comment +FirstPersonHandsAndItemsRenderer.renderPlayerHand port crates/horizon-client/src/game.rs::Game::hand_geometry empty main hand's arm model with the player skin +FirstPersonHandsAndItemsRenderer.renderMapHand missing player: holding a map in both hands, neither arm is drawn behind it (vanilla draws both arms) +FirstPersonHandsAndItemsRenderer.renderOneHandedMap port crates/horizon-client/src/first_person.rs::one_handed_map_pose gap: player: the arm holding a map is not drawn; the map hand branch skips the arm +FirstPersonHandsAndItemsRenderer.renderTwoHandedMap port crates/horizon-client/src/game/maps.rs::Game::two_handed_map_geometry gap: player: the arms that hold a two-handed map are not drawn +FirstPersonHandsAndItemsRenderer.renderMap port crates/horizon-client/src/game/maps.rs::Game::held_map map background with its 7-pixel border, then the map contents +FirstPersonHandsAndItemsRenderer.applyEatTransform port crates/horizon-client/src/first_person.rs::item_pose ItemPose::Eat: bob and jiggle while eating or drinking +FirstPersonHandsAndItemsRenderer.applyBrushTransform missing player: a brush in use is held still; it does not sweep back and forth while brushing +FirstPersonHandsAndItemsRenderer.applyItemArmAttackTransform port crates/horizon-client/src/first_person.rs::item_pose attack swing rotation in the Swing pose +FirstPersonHandsAndItemsRenderer.applyItemArmTransform port crates/horizon-client/src/first_person.rs::item_pose base item-in-arm translation +FirstPersonHandsAndItemsRenderer.submitHandsWithItems port crates/horizon-client/src/game.rs::Game::hand_geometry sway, both hands, empty-hand arm and item pose +FirstPersonHandsAndItemsRenderer.swingArm port crates/horizon-client/src/first_person.rs::item_pose attack swing translation and arm rotation +GameRenderer. port crates/horizon-client/src/gfx.rs::Renderer::new lightmap, hand target, debug crosshair and screen effects are built here +GameRenderer.close skip:other GPU resource teardown; wgpu drops the renderer's objects +GameRenderer.checkEntityPostEffect missing player: spectating a creeper, spider or enderman does not put their overlay shader on the screen +GameRenderer.processBlurEffect port crates/horizon-client/src/menu_blur.rs::MenuBlur::apply menu background blur pass +GameRenderer.preloadUiShader skip:other GPU pipeline precompile at load; Horizon builds its pipelines in Renderer::new +GameRenderer.update port crates/horizon-client/src/camera.rs::AvatarBob::update_bob gap: minor: the requested post-effect list (from /postEffect) is not built +GameRenderer.extract skip:other render-state copy for the vanilla render thread; Horizon renders from game state +GameRenderer.preparePostEffects missing minor: vanilla post chains from requested effects are not applied; shader packs use pack_post +GameRenderer.applyPostEffects missing minor: vanilla post chains are not run; shader packs use pack_post +GameRenderer.tryTakeScreenshotIfNeeded missing player: a new singleplayer world gets no icon.png, so the world list shows no preview picture +GameRenderer.takeAutoScreenshot missing player: no icon.png is written from the first rendered frame; the world list keeps a blank picture +GameRenderer.integrate3DHudDepth missing minor: only runs when a post effect is applied (consistentDepthRequired); Horizon applies none +GameRenderer.extractWindow skip:other render-state copy of the window size; Horizon reads the window directly +GameRenderer.extractOptions skip:other render-state copy of options; Horizon reads settings directly +GameRenderer.extractCamera skip:other render-state copy of camera and fog; Horizon computes them in camera.rs and fog.rs +GameRenderer.resetData inline crates/horizon-client/src/game.rs::Game::finish_close_world boss overlay reset on world close; camera and map-texture resets not verified here +GameRenderer.projectPointToScreen skip:other no callers in vanilla 26.3 +GameRenderer.useImprovedTransparency missing player: the Improved transparency option is saved and listed but nothing it gates exists in the renderer +GlobalSettingsUniform.update skip:other global uniform (window size, time, gamma, glint) written per pass; Horizon sets its own uniforms +GpuWarnlistManager.getAllWarnings missing minor: no GPU driver warnlist; Horizon does not check the driver against known-bad lists +GpuWarnlistManager.compilePatterns missing minor: no GPU driver warnlist patterns +GpuWarnlistManager.parseJson missing minor: no GPU driver warnlist JSON +GpuWarnlistManager.Preparations.matchAny missing minor: no GPU driver warnlist matching +LevelEventHandler.playJukeboxSong port crates/horizon-client/src/game/jukebox.rs::Game::start_song song starts and the now-playing overlay is shown +LevelEventHandler.stopJukeboxSong port crates/horizon-client/src/game/jukebox.rs::Game::stop_song song sound stops for that jukebox +LevelRenderer. port crates/horizon-client/src/gfx.rs::Renderer::new targets, pipelines and buffers built once +LevelRenderer.submitFeatures port crates/horizon-client/src/gfx.rs::Renderer::draw_frame entities, block entities, crumbling, particles, outline and gizmos are submitted and drawn in the frame +LevelRenderer.repositionCamera port crates/horizon-client/src/gfx/resident.rs::camera_column camera section column that the section grid is centred on; gap: minor: world border invalidation is not part of it +LevelRenderer.addMainPass port crates/horizon-client/src/gfx.rs::Renderer::draw_frame gap: minor: the Improved transparency targets (depth bounds, transmittance, accumulate) are not created +LevelRenderer.executeAlwaysOnTop port crates/horizon-client/src/game.rs::Game::render always-on-top debug lines and crosshair drawn over the world +LevelRenderer.executeSolid port crates/horizon-client/src/gfx.rs::Renderer::draw_sections solid section draws in arena page order +LevelRenderer.prepareTranslucents port crates/horizon-client/src/gfx/translucency.rs::Sorter::sort translucent quads sorted from the camera each frame +LevelRenderer.executeOit missing player: with Improved transparency on, translucent blocks and water blend order-independently; Horizon has no OIT pass and the option does nothing +LevelRenderer.executeDepthBoundsCull missing player: part of Improved transparency's pass; no depth-bounds cull exists in Horizon +LevelRenderer.executeOitWaterMask missing player: part of Improved transparency; no water mask pass exists in Horizon +LevelRenderer.extractSectionDrawGroups port crates/horizon-client/src/gfx.rs::Renderer::draw_frame section draws grouped per layer and arena page from the visible sections +LevelRenderer.prepareChunkRenders port crates/horizon-client/src/gfx.rs::Renderer::draw_frame per-layer draw list built for the frame, reversed for translucent layers +LevelRenderer.performTransientBlockRemovals missing minor: removes transient blocks when their section recompiles; Horizon has no transient blocks +LevelRenderer.prepareChunkRendersIndirect port crates/horizon-client/src/gfx.rs::Renderer::draw_frame indirect draw arguments written and multi-draw issued +LevelRenderer.compileSections port crates/horizon-client/src/streaming.rs::Streaming::update section meshing jobs queued for dirty sections nearest the camera +LevelRenderer.submitEntities port crates/horizon-client/src/gfx.rs::Renderer::draw_frame entity models drawn with their batches +LevelRenderer.submitBlockEntities port crates/horizon-client/src/gfx.rs::Renderer::block_entities block entity list submitted for drawing +LevelRenderer.submitBlockDestroyAnimation port crates/horizon-client/src/gfx.rs::Renderer::draw_frame crumbling overlay drawn on the block being broken +LevelRenderer.submitTransientBlocks missing player: a block the server sends as temporary (ClientboundAddTransientBlockPacket) does not appear; the packet is not handled +LevelRenderer.submitHitOutline port crates/horizon-client/src/game.rs::Game::outline_boxes outline of the block under the crosshair +LevelRenderer.close skip:other GPU resource teardown +LevelRenderer.invalidateCompiledGeometry port crates/horizon-client/src/gfx.rs::Renderer::clear_sections all section meshes dropped and rebuilt when settings are applied +LevelRenderer.scheduleTranslucentSectionResort port crates/horizon-client/src/gfx/translucency.rs::SortState::update resort scheduled when the camera point of view changes +LevelRenderer.scheduleResort port crates/horizon-client/src/gfx/translucency.rs::SortState::update per-section resort gated by point-of-view change and translucent geometry +LevelRenderer.resetLevelRenderData port crates/horizon-client/src/gfx.rs::Renderer::clear_sections section data released when a level is left +LevelRenderer.hasRenderedAllSections missing minor: only the world icon screenshot asks this, and that screenshot is missing +LevelRenderer.isSectionCompiledAndVisible port crates/horizon-client/src/gfx/resident.rs::Resident::contains gap: minor: no section fade-in, so the 0.3 visibility threshold is not checked +LevelRenderer.finalizeGizmoCollection port crates/horizon-client/src/game/debug_keys.rs::Game::debug_gizmos debug gizmos collected for the frame +LevelTargetBundle.replace skip:other render-target bundle for post chains; Horizon has no frame graph +LevelTargetBundle.get skip:other render-target bundle for post chains; Horizon has no frame graph +Lightmap.render port crates/horizon-client/src/gfx.rs::Renderer::update_lightmap lightmap pass from sky, block, darkness, gamma, night vision, flicker and boss darkening +LightmapRenderStateExtractor.calculateDarknessScale port crates/horizon-client/src/game/sculk.rs::Game::darkness_lighting pulsing darkness scale from the darkness blend and the option +LightmapRenderStateExtractor.extract port crates/horizon-client/src/game.rs::Game::render lightmap inputs gathered into the frame; the end flash is added to sky_light_factor in end.rs::end_flash_sky +MapRenderer.extractRenderState port crates/horizon-client/src/game/maps.rs::Game::map_vertices map texture and decorations gathered for drawing +MapRenderer.extractDecorationRenderState port crates/horizon-client/src/game/maps.rs::Game::map_vertices decoration sprite, position and rotation drawn per map +MappableRingBuffer.currentBuffer skip:other GPU ring buffer bookkeeping +MappableRingBuffer.rotate skip:other GPU ring buffer bookkeeping +MappableRingBuffer.close skip:other GPU buffer release +MultiblockChestResources.select inline crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_one single, left and right chest sprite chosen by ChestPart +MultiblockChestResources.map skip:java record mapping of the three sprites +Octree.isClose missing unsure: vanilla flags sections next to the camera so the occlusion walk keeps them; not traced in Horizon's occlusion path +Octree.AxisSorting.getAxisSorting skip:java traversal-order helper; Resident::visible sorts visible sections by distance +Octree.Branch. skip:other octree node; Horizon keeps sections in a resident grid instead +Octree.Branch.add skip:other octree insert; Horizon keeps sections in a resident grid instead +Octree.Branch.getNodeIndex skip:other octree child index; Horizon keeps sections in a resident grid instead +Octree.Branch.createChildBoundingBox skip:other octree child bounds; Horizon keeps sections in a resident grid instead +Octree.Branch.visitNodes port crates/horizon-client/src/gfx/resident.rs::Resident::visible frustum and distance visit of sections, nearest first +Octree.Branch.getAABB skip:java no callers in vanilla 26.3 +Octree.Leaf.visitNodes port crates/horizon-client/src/gfx/resident.rs::Resident::visible leaf sections visited in the same frustum and distance walk +OrderedSubmitNodeCollector.submitModel skip:other submit queue of vanilla's render thread; Horizon draws each model in place +OrderedSubmitNodeCollector.submitCrumblingOverlay port crates/horizon-client/src/gfx.rs::Renderer::draw_frame crumbling overlay drawn on the block being broken +OrderedSubmitNodeCollector.submitModelPart skip:other submit queue of vanilla's render thread; Horizon draws each model part in place +Panorama.extractRenderState port crates/horizon-client/src/gfx/panorama.rs::Rotation::update spin accumulated from the panorama speed option; overlay blit is drawn by the title screen +PlayerSkinRenderCache.createLookup port crates/horizon-client/src/skins.rs::lookup skin texture chosen from the profile, falling back to the default skin +PlayerSkinRenderCache.RenderInfo.renderType port crates/horizon-client/src/game/item_draw.rs::Host::player_skin skin texture path a head is drawn with; no separate render type in Horizon +PlayerSkinRenderCache.RenderInfo.textureView skip:other GPU texture view cache; Horizon binds skin textures by atlas name +PlayerSkinRenderCache.RenderInfo.glyphRenderTypes skip:other font render types for name tags; Horizon's text uses its own glyph pipeline +PlayerSkinRenderCache.RenderInfo.equals skip:java value equality for a cache key +PlayerSkinRenderCache.RenderInfo.hashCode skip:java hash for a cache key +PostChain.load missing minor: no data-driven post chain loader; only the blur and entity outline chains exist, hard-coded +PostChain.getReferencedExternalTargets skip:other config validation for data-driven post chains +PostChain.createPass missing minor: no data-driven post pass creation; the outline and blur passes are fixed +PostChain.addToFrame port crates/horizon-client/src/gfx/entity_outline.rs::EntityOutline::draw gap: minor: runs the entity outline chain only; other chains are not built +PostChain.process port crates/horizon-client/src/menu_blur.rs::MenuBlur::apply gap: minor: runs the menu blur only; other chains are not run +PostChain.getOrCreatePersistentTarget skip:other GPU persistent target cache +PostChain.close skip:other GPU resource teardown +PostChain.TargetBundle.getOrThrow skip:other frame-graph target lookup; Horizon has no frame graph +PostChainConfig.Pass.referencedTargets skip:data config accessor for post chain JSON +PostPass. skip:other GPU pipeline creation for a post pass +PostPass.addToFrame port crates/horizon-client/src/gfx/entity_outline.rs::EntityOutline::draw gap: minor: the pass runs inside the entity outline draw only +PostPass.close skip:other GPU resource teardown +PostPass.TargetInput.getHandle skip:other frame-graph input handle +PostPass.TargetInput.texture skip:other frame-graph input texture view diff --git a/tools/port_map/client2-002.tsv b/tools/port_map/client2-002.tsv new file mode 100644 index 0000000000..e5baa4006b --- /dev/null +++ b/tools/port_map/client2-002.tsv @@ -0,0 +1,150 @@ +Projection.setupPerspective inline crates/horizon-client/src/camera.rs::perspective gap: minor: the dirty-flag cache and the stored fov/near/far are not kept; the matrix is built from the arguments per call +Projection.setupOrtho skip:ui GUI draws in pixels through Horizon's own HUD pipeline; no ortho projection state of its own +Projection.getMatrixVersion skip:java matrix version counter for the dirty-check cache +ProjectionMatrixBuffer.getBuffer skip:other GPU buffer handle getter; Horizon owns its uniform buffers in gfx.rs +ProjectionMatrixBuffer.writeBuffer port crates/horizon-client/src/gfx/upload.rs::Uploads::buffer gap: minor: the caller decides which uniform receives the projection +Rect2i.intersect skip:ui GUI clip rectangle math; Horizon clips each draw in hud.rs (clip_since), no rectangle type +Rect2i.contains skip:ui GUI hit test on a rectangle; Horizon's widgets hit-test in their own UI code +RenderPipelines.register skip:data pipeline registry; Horizon creates its pipelines in gfx.rs +RunningTrimmedMean.registerValueAndGetMean skip:debug frame-time smoothing helper; no vanilla caller in this source tree +ScreenEffectRenderer.submit port crates/horizon-client/src/screen_effects.rs::ScreenEffects::build gap: minor: the item activation is not built here (see renderItemActivationAnimation) +ScreenEffectRenderer.renderItemActivationAnimation missing player: when a totem of undying saves the player, the totem does not pop up in front of the screen; Horizon only plays the totem particles and sound (game/totem.rs totem_event) +ScreenEffectRenderer.submitWater inline crates/horizon-client/src/screen_effects.rs::ScreenEffects::build underwater overlay branch (misc/underwater) +ScreenEffectRenderer.submitFire inline crates/horizon-client/src/screen_effects.rs::ScreenEffects::build first-person fire overlay branch (block/fire_1, two quads) +ScreenEffectRenderer.buildFireQuad inline crates/horizon-client/src/screen_effects.rs::ScreenEffects::build the two fire quads' corners and offsets +ScreenEffectRenderer.buildSpriteQuad inline crates/horizon-client/src/screen_effects.rs::ScreenEffects::build hand_quad closure: sprite rect and uv for overlay quads +ScreenEffectRenderer.buildQuad inline crates/horizon-client/src/screen_effects.rs::ScreenEffects::build hand_quad closure: corners, uv and colour of one overlay quad +SectionBufferBuilderPool.allocate skip:java heap-sized pool of section builders; Horizon's mesher uses its own worker threads +SectionOcclusionGraph.waitAndReset port crates/horizon-client/src/gfx/occlusion.rs::Occlusion::clear gap: minor: clear discards a running search instead of waiting for it +SectionOcclusionGraph.expectedChunks missing unsure: Horizon's occlusion graph keeps no list of sections waiting on chunk loads (streaming.rs has its own waiting set) +SectionOcclusionGraph.invalidateIfNeeded port crates/horizon-client/src/gfx/occlusion.rs::Occlusion::update gap: minor: the search is keyed on camera section and radius only; fov and smart-cull changes do not restart it +SectionOcclusionGraph.addSectionsInFrustum port crates/horizon-client/src/gfx/occlusion.rs::Occlusion::contains gap: minor: the frustum test is not here; the caller builds the visible list +SectionOcclusionGraph.schedulePropagationFrom port crates/horizon-client/src/gfx/occlusion.rs::Occlusion::changed queues the changed section; propagation runs in propagate_changes +SectionOcclusionGraph.scheduleFullUpdate port crates/horizon-client/src/gfx/occlusion.rs::Occlusion::start gap: minor: the full search goes to a worker and is collected later (collect), not to a background executor +SectionOcclusionGraph.runPartialUpdate port crates/horizon-client/src/gfx/occlusion.rs::Occlusion::propagate_changes gap: minor: chunk-load waiting is not handled here (see expectedChunks) +SectionOcclusionGraph.initializeQueueForFullUpdate port crates/horizon-client/src/gfx/occlusion.rs::Graph::seed_outside gap: minor: seeds the sections around a camera outside the world; the camera-inside seed is in Graph::search +SectionOcclusionGraph.runUpdates port crates/horizon-client/src/gfx/occlusion.rs::Graph::propagate gap: minor: BFS over face directions; vanilla's distant-section ray check for smart cull is not checked here +SectionOcclusionGraph.getRelativeFrom inline crates/horizon-client/src/gfx/occlusion.rs::Graph::propagate neighbour lookup inside the BFS +SectionOcclusionGraph.updateEmptySections port crates/horizon-client/src/gfx/occlusion.rs::Occlusion::set gap: minor: empty sections are not kept in a separate set; their visibility arrives through set +SectionOcclusionGraph.updateLoadedChunks missing unsure: no loaded-chunk set for the occlusion graph found in gfx/occlusion.rs +SectionOcclusionGraph.Node.setDirections inline crates/horizon-client/src/gfx/occlusion.rs::Graph::propagate face direction bits kept per section in directions[] +SectionOcclusionGraph.Node.hasDirection inline crates/horizon-client/src/gfx/occlusion.rs::Graph::propagate face direction bit test inside the BFS +SectionOcclusionGraph.Node.addSourceDirection inline crates/horizon-client/src/gfx/occlusion.rs::Graph::propagate entered-face bits kept per section in entered[] +SectionOcclusionGraph.Node.hasSourceDirection inline crates/horizon-client/src/gfx/occlusion.rs::Graph::propagate entered-face bit test inside the BFS +SectionOcclusionGraph.Node.equals skip:java node equality for vanilla's hash sets; Horizon indexes sections by position +SectionOcclusionGraph.SectionToNodeMap.get port crates/horizon-client/src/gfx/occlusion.rs::Graph::index gap: minor: a per-section array index instead of a map +ShaderDefines.withOverrides skip:other merges GLSL #define sets; Horizon's WGSL uses override constants +ShaderDefines.asSourceDirectives skip:other emits GLSL #define text; Horizon has no GLSL source +ShaderDefines.isEmpty skip:other helper for the define set; see ShaderDefines +ShaderDefines.Builder.define skip:other builder for the define set; see ShaderDefines +ShaderManager.reload skip:other shader resource reload; Horizon builds its pipelines in gfx.rs +ShaderManager.compilePipelines skip:other pipeline compile plumbing; Horizon compiles wgpu pipelines in gfx.rs +ShaderManager.loadConfigs skip:other reads GLSL shader resources; Horizon's shader packs are read by horizon-shaders +ShaderManager.loadShader skip:other reads one GLSL shader resource; see loadConfigs +ShaderManager.loadInclude skip:other reads one GLSL include; WGSL has no include files +ShaderManager.loadPostChain missing minor: JSON post chains (shaders/post) are not loaded; vanilla's spectator entity effects (creeper, spider, enderman) do not show in Horizon +ShaderManager.listAllIncludes skip:other lists GLSL include files; WGSL has none +ShaderManager.apply skip:other swaps in compiled pipelines; Horizon compiles at startup and on reload +ShaderManager.tryTriggerRecovery skip:other error hook for broken post chains; Horizon has none +ShaderManager.isPostEffectValid missing minor: checks spectator entity post effects, which Horizon does not have (see loadPostChain) +ShaderManager.getPostChain missing minor: spectator entity post effects (creeper, spider, enderman) are not drawn; spectator mode only +ShaderManager.PostChainCache.getOrLoadPostChain missing minor: cache for the spectator entity post effects Horizon lacks +ShaderManager.PostChainCache.loadPostChain missing minor: loads the spectator entity post effects Horizon lacks +ShaderManager.PostChainCache.close skip:other releases post chains; Horizon has none +Sheets.chooseSprite port crates/horizon-client/src/block_entity_render.rs::classify gap: minor: the Christmas chest texture is picked outside this fn (block_entity_render.rs extended_christmas) +SkyRenderer. inline crates/horizon-client/src/gfx/pack_sky.rs::PackSky::new gap: minor: builds the disc, sunset fan, stars and void up front; sun and moon quads are built per frame in game.rs celestial_geometry +SkyRenderer.extractRenderState port crates/horizon-client/src/environment.rs::Environment::sample gap: minor: the End flash state is read in game/end.rs (end_flash_sky) +SkyRenderer.render port crates/horizon-client/src/gfx.rs::Renderer::draw_frame sky pass; the frame's sky and dark-disc flags come from Game::render +SkyRenderer.renderSkyDisc port crates/horizon-client/src/gfx/pack_sky.rs::disc top sky disc fan, sky colour from PackSky::prepare +SkyRenderer.renderDarkDisc inline crates/horizon-client/src/gfx.rs::Renderer::draw_frame dark disc pipeline (fs_dark_disc) drawn when frame.dark_disc is set +SkyRenderer.renderSun port crates/horizon-client/src/game.rs::Game::celestial_geometry sun quad 100 blocks out, half-size 30, faded by rain +SkyRenderer.renderMoon port crates/horizon-client/src/game.rs::Game::celestial_geometry moon quad 100 blocks out, half-size 20, faded by rain +SkyRenderer.applyCelestialBodyTransform inline crates/horizon-client/src/game.rs::Game::celestial_geometry the body frame: rotate Y -90, then X by the body's angle +SkyRenderer.drawCelestialBody inline crates/horizon-client/src/gfx.rs::Renderer::draw_frame draws the body quads with the celestial pipeline +SkyRenderer.renderSunriseAndSunset port crates/horizon-client/src/gfx/pack_sky.rs::PackSky::prepare sunset fan turned to the sun's horizon and its alpha uploaded as sunrise +SkyRenderer.renderEndSky port crates/horizon-client/src/gfx/end_sky.rs::EndSky::draw End sky cube +SkyRenderer.buildSunriseFan port crates/horizon-client/src/gfx/pack_sky.rs::PackSky::new gap: minor: a unit 16-segment fan; the 120 and -40 block shape is not built here +SkyRenderer.buildCelestialQuad port crates/horizon-client/src/game.rs::Game::celestial_geometry sprite quad for the sun and moon (body closure) +SkyRenderer.buildMoonPhases port crates/horizon-client/src/game.rs::Game::celestial_geometry gap: minor: picks the phase from day time (day_time / 24000), not from the environment's moon phase value +SkyRenderer.buildSkyDisc port crates/horizon-client/src/gfx/pack_sky.rs::disc 16-block disc fan (top and bottom) +SkyRenderer.close skip:other releases sky buffers; Rust drops the wgpu buffers +SpriteCoordinateExpander.addVertex skip:other no callers in this vanilla tree; UV wrapper for sprite quads +SpriteCoordinateExpander.setColor skip:other no callers in this vanilla tree; see addVertex +SpriteCoordinateExpander.setUv skip:other no callers in this vanilla tree; see addVertex +SpriteMapper.apply skip:other atlas id plumbing; Horizon looks sprites up by name in its atlas +StagedVertexBuffer.appendDraw skip:other staging draw object; Horizon's arena allocates vertex ranges directly +StagedVertexBuffer.getVertexBuilder skip:other builder for the next mesh; Horizon's mesher writes straight into the section mesh +StagedVertexBuffer.finishLastVertexBuilder skip:other closes the last mesh builder; see getVertexBuilder +StagedVertexBuffer.upload port crates/horizon-client/src/gfx/arena.rs::Arena::alloc gap: minor: vertex ranges come from the arena; the copy is Uploads::buffer +StagedVertexBuffer.uploadDrawsToBuffers inline crates/horizon-client/src/gfx/upload.rs::Uploads::buffer copies the staged vertex data into the buffers +StagedVertexBuffer.decodeSortingPoints port crates/horizon-client/src/gfx/translucency.rs::quad_center gap: minor: the centre of one quad from two corners; reading the vertex buffer itself is not here +StagedVertexBuffer.getExecuteInfo skip:other describes a draw for execution; Horizon issues draws in gfx.rs::Renderer::draw_sections +StagedVertexBuffer.requestIndexCount port crates/horizon-client/src/gfx/arena.rs::Arena::indices gap: minor: the index buffer is sized when created, not requested per draw +StagedVertexBuffer.endFrame port crates/horizon-client/src/gfx/arena.rs::Arena::trim gap: minor: releases unused pages; no fence-based recycling +StagedVertexBuffer.close skip:other releases buffers; Rust drops them +StagedVertexBuffer.Draw.append skip:other accumulates mesh counts for one draw; Horizon counts vertices in the arena +StagedVertexBuffer.ExecuteInfo.indexBuffer skip:other picks the index buffer for a draw; Horizon keeps its own index buffers +StagedVertexBuffer.GpuBufferPool.tryRecycleBuffers port crates/horizon-client/src/gfx/arena.rs::FreeList::give_all gap: minor: freed ranges return to the free list at once; no fence wait +StagedVertexBuffer.GpuBufferPool.acquire port crates/horizon-client/src/gfx/arena.rs::Arena::alloc gap: minor: allocates a range in an existing page instead of a new buffer per size +StagedVertexBuffer.GpuBufferPool.takeBestAvailable port crates/horizon-client/src/gfx/arena.rs::FreeList::take gap: minor: first fit, where vanilla takes the smallest buffer that fits +StagedVertexBuffer.GpuBufferPool.endFrame skip:other fence bookkeeping for GPU reuse; Horizon's arena releases ranges directly +StagedVertexBuffer.GpuBufferPool.close skip:other releases buffers; Rust drops them +StagedVertexBuffer.GpuBufferPool.PendingRecycle.tryRecycle skip:other fence wait for GPU reuse; Horizon has no fence here +SubmitNodeCollection. skip:other builds the phase lists (improved transparency or not); Horizon has fixed passes in gfx.rs +SubmitNodeCollection.submitShadow port crates/horizon-client/src/game.rs::Game::entity_geometry entity shadow quads (misc/shadow) when the entity_shadows option is on +SubmitNodeCollection.nameTag port crates/horizon-client/src/game/names.rs::Game::name_tag_geometry name tags +SubmitNodeCollection.submitNameTagPart port crates/horizon-client/src/game/names.rs::Game::name_tag_quads adds the text quads of one name tag part +SubmitNodeCollection.canRenderAsSolid inline crates/horizon-client/src/game/names.rs::Game::name_tag_geometry the choice between the opaque and see-through pass +SubmitNodeCollection.submitText port crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry world text (text displays) +SubmitNodeCollection.submitTextBackground inline crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry background quad of a text display +SubmitNodeCollection.submitTextPart inline crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry the see-through or opaque choice for a text part +SubmitNodeCollection.submitFlame port crates/horizon-client/src/fire_render.rs::flames entity fire flames +SubmitNodeCollection.submitLeash port crates/horizon-client/src/game/leads.rs::Game::leash_geometry lead rope from a leashed mob to its holder +SubmitNodeCollection.submitModel port crates/horizon-client/src/game/creatures.rs::Game::mob_geometry mob models +SubmitNodeCollection.submitCrumblingOverlay port crates/horizon-client/src/game.rs::Game::cracks block break cracks (destroy_stage sprites) over a block +SubmitNodeCollection.submitMovingBlock port crates/horizon-client/src/game/moving_pistons.rs::Game::moving_piston_geometry moving piston blocks +SubmitNodeCollection.submitBlockModel port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::render_block gap: minor: block models driven by block entities only +SubmitNodeCollection.getOutlineRenderType inline crates/horizon-client/src/gfx.rs::Renderer::draw_outline one outline pass; the render type choice is folded into it +SubmitNodeCollection.submitBreakingBlockModel port crates/horizon-client/src/game.rs::Game::cracks gap: minor: drawn as cracks; the block model itself is not redrawn +SubmitNodeCollection.submitShapeOutline port crates/horizon-client/src/gfx.rs::Renderer::draw_outline block shape outline (selection box) +SubmitNodeCollection.submitItem port crates/horizon-client/src/game/item_draw.rs::Game::dropped_item_geometry dropped item entities +SubmitNodeCollection.submitCustomGeometry port crates/horizon-client/src/mobs/guardian.rs::beam_geometry gap: minor: the guardian beam only; screen overlay quads are built in ScreenEffects::build +SubmitNodeCollection.submitGizmoPrimitives skip:debug debug gizmo overlay +SubmitNodeStorage.setUseImprovedTransparency skip:other storage toggle for the transparency path; Horizon fixes its path in Renderer::new +SubmitNodeStorage.order skip:other per-order submit collection; Horizon draws in fixed passes +SubmitNodeStorage.submitNameTag skip:other submit wrapper; the drawing is in SubmitNodeCollection.nameTag +SubmitNodeStorage.submitText skip:other submit wrapper; see SubmitNodeCollection.submitText +SubmitNodeStorage.submitTextBackground skip:other submit wrapper; see SubmitNodeCollection.submitTextBackground +SubmitNodeStorage.submitModel skip:other submit wrapper; see SubmitNodeCollection.submitModel +SubmitNodeStorage.submitCrumblingOverlay skip:other submit wrapper; see SubmitNodeCollection.submitCrumblingOverlay +SubmitNodeStorage.submitBlockModel skip:other submit wrapper; see SubmitNodeCollection.submitBlockModel +SubmitNodeStorage.submitShapeOutline skip:other submit wrapper; see SubmitNodeCollection.submitShapeOutline +SubmitNodeStorage.submitItem skip:other submit wrapper; see SubmitNodeCollection.submitItem +SubmitNodeStorage.drainPhases inline crates/horizon-client/src/gfx.rs::Renderer::draw_frame the pass order that draws each phase +ViewArea.releaseAllBuffers port crates/horizon-client/src/gfx.rs::Renderer::clear_sections drops every section's buffers +ViewArea.repositionCamera inline crates/horizon-client/src/gfx/occlusion.rs::Occlusion::update gap: minor: the occlusion graph restarts on a camera section change; sections are keyed by absolute position, nothing is re-centred +WeatherEffectRenderer.renderWeather inline crates/horizon-client/src/gfx/weather.rs::Weather::draw draws the rain and snow column ranges +WeatherEffectRenderer.uploadVertexBuffer port crates/horizon-client/src/gfx/weather.rs::Weather::prepare gap: minor: the buffer grows to the next power of two +WeatherEffectRenderer.prepare port crates/horizon-client/src/weather.rs::column_quad gap: minor: the column list comes from rain_column and snow_column; this fn builds the quads +WeatherEffectRenderer.render port crates/horizon-client/src/gfx/weather.rs::Weather::draw draws the rain and snow columns +WeatherEffectRenderer.createSnowColumnInstance port crates/horizon-client/src/weather.rs::snow_column snow drift and brightened light +WeatherEffectRenderer.prepareInstances port crates/horizon-client/src/weather.rs::column_quad gap: minor: one column per call; the loop over columns is in the caller +WorldBorderRenderer.renderOit inline crates/horizon-client/src/gfx.rs::Renderer::draw_frame border wall draw when frame.border is set +WorldBorderRenderer.prepareRenderPass inline crates/horizon-client/src/border_render.rs::BorderRenderer::transforms the uniforms (colour, offset, texture scroll) for the wall +WorldBorderRenderer.shouldRebuildWorldBorderBuffer port crates/horizon-client/src/border_render.rs::BorderRenderer::prepare rebuilds when the border moved or was invalidated +FluidRenderer.getHeight inline crates/horizon-client/src/mesher/fluid.rs::tesselate corner heights from own_height and the neighbour average +ChunkSectionLayer.byTransparency inline crates/horizon-client/src/mesher.rs::Mesher::face a face goes to the translucent list when it is translucent +ChunkSectionsToRender.renderGroup port crates/horizon-client/src/gfx.rs::Renderer::draw_sections gap: minor: draws the given page runs; the terrain uniforms are bound by the caller +ChunkSectionsToRender.renderOit port crates/horizon-client/src/gfx.rs::Renderer::draw_sections gap: minor: the OIT pass set-up is not in this fn +ChunkSectionsToRender.renderLayers inline crates/horizon-client/src/gfx.rs::Renderer::draw_sections per-layer loop over the page runs +ChunkSectionsToRender.DrawIndirect.render port crates/horizon-client/src/gfx.rs::Renderer::draw_sections multi-draw indirect branch +ChunkSectionsToRender.DrawSeparate.render port crates/horizon-client/src/gfx.rs::Renderer::draw_sections per-draw branch without multi-draw +RenderRegionCache.createRegion inline crates/horizon-client/src/mesher.rs::borrow gap: minor: borrows the 3x3 chunk columns around the section; nothing is copied +RenderRegionCache.getSectionDataCopy skip:java per-thread copy cache; Horizon borrows the chunks +RenderSectionRegion.getBlockState inline crates/horizon-client/src/mesher.rs::Mesher::mesh block reads of the mesh loop +RenderSectionRegion.getFluidState port crates/horizon-client/src/mesher/fluid.rs::FlowView::fluid fluid read used by the fluid mesher +RenderSectionRegion.getBlockEntity skip:other block entities are drawn from the world's list, not from the section region +RenderSectionRegion.getSection skip:java section copy objects; Horizon borrows chunks +RenderSectionRegion.index inline crates/horizon-client/src/mesher.rs::Mesher::mesh gap: minor: Horizon indexes 3x3 chunk columns, the section is picked by sy +SectionCompiler.compile port crates/horizon-client/src/mesher.rs::Mesher::mesh gap: minor: block-entity tiles are not built here; they are drawn by block_entity_render +SectionCompiler.getOrBeginLayer inline crates/horizon-client/src/mesher.rs::Mesher::mesh per-layer builders created on first use diff --git a/tools/port_map/client2-003.tsv b/tools/port_map/client2-003.tsv new file mode 100644 index 0000000000..f88bd434d1 --- /dev/null +++ b/tools/port_map/client2-003.tsv @@ -0,0 +1,104 @@ +SectionCopy.getBlockState inline crates/horizon-client/src/mesher.rs::Padded::new the copy is Padded's block array; no debug-world barrier or DebugLevelSource blocks +SectionRenderDispatcher. skip:other builds the GPU arena and staging for chunk upload; Horizon creates its arena and uploads in gfx.rs with the renderer +SectionRenderDispatcher.runTask port crates/horizon-client/src/streaming.rs::Streaming::new the worker loop pops the nearest queued job and runs it +SectionRenderDispatcher.getRenderSectionSlice port crates/horizon-client/src/gfx.rs::Renderer::draw_sections the per-layer vertex and index slices of a section are read when drawing +SectionRenderDispatcher.uploadTerrainBuffersToGpu port crates/horizon-client/src/gfx/upload.rs::Uploads::finish closes the frame's staging belt after the terrain copies +SectionRenderDispatcher.schedule port crates/horizon-client/src/streaming.rs::Streaming::submit the mesh job goes into the priority queue by distance +SectionRenderDispatcher.dispose port crates/horizon-client/src/gfx.rs::Renderer::clear_sections frees every section's GPU buffers +SectionRenderDispatcher.getStats skip:debug F3 text: compile queue size and free buffers +SectionRenderDispatcher.RenderSection.getVisibility missing player: newly meshed terrain appears at full strength; vanilla fades each section in over the chunkSectionFadeInTime option, and block entities wait for 0.3 +SectionRenderDispatcher.RenderSection.setSectionNode port crates/horizon-client/src/gfx.rs::section_instance the section's render origin; its 16-block bounds are derived from the origin +SectionRenderDispatcher.RenderSection.reset port crates/horizon-client/src/gfx.rs::Renderer::release_section drops the section's mesh and GPU slices +SectionRenderDispatcher.RenderSection.resortTransparency port crates/horizon-client/src/gfx/translucency.rs::SortState::update translucent quads re-sorted for the camera; gap: minor: the new order goes to the next draw, not a newly uploaded index buffer +SectionRenderDispatcher.RenderSection.transparencyResortingScheduled skip:other Horizon sorts during the frame, so there is no pending resort task to report +SectionRenderDispatcher.RenderSection.cancelTasks inline crates/horizon-client/src/streaming.rs::Streaming::update a finished job whose seq no longer matches is dropped on arrival; queued jobs are not removed +SectionRenderDispatcher.RenderSection.createCompileTask port crates/horizon-client/src/streaming.rs::Streaming::submit the mesh job is queued; the recompile flag is not kept +SectionRenderDispatcher.RenderSection.compileSync port crates/horizon-client/src/mesher.rs::Mesher::mesh meshes on the calling thread with no queue +SectionRenderDispatcher.RenderSection.setSectionMesh port crates/horizon-client/src/gfx.rs::Renderer::upload stores the new mesh for the section and hands back the old one +SectionRenderDispatcher.RenderSection.releaseSectionMesh port crates/horizon-client/src/gfx.rs::Renderer::release_mesh frees the old mesh's GPU slices +SectionRenderDispatcher.RenderSection.createVertexSorting port crates/horizon-client/src/gfx/translucency.rs::Sorter::sort sort by squared distance from the camera to each quad centre +SectionRenderDispatcher.RenderSection.checkSectionMesh inline crates/horizon-client/src/gfx.rs::Renderer::upload uploads are synchronous, so there is no wait for every layer to land before publishing +SectionRenderDispatcher.RenderSection.addSectionBuffersToUberBuffer port crates/horizon-client/src/gfx.rs::Renderer::upload_mesh copies a layer's vertex and index bytes into arena pages +SectionRenderDispatcher.RenderSection.CompileTask.doTask port crates/horizon-client/src/mesher.rs::Mesher::mesh meshes the section; the empty result and upload are handled in streaming and Renderer::upload +SectionRenderDispatcher.RenderSection.ResortTransparencyTask.doTask port crates/horizon-client/src/gfx/translucency.rs::SortState::update re-sorts translucent quads when the camera's point of view changes +SectionTaskDynamicQueue.poll port crates/horizon-client/src/streaming.rs::Queued::cmp nearest job first; gap: minor: no recompile quota, new and remesh jobs share one priority +SectionTaskDynamicQueue.clear skip:other Horizon has no queue clear; stale results are dropped by seq in Streaming::update +TranslucencyPointOfView.set inline crates/horizon-client/src/gfx/translucency.rs::SortState::update the point of view: camera block minus section, clamped to -1..1 per axis +TranslucencyPointOfView.isAxisAligned inline crates/horizon-client/src/gfx/translucency.rs::SortState::update the axis-aligned test: a zero component in the point of view +TranslucencyPointOfView.equals inline crates/horizon-client/src/gfx/translucency.rs::SortState::update compares the last point of view with the new one +TranslucencyPointOfView.hashCode skip:java hash for a map key +VisGraph.setOpaque inline crates/horizon-client/src/mesher.rs::visibility opaque cells are marked in the seen map before the flood fill +VisGraph.getIndex inline crates/horizon-client/src/mesher.rs::visibility cell index y<<8 | z<<4 | x in the seen map +VisGraph.floodFill inline crates/horizon-client/src/mesher.rs::visibility flood fill from each open cell; gap: minor: no shortcut for sections with fewer than 256 opaque cells, which vanilla marks fully visible +VisGraph.addEdges inline crates/horizon-client/src/mesher.rs::visibility the faces of the section a cell touches +VisGraph.getNeighborIndexAtFace inline crates/horizon-client/src/mesher.rs::visibility the neighbour index inside the section, -1 at its border +VisibilitySet.add inline crates/horizon-client/src/mesher.rs::visibility sets the bit for each pair of faces an open region touches (bit a*6+b) +VisibilitySet.set inline crates/horizon-client/src/mesher.rs::visibility sets both bits of a face pair +VisibilitySet.visibilityBetween port crates/horizon-client/src/gfx/occlusion.rs::Graph::propagate the faces a section connects are read from its visibility bits when the search moves on +VisibilitySet.toString skip:debug debug text +Frustum.set skip:java copies the frustum; Horizon passes the Frustum value +Frustum.offset missing minor: particles are not culled against a frustum pulled back 3 blocks (ParticleEngine.extract); Horizon has no particle frustum test +Frustum.offsetToFullyIncludeCameraCube port crates/horizon-client/src/gfx/occlusion.rs::Graph::search the occlusion search starts in the camera's own section, so its cube is always included; gap: minor: no frustum offset +Frustum.calculateFrustum port crates/horizon-client/src/gfx.rs::frustum the six planes from projection times view +Frustum.isVisible port crates/horizon-client/src/gfx.rs::Frustum::intersects box against the planes; gap: minor: infinite bounds are not special-cased +Frustum.cubeInFrustum port crates/horizon-client/src/gfx.rs::Frustum::intersects box test on camera-relative bounds +Frustum.pointInFrustum missing minor: only particle culling uses it (QuadParticleGroup), and Horizon has no particle frustum test +Frustum.getFrustumPoints skip:debug the eight frustum corners, for debug drawing only +Frustum.getNearPlaneBounds missing player: the block texture overlay drawn when your eye is inside a block (view-blocking block) is never shown; ScreenEffectRenderer's block_overlay is never set outside tests +BeeDebugRenderer missing minor: F3 bee and hive debug view (hive blacklist, flowers, ghost hives); not drawn +BrainDebugRenderer missing minor: F3 brain debug view (activities, memories, POIs); not drawn +BreezeDebugRenderer missing minor: F3 breeze jump target view; breeze_jump_target is kept in monster_brain.rs but never drawn +ChunkCullingDebugRenderer missing minor: F3 chunk culling frustum view; the occlusion graph has no draw +ChunkDebugRenderer missing minor: F3 chunk info view fed by the integrated server; not drawn +ChunkDebugRenderer.ChunkData missing minor: data for the F3 chunk info view; not built +CollisionBoxRenderer missing minor: F3 block collision box view; not drawn +DebugRenderer.refreshRendererList inline crates/horizon-client/src/game/debug_keys.rs::Game::debug_gizmos the enabled views; Horizon has only chunk borders (show_chunk_borders) and hitboxes (show_hitboxes), the rest are missing +DebugRenderer.getTargetedEntity skip:debug entity under the crosshair for debug views only +DebugRenderer.mixColor skip:debug colour helper for debug paths +DebugRenderer.shiftHue skip:debug colour helper with no callers +EntityBlockIntersectionDebugRenderer missing minor: F3 entity and block intersection view; not drawn +EntityHitboxDebugRenderer.emitGizmos port crates/horizon-client/src/game/debug_keys.rs::Game::debug_gizmos the F3+B hitbox loop for mobs, items, paintings, frames, boats, carts, projectiles, the dragon and the player +EntityHitboxDebugRenderer.getServerEntity skip:debug the integrated server's entity for a hitbox; Horizon draws its own client entities +GameEventListenerRenderer missing minor: F3 game event listener view; not drawn +GameTestBlockHighlightRenderer skip:debug game test block markers; Horizon does not run game tests +GoalSelectorDebugRenderer missing minor: F3 mob goal view; not drawn +HeightMapRenderer missing minor: F3 heightmap view; not drawn +LightDebugRenderer missing minor: F3 light level view; not drawn +LightSectionDebugRenderer missing minor: F3 light section view; not drawn +LightSectionDebugRenderer.SectionData missing minor: data for the F3 light section view; not built +NeighborsUpdateRenderer missing minor: F3 neighbour update view; not drawn +NeighborsUpdateRenderer.LastUpdate missing minor: data for the F3 neighbour update view; not kept +OctreeDebugRenderer missing minor: F3 chunk section octree view; not drawn +PathfindingRenderer missing minor: F3 mob path view; not drawn +PoiDebugRenderer missing minor: F3 point of interest view; not drawn +RaidDebugRenderer missing minor: F3 raid centre view; not drawn +RedstoneWireOrientationsRenderer missing minor: F3 redstone wire orientation view; not drawn +SolidFaceRenderer missing minor: F3 solid face view; not drawn +StructureRenderer missing minor: F3 structure bounding box view fed by debug subscriptions; Horizon sends none +SupportBlockRenderer missing minor: F3 support block view; not drawn +VillageSectionsDebugRenderer missing minor: F3 village section view; not drawn +WaterDebugRenderer missing minor: F3 water level view; not drawn +LevelExtractor.extract port crates/horizon-client/src/game.rs::Game::render the frame's extraction and draw; gap: minor: no separate render state, the frame is built and drawn in one fn +LevelExtractor.drainTransientBlockQueue missing player: temporary blocks the server shows (ClientboundAddTransientBlockPacket) never appear; the packet has an id in horizon-protocol but no handler +LevelExtractor.extractVisibleBlockEntities inline crates/horizon-client/src/game.rs::Game::render block entities of the visible sections; gap: minor: no 0.3 visibility gate, see RenderSection.getVisibility +LevelExtractor.extractBlockDestroyAnimation port crates/horizon-client/src/game.rs::Game::destroy_block_progress keeps the crumbling progress per breaker for 400 ticks; gap: minor: no 32-block distance cut here +LevelExtractor.extractGizmos port crates/horizon-client/src/game/debug_keys.rs::Game::debug_gizmos the frame's debug gizmos +LevelExtractor.extractPlayerState inline crates/horizon-client/src/game.rs::Game::render player and hands read from the game state at draw time; gap: minor: the view-blocking block is missing, see Frustum.getNearPlaneBounds +LevelExtractor.applyFrustum port crates/horizon-client/src/gfx/resident.rs::Resident::visible the frustum decides which sections are visible +LevelExtractor.setLevel inline crates/horizon-client/src/game.rs::Game::all_sections_changed a new level rebuilds every loaded section (allChanged); the null-level branch is not separate +LevelExtractor.blockChanged port crates/horizon-client/src/world.rs::World::set a block edit marks its 3x3x3 neighbourhood; gap: minor: the player-changed flag (update bit 8) is not kept +LevelExtractor.setBlockDirty port crates/horizon-client/src/world.rs::World::set marks the 3x3x3 section neighbourhood of an edited block +LevelExtractor.setBlocksDirty inline crates/horizon-client/src/world.rs::World::set each block of the range goes through the same 3x3x3 marking +LevelExtractor.setSectionDirtyWithNeighbors port crates/horizon-client/src/streaming.rs::Streaming::light_updated the section and its neighbours remesh +LevelExtractor.setSectionRangeDirty inline crates/horizon-client/src/streaming.rs::Streaming::light_updated section range marked in world.dirty_sections +LevelExtractor.setSectionDirty port crates/horizon-client/src/world.rs::DirtySections::insert marks one section for remesh +LevelExtractor.countRenderedSections skip:debug F3 count of sections with renderable layers; Horizon reports Renderer::section_count +LevelExtractor.sectionStatistics skip:debug F3 text +LevelExtractor.entityStatistics skip:debug F3 text +BlockModelFeatureRenderer skip:other feature pass plumbing; Horizon draws block models in its own passes with no feature groups +CustomFeatureRenderer.buildGroup skip:other feature pass plumbing; Horizon has no feature groups +FeatureRenderDispatcher. skip:other feature dispatcher set-up; Horizon has no feature dispatcher +FeatureRenderDispatcher.prepareFrame skip:other feature dispatcher; the frame's draw order is fixed in Renderer::draw_frame +FeatureRenderDispatcher.prepareFrameWithContext skip:other feature dispatcher; the frame's draw order is fixed in Renderer::draw_frame +FeatureRenderDispatcher.renderAllFeatures port crates/horizon-client/src/gfx.rs::Renderer::draw_frame draws the frame's passes; gap: minor: the order is fixed, not taken from a feature list +FeatureRenderDispatcher.PhaseSubmitGrouper skip:other groups feature submissions per phase for vanilla's dispatcher; Horizon submits in fixed passes diff --git a/tools/port_map/client2-004.tsv b/tools/port_map/client2-004.tsv new file mode 100644 index 0000000000..9c492f4f3e --- /dev/null +++ b/tools/port_map/client2-004.tsv @@ -0,0 +1,118 @@ +FeatureRenderDispatcher.PreparedFrame skip:other render-pass order and draw-group plumbing of vanilla's feature renderer; Horizon orders its own passes +FeatureRenderDispatcher.PreparedGroup skip:other runs one feature group through its renderer; the per-feature draws are mapped by their own renderers +FeatureRendererMap skip:java type-indexed lookup table of feature renderers +FlameFeatureRenderer.buildGroup port crates/horizon-client/src/fire_render.rs::flames loop over burning entities +FlameFeatureRenderer.prepare port crates/horizon-client/src/fire_render.rs::flames two alternating fire sprites stacked up the entity +FlameFeatureRenderer.fireVertex inline crates/horizon-client/src/fire_render.rs::flames vertex push folded into flames +GizmoFeatureRenderer.buildGroup port crates/horizon-client/src/gizmos.rs::camera_relative gap: minor: only lines are drawn; quads, triangle fans, points and texts are not +GizmoFeatureRenderer.buildTexts missing minor: debug gizmo text labels are not drawn; Gizmos has no text kind +GizmoFeatureRenderer.buildTriangleFans missing minor: debug triangle fans are not drawn; Gizmos has no fan kind +GizmoFeatureRenderer.buildQuads missing minor: debug filled quads are not drawn; cuboid gizmos are edges only +GizmoFeatureRenderer.buildPoints missing minor: debug points are not drawn; Gizmos has no point kind +ItemFeatureRenderer.buildGroup port crates/horizon-client/src/item_render.rs::ItemRenderState::submit loop over item submits +ItemFeatureRenderer.prepareSubmit port crates/horizon-client/src/item_render.rs::ItemRenderState::submit layer loop, quads or special model +ItemFeatureRenderer.prepareOutlineSubmit port crates/horizon-client/src/game/glowing.rs::Game::mark_outline gap: minor: outline colour is only applied to dropped items (item_draw.rs dropped_item_geometry); held-item outlines not checked +ItemFeatureRenderer.computeFoilDecalPose missing minor: unsure: the 0.5 GUI and 0.75 first-person foil decal scale is not found; glint scale is per context in item_render.rs Glint +ItemFeatureRenderer.getLayerColorSafe port crates/horizon-client/src/item_render.rs::submit_quads tint index past the tints draws white, both overloads +LeashFeatureRenderer.buildGroup port crates/horizon-client/src/game/leads.rs::Game::leash_geometry loop over leash submits +LeashFeatureRenderer.prepare port crates/horizon-client/src/game/leads.rs::Game::leash_geometry gap: player: rope never sags when slack (vanilla droops the rope); no slack state in the leash render +LeashFeatureRenderer.addVertexPair inline crates/horizon-client/src/game/leads.rs::Game::leash_geometry vertex pair with block and sky light lerp folded into leash_geometry +ModelFeatureRenderer.buildGroup port crates/horizon-client/src/mobs.rs::render_model loop over model submits +ModelFeatureRenderer.prepareModel port crates/horizon-client/src/mobs.rs::render_model gap: minor: setupAnim is done by the mob code, and the sheeted decal and uv wrap are not checked +MovingBlockFeatureRenderer.buildGroup port crates/horizon-client/src/game/moving_pistons.rs::Game::moving_piston_geometry moving piston block and base geometry +MovingBlockFeatureRenderer.putBakedQuad port crates/horizon-client/src/game/moving_pistons.rs::Game::moving_block_vertices quad placed at the moving offset +QuadParticleFeatureRenderer skip:other GPU batching and draw plumbing of particle quads; Horizon's particle pass lives in particles.rs and gfx/pack_entities.rs +RenderTypeFeatureRenderer skip:other GPU render-type batching; Horizon batches its own draws in gfx/entity_batch.rs +RenderTypeFeatureRenderer.Group skip:other GPU render-type batching, as above +ShadowFeatureRenderer.buildGroup port crates/horizon-client/src/shadows.rs::shadow loop over shadow submits +ShadowFeatureRenderer.prepare port crates/horizon-client/src/shadows.rs::shadow shadow quads per piece, clamped by the block below +ShadowFeatureRenderer.shadowVertex inline crates/horizon-client/src/shadows.rs::shadow vertex push folded into shadow +ShapeOutlineFeatureRenderer.buildGroup port crates/horizon-client/src/gfx.rs::outline_vertices block selection outline edges as line vertices +TextFeatureRenderer.buildGroup skip:other dispatch over text and background submits; each kind is covered below or by Horizon's text draw +TextFeatureRenderer.renderText missing minor: unsure: no outlined-text path found (font.rs, hud.rs, names.rs); the outline colour of a text submit is not drawn +TextFeatureRenderer.GlyphRenderer.acceptRenderable skip:other glyph sink for the text pass; Horizon's font pass draws the glyphs +FeatureRenderPhase.Output.acceptFeatureGroup skip:other phase plumbing: hands sorted groups to the renderer pass +SimpleFeatureRenderPhase skip:other submit, sort, shuffle (debug only) and clear of feature submits; Horizon sorts its own draws +SimpleFeatureRenderPhase.FeatureSubmits skip:other batching bookkeeping of feature submits +TranslucentFeatureRenderPhase.submit inline crates/horizon-client/src/gfx/translucency.rs::Sorter::sort collects submits; distances are taken in sort +TranslucentFeatureRenderPhase.sortInto port crates/horizon-client/src/gfx/translucency.rs::Sorter::sort back-to-front by distance, equal distances keep submit order +TranslucentFeatureRenderPhase.sortIndices port crates/horizon-client/src/gfx/translucency.rs::Sorter::sort far to near, stable on ties +TranslucentSubmit.computeDistanceToCameraSq inline crates/horizon-client/src/gfx/translucency.rs::Sorter::sort distance from the quad centre to the camera, computed inside sort +FogRenderer.getBuffer skip:other selects the fog uniform slice; Horizon hands its fog values to its own shader per frame +FogRenderer.updateBuffer skip:other writes the fog uniform buffer; Horizon's frame_fog passes FogData to its own shader +AtmosphericFogEnvironment.getBaseColor port crates/horizon-client/src/fog.rs::setup_fog gap: minor: sky fog end uses constant 512 instead of the SKY_FOG_END_DISTANCE attribute +AtmosphericFogEnvironment.applyWeatherDarken inline crates/horizon-client/src/fog.rs::setup_fog rain and thunder darkening folded into the atmospheric colour +BlindnessFogEnvironment.setupFog port crates/horizon-client/src/fog.rs::setup_fog blindness distance and sky/cloud ends +BlindnessFogEnvironment.getModifiedDarkness inline crates/horizon-client/src/fog.rs::setup_fog blindness darkness folded into setup_fog +LavaFogEnvironment.setupFog port crates/horizon-client/src/fog.rs::setup_fog gap: player: spectator (-8 start, half render distance) and fire resistance (0 to 5) are not applied in lava +MobEffectFogEnvironment.isApplicable inline crates/horizon-client/src/game/render_tick.rs::Game::frame_fog effect gate is where frame_fog builds the blindness and darkness inputs +PowderedSnowFogEnvironment.setupFog port crates/horizon-client/src/fog.rs::setup_fog gap: player: spectator case (-8 start, half render distance) is not applied +WaterFogEnvironment.setupFog port crates/horizon-client/src/fog.rs::setup_fog gap: minor: water fog start is fixed at -8; vanilla reads the WATER_FOG_START_DISTANCE attribute +DrawableGizmoPrimitives.addPoint missing minor: debug points are not drawn; Gizmos has no point kind +DrawableGizmoPrimitives.addLine port crates/horizon-client/src/gizmos.rs::Gizmos::line +DrawableGizmoPrimitives.addTriangleFan missing minor: debug triangle fans are not drawn +DrawableGizmoPrimitives.addQuad missing minor: debug filled quads are not drawn +DrawableGizmoPrimitives.addText missing minor: debug gizmo text labels are not drawn +DrawableGizmoPrimitives.submit port crates/horizon-client/src/game.rs::Game::render gap: minor: lines of both lists go to camera_relative in Game::render; only lines are drawn +BundleSelectedItemSpecialRenderer.update port crates/horizon-assets/src/item_model.rs::Resolve::node bundle selected item layers via the BundleSelectedItem arm +CompositeModel.update port crates/horizon-assets/src/item_model.rs::Resolve::node composite arm: each child in order +CompositeModel.Unbaked.resolveDependencies port crates/horizon-assets/src/item_model.rs::ItemModelNode::dependencies +CompositeModel.Unbaked.bake inline crates/horizon-assets/src/item_model.rs::Resolve::node child transform composed in the composite arm +ConditionalItemModel.update port crates/horizon-assets/src/item_model.rs::Resolve::node condition arm picks the branch +ConditionalItemModel.Unbaked.bake inline crates/horizon-assets/src/item_model.rs::Resolve::node child transform composed in the condition arm +ConditionalItemModel.Unbaked.adaptProperty skip:java per-level cache of a registry-remapped property; Horizon resolves properties from its own tables +ConditionalItemModel.Unbaked.swapContext skip:data registry remap of a property for a new level; Horizon reads item definitions as data +CuboidItemModelWrapper.update port crates/horizon-assets/src/item_model.rs::Resolve::node model layer: foil, tints, transform and quads +CuboidItemModelWrapper.validateAtlasUsage skip:other load-time check that a model uses only the item and block atlases; throws on bad data, nothing at play time +CuboidItemModelWrapper.hasSpecialAnimatedTexture inline crates/horizon-assets/src/item_model.rs::Resolve::node clock and compass tag give special foil, folded into the model arm +ItemModelResolver.updateForLiving port crates/horizon-client/src/item_render.rs::ItemRenderer::resolve gap: minor: the seed is passed in by the caller; the entity id plus display ordinal seed is not checked here +ItemModelResolver.updateForNonLiving port crates/horizon-client/src/item_render.rs::ItemRenderer::resolve gap: minor: the seed is passed in by the caller; the entity id seed is not checked here +ItemStackRenderState.ensureCapacity skip:java layer array capacity; Horizon builds layer lists per frame +ItemStackRenderState.newLayer skip:java layer array allocation +ItemStackRenderState.clear skip:java resets the per-frame layer list +ItemStackRenderState.visitExtents port crates/horizon-client/src/item_render.rs::ItemRenderState::bounding_box extents transformed per layer +ItemStackRenderState.getModelBoundingBox port crates/horizon-client/src/item_render.rs::ItemRenderState::bounding_box bounding box from the layer extents +ItemStackRenderState.LayerRenderState.clear skip:java resets one layer's fields +ItemStackRenderState.LayerRenderState.tintLayers skip:java lazy list allocation for tint values +ItemStackRenderState.LayerRenderState.submit port crates/horizon-client/src/item_render.rs::ItemRenderState::submit special renderer or quads per layer +MissingItemModel.update port crates/horizon-assets/src/item_model.rs::Resolve::missing missing-model layer +MissingItemModel.withTransform port crates/horizon-assets/src/item_model.rs::Resolve::missing missing-model layer under the given transform +ModelRenderProperties.fromResolvedModel port crates/horizon-assets/src/item_bake.rs::bake_item_model particle sprite and GUI light taken at bake +ModelRenderProperties.applyToLayer port crates/horizon-client/src/item_render.rs::ItemRenderer::resolve uses block light, particle and display transform on the layer +RangeSelectItemModel.update port crates/horizon-assets/src/item_model.rs::Resolve::node range dispatch arm picks the entry +RangeSelectItemModel.Unbaked.bake port crates/horizon-assets/src/item_model.rs::ItemModelNode::decode entries built at load time +SelectItemModel.update port crates/horizon-assets/src/item_model.rs::Resolve::node select arm picks the case +SelectItemModel.SwitchCase.codec port crates/horizon-assets/src/item_model.rs::SwitchCase::decode case decode from JSON +SelectItemModel.Unbaked.bake port crates/horizon-assets/src/item_model.rs::ItemModelNode::decode cases built at load time +SelectItemModel.Unbaked.resolveDependencies port crates/horizon-assets/src/item_model.rs::ItemModelNode::dependencies +SelectItemModel.UnbakedSwitch.bake port crates/horizon-assets/src/item_model.rs::ItemModelNode::decode switch cases built at load time +SelectItemModel.UnbakedSwitch.createModelGetter port crates/horizon-assets/src/item_model.rs::Resolve::node case lookup by value in the select arm +SelectItemModel.UnbakedSwitch.resolveDependencies port crates/horizon-assets/src/item_model.rs::ItemModelNode::dependencies +SpecialModelWrapper.update port crates/horizon-assets/src/item_model.rs::Resolve::node special arm: model argument from components +SpecialModelWrapper.Unbaked.bake port crates/horizon-assets/src/item_model.rs::ItemModelNode::decode special node built at load time +SpecialModelWrapper.Unbaked.getProperties port crates/horizon-assets/src/item_model.rs::ItemModels::properties hand animation, GUI oversize and swap scale +properties/conditional/CustomModelDataProperty.get port crates/horizon-assets/src/item_model.rs::Resolve::condition conditional property: custom model data flag +FishingRodCast.get port crates/horizon-assets/src/item_model.rs::Resolve::condition owner is casting this item +HasComponent.get port crates/horizon-assets/src/item_model.rs::Resolve::condition component present, default ignored when asked +IsCarried.get port crates/horizon-assets/src/item_model.rs::Resolve::condition local player carrying the stack on the cursor +IsSelected.get port crates/horizon-assets/src/item_model.rs::Resolve::condition local player's selected slot +IsUsingItem.get port crates/horizon-assets/src/item_model.rs::Resolve::condition owner using this item +IsViewEntity.get port crates/horizon-assets/src/item_model.rs::Resolve::condition owner is the camera entity +CompassAngleState.getRandomlySpinningRotation port crates/horizon-assets/src/item_model.rs::Resolve::compass no-target spin, with the no-target wobbler +CompassAngleState.getRotationTowardsCompassTarget port crates/horizon-assets/src/item_model.rs::Resolve::compass target angle and local-player wobble +CompassAngleState.isValidCompassTargetPos inline crates/horizon-assets/src/item_model.rs::Resolve::compass dimension and distance check folded into compass +CompassAngleState.getAngleFromEntityToPos inline crates/horizon-assets/src/item_model.rs::Resolve::compass atan2 folded into compass +Cooldown.get port crates/horizon-assets/src/item_model.rs::Resolve::numeric owner cooldown percent +Count.get port crates/horizon-assets/src/item_model.rs::Resolve::numeric count, normalised or clamped +CrossbowPull.get port crates/horizon-assets/src/item_model.rs::Resolve::numeric charge progress, zero while loaded +properties/numeric/CustomModelDataProperty.get port crates/horizon-assets/src/item_model.rs::Resolve::numeric custom model data float +Damage.get port crates/horizon-assets/src/item_model.rs::Resolve::numeric damage, normalised or clamped +UseCycle.get port crates/horizon-assets/src/item_model.rs::Resolve::numeric remaining use ticks modulo the period +UseDuration.get port crates/horizon-assets/src/item_model.rs::Resolve::numeric remaining or elapsed use ticks +Charge.get port crates/horizon-assets/src/item_model.rs::Resolve::select_value none, arrow or rocket +properties/select/ComponentContents.createType skip:data codec for a component-keyed select; Horizon decodes select cases in item_model.rs +ContextEntityType.get port crates/horizon-assets/src/item_model.rs::Resolve::select_value owner entity type +properties/select/CustomModelDataProperty.get port crates/horizon-assets/src/item_model.rs::Resolve::select_value custom model data string +ItemBlockState.get port crates/horizon-assets/src/item_model.rs::Resolve::select_value block state property of the stack +LocalTime.create port crates/horizon-client/src/game/item_draw.rs::local_time gap: minor: locale and time zone are ignored; only the pattern letters M d y H m s are formatted +LocalTime.get port crates/horizon-client/src/game/item_draw.rs::local_time gap: minor: same as create; formatted on every call, no 1-second cache diff --git a/tools/port_map/client2-005.tsv b/tools/port_map/client2-005.tsv new file mode 100644 index 0000000000..5ca6a13ae2 --- /dev/null +++ b/tools/port_map/client2-005.tsv @@ -0,0 +1,145 @@ +SelectItemModelProperty.Type.create skip:java codec builder +SelectItemModelProperty.Type.validateCases port crates/horizon-assets/src/item_model.rs::ItemModelNode::decode empty and duplicate case checks in the Select arm +TrimMaterialProperty.get port crates/horizon-assets/src/item_model.rs::Resolve::select_value trim material id from the minecraft:trim component +OitPipelineSet.getPipeline missing minor: opt-in Improved Transparency (off by default), no OIT pipelines or passes in Horizon +OitPipelineSet.Builder.composeModifiers missing minor: opt-in Improved Transparency (off by default), no OIT pipelines or passes in Horizon +OitPipelineSet.Builder.build missing minor: opt-in Improved Transparency (off by default), no OIT pipelines or passes in Horizon +OitRenderPassProvider.createRenderPass missing minor: opt-in Improved Transparency (off by default), no OIT pipelines or passes in Horizon +OitRenderPassProvider.createDepthBoundsPass missing minor: opt-in Improved Transparency (off by default), no OIT pipelines or passes in Horizon +OitRenderPassProvider.createTransmittancePass missing minor: opt-in Improved Transparency (off by default), no OIT pipelines or passes in Horizon +OitRenderPassProvider.createAccumulatePass missing minor: opt-in Improved Transparency (off by default), no OIT pipelines or passes in Horizon +PreparedRenderType.drawFromBufferOit missing minor: opt-in Improved Transparency (off by default), no OIT pipelines or passes in Horizon +PreparedRenderType.draw inline crates/horizon-client/src/gfx.rs::Renderer::draw_frame gap: minor: the indexed draw runs in the gfx/ pass modules that draw_frame calls; RenderSetup scissor not seen +RenderSetup.toString skip:debug +RenderSetup.prepareTextures inline crates/horizon-client/src/gfx.rs::Renderer::draw_frame overlay and lightmap samplers are bound per pass in gfx/ +RenderSetup.RenderSetupBuilder.withTexture skip:java builder +RenderSetup.RenderSetupBuilder.setOutline skip:java builder setter; outline behaviour is in gfx/entity_outline.rs +RenderSetup.RenderSetupBuilder.createRenderSetup skip:java builder +RenderType.calculateHasBlending skip:other blend flag from the pipeline colour targets; Horizon picks blend per pipeline in gfx.rs +RenderType.prepare inline crates/horizon-client/src/gfx.rs::Renderer::draw_frame gap: minor: texture and overlay/lightmap binding folded into the frame passes +RenderType.writeDynamicTransforms inline crates/horizon-client/src/gfx.rs::Renderer::draw_frame gap: minor: no per-render-type layering offset found (only perspective layering scale) +RenderTypes.createMovingBlockSetup inline crates/horizon-client/src/game/moving_pistons.rs::Game::moving_piston_geometry block atlas and lightmap for moving blocks; gap: minor: crumbling and outline flags not checked here +RenderTypes.breezeWind inline crates/horizon-client/src/mobs/render.rs::layers gap: player: the breeze wind layer does not scroll; the UV offset is not applied (render.rs comment) +BannerSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit Banner arm: flag, base and up to 16 pattern layers +BannerSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts models are baked once from entity-models.json +BellSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit Bell arm +BellSpecialRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents Bell arm +BellSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts models are baked once from entity-models.json +BookSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit Book arm with book_pose +BookSpecialRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents Book arm with book_pose +BookSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts models are baked once from entity-models.json +ChestSpecialRenderer.createDefaultTextures inline crates/horizon-client/src/item_render/special.rs::submit default chest texture ids come from mapped("chest", ...) in the Chest arm +ChestSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit Chest arm with chest_pose +ChestSpecialRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents Chest arm with chest_pose +ChestSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts models are baked once from entity-models.json +ConduitSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit Conduit arm +CopperGolemStatueSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit CopperGolemStatue arm +CopperGolemStatueSpecialRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents CopperGolemStatue arm +CopperGolemStatueSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts COPPER_GOLEM_LAYERS are baked once +CopperGolemStatueSpecialRenderer.Unbaked.getModel port crates/horizon-client/src/item_render/special.rs::statue_layer pose to model layer +DecoratedPotSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit DecoratedPot arm +EndCubeSpecialRenderer.Unbaked.bake missing minor: no bundled item uses the end cube model (special.rs notes it), so no end portal or gateway effect in hand +PlayerHeadSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit PlayerHead arm +PlayerHeadSpecialRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents PlayerHead arm +PlayerHeadSpecialRenderer.extractArgument port crates/horizon-client/src/skins.rs::head_skin profile to skin lookup +PlayerHeadSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts models are baked once from entity-models.json +ShieldSpecialRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents Shield arm +ShieldSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts models are baked once from entity-models.json +ShulkerBoxSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit ShulkerBox arm with shulker_pose +ShulkerBoxSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts models are baked once from entity-models.json +SkullSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit Head arm with skull_pose and skull_texture +SkullSpecialRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents Head arm with skull_pose +SkullSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts models are baked once from entity-models.json +SpecialModelRenderers.bootstrap skip:data registers special model kinds; Horizon matches SpecialKind in item_model.rs +TridentSpecialRenderer.submit port crates/horizon-client/src/item_render/special.rs::submit Trident arm; has_foil gives the standard foil +TridentSpecialRenderer.getExtents port crates/horizon-client/src/item_render/special.rs::extents Trident arm +TridentSpecialRenderer.Unbaked.bake inline crates/horizon-client/src/item_render/special.rs::load_parts models are baked once from entity-models.json +BlitRenderState.buildVertices skip:ui GUI draw-queue geometry; Horizon draws its own HUD +BlitRenderState.getBounds skip:ui GUI draw-queue geometry; Horizon draws its own HUD +ColoredRectangleRenderState.buildVertices skip:ui GUI draw-queue geometry; Horizon draws its own HUD +ColoredRectangleRenderState.getBounds skip:ui GUI draw-queue geometry; Horizon draws its own HUD +GlyphRenderState.buildVertices skip:ui GUI draw-queue geometry; Horizon draws its own HUD +GuiItemRenderState.calculateOversizedItemBounds skip:ui GUI draw-queue geometry; Horizon draws its own HUD +GuiItemRenderState.calculateBounds skip:ui GUI draw-queue geometry; Horizon draws its own HUD +GuiRenderState.nextStratum skip:ui GUI draw-queue ordering +GuiRenderState.blurBeforeThisStratum port crates/horizon-client/src/game.rs::Game::hud_layers blur_at marks the vertices drawn before the blur (game.rs hud batch) +GuiRenderState.up skip:ui GUI draw-queue ordering +GuiRenderState.addItem skip:ui GUI draw-queue ordering +GuiRenderState.addText skip:ui GUI draw-queue ordering +GuiRenderState.addPicturesInPictureState skip:ui GUI draw-queue ordering +GuiRenderState.addGuiElement skip:ui GUI draw-queue ordering +GuiRenderState.addDebugRectangleIfEnabled skip:debug UI layering debug rectangles +GuiRenderState.findAppropriateNode skip:ui GUI draw-queue ordering and traversal +GuiRenderState.navigateToAboveHighestElementWithIntersectingBounds skip:ui GUI draw-queue ordering and traversal +GuiRenderState.hasIntersection skip:ui GUI draw-queue ordering and traversal +GuiRenderState.forEachElement skip:ui GUI draw-queue ordering and traversal +GuiRenderState.forEachItem skip:ui GUI draw-queue ordering and traversal +GuiRenderState.forEachText skip:ui GUI draw-queue ordering and traversal +GuiRenderState.forEachPictureInPicture skip:ui GUI draw-queue ordering and traversal +GuiRenderState.sortElements skip:ui GUI draw-queue ordering and traversal +GuiRenderState.traverse skip:ui GUI draw-queue ordering and traversal +GuiRenderState.reset skip:ui GUI draw-queue ordering and traversal +GuiRenderState.Node.addItem skip:ui GUI draw-queue node +GuiRenderState.Node.addText skip:ui GUI draw-queue node +GuiRenderState.Node.addPicturesInPictureState skip:ui GUI draw-queue node +GuiRenderState.Node.addGuiElement skip:ui GUI draw-queue node +GuiRenderState.Node.addGlyph skip:ui GUI draw-queue node +GuiTextRenderState.ensurePrepared skip:ui text layout for the GUI draw queue +TiledBlitRenderState.buildVertices skip:ui GUI tiled draw geometry +TiledBlitRenderState.getBounds skip:ui GUI draw bounds +PictureInPictureRenderState.getBounds skip:ui GUI draw bounds +LevelRenderState.reset skip:java per-frame state clear +ParticlesRenderState.submit inline crates/horizon-client/src/particles.rs::ParticleEngine::render submits particle groups; draws in render +PlayerRenderState.reset skip:java per-frame state clear +QuadParticleRenderState.add port crates/horizon-client/src/particles.rs::ParticleEngine::add quad particle queued per layer +QuadParticleRenderState.buildLayer port crates/horizon-client/src/particles.rs::ParticleEngine::render per-layer quad build and draw +QuadParticleRenderState.renderVertex inline crates/horizon-client/src/particles.rs::ParticleEngine::render vertex math inside the render rotated quad +QuadParticleRenderState.Storage.add port crates/horizon-client/src/particles.rs::ParticleEngine::push store one quad particle +QuadParticleRenderState.Storage.forEachParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::render iterates stored particles in render +QuadParticleRenderState.Storage.grow skip:java array capacity growth +WeatherRenderState.reset skip:java per-frame state clear +AbstractTexture.releaseTextures skip:java GPU handle release; Rust drop +AbstractTexture.getTexture skip:java accessor +AbstractTexture.getTextureView skip:java accessor +CubeMapTexture.loadContents port crates/horizon-client/src/gfx/panorama.rs::Faces::try_load six faces read, flipped and stacked +CubeMapTexture.doLoad port crates/horizon-client/src/gfx/panorama.rs::Panorama::new cube texture creation and upload +DynamicAtlasTree.insertInner skip:other dynamic atlas packing; Horizon uses texture-array layers, not a rectangle packer +DynamicTexture.createTexture skip:other GPU texture creation for CPU pixels; Horizon has no DynamicTexture class +DynamicTexture.upload port crates/horizon-client/src/gfx.rs::Renderer::update_lightmap gap: minor: only the lightmap upload goes through here +DynamicTexture.dumpContents skip:debug dump debug action +MipmappedTexture.setSampler port crates/horizon-client/src/screen_effects.rs::sampling gap: minor: only blur and clamp bits; filters fixed in gfx +MipmappedTexture.doLoad port crates/horizon-client/src/mipmap.rs::generate mip chain generation +MipmappedTexture.clampMipLevel port crates/horizon-client/src/sprites.rs::sprite_mipmap_limit mip level cap from texture size +MissingTextureAtlasSprite.create port crates/horizon-client/src/sprites.rs::Sprites::insert generated magenta and black missing texture +OverlayTexture.pack skip:other packs u and v into one int for vertex data; overlay.rs::living uses one code +ReloadableTexture.apply inline crates/horizon-client/src/sprites.rs::Sprites::load gap: minor: sampler flags read from the texture section; upload happens later in gfx +ReloadableTexture.doLoad inline crates/horizon-client/src/sprites.rs::Sprites::finish gap: minor: lays out layers; the GPU upload is in gfx +SkinTextureDownloader.downloadAndRegisterSkin port crates/horizon-client/src/skins.rs::Downloader::state starts the skin download and decode +SkinTextureDownloader.downloadSkin port crates/horizon-client/src/skins.rs::Downloader::state disk cache read or HTTP fetch with cache write, in the spawned thread +SkinTextureDownloader.registerTextureInManager inline crates/horizon-client/src/skins.rs::Downloader::queue ready skin queued for the atlas upload +SkinTextureDownloader.doNotchTransparencyHack inline crates/horizon-client/src/skins.rs::process_legacy_skin hat layer cleared when fully opaque +SkinTextureDownloader.setNoAlpha inline crates/horizon-client/src/skins.rs::process_legacy_skin legacy skin regions made opaque +SpriteContents.increaseMipLevel port crates/horizon-client/src/mipmap.rs::generate mip chain generation +SpriteContents.createAnimatedTexture port crates/horizon-client/src/sprites.rs::Sprites::load animation frames from mcmeta +SpriteContents.createAnimationState inline crates/horizon-client/src/sprites.rs::Sprites::finish animation frames laid out per layer +SpriteContents.getAdditionalMetadata skip:java generic metadata lookup +SpriteContents.close skip:java image release; Rust drop +SpriteContents.toString skip:debug +SpriteContents.isTransparent inline crates/horizon-client/src/sprites.rs::Sprites::region_opaque gap: minor: single-texel alpha query is not kept; region_opaque tests alpha 255 over a UV box +SpriteContents.uploadFirstFrame inline crates/horizon-client/src/sprites.rs::Sprites::finish first frame copied into the layer pixels +SpriteContents.AnimatedTexture.createAnimationState inline crates/horizon-client/src/sprites.rs::Sprites::finish animation frames laid out per layer +SpriteContents.AnimationState.needsToDraw skip:java redraw skip optimisation +SpriteContents.AnimationState.close skip:java frame texture release; Rust drop +SpriteLoader.stitch port crates/horizon-client/src/sprites.rs::sprite_mipmap_limit gap: minor: atlas stitching replaced by one texture-array layer per sprite +SpriteLoader.runSpriteSuppliers skip:java parallel sprite loading +SpriteLoader.loadAndStitch inline crates/horizon-client/src/sprites.rs::Sprites::insert gap: minor: sprites are read on first use, not as one batch +SpriteLoader.getStitchedSprites skip:other maps sprites to atlas rectangles; Horizon uses array layers +Stitcher.registerSprite skip:other atlas rectangle packing; Horizon uses one texture-array layer per sprite +Stitcher.stitch skip:other atlas rectangle packing; Horizon uses one texture-array layer per sprite +Stitcher.gatherSprites skip:other atlas rectangle packing; Horizon uses one texture-array layer per sprite +Stitcher.smallestFittingMinTexel skip:other atlas rectangle packing; Horizon uses one texture-array layer per sprite +Stitcher.addToStorage skip:other atlas rectangle packing; Horizon uses one texture-array layer per sprite +Stitcher.expand skip:other atlas rectangle packing; Horizon uses one texture-array layer per sprite +Stitcher.Region.add skip:other atlas rectangle packing +Stitcher.Region.walk skip:other atlas rectangle packing +Stitcher.Region.toString skip:debug diff --git a/tools/port_map/client2-006.tsv b/tools/port_map/client2-006.tsv new file mode 100644 index 0000000000..9be675c0b5 --- /dev/null +++ b/tools/port_map/client2-006.tsv @@ -0,0 +1,150 @@ +TextureAtlas.createTexture inline crates/horizon-client/src/sprites.rs::Sprites::finish gap: minor: no GPU texture objects or mip views; array size and mip count are set in finish +TextureAtlas.upload inline crates/horizon-client/src/sprites.rs::Sprites::finish gap: minor: no GPU upload or animated UBOs; sprites are inserted before finish +TextureAtlas.uploadInitialContents inline crates/horizon-client/src/sprites.rs::Sprites::finish static frames copied into the array pixels +TextureAtlas.dumpContents skip:debug DEBUG_DUMP_TEXTURE_ATLAS PNG dump +TextureAtlas.dumpSpriteNames skip:debug DEBUG_DUMP_TEXTURE_ATLAS name list +TextureAtlas.cycleAnimationFrames port crates/horizon-client/src/gfx/animate.rs::SpriteAnimator::animate advances every animated layer to the frame for the tick +TextureAtlas.uploadAnimationFrames port crates/horizon-client/src/gfx/animate.rs::mix_level per mip level frame mix (drawToAtlas) +TextureAtlas.getSprite port crates/horizon-client/src/sprites.rs::Sprites::insert gap: minor: loads a texture on first lookup; vanilla returns the missing sprite for names not in the atlas +TextureAtlas.clearTextureData skip:java frees sprite and UBO memory; Rust ownership +TextureAtlas.releaseTextures skip:java frees GPU mip views; Rust ownership +TextureAtlasSprite.getV inline crates/horizon-client/src/particles.rs::ParticleEngine::render gap: minor: only the particle quad V lerp is here; other getV callers keep their own lerp +TextureAtlasSprite.toString skip:debug debug string +TextureAtlasSprite.uploadSpriteUbo skip:other GPU uniform block for the atlas blit; Horizon samples its layer array and SpriteAnimator computes frames, so no UBO is needed +TextureContents.load port crates/horizon-client/src/sprites.rs::Sprites::load reads the PNG and the .mcmeta animation section +TextureManager.registerAndLoad port crates/horizon-client/src/sprites.rs::Sprites::insert loads the texture into a layer and registers it by name +TextureManager.loadContentsSafe inline crates/horizon-client/src/sprites.rs::Sprites::insert missing texture fallback when a texture does not load +TextureManager.register inline crates/horizon-client/src/sprites.rs::Sprites::insert adds the layer under its name +TextureManager.safeClose skip:java closes a texture object; Rust ownership +TextureManager.getTexture port crates/horizon-client/src/sprites.rs::Sprites::insert loads on first use, as SimpleTexture does +TextureManager.tick port crates/horizon-client/src/gfx/animate.rs::SpriteAnimator::animate gap: minor: animated layers advance in the per frame animate pass; there is no tickable texture list +TextureManager.release skip:java drops one texture object; Rust ownership +TextureManager.close skip:java drops all texture objects; Rust ownership +TextureManager.reload port crates/horizon-client/src/assets.rs::Assets::reload gap: minor: rebuilds every render asset synchronously; no pending futures +TextureManager.dumpAllSheets skip:debug debug dump of every atlas +TextureManager.scheduleLoad skip:other asynchronous texture preparation; Horizon loads each texture on the calling thread in Sprites::insert +SpriteSource.Output.add port crates/horizon-client/src/atlas_sources.rs::list records a sprite id and its origin; a later source with the same id replaces it +SpriteSourceList.load port crates/horizon-client/src/atlas_sources.rs::atlases gap: minor: reads one resource root's atlases/*.json, not every pack's stacked definitions +DirectoryLister.run port crates/horizon-client/src/atlas_sources.rs::list Directory arm: textures under source/ get the prefix +LazyLoadedImage.get skip:java shared image cache; generate reads the source texture each time it builds a sprite +LazyLoadedImage.release skip:java releases the shared image cache +PalettedPermutations.loadPaletteEntryFromImage inline crates/horizon-client/src/atlas_sources.rs::generate palette images are loaded inside the Paletted arm of generate +PalettedPermutations.PalettedSpriteSupplier.get port crates/horizon-client/src/atlas_sources.rs::generate Paletted arm: base colours mapped through the palette pair (apply_palette) +SingleFile.run port crates/horizon-client/src/atlas_sources.rs::list Single arm: the sprite named by the resource, or its sprite id +Unstitcher.run port crates/horizon-client/src/atlas_sources.rs::list Unstitch arm: one sprite per region of the texture +Unstitcher.RegionInstance.get port crates/horizon-client/src/atlas_sources.rs::generate Region arm: the region cut out of the texture +AshParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Ash arm +AttackSweepParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle SweepAttack uses the HugeExplosion behaviour: age, then sprite from age +AttackSweepParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make SweepAttack arm: size from xAux +BaseAshSmokeParticle. port crates/horizon-client/src/particles.rs::ParticleEngine::make Smoke, Ash, WhiteAsh and DustPlume arms: motion, GrowIn size, lifetime +BreakingItemParticle.CobwebProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make ItemCobweb arm; the layer comes from item_sprites (game/item_particles.rs) +BreakingItemParticle.ItemParticleProvider.getSprite port crates/horizon-client/src/particles.rs::ParticleEngine::make Item arms pick a layer with the engine random; ItemCobweb, ItemSlime and ItemSnowball read item_sprites +BreakingItemParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Item arm +BreakingItemParticle.SlimeProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make ItemSlime arm +BreakingItemParticle.SnowballProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make ItemSnowball arm +BreakingItemParticle.SulfurCubeProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make SulfurCubeGoo arm: the first sprite of its set +BubbleColumnUpParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle BubbleColumnUp arm: removed once it leaves water +BubbleColumnUpParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make BubbleColumnUp arm +BubbleParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Bubble arm: rises, drag 0.85, removed out of water +BubbleParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Bubble arm +BubblePopParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle BubblePop arm +BubblePopParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make BubblePop arm +CampfireSmokeParticle.CosyProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make CampfireCosySmoke arm, alpha 0.9 +CampfireSmokeParticle.SignalProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make CampfireSignalSmoke arm, alpha 0.95 +CritParticle. port crates/horizon-client/src/particles.rs::ParticleEngine::make Crit arm; the constructor tick runs on the next engine tick (pre_tick) +CritParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Crit arm: green and blue fade +CritParticle.DamageIndicatorProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make DamageIndicator arm; lifetime 20 from after_constructor_tick +CritParticle.MagicProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make EnchantedHit arm; red and green scaled by after_constructor_tick +CritParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Crit arm +DragonBreathParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle DragonBreath arm: hit-ground flag and friction +DragonBreathParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size GrowIn size curve +DragonBreathParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make DragonBreath arm; power through set_power +DripParticle.getLightCoords port crates/horizon-client/src/particles.rs::Particle::light_coords Block15 for the glowing tears; World for the rest +DripParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::Drip: pre move, gravity and move, post move, fluid check +DripParticle.preMoveUpdate port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Drip: lifetime countdown and removal +DripParticle.CoolingDripHangParticle.preMoveUpdate port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Drip CoolingHang stage: red, green and blue fade +DripParticle.DripHangParticle.preMoveUpdate port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Drip Hang stage: spawns the falling drip when the countdown ends +DripParticle.DripHangParticle.postMoveUpdate port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Drip Hang stage: velocity times 0.02 +DripParticle.DripstoneFallAndLandParticle.postMoveUpdate port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle FallAndLand stage with the dripstone land sound +DripParticle.DripstoneLavaFallProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make FallingDripstoneLava arm +DripParticle.DripstoneLavaHangProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make DrippingDripstoneLava arm +DripParticle.DripstoneWaterFallProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make FallingDripstoneWater arm +DripParticle.DripstoneWaterHangProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make DrippingDripstoneWater arm +DripParticle.FallAndLandParticle.postMoveUpdate port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle FallAndLand stage: removed on the ground, spawns the land particle +DripParticle.HoneyFallAndLandParticle.postMoveUpdate port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle FallAndLand for FallingHoney: beehive drip sound +DripParticle.LavaFallProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make FallingLava arm +DripParticle.LavaHangProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make DrippingLava arm +DripParticle.LavaLandProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make LandingLava arm +DripParticle.ObsidianTearFallProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make FallingObsidianTear arm +DripParticle.ObsidianTearHangProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make DrippingObsidianTear arm +DripParticle.ObsidianTearLandProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make LandingObsidianTear arm +DripParticle.SporeBlossomFallProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make FallingSporeBlossom arm +DripParticle.WaterFallProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make FallingWater arm +DripParticle.WaterHangProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make DrippingWater arm +DustColorTransitionParticle.randomizeColor port crates/horizon-client/src/particles.rs::ParticleEngine::make DustColorTransition arm: the randomize closure for from and to colours +DustColorTransitionParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make DustColorTransition arm +DustParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Dust arm +DustParticleBase.randomizeColor port crates/horizon-client/src/particles.rs::ParticleEngine::make Dust and DustColorTransition arms: the randomize closure +DustParticleBase.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size GrowIn size curve +DustPlumeParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make DustPlume arm, base 1.0 +ElderGuardianParticleGroup.extractRenderState port crates/horizon-client/src/game/mob_blocks.rs::Game::elder_apparition_geometry gap: minor: one apparition timer that a new curse resets, so two curses show one ghost +ElderGuardianParticleGroup.ElderGuardianParticleRenderState.fromParticle port crates/horizon-client/src/game/mob_blocks.rs::Game::elder_apparition_geometry fade, camera pose and offsets +ElderGuardianParticleGroup.State.submit port crates/horizon-client/src/game/mob_blocks.rs::Game::elder_apparition_geometry draws the guardian model +EmissiveRisingParticle.getLightCoords port crates/horizon-client/src/particles.rs::Particle::light_coords Block15 for sculk soul only (the SculkSoul arm) +EmissiveRisingParticle.EmissiveProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make SculkSoul arm of the Soul arms +EmissiveRisingParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Soul arm +EndRodParticle.move port crates/horizon-client/src/particles.rs::Particle::move_by free_move: moves the box without collision +EndRodParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make EndRod arm +ExplodeParticle. port crates/horizon-client/src/particles.rs::ParticleEngine::make Poof and Spit arms: puff motion, size and lifetime +ExplodeParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Poof arm +FallingDustParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size GrowIn size curve +FallingDustParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle FallingDust arm: roll and gravity clamp +FallingDustParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make gap: minor: the invisible-block skip and the tint lookup happen in animate_tick (Animate::Falling), not here +FallingLeavesParticle.CherryProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make CherryLeaves arm +FallingLeavesParticle.PaleOakProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make PaleOakLeaves arm +FallingLeavesParticle.PoplarProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make RedPoplarLeaves, OrangePoplarLeaves and YellowPoplarLeaves arms +FallingLeavesParticle.TintedLeavesProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make TintedLeaves arm with the option colour +FallingParticle. port crates/horizon-client/src/particles.rs::ParticleEngine::make Leaves arm: size, gravity, start velocity, flow and swirl +FallingParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Leaves arm: wind, spin and removal on the ground +FireflyParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Firefly arm: removed in a solid block, fade, random drift +FireflyParticle.FireflyProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Firefly arm +FireworkParticles.FlashProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Flash arm +FireworkParticles.SparkProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Firework arm, alpha 0.99 +FireworkParticles.Starter.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_starters launch sound, a burst every 2 ticks, flash, twinkle sound at the end +FireworkParticles.Starter.isFarAwayFromCamera inline crates/horizon-client/src/particles.rs::ParticleEngine::fireworks gap: minor: the distance is taken when the rocket is launched, not on each tick +FireworkParticles.Starter.createParticleBall port crates/horizon-client/src/particles.rs::ParticleEngine::spark_ball +FireworkParticles.Starter.createParticleShape port crates/horizon-client/src/particles.rs::ParticleEngine::spark_shape +FireworkParticles.Starter.createParticleBurst inline crates/horizon-client/src/particles.rs::ParticleEngine::tick_starters Burst shape arm of tick_starters +FlameParticle.move port crates/horizon-client/src/particles.rs::Particle::move_by free_move: moves the box without collision +FlameParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size Flame size curve +FlameParticle.getLightCoords port crates/horizon-client/src/particles.rs::Particle::light_coords AgeEmission light +FlameParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Flame arm (also copper and soul fire flame) +FlameParticle.SmallFlameProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make SmallFlame arm, scaled 0.5 +FlyStraightTowardsParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle FlyStraight arm: position and colour lerp +FlyStraightTowardsParticle.OminousSpawnProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make OminousSpawning arm +FlyTowardsPositionParticle. port crates/horizon-client/src/particles.rs::ParticleEngine::make Enchant, Nautilus and VaultConnection arms +FlyTowardsPositionParticle.move port crates/horizon-client/src/particles.rs::Particle::move_by free_move: moves the box without collision +FlyTowardsPositionParticle.getLightCoords port crates/horizon-client/src/particles.rs::Particle::light_coords AgeEmission4 for enchant and nautilus; Block15 for vault connection +FlyTowardsPositionParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle FlyTowards arm +FlyTowardsPositionParticle.extract port crates/horizon-client/src/particles.rs::ParticleEngine::render alpha from the lifetime curve at draw time +FlyTowardsPositionParticle.EnchantProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Enchant arm +FlyTowardsPositionParticle.NautilusProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Nautilus arm +FlyTowardsPositionParticle.VaultConnectionProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make VaultConnection arm: block light, lifetime alpha, scale 1.5 +GeyserBaseParticle.Provider.createParticle missing player: sulfur geysers do not erupt here: no water column, base puff or eruption sound (only the potent sulfur block state exists), and the geyser particle types are not in ParticleType +GeyserEruptionParticle.tick missing player: the water column of a geyser eruption is not drawn; no geyser particle types exist here +GeyserEruptionParticle.Provider.createParticle missing player: no geyser eruption particle is made +GeyserPlumeParticle. missing player: no water plume is made when a sulfur geyser erupts +GeyserPlumeParticle.tick missing player: the plume does not rise, spray or fall back +GeyserPlumeParticle.Provider.createParticle missing player: no water plume is made when a sulfur geyser erupts +GlowParticle.getLightCoords port crates/horizon-client/src/particles.rs::Particle::light_coords AgeEmission light (glow, wax, electric spark) +GlowParticle.ElectricSparkProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make ElectricSpark arm +GlowParticle.GlowSquidProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Glow arm +GlowParticle.WaxOffProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make WaxOff arm +GlowParticle.WaxOnProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make WaxOn arm +GustParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle HugeExplosion behaviour: age, then sprite from age +GustParticle.SmallProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make SmallGust arm, scaled 0.15 +GustSeedParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle GustSeed arm: a gust every tick or two, then removal +GustSeedParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make GustEmitterLarge and GustEmitterSmall arms: scale, lifetime, delay +HeartParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size GrowIn size curve +HeartParticle.AngryVillagerProvider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make AngryVillager arm, raised 0.5; the default colour is already white +HeartParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make Heart arm diff --git a/tools/port_map/client2-007.tsv b/tools/port_map/client2-007.tsv new file mode 100644 index 0000000000..8d5520ae8b --- /dev/null +++ b/tools/port_map/client2-007.tsv @@ -0,0 +1,149 @@ +HugeExplosionParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::HugeExplosion: age, sprite from age, removed at lifetime +HugeExplosionParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Explosion arm (SonicBoom shares it): quad size from xAux, full bright +HugeExplosionSeedParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::ExplosionSeed: six EXPLOSION particles per tick, removed at lifetime +ItemPickupParticle.tick port crates/horizon-client/src/game/pickups.rs::Game::tick_pickups life to LIFE_TIME (3) then removed; follows the collector +ItemPickupParticle.saveOldPosition inline crates/horizon-client/src/game/pickups.rs::Game::tick_pickups target_old = target, done inside tick_pickups +ItemPickupParticleGroup.extractRenderState port crates/horizon-client/src/game/pickups.rs::position gap: minor: returns the flight positions only; each kind is drawn by dropped_item_geometry (item), entity_geometry (orb) and mob_geometry (arrow) +ItemPickupParticleGroup.State.submit port crates/horizon-client/src/game/item_draw.rs::Game::dropped_item_geometry gap: minor: draws the item kind only; orb and arrow pickups are drawn in entity_geometry and mob_geometry +LargeSmokeParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Smoke arm, LargeSmoke branch (scale 2.5) +LavaParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size Size::Lava: size * (1 - t^2) +LavaParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::Lava: smoke puffs while the random draw beats age/lifetime +LavaParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Lava arm: gravity 0.75, friction 0.999, lifetime 16 / (rand*0.8+0.2) +NoteParticle. inline crates/horizon-client/src/particles.rs::ParticleEngine::make Note arm: colour from the pitch, friction 0.66, lifetime 6, size x1.5 +NoteParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size Size::GrowIn over 32 ticks +NoteParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Note arm +NoxiousGasCloudParticle.tick missing player: potent sulfur in vanilla 26.3 gives off drifting noxious gas clouds; Horizon has no noxious_gas particle type and no PotentSulfurBlockEntity client code, so no cloud appears +NoxiousGasCloudParticle.pickRandomParticleSpawnPoint missing player: potent sulfur in vanilla 26.3 gives off drifting noxious gas clouds; Horizon has no noxious_gas particle type and no PotentSulfurBlockEntity client code, so no cloud appears +NoxiousGasCloudParticle.spawnNoxiousGasParticle missing player: potent sulfur in vanilla 26.3 gives off drifting noxious gas clouds; Horizon has no noxious_gas particle type and no PotentSulfurBlockEntity client code, so no cloud appears +NoxiousGasParticle.tick missing player: potent sulfur in vanilla 26.3 gives off drifting noxious gas clouds; Horizon has no noxious_gas particle type and no PotentSulfurBlockEntity client code, so no cloud appears; the fade-out is missing with it +NoxiousGasParticle.Provider.createParticle missing player: potent sulfur in vanilla 26.3 gives off drifting noxious gas clouds; Horizon has no noxious_gas particle type and no PotentSulfurBlockEntity client code, so no cloud appears +Particle. port crates/horizon-client/src/particles.rs::Particle::base_at 4-arg constructor (box and lifetime draw); the 7-arg one is Particle::base_moving +Particle.toString skip:debug +Particle.setLocationFromBoundingbox port crates/horizon-client/src/particles.rs::Particle::set_location_from_bb +Particle.LifetimeAlpha.isOpaque inline crates/horizon-client-core/src/particle_type.rs::ParticleType::translucent_layer the layer is a fixed per-type table; VaultConnection is the non-opaque case +ParticleDescription.fromJson port crates/horizon-client/src/particles.rs::ParticleEngine::load_sprites reads the textures list from particles/*.json +ParticleEngine.makeParticle port crates/horizon-client/src/particles.rs::ParticleEngine::make gap: minor: VibrationSignal options return None here; ParticleEngine::vibration creates them +ParticleEngine.createParticleGroup skip:other no render-type groups: ParticleEngine::render draws every particle in one pass, and the ITEM_PICKUP group is tick_pickups +ParticleEngine.countParticles skip:debug +ParticleEngine.hasSpaceInParticleLimit missing minor: no per-type particle cap; vanilla limits live SPORE_BLOSSOM air particles to 1000 +ParticleEngine.clearParticles port crates/horizon-client/src/particles.rs::ParticleEngine::clear clears emitters, live and pending particles; no limit counts to clear +ParticleGroup.tickParticles port crates/horizon-client/src/particles.rs::ParticleEngine::tick ticks live particles and drops the removed ones +ParticleGroup.tickParticle skip:debug crash-report wrapper around particle.tick +ParticleResources.register skip:data provider table; Horizon matches particle types inside ParticleEngine::make +ParticleResources.reload port crates/horizon-client/src/particles.rs::ParticleEngine::reload gap: minor: vanilla clears live particles on a resource reload; this keeps them +ParticleResources.loadParticleDescription port crates/horizon-client/src/particles.rs::ParticleEngine::load_sprites +ParticleResources.MutableSpriteSet.get skip:other index form of SpriteSet.get: no vanilla caller; Horizon picks a random set sprite (sprite_from_set) +PlayerCloudParticle. inline crates/horizon-client/src/particles.rs::ParticleEngine::make Cloud arm: friction 0.96, lifetime from the random draw x2.5, GrowIn +PlayerCloudParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size Size::GrowIn +PlayerCloudParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::Cloud: sinks to the local player's feet within 2 blocks +PlayerCloudParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Cloud arm +PlayerCloudParticle.SneezeProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Sneeze arm: tint 0.22/1.0/0.53, alpha 0.4 +PortalParticle.move port crates/horizon-client/src/particles.rs::Particle::move_by free_move path: moves the box without block collision +PortalParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size Size::Portal +PortalParticle.getLightCoords port crates/horizon-client/src/particles.rs::Particle::light_coords Light::AgeEmission4: block light plus age^4 emission +PortalParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::Portal: eases from the start point along the velocity +PortalParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Portal arm +QuadParticleGroup.extractRenderState port crates/horizon-client/src/particles.rs::ParticleEngine::render gap: minor: no frustum point test; every live particle is drawn +ReversePortalParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size Size::ReversePortal +ReversePortalParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::ReversePortal +ReversePortalParticle.ReversePortalProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make ReversePortal branch of the Portal arm +RisingParticle. inline crates/horizon-client/src/particles.rs::ParticleEngine::make rising arm (Soul, SculkSoul, flames): velocity scaled by 0.01, position jitter 0.05 +SculkChargeParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make SculkCharge arm: roll from the option, lifetime nextInt(12)+8 +SculkChargePopParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make SculkChargePop arm: lifetime nextInt(4)+6 +ShriekParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::Shriek: waits delay ticks, then base tick +ShriekParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Shriek arm: size 0.85, lifetime 30, delay from the option +SimpleAnimatedParticle.setFadeColor inline crates/horizon-client/src/particles.rs::ParticleEngine::make EndRod arm: fade colour 0xF2DEC9; applied by Behaviour::SimpleAnimated +SimpleVerticalParticle.PauseMobGrowthProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make PauseMobGrowth arm +SimpleVerticalParticle.ResetMobGrowthProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make ResetMobGrowth arm +SingleQuadParticle.extract port crates/horizon-client/src/particles.rs::ParticleEngine::render roll lerp and camera-facing rotation per particle +SingleQuadParticle.extractRotatedQuad port crates/horizon-client/src/particles.rs::ParticleEngine::render quad centre from the lerped position minus the camera +SingleQuadParticle.toString skip:debug +SingleQuadParticle.Layer.bySprite port crates/horizon-client/src/particles.rs::ParticleEngine::reload gap: minor: one translucent flag per sprite from the atlas; vanilla also splits block and item atlases (TRANSLUCENT_TERRAIN, TRANSLUCENT_ITEMS) +SmokeParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Smoke arm, Smoke branch (scale 1.0) +SnowflakeParticle. inline crates/horizon-client/src/particles.rs::ParticleEngine::make Poof/Snowflake/Spit arm, Snowflake branch: gravity 0.225, friction 1.0 +SnowflakeParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::Snowflake: base tick, sprite from age, drag 0.95/0.9/0.95 +SnowflakeParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Snowflake branch: colour 0.923/0.964/0.999 +SonicBoomParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Explosion|SonicBoom arm, SonicBoom branch: lifetime 16, size 1.5 +SpellParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle gap: player: the spyglass check is missing: while scoping in first person, vanilla makes spell particles within 3 blocks of the eye fully transparent; Horizon only lerps alpha +SpellParticle.isCloseToScopingPlayer missing player: spell particles near the eye stay visible while the player looks through a spyglass in first person; vanilla hides them +SpellParticle.InstantProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Effect|InstantEffect arm: colour and power from the option +SpellParticle.MobEffectProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make EntityEffect arm: colour and alpha from the option +SpellParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make uncoloured spell arm (Infested, TrialOmen, RaidOmen) +SpellParticle.WitchProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Witch arm: purple brightness from the engine random +SpitParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Poof/Snowflake/Spit arm, Spit branch: gravity 0.5, Animated +SplashParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Rain|Splash arm, Splash branch: gravity 0.04 +SquidInkParticle.GlowInkProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make GlowSquidInk arm: colour (0.2, 0.8, 0.6) +SquidInkParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make SquidInk arm: black +SulfurBubbleParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::SulfurBubble: still-water check, bounds, growth +SulfurBubbleParticle.randomHorizontalWiggling inline crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle SulfurBubble arm, the wiggle closure applied to x and z +SulfurBubbleParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make SulfurBubbles arm +SuspendedParticle. inline crates/horizon-client/src/particles.rs::ParticleEngine::make Underwater/CrimsonSpore/WarpedSpore/SporeBlossomAir arm: spawned 0.125 below the point +SuspendedParticle.CrimsonSporeProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make CrimsonSpore arm: gaussian drift, colour 0.9/0.4/0.5 +SuspendedParticle.SporeBlossomAirProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make gap: minor: SporeBlossomAir arm; the SPORE_BLOSSOM particle limit (1000) is not applied +SuspendedParticle.UnderwaterProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Underwater arm: colour 0.4/0.4/0.7 +SuspendedParticle.WarpedSporeProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make WarpedSpore arm: colour 0.1/0.1/0.3, size 0.001 +SuspendedTownParticle.move port crates/horizon-client/src/particles.rs::Particle::move_by free_move path: moves the box without block collision +SuspendedTownParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::SuspendedTown: lifetime countdown, drift, 0.99 damping +SuspendedTownParticle.ComposterFillProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Composter arm: white, lifetime 3 + nextInt(5) +SuspendedTownParticle.DolphinSpeedProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Dolphin arm: blue, alpha from the engine random, half lifetime +SuspendedTownParticle.EggCrackProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make EggCrack arm: white +SuspendedTownParticle.HappyVillagerProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make HappyVillager arm: white +SuspendedTownParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make plain arm (Mycelium): no colour change +TerrainParticle. port crates/horizon-client/src/particles.rs::Particle::terrain crumb from a quarter of the block sprite, tinted, half size, mirrored UV +TerrainParticle.createTerrainParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make gap: minor: the air, moving-piston and noTerrainParticles checks are not in this arm; the callers' gating was not checked +TerrainParticle.CrumblingProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make BlockCrumble arm: zero speed, lifetime nextInt(10)+1 +TerrainParticle.DustPillarProvider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make DustPillar arm: gaussian speed, lifetime nextInt(20)+20 +TotemParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make TotemOfUndying arm +TrackingEmitter.tick port crates/horizon-client/src/particles.rs::ParticleEngine::emit 16 tries per tick; the countdown and removal are in ParticleEngine::tick +TrailParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::Trail: eases towards the target over its lifetime +TrailParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Trail arm: target, colour jitter, duration +TrialSpawnerDetectionParticle. inline crates/horizon-client/src/particles.rs::ParticleEngine::make TrialSpawnerDetection arm: gravity -0.1, Block15 light, GrowIn +TrialSpawnerDetectionParticle.getQuadSize port crates/horizon-client/src/particles.rs::Particle::quad_size Size::GrowIn +TrialSpawnerDetectionParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make TrialSpawnerDetection arm with scale 1.5 +VibrationSignalParticle.Provider.createParticle port crates/horizon-client/src/particles.rs::ParticleEngine::vibration flies to the listener in the arrival ticks +WakeParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Fishing arm (WakeParticle) +WaterCurrentDownParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::CurrentDown: steers by angle, ends off water or on ground +WaterCurrentDownParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make CurrentDown arm +WaterDropParticle.tick port crates/horizon-client/src/particles.rs::ParticleEngine::tick_particle Behaviour::WaterDrop: gravity, drag, ends on ground or in fluid +WaterDropParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Rain arm +WhiteAshParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make WhiteAsh branch of the Smoke arm; the provider's motion is drawn from the engine random +WhiteSmokeParticle.Provider.createParticle inline crates/horizon-client/src/particles.rs::ParticleEngine::make Smoke arm, WhiteSmoke branch (scale 1.0) +ClientPackSource.findExplodedAssetPacks skip:other IDE-only: looks for a resourcepacks folder next to the asset dir in development runs +ClientPackSource.createVanillaPackSource skip:data pack source assembly; Horizon reads its assets from folders directly +ClientPackSource.getPackTitle skip:data pack display names +ClientPackSource.populatePackList skip:data pack discovery +DefaultPlayerSkin.create port crates/horizon-client/src/tab_list.rs::default_skin gap: minor: returns the texture path (the model is in the path); no PlayerSkin record +DryFoliageColorReloadListener.prepare port crates/horizon-client/src/biome.rs::colormap PNG decode of colormap/dry_foliage; the caller loads the three colormaps +FoliageColorReloadListener.prepare port crates/horizon-client/src/biome.rs::colormap PNG decode of colormap/foliage +GrassColorReloadListener.prepare port crates/horizon-client/src/biome.rs::colormap PNG decode of colormap/grass +IndexedAssetSource.createIndexFs skip:data legacy objects/ asset index; Horizon reads assets/ folders directly +LegacyStuffWrapper.getPixels port crates/horizon-client/src/hud.rs::decode gap: minor: PNG decode to bytes; vanilla returns ARGB ints +MapTextureManager.resetData skip:other frees GPU map textures on close; Horizon keeps its map atlas slots in Game::maps, no gameplay effect +MapTextureManager.getOrCreateMapInstance port crates/horizon-client/src/game/maps.rs::Game::map_texture gives the map its atlas slot, allocated on first use +MapTextureManager.MapInstance.replaceMapData port crates/horizon-client/src/game/maps.rs::MapData::update_color gap: minor: vanilla also flags an upload when the saved-data object is swapped; Horizon flags per changed colour +MapTextureManager.MapInstance.updateTextureIfNeeded port crates/horizon-client/src/game/maps.rs::Game::map_texture uploads the 128x128 colours when dirty +SkinManager.get port crates/horizon-client/src/skins.rs::lookup gap: minor: the packed textures come from ResolvedProfile::textures; the debug default-skin override is not ported +SkinManager.registerTextures port crates/horizon-client/src/skins.rs::lookup gap: minor: returns the skin location only; cape and elytra are downloaded here but not returned +SkinManager.TextureCache.getOrLoad port crates/horizon-client/src/skins.rs::Downloader::state one download per URL, then its state +SkinManager.TextureCache.getTextureLocation port crates/horizon-client/src/skins.rs::skin_location gap: minor: only the skins/ location; capes and elytra have no location fn +SplashManager.getSplash port crates/horizon-client/src/title.rs::choose_splash gap: player: no ' IS YOU' splash; vanilla sometimes shows the logged-in player's name instead of a splash on the title screen +WaypointStyle.validate port crates/horizon-client/src/locator_bar.rs::WaypointStyle::parse validation is folded into the parse +WaypointStyleManager.apply port crates/horizon-client/src/locator_bar.rs::WaypointStyles::load gap: minor: loads the arrow and dot sprites from the assets folder into one map +ClientLanguage.appendFrom port crates/horizon-client-core/src/lang.rs::Language::load layers the translation JSON from the sources +LanguageManager.extractLanguages port crates/horizon-client-core/src/lang.rs::available lists the languages the packs declare +LanguageManager.onResourceManagerReload port crates/horizon-client/src/game/options.rs::Game::load_language gap: minor: no error screen when the translations are empty or missing +AnimationMetadataSection.calculateFrameSize port crates/horizon-assets/src/animation.rs::AnimationMetadata::frame_size +GuiSpriteScaling.NineSlice.validate missing minor: Horizon accepts nine-slice metadata whose borders cover the whole sprite (parse_nine_slice has no check) +GuiSpriteScaling.NineSlice.Border.unpackValue skip:java codec helper that collapses a uniform border to one number; Horizon stores four values +BlockStateDefinitions.createItemFrameFakeState skip:data block state definition for the map property; Horizon chooses the frame model by flags +BlockStateDefinitions.getItemFrameFakeState port crates/horizon-client/src/game/frames.rs::Game::frame_geometry gap: minor: picks the glow and map frame model by flags; no block state +BlockStateDefinitions.definitionLocationToBlockStateMapper skip:data registry lookup of block state definitions +BlockStateModelLoader.loadBlockStates skip:data blockstate JSON loading; Horizon's block models come from its data tables +BlockStateModelLoader.loadBlockStateDefinitionStack skip:data blockstate JSON stacking +ClientItemInfoLoader.scheduleLoad skip:data item model JSON loading +EquipmentAssetManager.apply skip:data equipment asset table reload +EquipmentClientInfo.Builder.addHumanoidLayers skip:java builder +EquipmentClientInfo.Builder.addMainHumanoidLayer skip:java builder +EquipmentClientInfo.Builder.addLayers skip:java builder +EquipmentClientInfo.Builder.replaceTrimPalette skip:java builder +EquipmentClientInfo.Builder.build skip:java builder diff --git a/tools/port_map/client2-008.tsv b/tools/port_map/client2-008.tsv new file mode 100644 index 0000000000..f93fb7e136 --- /dev/null +++ b/tools/port_map/client2-008.tsv @@ -0,0 +1,149 @@ +EquipmentClientInfo.Layer.leatherDyeable skip:data equipment asset factory; Horizon reads the equipment JSON in armor_stands.rs +EquipmentClientInfo.Layer.onlyIfDyed skip:data equipment asset factory; Horizon reads the equipment JSON in armor_stands.rs +EquipmentClientInfo.Layer.getTextureLocation port crates/horizon-client/src/game/armor_stands.rs::ArmorStandAssets::load builds entity/equipment//; gap: minor: only humanoid, humanoid_leggings and wings are read here +EquipmentClientInfo.TrimOverride.validate skip:java codec validator; TrimOverride::list keeps an override with neither texture nor palette (minor) +EquipmentClientInfo.TrimPredicate.validate skip:java codec validator; TrimOverride::list keeps a predicate with neither material nor pattern (minor) +EquipmentClientInfo.TrimPredicate.matches port crates/horizon-client/src/trims.rs::TrimOverride::matches material and pattern id comparison +ModelBakery.bakeModels port crates/horizon-client/src/models/catalog.rs::StateModels::from_facts_with_models bakes block states; gap: minor: items and standalone models bake in separate fns (item_bake.rs::bake_item_model) +ModelBakery.ModelBakerImpl.getModel port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve an unknown id falls back to the missing model +ModelDiscovery.createAndQueueWrapper inline crates/horizon-client/src/model_library.rs::ParsedModels::block_models parent queue; no wrapper object is kept +ModelDiscovery.addSpecialModel inline crates/horizon-assets/src/resolve.rs::ModelLibrary::with_shared_item_generator gap: minor: only builtin/generated is registered; a non-root special model is not rejected +ModelDiscovery.resolve port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve resolved per model id, not as a batch map +ModelDiscovery.discoverDependencies inline crates/horizon-client/src/model_library.rs::ParsedModels::block_models pushes each model's parent until the set is complete +ModelDiscovery.propagateValidity port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve a chain that reaches a cycle is an error when resolved +ModelDiscovery.ModelWrapper.updateSlot skip:java per-model result cache +ModelDiscovery.ModelWrapper.getSimpleProperty skip:java per-model result cache +ModelDiscovery.ModelWrapper.resolveParticleMaterial port crates/horizon-client/src/models/catalog.rs::StateModels::from_facts_with_models block particle sprite; the item side is item_bake.rs::bake_item_model +ModelDiscovery.ModelWrapper.bakeDefaultState skip:java cache of the identity-rotation geometry +ModelDiscovery.ModelWrapper.bakeTopGeometry port crates/horizon-assets/src/bake.rs::bake_block_model bakes with the blockstate rotation; per-state cache skipped +ModelGroupCollector.build skip:java groups states for shared baked geometry; Horizon has no model-group ids +ModelGroupCollector.GroupKey.create skip:java group key of build +ModelGroupCollector.GroupKey.getColoringValues skip:java group key of build +ModelManager.reload port crates/horizon-client/src/assets.rs::RenderAssets::load one synchronous load: models, block states, item infos, then bake and store +ModelManager.loadBlockModels port crates/horizon-client/src/model_library.rs::ParsedModels::load every models/**.json by id, parsed in parallel +ModelManager.discoverModelDependencies port crates/horizon-client/src/model_library.rs::ParsedModels::block_models referenced models and their parents +ModelManager.loadModels port crates/horizon-client/src/models/catalog.rs::StateModels::from_facts_with_models bakes block states and their models +ModelManager.createBlockStateToModelDispatch inline crates/horizon-client/src/models/catalog.rs::StateModels::from_facts_with_models missing model for a state without a baked model +ModelManager.buildModelGroups skip:java wrapper of ModelGroupCollector.build; render sharing only +ModelManager.apply inline crates/horizon-client/src/assets.rs::RenderAssets::load the baked result is stored in the returned struct +ModelManager.requiresRender skip:java render-skip optimisation for equal model groups; no visible effect +ResolvedModel.findTopTextureSlots port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve texture slots merged along the parent chain +ResolvedModel.findTopAmbientOcclusion port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve ambient occlusion from the first model that sets it, default true +ResolvedModel.findTopGuiLight port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve gui_light from the first model that sets it, default side +ResolvedModel.findTopGeometry port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve first element list up the parent chain +ResolvedModel.resolveParticleMaterial port crates/horizon-client/src/models/catalog.rs::StateModels::from_facts_with_models block particle slot; the item side is item_bake.rs::bake_item_model +ResolvedModel.findTopTransform port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve display transform per context, inherited up the chain +ResolvedModel.findTopTransforms port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve display transforms for every context +SimpleModelWrapper.bake port crates/horizon-assets/src/bake.rs::bake_block_model gap: minor: no rejection of block models that use non-block sprites +SimpleModelWrapper.findNonBlockSprites missing minor: vanilla replaces a block model whose quads use sprites outside the block atlas; Horizon keeps every sprite +UnbakedModel.GuiLight.getByName port crates/horizon-assets/src/model.rs::CuboidModel::decode front and side only; other values are an error +CuboidFace.Deserializer.deserialize port crates/horizon-assets/src/model.rs::Face::decode texture, cullface, tintindex and rotation +CuboidFace.Deserializer.getUVs port crates/horizon-assets/src/model.rs::Face::decode uv as four numbers; absent uses default_uv in bake.rs +CuboidFace.UVs.getVertexU inline crates/horizon-assets/src/bake.rs::bake_face the U corner is picked in bake_face +CuboidFace.UVs.getVertexV inline crates/horizon-assets/src/bake.rs::bake_face the V corner is picked in bake_face +CuboidModel.Deserializer.deserialize port crates/horizon-assets/src/model.rs::CuboidModel::decode whole model JSON +CuboidModel.Deserializer.getTextureMap port crates/horizon-assets/src/model.rs::CuboidModel::decode each textures entry through texture() +CuboidModel.Deserializer.getAmbientOcclusion port crates/horizon-assets/src/model.rs::CuboidModel::decode ambientocclusion flag +CuboidModel.Deserializer.getElements port crates/horizon-assets/src/model.rs::CuboidModel::decode elements list with null entries +CuboidModelElement.Deserializer.deserialize port crates/horizon-assets/src/model.rs::Element::decode whole element JSON +CuboidModelElement.Deserializer.getShadeDirectionOverride port crates/horizon-assets/src/model.rs::Element::decode shade_direction_override +CuboidModelElement.Deserializer.getRotation port crates/horizon-assets/src/model.rs::ElementRotation::decode rotation object +CuboidModelElement.Deserializer.getAxis port crates/horizon-assets/src/model.rs::ElementRotation::decode axis x, y or z +CuboidModelElement.Deserializer.getFaces port crates/horizon-assets/src/model.rs::Element::decode faces object +CuboidModelElement.Deserializer.filterNullFromFaces inline crates/horizon-assets/src/model.rs::Element::decode null faces are kept as None through nullable() +CuboidModelElement.Deserializer.getFacing port crates/horizon-assets/src/model.rs::Element::decode face names through Direction::by_name +CuboidModelElement.Deserializer.getPosition port crates/horizon-assets/src/model.rs::Element::decode from and to, bounded to -16..32 +CuboidModelElement.Deserializer.getVector3f port crates/horizon-assets/src/json.rs::vector three-number array +CuboidRotation.computeTransform port crates/horizon-assets/src/bake.rs::ElementMatrix::from_rotation rotation matrix, then rescale when set +CuboidRotation.computeRescale inline crates/horizon-assets/src/bake.rs::ElementMatrix::from_rotation per-axis rescale inside from_rotation +CuboidRotation.scaleFactorForAxis inline crates/horizon-assets/src/bake.rs::ElementMatrix::from_rotation per-axis factor inside from_rotation +CuboidRotation.EulerXYZRotation.transformation port crates/horizon-assets/src/bake.rs::ElementMatrix::from_rotation Euler XYZ branch +CuboidRotation.SingleAxisRotation.transformation port crates/horizon-assets/src/bake.rs::ElementMatrix::from_rotation single-axis branch +FaceBakery.defaultFaceUV port crates/horizon-assets/src/bake.rs::default_uv default face UVs by direction +FaceBakery.computeMaterialTransparency port crates/horizon-client/src/sprites.rs::Sprites::region_opaque gap: minor: only the opaque-region check; force_translucent is not applied here +FaceBakery.bakeVertex inline crates/horizon-assets/src/bake.rs::bake_face vertex position and UV inside bake_face +FaceBakery.rotateVertexBy inline crates/horizon-assets/src/bake.rs::bake_face element rotation about its origin +FaceBakery.calculateFacing port crates/horizon-assets/src/bake.rs::facing quad normal, then closest direction +FaceBakery.findClosestDirection port crates/horizon-assets/src/bake.rs::facing closest axis direction with a non-negative dot +FaceBakery.recalculateWinding inline crates/horizon-assets/src/bake.rs::bake_face corner reorder for unrotated elements +FaceBakery.findVertex inline crates/horizon-assets/src/bake.rs::bake_face search inside the winding reorder +FaceBakery.swap inline crates/horizon-assets/src/bake.rs::bake_face swap inside the winding reorder +ItemModelGenerator.bake port crates/horizon-assets/src/item_bake.rs::bake_item_model layer loop of generated item models +ItemModelGenerator.bakeSideFaces port crates/horizon-assets/src/item_bake.rs::extrude_layer side faces of one layer +ItemModelGenerator.getSideFaces inline crates/horizon-assets/src/item_bake.rs::extrude_layer edge collection inside extrude_layer +ItemModelGenerator.checkTransition inline crates/horizon-assets/src/item_bake.rs::extrude_layer transparent neighbour check inside extrude_layer +ItemModelGenerator.isTransparent port crates/horizon-assets/src/item_bake.rs::SpriteMask::transparent alpha mask lookup +ItemModelGenerator.ItemLayerKey.compute skip:java layer bake cache key; Horizon bakes each item model once +ItemTransform.Deserializer.deserialize port crates/horizon-assets/src/model.rs::ItemTransform::decode rotation, translation and scale +ItemTransform.Deserializer.getVector3f port crates/horizon-assets/src/json.rs::vector three-number array +ItemTransforms.Deserializer.deserialize port crates/horizon-assets/src/model.rs::ItemTransforms::decode display contexts +ItemTransforms.Deserializer.getTransform port crates/horizon-assets/src/item_bake.rs::BakedItemModel::transform transform for one display context +BakedQuad.position skip:java record accessor; ModelQuad.positions field +BakedQuad.packedUV skip:java record accessor; ModelQuad.uv field +BakedQuad.MaterialInfo.of port crates/horizon-client/src/item_render.rs::FileSprites::translucent gap: minor: picks solid or translucent only; no per-material render type or glint sheet +BakedQuad.MaterialInfo.flags skip:java translucent and animated bit flags used to skip work; Horizon ticks animated textures by its setting +ItemQuads.split port crates/horizon-client/src/item_render.rs::submit_quads quads with translucent sprites go to the translucent pass +QuadCollection.computeMaterialFlags skip:java flag bits of the quad list; see BakedQuad.MaterialInfo.flags +QuadCollection.getQuads port crates/horizon-client/src/mesher.rs::Mesher::mesh quads filtered by cull direction +QuadCollection.materialFlags skip:java cached flag bits +QuadCollection.Builder.addAll skip:java collection builder +QuadCollection.Builder.createFromSublists skip:java sublist layout of a quad collection +AtlasManager.getAtlasOrThrow skip:java atlas map lookup; Horizon keeps one sprite table +AtlasManager.forEach skip:java map iteration +AtlasManager.close skip:java GPU release; freed with its owner +AtlasManager.get port crates/horizon-client/src/sprites.rs::Sprites::insert sprite layer added on first use; missing texture when it fails to load +AtlasManager.prepareSharedState skip:java future-based stitch plumbing +AtlasManager.reload skip:java async reload; sprites load synchronously in RenderAssets::load +AtlasManager.updateSpriteMaps port crates/horizon-client/src/sprites.rs::Sprites::finish lays out every layer at one size; gap: minor: no duplicate-sprite warning +AtlasManager.AtlasEntry.scheduleLoad port crates/horizon-client/src/sprites.rs::Sprites::insert gap: minor: sprites load on demand, not per atlas source +AtlasManager.PendingStitch.joinAndUpload skip:java future join +AtlasManager.PendingStitchResults.joinAndUpload skip:java future join +MaterialBaker.get port crates/horizon-assets/src/item_bake.rs::bake_item_model sprite that is not known falls back to missingno +MaterialBaker.bake skip:java Material.Baked wrapper +MaterialBaker.bakeForAtlas skip:java Material.Baked wrapper for an atlas +MaterialBaker.resolveSlot port crates/horizon-assets/src/resolve.rs::ResolvedCuboidModel::material named texture slot lookup +MaterialBaker.logMissingTextures skip:debug log of missing texture slots only +TextureSlots.getMaterial port crates/horizon-assets/src/resolve.rs::ResolvedCuboidModel::material named texture slot lookup +TextureSlots.parseTextureMap port crates/horizon-assets/src/model.rs::CuboidModel::decode textures object +TextureSlots.parseEntry port crates/horizon-assets/src/model.rs::texture # reference or sprite material +TextureSlots.Data.Builder.addReference skip:java builder +TextureSlots.Data.Builder.addTexture skip:java builder +TextureSlots.Data.Builder.build skip:java builder +TextureSlots.Resolver.resolve port crates/horizon-assets/src/resolve.rs::ModelLibrary::resolve alias chain resolution; the batch hint (horizon-protocol resolver.rs) is a DNS resolver and is not this +resources/palette/Palette.listAndLoad port crates/horizon-client/src/trims.rs::TrimTexture::generate gap: minor: palettes load on demand, none listed up front +resources/palette/Palette.tryLoad port crates/horizon-client/src/trims.rs::TrimTexture::generate gap: minor: a broken palette skips the recolour instead of an empty palette +resources/palette/Palette.load port crates/horizon-client/src/trims.rs::TrimTexture::generate palette PNG read as ARGB +PaletteMapping.apply port crates/horizon-assets/src/palette.rs::apply_palette colour swap; alpha scaled by the target's alpha +PalettedTextureManager.loadBaseTexture port crates/horizon-client/src/trims.rs::TrimTexture::generate base texture and its base_palette from the mcmeta +PalettedTextureManager.allocateSlot port crates/horizon-client/src/sprites.rs::Sprites::insert gap: minor: one array layer per texture, no dynamic atlas tree +PalettedTextureManager.close skip:java GPU release +PalettedTextureManager.reload port crates/horizon-client/src/game/armor_stands.rs::ArmorStandAssets::reload_trims trim data reloaded; models kept +PalettedTextureManager.AtlasTexture.dumpContents skip:debug debug dump +PalettedTextureManager.Slot.getU skip:java UV accessor of a dynamic atlas slot; Horizon uses sprite rects +PalettedTextureManager.Slot.getV skip:java UV accessor of a dynamic atlas slot; Horizon uses sprite rects +DownloadedPackSource.configureSource inline crates/horizon-client/src/game/server_packs.rs::Game::start_pack_reload pack list staged for the reload; empty when none +DownloadedPackSource.onRecovery port crates/horizon-client/src/server_packs.rs::ServerPacks::reloaded reloads the packs that were active after a failure +DownloadedPackSource.onRecoveryFailure port crates/horizon-client/src/server_packs.rs::ServerPacks::reloaded second failure leaves the server's packs inactive +DownloadedPackSource.waitForPackFeedback skip:java future over the feedback callback; replies go through ServerPacks::take_replies +ServerPackManager.markExistingPacksAsRemoved inline crates/horizon-client/src/server_packs.rs::ServerPacks::push packs with the same id are removed as ServerReplaced +ServerPackManager.pushPack port crates/horizon-client/src/server_packs.rs::ServerPacks::push declined prompt answers DECLINED +ServerPackManager.pushLocalPack missing player: a world's own resource pack (resources.zip in the save) is not loaded when the world opens +ServerPackManager.pushNewPack inline crates/horizon-client/src/server_packs.rs::ServerPacks::push adds the pack and accepts it when allowed +ServerPackManager.findPackInfo inline crates/horizon-client/src/server_packs.rs::ServerPacks::pop finds the live pack with the id +ServerPackManager.popPack port crates/horizon-client/src/server_packs.rs::ServerPacks::pop marks the pack ServerRemoved +ServerPackManager.allowServerPacks port crates/horizon-client/src/server_packs.rs::ServerPacks::allow prompt allowed; accepts the waiting packs +ServerPackManager.rejectServerPacks port crates/horizon-client/src/server_packs.rs::ServerPacks::reject prompt declined; removes the unaccepted packs +ServerPackManager.cleanupRemovedPacks inline crates/horizon-client/src/server_packs.rs::ServerPacks::tick forgets removed packs and reports why +ServerPackManager.updateDownloads port crates/horizon-client/src/server_packs.rs::ServerPacks::tick gap: minor: returns the download requests; the download itself starts in game/server_packs.rs::Game::start_pack_downloads +AbstractSoundInstance.getOrResolve port crates/horizon-client/src/sound_engine.rs::SoundEngine::resolve event lookup, then a random sound from it +BeeSoundInstance.tick port crates/horizon-client/src/sound_engine.rs::SoundInstance::tick Ticking::Bee follows the bee; pitch and volume from speed +DirectionalSoundInstance.setPosition port crates/horizon-client/src/game/end.rs::Game::tick_end gap: minor: the end flash plays without a position, so the 10-block offset toward the view is not kept +ElytraOnPlayerSoundInstance.tick missing player: no elytra wind loop (entity.elytra.flying) while gliding; gliding is silent +EntityBoundSoundInstance.tick missing minor: a sound tied to an entity does not follow it; it stays at its start point +RidingEntitySoundInstance.tick missing player: no riding loop for a happy ghast or nautilus (HAPPY_GHAST_RIDING, NAUTILUS_RIDING) +RidingMinecartSoundInstance.shoudlPlaySound port crates/horizon-client/src/sound_engine.rs::SoundInstance::tick RidingMinecart arm; the predicate is always true for OldMinecartBehavior, Horizon's only minecart +SnifferSoundInstance.tick port crates/horizon-client/src/game/sniffers.rs::Game::tick_sniffer_effects gap: player: the digging sound plays once when digging starts; vanilla loops it while digging +Sound.Type.getByName port crates/horizon-client/src/sound_events.rs::parse_sound type file or event; other values reject the sound +SoundEventRegistrationSerializer.deserialize port crates/horizon-client/src/sound_events.rs::parse_file replace and subtitle per event +SoundEventRegistrationSerializer.getSounds inline crates/horizon-client/src/sound_events.rs::parse_file sounds array inside parse_file +SoundEventRegistrationSerializer.getType inline crates/horizon-client/src/sound_events.rs::parse_sound type field inside parse_sound diff --git a/tools/port_map/client2-009.tsv b/tools/port_map/client2-009.tsv new file mode 100644 index 0000000000..72d0c45dda --- /dev/null +++ b/tools/port_map/client2-009.tsv @@ -0,0 +1,149 @@ +ActiveTextCollector.accept inline crates/horizon-client/src/screen_draw.rs::text default overload: left-aligned text at x,y with default parameters +ActiveTextCollector.acceptScrolling missing minor: forwards to the scrolling helper, which Horizon lacks; long text is clipped +ActiveTextCollector.acceptScrollingWithDefaultCenter port crates/horizon-client/src/ui.rs::centered_text gap: minor: centres the message; wider text is clipped instead of scrolled +ActiveTextCollector.defaultScrollingHelper missing minor: wider text does not slide back and forth; ui.rs centered_text clips it +ActiveTextCollector.findElementUnderCursor port crates/horizon-client/src/chat.rs::Chat::style_at gap: minor: hit test covers chat lines only; book links use game/books.rs book_link_at +ActiveTextCollector.isPointInRectangle port crates/horizon-client/src/ui.rs::hovered half-open rectangle test with the same bounds +ActiveTextCollector.ClickableStyleFinder.accept port crates/horizon-client/src/chat.rs::Chat::style_at gap: minor: only chat lines are searched for click and hover styles +ActiveTextCollector.ClickableStyleFinder.acceptScrolling missing minor: no scrolling text (see defaultScrollingHelper) +ActiveTextCollector.Parameters.withScale skip:java record copy-with helper; Horizon has no pose matrix per text +ActiveTextCollector.Parameters.withOpacity skip:java record copy-with helper; opacity is the colour's alpha byte in Horizon +ActiveTextCollector.Parameters.withScissor missing minor: a nested scissor is not intersected with the enclosing rectangle; Draw::Scissor replaces it +ComponentPath.path skip:other focus path builder for nested containers; Horizon focus is a flat per-screen state +ComponentPath.Path.applyFocus skip:other focus path propagation; Horizon focus is a flat per-screen state +Font.bidirectionalShaping port crates/horizon-client/src/bidi.rs::visual_str Arabic shaping and bidi reorder +Font.prepare8xTextOutline skip:other no vanilla 26.3 caller passes a non-zero outline colour; text displays, name tags and map labels pass 0 or a background +Font.prepareBackground port crates/horizon-client/src/hud_overlays.rs::text_with_backdrop background box behind text; callers pass their own bounds +Font.getGlyph port crates/horizon-client/src/font.rs::Font::glyph_styled obfuscated glyph is picked in HudBatch::text_styled via random_glyph +Font.PreparedTextBuilder.markSize skip:other bounds bookkeeping of prepared text; Horizon has no prepared-text object and hit-tests by advance +Font.PreparedTextBuilder.markBackground inline crates/horizon-client/src/hud_overlays.rs::text_with_backdrop gap: minor: the background box is 2 px around the text; vanilla grows it by 1 px around each glyph +Font.PreparedTextBuilder.addGlyph inline crates/horizon-client/src/hud.rs::HudBatch::text_styled glyph quad pushed directly into the batch +Font.PreparedTextBuilder.addEffect inline crates/horizon-client/src/hud.rs::HudBatch::text_styled underline and strikethrough rectangles drawn inline +Font.PreparedTextBuilder.addEmptyGlyph skip:other hit areas for glyphs with no image; Chat::style_at hit-tests by advance width instead +Font.PreparedTextBuilder.accept port crates/horizon-client/src/hud.rs::HudBatch::text_styled per-character draw with bold, italic, shadow, underline and strikethrough +Font.PreparedTextBuilder.visit skip:other visitor over prepared glyphs for draw order and hit tests; Horizon draws straight into the batch +Font.PreparedTextBuilder.getTextColor inline crates/horizon-client/src/hud.rs::HudBatch::text_styled style colour with the base colour's alpha +Font.PreparedTextBuilder.getShadowColor inline crates/horizon-client/src/hud.rs::HudBatch::text_styled gap: minor: style shadow colour is ignored; shadow is always 0.25 of the text colour +Font.PreparedTextBuilder.bounds skip:other bounds of prepared text for vanilla hit tests; Horizon hit-tests by advance +Gui.update skip:other narrator-only update (handleDelayedNarration); Horizon has no narrator +Gui.isPausing port crates/horizon-client/src/game.rs::Game::pauses_game gap: minor: checks the open screen only, not the overlay part +Gui.addSocialInteractionsToast skip:service social interactions are not offered by Horizon +Gui.handleKeybinds port crates/horizon-client/src/game.rs::Game::key_pressed gap: minor: the social interactions key has no port (see addSocialInteractionsToast) +Gui.openChatScreen port crates/horizon-client/src/game/chat.rs::Game::open_chat +Gui.openChatAndAddText port crates/horizon-client/src/game/chat.rs::Game::open_chat the text is passed as the initial input +Gui.buildInitialScreens missing player: first launch skips vanilla's accessibility onboarding screen, and ban notices are not shown before the title screen +Gui.addInitialScreens missing player: same as buildInitialScreens: no onboarding screen or ban notice is queued +Gui.canInterruptScreen missing minor: unsure: no check that stops a server-opened screen from replacing an open one was found +Gui.renderActiveTextDebug skip:debug debug overlay of text bounds +GuiGraphicsExtractor.enableScissor port crates/horizon-client/src/screen_draw.rs::paint gap: minor: callers push a rectangle; there is no stack, so enclosing rectangles are not intersected +GuiGraphicsExtractor.outline missing minor: unsure: no one-pixel outline draw was found in Horizon's UI +GuiGraphicsExtractor.innerFill port crates/horizon-client/src/hud.rs::HudBatch::fill gap: minor: one colour only; a second gradient colour (color2) is not drawn +GuiGraphicsExtractor.textWithWordWrap port crates/horizon-client/src/screen_draw.rs::Measure::split gap: minor: only the wrap; the caller draws each line at y+9 +GuiGraphicsExtractor.blitSprite port crates/horizon-client/src/hud.rs::HudBatch::sprite_scaled gap: minor: tile and nine-slice scaling is in ui.rs (tiled, draw_nine_slice) +GuiGraphicsExtractor.blitNineSliceInnerSegment port crates/horizon-client/src/ui.rs::draw_nine_slice inner segments of a nine-slice sprite +GuiGraphicsExtractor.blitTiledSprite port crates/horizon-client/src/ui.rs::tiled +GuiGraphicsExtractor.innerBlit port crates/horizon-client/src/screen_draw.rs::blit texture rectangle by UV; the atlas lookup is in HudBatch::rect_uv +GuiGraphicsExtractor.innerTiledBlit port crates/horizon-client/src/ui.rs::tile one tiled quad +GuiGraphicsExtractor.item port crates/horizon-client/src/game.rs::Game::draw_item gap: minor: no seed or owner is passed to the item model +GuiGraphicsExtractor.fakeItem inline crates/horizon-client/src/game.rs::Game::draw_item same as item with no owner +GuiGraphicsExtractor.itemBar inline crates/horizon-client/src/hud_overlays.rs::item_decorations damage bar drawn in item_decorations +GuiGraphicsExtractor.itemCount inline crates/horizon-client/src/hud_overlays.rs::item_decorations stack count drawn in item_decorations +GuiGraphicsExtractor.map port crates/horizon-client/src/game/cartography.rs::Game::draw_cartography gap: player: the map image is drawn only in the cartography preview; map markers (decorations) are not drawn there +GuiGraphicsExtractor.entity port crates/horizon-client/src/game/perspective.rs::Game::draw_player_preview gap: player: only the local player is previewed; the smithing armour-stand preview has no port +GuiGraphicsExtractor.skin missing minor: no skin customisation preview was found; the inventory preview is draw_player_preview +GuiGraphicsExtractor.book missing player: the enchanting table screen does not draw its open book; Horizon's book drawing is for written books only +GuiGraphicsExtractor.profilerChart skip:debug F3 profiler pie chart; Horizon's debug_chart.rs covers its own charts +GuiGraphicsExtractor.setTooltipForNextFrame inline crates/horizon-client/src/screen_draw.rs::tooltip the overloads route to the tooltip draw +GuiGraphicsExtractor.setComponentTooltipForNextFrame port crates/horizon-client/src/screen_draw.rs::tooltip gap: minor: tooltip images (TooltipComponent) are not drawn +GuiGraphicsExtractor.setTooltipForNextFrameInternal port crates/horizon-client/src/ui.rs::tooltip frame and background sprites beside the cursor +GuiGraphicsExtractor.extractDeferredElements port crates/horizon-client/src/screen_draw.rs::paint gap: minor: deferred hover effects come from chat only (see componentHoverEffect) +GuiGraphicsExtractor.componentHoverEffect port crates/horizon-client/src/game/chat.rs::Game::draw_chat_screen gap: player: hover tooltips exist only for text in chat; item and entity hover events show nothing +GuiGraphicsExtractor.textRendererForWidget skip:ui builds a text collector for a widget; Horizon draws widget text directly +GuiGraphicsExtractor.textRenderer skip:ui builds a text collector; Horizon draws text directly +GuiGraphicsExtractor.createDefaultTextParameters skip:ui default pose, opacity and scissor for text; Horizon passes colour and rectangle directly +GuiGraphicsExtractor.RenderingTextCollector.accept port crates/horizon-client/src/screen_draw.rs::text gap: minor: no hover or click registration here (see Chat::style_at) +GuiGraphicsExtractor.RenderingTextCollector.acceptScrolling missing minor: no scrolling text (see defaultScrollingHelper) +GuiGraphicsExtractor.ScissorStack.push inline crates/horizon-client/src/screen_draw.rs::paint gap: minor: the pushed rectangle replaces the current one; it is not intersected +GuiGraphicsExtractor.ScissorStack.pop inline crates/horizon-client/src/screen_draw.rs::paint gap: minor: Draw::NoScissor clears the scissor instead of restoring the enclosing one +GuiGraphicsExtractor.ScissorStack.containsPoint missing minor: hover ignores the scissor, so a widget clipped out of view can still highlight +Hud.extractHotbarAndDecorations port crates/horizon-client/src/hud_layout.rs::hotbar items and decorations are drawn by Game::draw_item +Hud. skip:java constructor state setup +Hud.extractSubtitleOverlay port crates/horizon-client/src/hud_overlays.rs::SubtitleOverlay::extract +Hud.extractChat port crates/horizon-client/src/chat.rs::Chat::layout +Hud.getMobEffectSprite port crates/horizon-client/src/game/potions.rs::Game::draw_effect_icons sprite lookup is inside the effect icon draw +Hud.extractDemoOverlay skip:other Horizon has no demo mode, so there is no demo timer overlay +Hud.getCameraPlayer missing minor: unsure: no camera-entity lookup found; the HUD always uses the local player +Hud.getPlayerVehicleWithHealth port crates/horizon-client/src/hud_layout.rs::vehicle_health gap: minor: the vehicle is chosen by the caller +Hud.extractArmor inline crates/horizon-client/src/hud_layout.rs::HealthHud::layout armour row inside the health layout +Hud.extractHearts inline crates/horizon-client/src/hud_layout.rs::HealthHud::layout heart row inside the health layout +Hud.extractHeart port crates/horizon-client/src/hud_layout.rs::heart heart sprite choice for one heart +Hud.getAirBubbleYLine inline crates/horizon-client/src/hud_layout.rs::HealthHud::layout air bubble row position +Hud.getCurrentAirSupplyBubble inline crates/horizon-client/src/hud_layout.rs::HealthHud::layout air bubble count +Hud.extractFood inline crates/horizon-client/src/hud_layout.rs::HealthHud::layout food row inside the health layout +Hud.extractTextureOverlay port crates/horizon-client/src/hud_overlays.rs::camera_overlays gap: player: the powder-snow freeze outline is not drawn; only the equippable camera overlay (pumpkin) is +Hud.extractSlot port crates/horizon-client/src/game.rs::Game::draw_slot_item +Hud.setNowPlaying port crates/horizon-client/src/hud_overlays.rs::Titles::set_overlay the animate flag gives the rainbow colour +Hud.onDisconnected missing minor: unsure: no HUD reset on disconnect found; titles clear only on the ClearTitles packet +Hud.willPrioritizeJumpInfo port crates/horizon-client/src/locator_bar.rs::ContextualBar::next the jumping input comes from Game::mount_jumping +Hud.HeartType.getSprite port crates/horizon-client/src/hud_layout.rs::heart +Hud.HeartType.forPlayer inline crates/horizon-client/src/hud_layout.rs::HealthHud::layout heart kind comes from the health inputs +AbstractButton.extractDefaultSprite port crates/horizon-client/src/screen_draw.rs::button disabled, highlighted and normal sprites +AbstractButton.keyPressed missing minor: Enter or Space does not press a focused button; Enter only confirms a title text edit +AbstractContainerWidget.setFocused port crates/horizon-client/src/pack_screen.rs::PackScreen::set_focus gap: minor: screen-level focus only; no child-container focus +AbstractContainerWidget.mouseClicked port crates/horizon-client/src/ui.rs::Scroll::press gap: minor: child click dispatch is per screen; only the scrollbar press is here +AbstractScrollArea.isOverScrollbar inline crates/horizon-client/src/ui.rs::Scroll::press scrollbar column test folded into the press +AbstractScrollArea.scrollBarY port crates/horizon-client/src/ui.rs::scrollbar_thumb thumb top +AbstractScrollArea.extractScrollbar port crates/horizon-client/src/ui.rs::scrollbar gap: minor: no disabled-scroller sprite and no cursor request +AbstractScrollArea.defaultSettings skip:java settings record builder; the sizes are constants in ui.rs +AbstractSelectionList.swap port crates/horizon-client/src/multiplayer.rs::Screen::click gap: minor: mouse move up or down works; keyboard Shift+arrow reorder is not ported +AbstractSelectionList.clearEntriesExcept skip:other only the stats screen calls it; Horizon's stats screen draws its rows directly +AbstractSelectionList.replaceEntries port crates/horizon-client/src/pack_screen.rs::PackScreen::update_list +AbstractSelectionList.getNextY port crates/horizon-client/src/pack_screen.rs::List::next_y +AbstractSelectionList.addEntry port crates/horizon-client/src/pack_screen.rs::List::add +AbstractSelectionList.addEntryToTop skip:service only the chat report selection screen calls it; Horizon has no chat reporting +AbstractSelectionList.repositionEntries port crates/horizon-client/src/pack_screen.rs::List::reposition +AbstractSelectionList.removeEntryFromTop skip:service only the chat report selection screen calls it; Horizon has no chat reporting +AbstractSelectionList.updateSizeAndPosition port crates/horizon-client/src/pack_screen.rs::List::update_size +AbstractSelectionList.extractListSeparators port crates/horizon-client/src/pack_screen.rs::PackScreen::draw_list header and footer separators, with the in-world variant +AbstractSelectionList.extractListBackground port crates/horizon-client/src/pack_screen.rs::PackScreen::draw_list menu list background sprite +AbstractSelectionList.enableScissor port crates/horizon-client/src/pack_screen.rs::PackScreen::draw_list scissor clamped to the screen +AbstractSelectionList.scrollToEntry port crates/horizon-client/src/pack_screen.rs::List::scroll_to +AbstractSelectionList.setFocused port crates/horizon-client/src/pack_screen.rs::PackScreen::set_focus gap: minor: focusing a row does not select it; vanilla selects the focused entry +AbstractSelectionList.nextEntry missing minor: arrow keys do not move the selection between rows in the pack or server lists +AbstractSelectionList.extractListItems port crates/horizon-client/src/pack_screen.rs::PackScreen::draw_list rows culled to the visible band +AbstractSelectionList.extractItem port crates/horizon-client/src/pack_screen.rs::PackScreen::draw_list selection outline and row content +AbstractSelectionList.extractSelection port crates/horizon-client/src/pack_screen.rs::PackScreen::draw_list two fills: outline colour and black inner +AbstractSelectionList.getRowBottom skip:other only the LAN-found narration uses it; Horizon has no narrator +AbstractSelectionList.narrationPriority skip:other screen-reader priority; Horizon has no narrator +AbstractSelectionList.removeEntry skip:other rows are rebuilt from the model when they change (update_list, server list), so there is no single-row removal +AbstractSelectionList.narrateListElementPosition skip:other narration only +AbstractSelectionList.TrackedList.set skip:java delegate list wrapper +AbstractSliderButton.getSprite inline crates/horizon-client/src/ui.rs::slider gap: minor: track highlight follows hover only; keyboard focus does not highlight +AbstractSliderButton.getHandleSprite inline crates/horizon-client/src/ui.rs::slider gap: minor: handle highlight follows hover only +AbstractSliderButton.updateWidgetNarration skip:other narration only +AbstractSliderButton.extractWidgetRenderState port crates/horizon-client/src/ui.rs::slider gap: minor: the label is clipped rather than scrolled +AbstractSliderButton.handleCursor port crates/horizon-client/src/widgets.rs::widget_cursor gap: minor: no resize cursor while dragging +AbstractSliderButton.setFocused missing minor: keyboard focus does not enable value changes; there is no change-value state +AbstractSliderButton.keyPressed missing minor: Left and Right do not move a focused slider; Enter does not toggle it +AbstractSliderButton.setValue port crates/horizon-client/src/options_ui.rs::set_slider gap: minor: the 0..1 clamp is done by the caller (ui.rs slider_value) +AbstractStringWidget.extractWidgetRenderState port crates/horizon-client/src/screen_draw.rs::string_widget gap: minor: hover tooltip and click cursor for styled text are not drawn +AbstractStringWidget.onClick missing minor: clicking a styled label with a click event does nothing here; book links and chat links do work +AbstractStringWidget.setMessage skip:ui width is measured at draw time +AbstractStringWidget.handleStyleClick missing minor: same as onClick: no click handling for labels +AbstractTextAreaWidget.mouseClicked port crates/horizon-client/src/game/books.rs::Game::book_page_click gap: minor: a scrollbar press inside the area is not checked +AbstractTextAreaWidget.keyPressed port crates/horizon-client/src/game/books.rs::Game::book_page_key gap: minor: up and down scroll the area in vanilla; not checked here +AbstractTextAreaWidget.extractWidgetRenderState port crates/horizon-client/src/game/books.rs::Game::draw_book gap: minor: scroll offset inside the page not verified +AbstractTextAreaWidget.isMouseOver port crates/horizon-client/src/screen_draw.rs::inside gap: minor: the scrollbar width is not added to the hit area +AbstractTextAreaWidget.extractBackground skip:ui frame drawn by the book screen; no behaviour +AbstractTextAreaWidget.extractBorder skip:ui frame sprite; no behaviour +AbstractTextAreaWidget.withinContentAreaTopBottom skip:ui visibility cull while drawing lines +AbstractWidget.extractRenderState port crates/horizon-client/src/ui.rs::hovered gap: minor: the scissor test is not part of the hover check +AbstractWidget.extractTooltipForNextRenderPass port crates/horizon-client/src/ui.rs::tooltip gap: minor: unsure: widget hover delay not checked +AbstractWidget.extractScrollingStringOverContents port crates/horizon-client/src/ui.rs::centered_text gap: minor: text is clipped, not scrolled +AbstractWidget.mouseClicked port crates/horizon-client/src/title.rs::Title::click gap: minor: click dispatch is per screen; the press sound is audio.rs Audio::play_button_click +AbstractWidget.mouseReleased port crates/horizon-client/src/ui.rs::Scroll::release gap: minor: only the scroll release; widget onRelease is per screen +AbstractWidget.mouseDragged port crates/horizon-client/src/ui.rs::Scroll::drag gap: minor: only scroll drag; widget onDrag is per screen +AbstractWidget.nextFocusPath missing minor: unsure: no per-widget focus path; Horizon focus is per screen +AbstractWidget.isMouseOver port crates/horizon-client/src/screen_draw.rs::inside gap: minor: isActive is checked by the caller +AbstractWidget.narrationPriority skip:other narration only +AbstractWidget.defaultButtonNarrationText skip:other narration only diff --git a/tools/port_map/client2-010.tsv b/tools/port_map/client2-010.tsv new file mode 100644 index 0000000000..bccf69d994 --- /dev/null +++ b/tools/port_map/client2-010.tsv @@ -0,0 +1,144 @@ +BossHealthOverlay.extractBar port crates/horizon-client/src/hud_overlays.rs::boss_overlay background bar, then progress at the lerped width; the notch overlay only when not PROGRESS +BossHealthOverlay.shouldDarkenScreen port crates/horizon-client/src/boss_overlay.rs::BossOverlay::should_darken any bar asks to darken +Button.Builder.build skip:java builder: makes a Plain button and sets its tooltip +ChatComponent.forEachLine port crates/horizon-client/src/chat.rs::Chat::layout visible lines bottom-up with the alpha filter, inlined in layout +ChatComponent.addMessageToDisplayQueue port crates/horizon-client/src/chat.rs::Chat::add_lines wrap at width/scale, keep the scroll when chatting, trim to 100 lines +ChatComponent.addMessageToQueue port crates/horizon-client/src/chat.rs::Chat::add_message newest first, trimmed to 100 messages +ChatComponent.processMessageDeletionQueue missing player: a message the server deletes stays in chat; the delete-chat packet is only named in horizon-protocol/src/ids.rs and never decoded +ChatComponent.deleteMessage missing player: a server message delete does nothing in Horizon; the removed line stays visible +ChatComponent.deleteMessageOrDelay missing player: no 60-tick delayed removal and no deleted marker; see deleteMessage +ChatComponent.refreshTrimmedMessages port crates/horizon-client/src/chat.rs::Chat::rewrap gap: minor: rewrap also resets the scroll to 0; vanilla keeps the position +ChatComponent.scrollChat port crates/horizon-client/src/chat.rs::Chat::scroll_by clamp to the page and clear new-message marker at the bottom +ChatComponent.getHeight port crates/horizon-client/src/chat.rs::ChatOptions::height_px focused or unfocused chat height in px +ChatComponent.defaultUnfocusedPct inline crates/horizon-client/src/chat.rs::ChatOptions::default the default 70/160 is stored as a constant, not computed from the height formula +ChatComponent.getLineHeight port crates/horizon-client/src/chat.rs::ChatOptions::line_height 9 * (line spacing + 1) +ChatComponent.saveAsDraft missing player: Save Chat Drafts does nothing (options_ui.rs notes no drafts yet); text left in the box is not kept for the next open +ChatComponent.createScreen port crates/horizon-client/src/game/chat.rs::Game::open_chat gap: player: no draft is restored; the chat opens with the initial text its caller passes +ChatComponent.preserveCurrentChatScreen missing minor: unsure: Horizon keeps no chat screen across a dimension change; game/nether_portal.rs:577 only leaves Chat alone during a portal effect, the change path was not checked +ChatComponent.storeState inline crates/horizon-client/src/game/remote.rs::Game::poll_remote no snapshot: the Reconfigured arm only resets boss bars and registries, so the chat survives a reconfigure without a copy +ChatComponent.restoreState inline crates/horizon-client/src/game/remote.rs::Game::poll_remote no restore: the chat is never cleared on Reconfigured, so there is nothing to put back +ChatComponent.ClickableTextOnlyGraphicsAccess.updatePose skip:ui pose bookkeeping of the text collector; chat draws with its own scale and translate +ChatComponent.DrawingBackgroundGraphicsAccess.updatePose skip:ui pose bookkeeping of the text collector; chat draws with its own scale and translate +ChatComponent.DrawingBackgroundGraphicsAccess.handleMessage port crates/horizon-client/src/chat.rs::Chat::layout line text drawn at its bottom with the text opacity +ChatComponent.DrawingBackgroundGraphicsAccess.handleTag port crates/horizon-client/src/chat.rs::Chat::layout indicator bar filled from x -4 to -2 with the line alpha +ChatComponent.DrawingFocusedGraphicsAccess.updateLocalMousePos inline crates/horizon-client/src/chat.rs::Chat::line_at the mouse mapped into line space inside line_at +ChatComponent.DrawingFocusedGraphicsAccess.updatePose skip:ui pose bookkeeping; the mouse mapping is done in Chat::line_at +ChatComponent.DrawingFocusedGraphicsAccess.handleMessage port crates/horizon-client/src/chat.rs::Chat::style_at gap: player: no pointing-hand cursor over clickable chat text; only widgets set the cursor (widgets.rs:28) +ChatComponent.DrawingFocusedGraphicsAccess.isMouseOver inline crates/horizon-client/src/chat.rs::Chat::indicator_at the rectangle test of the indicator bar; tag icons are not drawn +ChatComponent.DrawingFocusedGraphicsAccess.handleTagIcon missing minor: no tag icons; Horizon never tags a message as modified, so the Modified icon never shows +ChatComponent.DrawingFocusedGraphicsAccess.showTooltip port crates/horizon-client/src/game/chat.rs::Game::draw_chat_screen gap: minor: every indicator shows the systemSinglePlayer text as its tooltip +Checkbox.adjustWidth missing minor: the checkbox width is never capped or its label wrapped; dialog_screen.rs:137 sets the width to the full label +Checkbox.overflowsRowLimit missing minor: no tooltip for a label that wraps past two rows; Horizon dialog labels never wrap +Checkbox.onPress port crates/horizon-client/src/game/dialogs.rs::Game::dialog_click toggles the boolean input value (dialogs.rs:448) +Checkbox.updateWidgetNarration skip:other narration text; Horizon has no narrator (the Narrator option is stored only) +Checkbox.extractContents port crates/horizon-client/src/game/dialogs.rs::Game::draw_dialog box sprite for checked and focused states, then the label (Part::Check) +Checkbox.Builder.selected skip:java builder setter +Checkbox.Builder.build skip:java builder: makes the checkbox and its tooltip +CommandSuggestions.mouseScrolled port crates/horizon-client/src/chat.rs::ChatScreen::scroll wheel over the suggestion list, clamped to the list +CommandSuggestions.mouseClicked port crates/horizon-client/src/chat.rs::ChatScreen::click click on a suggestion row uses it, else the input box +CommandSuggestions.getUsageNarration skip:other narration text; no narrator in Horizon +CommandSuggestions.sortSuggestions port crates/horizon-client/src/chat.rs::ChatScreen::show_suggestions suggestions starting with the typed word first, including the minecraft: prefix +CommandSuggestions.hasMessageArguments missing minor: a /say or /msg style command with a message argument counts as a command here; Horizon's parse keeps no argument kinds (see update_usage_info comment) +CommandSuggestions.getExceptionMessage port crates/horizon-client/src/chat.rs::ChatScreen::update_usage_info parse errors shown with their context (e.with_context) +CommandSuggestions.fillNodeUsage port crates/horizon-client/src/chat.rs::ChatScreen::update_usage_info smart usage lines, without the literal nodes +CommandSuggestions.recomputeUsageBoxWidth port crates/horizon-client/src/chat.rs::ChatScreen::update_usage_info usage width is the widest line +CommandSuggestions.formatChat port crates/horizon-client/src/chat.rs::ChatScreen::format argument colours over the input +CommandSuggestions.calculateSuggestionSuffix port crates/horizon-client/src/chat.rs::ChatScreen::select the grey suffix for the highlighted suggestion (strip the typed prefix) +CommandSuggestions.extractSuggestions port crates/horizon-client/src/chat.rs::ChatScreen::draw the suggestion list drawn when it is open +CommandSuggestions.extractUsage port crates/horizon-client/src/chat.rs::ChatScreen::draw usage lines drawn upward from the bottom, as vanilla's bottom anchor +CommandSuggestions.getNarrationMessage skip:other narration text; no narrator in Horizon +CommandSuggestions.hasAllowedInput port crates/horizon-client/src/chat.rs::ChatAbilities::allows gap: minor: a /say style command is judged as a command, not a message (see hasMessageArguments) +CommandSuggestions.SuggestionsList.extractRenderState port crates/horizon-client/src/chat.rs::ChatScreen::draw_list rows, scroll marks, highlight and tooltip; gap: minor: no pointing-hand cursor over the rows +CommandSuggestions.SuggestionsList.mouseClicked port crates/horizon-client/src/chat.rs::ChatScreen::click row click selects and uses the suggestion +CommandSuggestions.SuggestionsList.keyPressed port crates/horizon-client/src/chat.rs::ChatScreen::key up, down and tab cycle and use the suggestion +CommandSuggestions.SuggestionsList.cycle port crates/horizon-client/src/chat.rs::ChatScreen::cycle moves the selection and the scroll offset +CommandSuggestions.SuggestionsList.getNarrationMessage skip:other narration text; no narrator in Horizon +ComponentRenderUtils.stripColor port crates/horizon-client/src/chat.rs::Chat::displayed with Chat Colors off the section codes are stripped before wrapping +ContainerObjectSelectionList.nextFocusPath port crates/horizon-client-input/src/gamepad.rs::focus_step gap: minor: picks the nearest target by geometry, not row by row with a wrap to the next entry +ContainerObjectSelectionList.setFocused missing minor: unsure: no list focus model beyond gamepad focus_step; losing focus does not clear a selection here +ContainerObjectSelectionList.updateWidgetNarration skip:other narration text; no narrator in Horizon +ContainerObjectSelectionList.Entry.setFocused missing minor: unsure: focus inside a row is not tracked; focus_step works on target rectangles +ContainerObjectSelectionList.Entry.focusPathAtIndex missing minor: unsure: no per-row focus path; see focus_step +ContainerObjectSelectionList.Entry.nextFocusPath port crates/horizon-client-input/src/gamepad.rs::focus_step gap: minor: moves by nearest target rectangle, not by child index within the row +ContainerObjectSelectionList.Entry.updateNarration skip:other narration text; no narrator in Horizon +CycleButton.extractContents port crates/horizon-client/src/options_ui.rs::OptionsUi::draw option button sprite and its label +CycleButton.onPress port crates/horizon-client/src/options_ui.rs::Opt::cycle click cycles forward; the back flag comes from the caller +CycleButton.getCycledValue skip:other only feeds the narration of the next value +CycleButton.mouseScrolled missing player: the mouse wheel over an option button does not change its value; OptionsUi::scroll_at only scrolls the list +CycleButton.updateValue inline crates/horizon-client/src/options_ui.rs::Opt::cycle labels are read live from Settings (Opt::label), so no cached message needs updating +CycleButton.createLabelForValue port crates/horizon-client/src/options_ui.rs::Opt::label value label or full name, from the setting +CycleButton.updateWidgetNarration skip:other narration text; no narrator in Horizon +CycleButton.createDefaultNarrationMessage skip:other narration text; no narrator in Horizon +CycleButton.builder skip:java builder +CycleButton.booleanBuilder skip:java builder +CycleButton.onOffBuilder skip:java builder +CycleButton.Builder.withValues skip:java builder +CycleButton.Builder.create skip:java builder: makes the cycle button and its initial value +CycleButton.ValueListSupplier.create skip:java value list copy +DebugScreenOverlay. skip:java constructs the chart objects; Horizon's DebugCharts is plain state +DebugScreenOverlay.formatChart port crates/horizon-client/src/game/debug_keys.rs::Game::debug_chart_lines the [key] name visible or hidden line +DebugScreenOverlay.formatKeybind inline crates/horizon-client/src/game/debug_keys.rs::Game::debug_chart_lines the key closure inside debug_chart_lines +DebugScreenOverlay.getServerLevel missing minor: F3 draws no chunk or level lines; draw_debug_overlay has no chunk data +DebugScreenOverlay.getLevel missing minor: F3 draws no chunk or level lines; draw_debug_overlay has no chunk data +DebugScreenOverlay.getServerChunk missing minor: F3 draws no server chunk line +DebugScreenOverlay.getClientChunk missing minor: F3 draws no client chunk line +DebugScreenOverlay.showDebugScreen port crates/horizon-client/src/game/debug_keys.rs::Game::draw_debug_overlay debug on and (HUD shown or a screen open) +DebugScreenOverlay.showProfilerChart missing minor: no profiler chart; the profiling chart key is used up and draws nothing (debug_keys.rs:662) +DebugScreenOverlay.showNetworkCharts port crates/horizon-client/src/debug_chart.rs::DebugCharts::draw network chart drawn when its flag is set +DebugScreenOverlay.showFpsCharts port crates/horizon-client/src/debug_chart.rs::DebugCharts::draw fps chart drawn when its flag is set +DebugScreenOverlay.showLightmapTexture port crates/horizon-client/src/game/debug_keys.rs::Game::draw_debug_overlay lightmap drawn when its flag is set (debug_keys.rs:473) +DebugScreenOverlay.toggleNetworkCharts port crates/horizon-client/src/debug_chart.rs::DebugCharts::toggle_network toggles the network chart and clears the others +DebugScreenOverlay.toggleFpsCharts port crates/horizon-client/src/debug_chart.rs::DebugCharts::toggle_fps toggles the fps chart and clears the others +DebugScreenOverlay.toggleLightmapTexture port crates/horizon-client/src/debug_chart.rs::DebugCharts::toggle_lightmap toggles the lightmap and clears the others +DebugScreenOverlay.toggleProfilerChart missing minor: no profiler chart (see showProfilerChart) +DebugScreenOverlay.logRemoteSample missing minor: unsure: the debug_sample packet is only named in horizon-protocol/src/ids.rs, so a server's samples are not logged +EditBox.createNarrationMessage skip:other narration text; no narrator in Horizon +EditBox.getHighlighted port crates/horizon-client/src/ui.rs::EditBox::selected_text the text between cursor and highlight +EditBox.moveCursorTo port crates/horizon-client/src/ui.rs::EditBox::set_cursor sets the cursor and keeps the highlight unless extending +EditBox.setCursorPosition port crates/horizon-client/src/ui.rs::EditBox::set_cursor clamps and scrolls to the cursor +EditBox.canConsumeInput missing minor: unsure: no editable flag on the box; whether a box takes typed input is the caller's focus routing +EditBox.preeditUpdated missing minor: no IME composition; Horizon has no preedit input +EditBox.selectWord port crates/horizon-client/src/ui.rs::EditBox::click a double click selects the word (the double flag) +EditBox.applyFormat port crates/horizon-client/src/ui.rs::EditBox::draw the format text passed to draw +EditBox.updateTextPosition port crates/horizon-client/src/ui.rs::EditBox::text_x text_x of the shown part; text_y is its sibling +EditBox.setMaxLength port crates/horizon-client/src/ui.rs::Caret::sync gap: minor: sets the limit only; a longer value is not cut to it +EditBox.setFocused port crates/horizon-client/src/ui.rs::Caret::draw focus restarts the blink clock here; gap: minor: no IME or narration side effects +EditBox.setEditable missing minor: unsure: no read-only flag on the box in ui.rs; a box vanilla locks would accept typing +EditBox.setHighlightPos port crates/horizon-client/src/ui.rs::EditBox::set_cursor sets the highlight and scrolls +EditBox.setHint inline crates/horizon-client/src/title/create_world.rs::Title::create_field the hint is passed when the box is made and drawn while it is empty and unfocused +FittingMultiLineTextWidget.setWidth skip:ui layout: wrap width +FittingMultiLineTextWidget.minimizeHeight skip:ui layout: height to content +FittingMultiLineTextWidget.extractContents port crates/horizon-client/src/game/dialogs.rs::Game::draw_dialog message lines drawn as text parts +FocusableTextWidget.updateWidgetNarration skip:other narration text; no narrator in Horizon +FocusableTextWidget.extractWidgetRenderState port crates/horizon-client/src/game/lan_screen.rs::Game::draw_restrictions gap: minor: text only; no focus outline or background fill +FocusableTextWidget.updateWidth skip:ui layout: width from the message +FocusableTextWidget.updateHeight skip:ui layout: height from the wrapped lines +FocusableTextWidget.setMessage skip:ui layout: size follows the message +FocusableTextWidget.keyPressed missing minor: unsure: keyboard does not activate a clickable style in a focused text line; no such focus in Horizon +FocusableTextWidget.builder skip:java builder +FriendsButton.refreshIncomingRequestCount skip:service Microsoft friend requests; Horizon's friends use its own invite codes (multiplayer.rs:90) +FriendsButton.extractContents skip:service draws the request badge; Horizon has no incoming request count +IMEPreeditOverlay.extractRenderState missing minor: no IME composition overlay; Horizon has no preedit input +ImageButton.extractContents port crates/horizon-client/src/game/recipe_book.rs::Game::draw_book_panel tab buttons drawn with their sprite; the other vanilla ImageButton screens are not in Horizon +ImageWidget.texture skip:java factory +ImageWidget.Sprite.extractWidgetRenderState skip:other picture of a popup or social tab; Horizon's lock popup has no picture +ImageWidget.Texture.extractWidgetRenderState skip:other picture of a popup or social tab; Horizon's lock popup has no picture +ItemDisplayWidget.extractWidgetRenderState missing player: a dialog's item body is not drawn (dialog_screen.rs Body::Item gives no element) +ItemDisplayWidget.updateWidgetNarration skip:other narration text; no narrator in Horizon +LoadingDotsWidget.extractWidgetRenderState port crates/horizon-client/src/multiplayer.rs::Screen::draw the scanning label and the animated dots under it on the LAN row +LockIconButton.createNarrationMessage skip:other narration text; no narrator in Horizon +LockIconButton.extractContents port crates/horizon-client/src/game/lan_screen.rs::Game::draw_options_view lock or unlock sprite by state +LogoRenderer.extractRenderState port crates/horizon-client/src/title.rs::Title::draw logo top 44 rows and edition banner; gap: minor: no easter-egg logo variant +MultiLineEditBox.updateWidgetNarration skip:other narration text; no narrator in Horizon +MultiLineEditBox.onClick port crates/horizon-client/src/screen_draw.rs::TextField::click click places the cursor; gap: minor: no double-click word select +MultiLineEditBox.onDrag missing minor: unsure: TextField has no drag selection in screen_draw.rs +MultiLineEditBox.charTyped port crates/horizon-client/src/screen_draw.rs::TextField::insert_text typed text replaces the selection or goes at the cursor +MultiLineEditBox.preeditUpdated missing minor: no IME composition; Horizon has no preedit input +MultiLineEditBox.extractContents port crates/horizon-client/src/screen_draw.rs::TextField::draw placeholder, lines, caret and selection highlight +MultiLineEditBox.extractDecorations missing minor: no character count under a limited box; TextField has no limit +MultiLineEditBox.scrollToCursor port crates/horizon-client/src/screen_draw.rs::TextField::scroll_to keeps the cursor line in view +MultiLineEditBox.seekCursorScreen port crates/horizon-client/src/screen_draw.rs::TextField::click maps the pointer to a cursor position +MultiLineEditBox.setFocused inline crates/horizon-client/src/screen_draw.rs::TextField::draw gap: minor: focus does not restart the caret blink here; the caller passes the blink clock +MultiLineEditBox.Builder.build skip:java builder +MultiLineTextWidget.getHeight skip:ui layout: wrapped line count times 9; Measure::split does the wrap when drawing +MultiLineTextWidget.getFreshCacheKey skip:java cache key for the wrapped lines diff --git a/tools/port_map/client2-011.tsv b/tools/port_map/client2-011.tsv new file mode 100644 index 0000000000..74fda8c545 --- /dev/null +++ b/tools/port_map/client2-011.tsv @@ -0,0 +1,144 @@ +MultilineTextField.setCharacterLimit port crates/horizon-client/src/game/books.rs::book_page_box gap: minor: only the book page sets a character limit (PAGE_CHARS); other MultiLineEditBox users are not ported +MultilineTextField.setLineLimit inline crates/horizon-client/src/game/books.rs::Game::page_keep the 14-line limit (LINES) is checked in page_keep; there is no limit field +MultilineTextField.setValue port crates/horizon-client/src/ui.rs::EditBox::set_value gap: minor: no line-limit check when the text is set (allowOverflowLineLimit path) +MultilineTextField.insertText port crates/horizon-client/src/ui.rs::EditBox::insert_text the line limit is checked by Game::page_keep, not here +MultilineTextField.deleteText port crates/horizon-client/src/ui.rs::EditBox::delete_chars +MultilineTextField.getSelected port crates/horizon-client/src/ui.rs::EditBox::selected +MultilineTextField.getLineAtCursor port crates/horizon-client/src/game/books.rs::Game::caret_on the visual row holding the caret, with its x +MultilineTextField.getLineView port crates/horizon-client/src/game/books.rs::Game::page_rows visual rows as character ranges +MultilineTextField.seekCursor port crates/horizon-client/src/ui.rs::EditBox::set_cursor +MultilineTextField.seekCursorLine port crates/horizon-client/src/game/books.rs::Game::book_move_line gap: minor: column kept without vanilla's 2 px bias (LINE_SEEK_PIXEL_BIAS) +MultilineTextField.seekCursorToPoint port crates/horizon-client/src/game/books.rs::Game::page_index_at +MultilineTextField.selectWordAtCursor missing player: double-clicking a word on a book page does not select it; Game::book_page_click only sets the caret +MultilineTextField.keyPressed port crates/horizon-client/src/game/books.rs::Game::book_input gap: player: Home and End go to the start and end of the whole page, not of the visual line (EditBox::key); Enter, arrows and Ctrl keys are handled +MultilineTextField.getSelectedText port crates/horizon-client/src/ui.rs::EditBox::selected_text +MultilineTextField.getCursorLineView inline crates/horizon-client/src/game/books.rs::Game::book_move_line gap: player: Home and End do not use the caret's line range (see keyPressed) +MultilineTextField.getNextWord port crates/horizon-client/src/ui.rs::multiline_word_position start of the next word; Ctrl+Right and Ctrl+Delete reach it through EditBox::word_position +MultilineTextField.getWordEndPosition missing minor: the word end only feeds selectWordAtCursor, which is missing for book pages +MultilineTextField.onValueChange inline crates/horizon-client/src/game/books.rs::Game::page_keep the page text is stored there after the limit check; visual rows are computed on demand by page_rows +MultilineTextField.reflowDisplayLines port crates/horizon-client/src/game/books.rs::Game::page_rows same wrap breaks as font.split +MultilineTextField.truncateFullText port crates/horizon-client/src/ui.rs::EditBox::set_value truncates to max_length characters +MultilineTextField.truncateInsertionText inline crates/horizon-client/src/ui.rs::EditBox::insert_text room left under max_length +MultilineTextField.overflowsLineLimit port crates/horizon-client/src/game/books.rs::Game::page_keep counts page_rows, which adds the row after a trailing newline as vanilla does +ObjectSelectionList.nextFocusPath missing minor: no arrow-key focus or selection in the title, server or pack lists +ObjectSelectionList.updateWidgetNarration skip:other screen-reader narration; Horizon has no narration output +OptionsList.addBig port crates/horizon-client/src/options_ui.rs::OptionsUi::layout Row::Big: 310 wide at width/2 - 155 +OptionsList.addHeader port crates/horizon-client/src/options_ui.rs::OptionsUi::layout gap: minor: the header is centred text, not a focusable widget +OptionsList.findOption skip:ui widget lookup only; rows are rebuilt from Settings in OptionsUi::layout each frame +OptionsList.applyUnsavedChanges inline crates/horizon-client/src/options_ui.rs::set_slider gap: minor: delayed sliders (render, simulation, biome blend distance, wheel sensitivity) apply on every step, not 600 ms after the last move or on close +OptionsList.resetOption inline crates/horizon-client/src/options_ui.rs::OptionsUi::slider_shown gap: minor: only sliders show the stored value at once; other options redraw from Settings each frame +OptionsList.Entry.big port crates/horizon-client/src/options_ui.rs::option_widget 310 wide; the row's place is OptionsUi::layout (Row::Big) +OptionsList.Entry.small port crates/horizon-client/src/options_ui.rs::option_widget pair placement at width/2 - 155 and + 5 in OptionsUi::layout (Row::Pair) +OptionsList.Entry.extractContent inline crates/horizon-client/src/options_ui.rs::OptionsUi::layout positions the row's widgets; OptionsUi::draw paints them +OptionsList.Entry.findOption skip:ui widget lookup only; rows are rebuilt from Settings each frame +OptionsList.HeaderEntry.extractContent inline crates/horizon-client/src/options_ui.rs::OptionsUi::layout header centred on the row (Row::Header) +PlainTextButton.extractContents missing player: the title's bottom copyright text that opens Credits is not a button; Horizon draws "Not affiliated with Mojang or Microsoft" there instead +PlayerFaceExtractor.extractHat port crates/horizon-client/src/tab_list.rs::face hat layer: UV 40, flipped for Dinnerbone and Grumm +PlayerFaceWidget.extractWidgetRenderState skip:service head in the friends list (AbstractFriendsEntryContainerWidget) +PlayerSkinWidget.extractWidgetRenderState skip:service skin preview on the chat report screen (SkinReportScreen) +PlayerSkinWidget.onDrag skip:service drag-to-rotate of the chat report skin preview +PlayerTabOverlay.extractTablistScore inline crates/horizon-client/src/tab_list.rs::TabList::layout the score column is drawn in layout; hearts go through TabList::hearts +PopupScreen.init port crates/horizon-client/src/game/lan_screen.rs::Popup::new gap: minor: only the lock question (title, message, Yes and No); images and extra buttons are not ported +PopupScreen.buildButtonRow inline crates/horizon-client/src/game/lan_screen.rs::Popup::new two buttons min((214 - 6) / 2, 150) wide, 6 apart +PopupScreen.repositionElements inline crates/horizon-client/src/game/lan_screen.rs::Popup::new centres the popup on the screen +PopupScreen.extractBackground port crates/horizon-client/src/game/lan_screen.rs::Game::draw_lock_question dimmed gradient, then the popup background sprite +PopupScreen.getNarrationMessage skip:other screen-reader text; Horizon has no narration output +PopupScreen.onClose port crates/horizon-client/src/game/lan_screen.rs::Game::world_options_escape Escape on the lock question returns to the options page +PopupScreen.Builder.addButton skip:java builder setter; Horizon's lock popup has fixed Yes and No buttons +PopupScreen.Builder.build skip:java builder; Horizon constructs the popup directly +RealmsButton.extractContents skip:service Realms button +ScrollableLayout.setMinWidth skip:ui sizing only; each screen sizes its own scroll area (dialog_screen.rs, server_links.rs, lan_screen.rs) +ScrollableLayout.setMinHeight skip:ui sizing only; same screens +ScrollableLayout.setMaxHeight skip:ui sizing only; same screens +ScrollableLayout.arrangeElements skip:ui scrollbar reserve and layout live in each screen's own code (10 px each side in dialog and LAN screens) +ScrollableLayout.Container.refreshChildren skip:ui children are laid out each frame +ScrollableLayout.Container.extractWidgetRenderState skip:ui each screen draws its own clipped list (server links, dialog, LAN) +ScrollableLayout.Container.getBorderForArrowNavigation missing minor: arrow-key focus moves across the list are not ported; Horizon lists have no keyboard focus +ScrollableLayout.Container.setFocused missing minor: focusing a row does not scroll it into view; Horizon lists have no keyboard focus +ScrollableLayout.Container.setX skip:ui positions set by each screen's layout +ScrollableLayout.Container.setY skip:ui positions set by each screen's layout +ScrollableLayout.Container.setScrollAmount inline crates/horizon-client/src/game/server_links.rs::Game::server_links_scroll gap: minor: clamps the server links list only; other scroll areas clamp in their own code +SelectableEntry.mouseOverIcon port crates/horizon-client/src/pack_screen.rs::PackScreen::entry_click 32 px icon of a pack row; the world row's icon uses the same test in Title::click_action +SelectableEntry.mouseOverLeftHalf port crates/horizon-client/src/pack_screen.rs::PackScreen::entry_click left 16 px of the pack icon (unselect) +SelectableEntry.mouseOverRightHalf port crates/horizon-client/src/multiplayer.rs::over_right_half join half of a server row +SelectableEntry.mouseOverTopRightQuarter port crates/horizon-client/src/pack_screen.rs::PackScreen::entry_click move-up arrow of a pack row +SelectableEntry.mouseOverBottomRightQuarter port crates/horizon-client/src/pack_screen.rs::PackScreen::entry_click move-down arrow of a pack row +SelectableEntry.mouseOverTopLeftQuarter port crates/horizon-client/src/multiplayer.rs::over_top_left move-up arrow of a server row +SelectableEntry.mouseOverBottomLeftQuarter port crates/horizon-client/src/multiplayer.rs::over_bottom_left move-down arrow of a server row +SplashRenderer.extractRenderState missing player: the random splash line on the title screen is not drawn; Title::draw has no splash +SpriteIconButton.setLoading skip:service the loading state is used only by the friends buttons (AddFriendWidget, FriendEntry) +SpriteIconButton.extractSprite port crates/horizon-client/src/title.rs::Title::draw draws the title's icon buttons (language, accessibility); the loading state is service-only +SpriteIconButton.extractLoadingStateIfLoading skip:service loading spinner only for the friends buttons +SpriteIconButton.Builder.sprite skip:java builder setter; Horizon draws its icon buttons with fixed sprites +SpriteIconButton.Builder.build skip:java builder; no object is needed +SpriteIconButton.CenteredIcon.extractContents skip:service FriendsButton (social) +SpriteIconButton.TextAndIcon.extractContents skip:other no caller in 26.3 +StringWidget.setMaxWidth port crates/horizon-client/src/screen_draw.rs::string_widget clips at max_width (CLAMPED) +StringWidget.getWidth port crates/horizon-client/src/screen_draw.rs::label_width the smaller of max width and text width +StringWidget.visitLines port crates/horizon-client/src/screen_draw.rs::string_widget gap: minor: SCROLLING overflow (scrolling text) is not ported; CLAMPED draws "..." +TabButton.updateWidgetNarration skip:other screen-reader text; Horizon has no narration output +TextCursorUtils.extractInsertCursor port crates/horizon-client/src/ui.rs::EditBox::draw insert caret is a 1 px bar; blinks every 300 ms +Tooltip.create skip:java factory; Horizon passes the lines to ui::tooltip +Tooltip.toCharSequence port crates/horizon-client/src/screen_draw.rs::tooltip wraps at 170 px as splitTooltip does; no per-language cache +WidgetSprites.get port crates/horizon-client/src/ui.rs::button disabled and hovered pick the sprite; gap: minor: keyboard focus does not pick the highlighted sprite +WidgetTooltipHolder.refreshTooltipForNextRenderPass port crates/horizon-client/src/ui.rs::tooltip gap: minor: tooltips show only under the pointer, not for keyboard focus +WidgetTooltipHolder.createTooltipPositioner missing minor: a keyboard-focused widget gets no tooltip placed below or above it +DebugEntryBiome.display missing minor: F3 biome line not drawn +DebugEntryBiome.printBiome missing minor: biome id text for the F3 biome line +DebugEntryChunkGeneration.display missing minor: F3 chunk generation line (Blending: Old) not drawn +DebugEntryChunkGeneration.update missing minor: F3 chunk generation sampling not ported +DebugEntryChunkSourceStats.display missing minor: F3 chunk source stats line not drawn +DebugEntryDayCount.display missing minor: F3 "Day #" line not drawn +DebugEntryDetailedMemory.display skip:java JVM heap and non-heap readout (MemoryMXBean); Rust has no JVM heap +DebugEntryDetailedMemory.printMemoryUsage skip:java JVM memory format for the heap readout +DebugEntryFps.display port crates/horizon-client/src/game/debug_keys.rs::Game::draw_debug_overlay first line: fps, framerate cap (inf), present mode and refresh rate +DebugEntryFps.presentModeName port crates/horizon-client/src/game/debug_keys.rs::Game::draw_debug_overlay present mode suffix (immediate, mailbox, fifo) +DebugEntryGpuUtilization.display missing minor: F3 GPU utilisation percentage not drawn; Horizon shows the GPU name only +DebugEntryHeightmap.display missing minor: F3 heightmap line not drawn +DebugEntryLight.display missing minor: F3 client and server light line not drawn +DebugEntryLocalDifficulty.display missing minor: F3 local difficulty line not drawn +DebugEntryLookingAt.display missing minor: F3 targeted block line not drawn +DebugEntryLookingAt.addTagEntries missing minor: block tag lines of the targeted block (F3) not drawn +DebugEntryLookingAt.DebugEntryLookingAtState.extractInfo missing minor: targeted block state (F3) not drawn +DebugEntryLookingAt.DebugEntryLookingAtState.addStateProperties missing minor: targeted block state properties (F3) not drawn +DebugEntryLookingAt.DebugEntryLookingAtState.getPropertyValueString missing minor: property values of the targeted block state (F3) not drawn +DebugEntryLookingAtEntity.display missing minor: F3 targeted entity line not drawn +DebugEntryLookingAtEntityTags.display missing minor: entity tag lines of the targeted entity (F3) not drawn +DebugEntryMemory.display skip:java JVM heap and allocation counters (Runtime, GC); Rust has none +DebugEntryMemory.AllocationRateCalculator.bytesAllocatedPerSecond skip:java JVM allocation counter from GC data +DebugEntryMemory.AllocationRateCalculator.gcCounts skip:java JVM garbage collector counts +DebugEntryPlayerSpeed.display missing minor: F3 speed line (blocks per tick) not drawn +DebugEntryPosition.display port crates/horizon-client/src/debug_overlay.rs::position XYZ, block, chunk and facing lines +DebugEntryPostEffects.display missing minor: F3 post effect chain line not drawn +DebugEntrySectionPosition.display port crates/horizon-client/src/debug_overlay.rs::position section-relative line +DebugEntrySoundCache.display missing minor: F3 sound cache line not drawn +DebugEntrySoundMood.display missing minor: F3 sound mood line not drawn +DebugEntrySpawnCounts.display missing minor: F3 spawn counts line not drawn +DebugEntrySystemSpecs.getCpuInfo port crates/horizon-client/src/game/debug_keys.rs::Game::draw_debug_overlay CPU name line +DebugEntrySystemSpecs.display port crates/horizon-client/src/game/debug_keys.rs::Game::draw_debug_overlay gap: minor: no Java version line; CPU, display, window and GPU lines are drawn +DebugEntrySystemSpecs.typeName port crates/horizon-client/src/game/debug_keys.rs::Game::draw_debug_overlay GPU type suffix (iGPU, dGPU, vGPU, software) +DebugEntryTps.display port crates/horizon-client/src/game/debug_keys.rs::Game::draw_debug_overlay integrated or remote server line: tick ms against target, sprinting or frozen, tx and rx +DebugScreenEntries.register skip:debug registry of F3 entries; Horizon's overlay has a fixed profile +DebugScreenEntryList.load skip:debug reads the F3 profile file; F3 profile editing is developer tooling (Horizon's F3 hint says To edit but has no editor) +DebugScreenEntryList.setStatus skip:debug F3 profile editor (developer tooling) +DebugScreenEntryList.toggleStatus skip:debug F3 profile editor (developer tooling) +DebugScreenEntryList.rebuildCurrentList skip:debug F3 profile editor (developer tooling) +DebugScreenEntryList.save skip:debug F3 profile editor (developer tooling) +AbstractDebugChart.extractSampleBars port crates/horizon-client/src/debug_chart.rs::Chart::bars one bar per sample +AbstractDebugChart.extractMainSampleBar port crates/horizon-client/src/debug_chart.rs::Chart::bars the main value's bar (dimension 0) +BandwidthDebugChart.extractLabeledLineAtValue port crates/horizon-client/src/debug_chart.rs::Chart::lines_and_labels +BandwidthDebugChart.extractLineWithLabel port crates/horizon-client/src/debug_chart.rs::Chart::lines_and_labels +FpsDebugChart.extractAdditionalLinesAndLabels port crates/horizon-client/src/debug_chart.rs::Chart::lines_and_labels +FpsDebugChart.toDisplayString port crates/horizon-client/src/debug_chart.rs::Chart::display +FpsDebugChart.getSampleColor port crates/horizon-client/src/debug_chart.rs::Chart::color +PingDebugChart.extractAdditionalLinesAndLabels port crates/horizon-client/src/debug_chart.rs::Chart::lines_and_labels +PingDebugChart.toDisplayString port crates/horizon-client/src/debug_chart.rs::Chart::display +PingDebugChart.getSampleColor port crates/horizon-client/src/debug_chart.rs::Chart::color +ProfilerPieChart.extractRenderState missing minor: F3+1 profiler pie chart is not drawn; the chart toggle always reads hidden +ProfilerPieChart.splitNodeName missing minor: profiler node names for the pie chart not ported +ProfilerPieChart.profilerPieChartKeyPress missing minor: profiler pie keys are not handled +TpsDebugChart.extractAdditionalLinesAndLabels port crates/horizon-client/src/debug_chart.rs::Chart::lines_and_labels +TpsDebugChart.getValueForAggregation port crates/horizon-client/src/debug_chart.rs::Chart::aggregated tick time without its idle part +TpsDebugChart.toDisplayString port crates/horizon-client/src/debug_chart.rs::Chart::display +TpsDebugChart.getSampleHeight port crates/horizon-client/src/debug_chart.rs::Chart::height +TpsDebugChart.getSampleColor port crates/horizon-client/src/debug_chart.rs::Chart::color diff --git a/tools/port_map/client2-012.tsv b/tools/port_map/client2-012.tsv new file mode 100644 index 0000000000..2855bf644f --- /dev/null +++ b/tools/port_map/client2-012.tsv @@ -0,0 +1,150 @@ +ContainerEventHandler.getChildAt port crates/horizon-client/src/pack_screen.rs::PackScreen::button_at gap: minor: says whether a button is under the pointer; list rows are hit-tested in PackScreen::click +ContainerEventHandler.mouseReleased port crates/horizon-client/src/pack_screen.rs::PackScreen::release gap: minor: only clears list scrollbar holds; no focused child gets the release +ContainerEventHandler.mouseDragged port crates/horizon-client/src/pack_screen.rs::PackScreen::drag drags a held list scrollbar +ContainerEventHandler.mouseScrolled port crates/horizon-client/src/pack_screen.rs::PackScreen::scroll list under the pointer scrolls 18 px per notch +ContainerEventHandler.keyPressed port crates/horizon-client/src/pack_screen.rs::PackScreen::shortcut gap: minor: only Ctrl+A/C/V/X here; Backspace is PackScreen::erase +ContainerEventHandler.keyReleased skip:other no widget in the pack screen acts on key release +ContainerEventHandler.charTyped port crates/horizon-client/src/pack_screen.rs::PackScreen::type_text typed text goes to the search box when it has focus +ContainerEventHandler.preeditUpdated missing minor: no IME composition (preedit) anywhere in Horizon +ContainerEventHandler.getBorderForArrowNavigation missing minor: arrow keys do not move focus; pack screen focus is set by clicks (PackScreen::set_focus) +ContainerEventHandler.getCurrentFocusPath missing minor: no focus path; PackScreen::focus is read only inside the screen +ContainerEventHandler.nextFocusPath missing minor: Tab and arrow keys do not move focus between search, lists and buttons +ContainerEventHandler.handleTabNavigation missing minor: Tab does not move focus (same gap as nextFocusPath) +ContainerEventHandler.handleArrowNavigation missing minor: arrow keys do not move focus (same gap) +ContainerEventHandler.nextFocusPathVaguelyInDirection missing minor: no directional focus search (same gap) +SpectatorGui.extractPage port crates/horizon-client/src/spectator_menu.rs::SpectatorGui::hotbar the nine-slot page with its frame and selection +SpectatorGui.extractSlot inline crates/horizon-client/src/spectator_menu.rs::SpectatorGui::hotbar icon and key label drawn in the hotbar loop +MenuTabBar.extractWidgetRenderState port crates/horizon-client/src/title/create_world.rs::Title::draw_create header separators and tab row drawn inside draw_create +MenuTabBar.Builder.addTab skip:java builder +MenuTabBar.Builder.addTabs skip:java builder +MenuTabBar.Builder.build skip:java builder +MenuTabBar.MenuTabButton.extractWidgetRenderState inline crates/horizon-client/src/title/create_world.rs::Title::draw_create tab loop: sprite by selected and hover state, underline, label +MenuTabBar.MenuTabButton.renderMenuBackground inline crates/horizon-client/src/title/create_world.rs::Title::draw_create menu background inside the selected tab +MenuTabBar.MenuTabButton.renderLabel inline crates/horizon-client/src/title/create_world.rs::Title::draw_create label centred, 3 px lower on unselected tabs +MenuTabBar.MenuTabButton.renderFocusUnderline inline crates/horizon-client/src/title/create_world.rs::Title::draw_create 1 px underline under the selected tab label +TabManager.setTabArea skip:ui tab area layout; create world computes the tab row on every draw (tabs()) +TabManager.setCurrentTab port crates/horizon-client/src/title.rs::Title::click a click on a tab sets options.tab +TabNavigationBar.arrangeElements skip:ui layout of the tab row +TabNavigationBar.isMouseOver port crates/horizon-client/src/title/create_world.rs::tab_at pointer over a tab +TabNavigationBar.setFocused missing minor: the tab row cannot take keyboard focus +TabNavigationBar.nextFocusPath missing minor: the Tab key never reaches the tab row +TabNavigationBar.narrationPriority skip:other screen-reader narration; Horizon has no narrator +TabNavigationBar.updateWidgetNarration skip:other screen-reader narration; Horizon has no narrator +TabNavigationBar.narrateListElementPosition skip:other screen-reader narration; Horizon has no narrator +TabNavigationBar.extractWidgetRenderState port crates/horizon-client/src/title/create_world.rs::Title::draw_create tab row drawn in draw_create +TabNavigationBar.selectTab port crates/horizon-client/src/title.rs::Title::click gap: minor: only clicks pick a tab; no select by index (Ctrl+number, init) +TabNavigationBar.setTabActiveState missing minor: tabs cannot be disabled; vanilla StatsScreen.java:91-93 disables two tabs, Horizon's stats tabs stay clickable +TabNavigationBar.setTabTooltip missing minor: the tab row has no per-tab tooltip +TabNavigationBar.keyPressed missing minor: Ctrl+1 to 9 and Ctrl+Tab do not switch create-world tabs +TabNavigationBar.getNextTabIndex missing minor: index math for Ctrl+number and Ctrl+Tab, which Horizon lacks +TabNavigationBar.currentTabIndex skip:other accessor; Horizon keeps the open tab in options.tab +TabNavigationBar.currentTabButton skip:other accessor; Horizon draws the tabs from options.tab +TabNavigationBar.Builder.addTab skip:java builder +TabNavigationBar.Builder.build skip:java builder +AdvancementToast.update port crates/horizon-client/src/game/advancements.rs::Toast::wants_show 5 s visible, scaled by the notification time option +AdvancementToast.isChallengeAdvancement inline crates/horizon-client/src/game/advancements.rs::Game::draw_toasts challenge check for the sound when a toast enters a slot +AdvancementToast.extractRenderState port crates/horizon-client/src/game/advancements.rs::Game::draw_toast gap: player: a long title is one line; vanilla wraps it and fades the frame label into it over 1.5 s +FriendToast.update skip:service friend toasts: social, Horizon has no friends system +FriendToast.extractRenderState skip:service friend toasts: social, Horizon has no friends system +FriendToast.add skip:service friend toasts: social, Horizon has no friends system +FriendToast.showFriendRequestSent skip:service friend toasts: social, Horizon has no friends system +FriendToast.showFriendRequestReceived skip:service friend toasts: social, Horizon has no friends system +FriendToast.showFriendRequestAccepted skip:service friend toasts: social, Horizon has no friends system +FriendToast.showFriendAdded skip:service friend toasts: social, Horizon has no friends system +NowPlayingToast.extractToast missing player: no Now Playing toast is drawn; with Music Toast on (default Never) the song name never shows +NowPlayingToast.tickMusicNotes missing player: the music notes beside the Now Playing toast are never drawn +NowPlayingToast.showToast missing player: nothing shows the toast when a song starts (same gap as extractToast) +NowPlayingToast.update missing player: the toast is never shown, so it never times out (same gap) +RecipeToast.update port crates/horizon-client/src/game/advancements.rs::Toast::wants_show recipe toast visibility; the cycling index is Toast::displayed_recipe +RecipeToast.extractRenderState port crates/horizon-client/src/game/advancements.rs::Game::draw_toast recipe branch: station at 0.6 scale over the cycling result +SystemToast.height missing minor: no multi-line height; a system toast always takes one 32 px slot +SystemToast.extractRenderState port crates/horizon-client/src/toasts.rs::SystemToast::draw gap: minor: one line per title and message, no wrapping +SystemToast.extractTextLines inline crates/horizon-client/src/toasts.rs::SystemToast::draw text lines drawn in draw +SystemToast.recalculateWidth inline crates/horizon-client/src/toasts.rs::SystemToast::draw width from the widest line, at least 160, plus 30 +SystemToast.onFileDropFailure missing minor: Horizon takes no dropped files, so this toast never shows +SystemToast.onChunkLoadFailure missing minor: no chunk load-failure toast; failures only log +SystemToast.onChunkSaveFailure missing minor: failed chunk saves only log (save.rs warn); no toast +ToastManager.findFreeSlotsIndex port crates/horizon-client/src/game/advancements.rs::Game::draw_toasts gap: minor: one 32 px slot per toast; no search for consecutive slots +ToastManager.getToast inline crates/horizon-client/src/game/recipe_book.rs::Game::recipe_toast only the recipe toast lookup (find is_recipe); system toasts are kept by their owners +ToastManager.showNowPlayingToast port crates/horizon-client/src/music.rs::MusicManager::show_now_playing_toast_if_needed gap: player: the request goes to toast_requests and nothing draws it +ToastManager.initializeMusicToast missing player: with Music Toast set to Pause or Pause and Toast (default Never) the Now Playing toast is never created +ToastManager.setMusicToastDisplayState missing player: the Music Toast setting does nothing in Horizon (same gap) +ToastManager.ToastInstance.resetToast port crates/horizon-client/src/toasts.rs::SystemToast::reset gap: minor: text and display timer restart; the slide-in does not +ToastManager.ToastInstance.calculateVisiblePortion port crates/horizon-client/src/toasts.rs::Slide::update 600 ms, squared ease in and out +ToastManager.ToastInstance.extractRenderState port crates/horizon-client/src/game/advancements.rs::Game::draw_toast slide x and slot y; system toasts slide in SystemToast::draw +TutorialToast.update missing player: new worlds show no movement or look tutorial toasts (vanilla tutorialStep defaults to movement) +TutorialToast.extractRenderState missing player: same gap; the tutorial toast is never drawn +AtlasGlyphProvider.Instance.renderSprite missing minor: sprite-font glyphs (atlas sprite objects in text) are not parsed +CodepointMap.get skip:java codepoint lookup table; Horizon uses HashMap and BTreeSet +CodepointMap.put skip:java codepoint lookup table; Horizon uses HashMap and BTreeSet +CodepointMap.computeIfAbsent skip:java codepoint lookup table; Horizon uses HashMap and BTreeSet +CodepointMap.remove skip:java codepoint lookup table; Horizon uses HashMap and BTreeSet +CodepointMap.forEach skip:java codepoint lookup table; Horizon uses HashMap and BTreeSet +CodepointMap.keySet skip:java codepoint lookup table; Horizon uses HashMap and BTreeSet +FontManager.createFontSet port crates/horizon-client/src/font.rs::Font::load_set a font's providers into a set +FontManager.reload port crates/horizon-client/src/hud.rs::HudAtlas::pack fonts are rebuilt with the atlas (Font::load at hud.rs:582); no async reload +FontManager.safeLoad inline crates/horizon-client/src/font.rs::Font::load_set a TrueType provider that fails to load is dropped; a bitmap sheet that fails warns +FontManager.resolveProviders port crates/horizon-client/src/font.rs::providers reference expansion in order, depth 8; no dependency sort +FontManager.finalizeProviderLoading skip:java prewarm of glyph lookups; the fallback is the missing box that Font::resolve gives +FontManager.getFontOptions port crates/horizon-client/src/game/options.rs::Game::apply_ui_settings force unicode and japanese flags read from settings +FontManager.apply port crates/horizon-client/src/hud.rs::HudAtlas::pack new font sets replace the old ones with the atlas +FontManager.updateOptions port crates/horizon-client/src/game/options.rs::Game::apply_ui_settings sets the Font uniform and japanese flags when they change (options.rs:299-301) +FontManager.loadResourceStack inline crates/horizon-client/src/font.rs::providers gap: minor: one fonts.json per font id from the resource root, not a stack of packs +FontManager.getSpriteFont missing minor: atlas sprite fonts (sprite objects in text) are not parsed; they draw the missing box +FontManager.close skip:java Rust drop frees the atlas, font sets and providers +FontManager.BuilderResult.resolve port crates/horizon-client/src/font.rs::providers reference builders expanded in place +FontManager.BuilderResult.mergeFilters inline crates/horizon-client/src/font.rs::providers the reference's filter merged onto each expanded provider +FontManager.CachedFontProvider.getGlyphSource port crates/horizon-client/src/font.rs::Font::glyph_in gap: minor: atlas sprite and player descriptions fall back to the missing glyph +FontManager.CachedFontProvider.glyphs skip:java one-entry lookup cache +FontManager.CachedFontProvider.effect port crates/horizon-client/src/font.rs::Font::white the white glyph under underline and strikethrough +FontManager.UnresolvedBuilderBundle.add skip:java collects builders; providers() reads them directly +FontManager.UnresolvedBuilderBundle.listBuilders skip:java collects builders +FontManager.UnresolvedBuilderBundle.resolve port crates/horizon-client/src/font.rs::providers reference expansion for the bundle +FontOption.Filter.apply port crates/horizon-client/src/font.rs::Filter::passes option match per provider +FontOption.Filter.merge inline crates/horizon-client/src/font.rs::providers filter merge while expanding references +FontSet.reload port crates/horizon-client/src/font.rs::Font::load_set providers into the font set +FontSet.resetTextures port crates/horizon-client/src/font.rs::Font::reset_cache glyph cache and atlas cursor cleared +FontSet.selectProviders port crates/horizon-client/src/font.rs::Font::glyphs_by_width gap: minor: width buckets hold only Space and atlas bitmap glyphs, so TrueType and unihex never appear as obfuscated characters +FontSet.hasFishyAdvance missing minor: no check for advances under 0 or over 32 +FontSet.computeGlyphInfo port crates/horizon-client/src/font.rs::Font::resolve gap: minor: no skip of a glyph with a fishy advance in favour of a later provider +FontSet.DelayedBake.get skip:java lazy bake into the atlas; Font::resolve bakes at once +FontTexture.dumpContents skip:debug writes the atlas to a PNG for debugging +GlyphRenderTypes.createForGrayscaleTexture skip:ui one textured quad path for all glyphs (hud.rs text_styled) +GlyphRenderTypes.createForColorTexture skip:ui one textured quad path for all glyphs (hud.rs text_styled) +GlyphRenderTypes.select port crates/horizon-client/src/game/names.rs::Game::name_tag_geometry gap: minor: normal and see-through only; polygon-offset text has no draw path +GlyphStitcher.reset port crates/horizon-client/src/font.rs::Font::reset_cache atlas cursor rewound +GlyphStitcher.stitch port crates/horizon-client/src/font.rs::Region::place rectangle placed in the atlas region +PlainTextRenderable.render port crates/horizon-client/src/hud.rs::HudBatch::text_styled shadow pass first, then the front text +PlayerGlyphProvider.Instance.renderSprite missing minor: player-head text objects are not parsed +PlayerGlyphProvider.Instance.renderQuad missing minor: no player-head glyphs (same gap) +TextFieldHelper.charTyped inline crates/horizon-client/src/game/sign_edit.rs::Game::sign_input typed-text branch; EditBox::insert_text filters the text +TextFieldHelper.moveBy inline crates/horizon-client/src/ui.rs::EditBox::key arrow keys; Ctrl moves by word +TextFieldHelper.moveByChars inline crates/horizon-client/src/ui.rs::EditBox::key arrow keys, Home and End +TextFieldHelper.moveByWords inline crates/horizon-client/src/ui.rs::EditBox::key Ctrl+arrow word moves +TextFieldHelper.removeFromCursor inline crates/horizon-client/src/ui.rs::EditBox::key Backspace and Delete +TextFieldHelper.removeWordsFromCursor inline crates/horizon-client/src/ui.rs::EditBox::key Ctrl+Backspace and Ctrl+Delete +TextFieldHelper.removeCharsFromCursor port crates/horizon-client/src/ui.rs::EditBox::delete_chars deletes the selection or n characters +TextFieldHelper.cut port crates/horizon-client-input/src/clipboard.rs::apply Cut arm: copy the selection, then clear it +TextFieldHelper.paste port crates/horizon-client-input/src/clipboard.rs::apply Paste arm: insert the clipboard text +TextFieldHelper.getSelected port crates/horizon-client/src/ui.rs::EditBox::selected_text +TextFieldHelper.deleteSelection inline crates/horizon-client/src/ui.rs::EditBox::delete_chars selection branch +TextFieldHelper.setCursorToEnd port crates/horizon-client/src/ui.rs::EditBox::move_cursor_to_end +TextFieldHelper.setCursorPos port crates/horizon-client/src/ui.rs::EditBox::set_cursor no vanilla caller (callers 0) +TextFieldHelper.setSelectionRange skip:other no vanilla caller; Horizon sets cursor and highlight separately (EditBox::set_cursor) +BakedSheetGlyph.left inline crates/horizon-client/src/hud.rs::HudBatch::text_styled glyph edge with bold thickness and italic shear +BakedSheetGlyph.top inline crates/horizon-client/src/hud.rs::HudBatch::text_styled glyph edge with bold thickness and italic shear +BakedSheetGlyph.right inline crates/horizon-client/src/hud.rs::HudBatch::text_styled glyph edge with bold thickness and italic shear +BakedSheetGlyph.bottom inline crates/horizon-client/src/hud.rs::HudBatch::text_styled glyph edge with bold thickness and italic shear +BakedSheetGlyph.renderChar inline crates/horizon-client/src/hud.rs::HudBatch::text_styled shadow pass, bold double draw, italic shear +BakedSheetGlyph.render inline crates/horizon-client/src/hud.rs::HudBatch::text_styled glyph quad with shear and bold +BakedSheetGlyph.renderEffect inline crates/horizon-client/src/hud.rs::HudBatch::text_styled underline and strikethrough rectangles +BakedSheetGlyph.buildEffect inline crates/horizon-client/src/hud.rs::HudBatch::text_styled rectangle quad of an effect +BakedSheetGlyph.createGlyph skip:java factory for a glyph instance +BakedSheetGlyph.createEffect skip:java factory for an effect instance +BakedSheetGlyph.EffectInstance.right inline crates/horizon-client/src/hud.rs::HudBatch::text_styled underline end: pen x plus advance +BakedSheetGlyph.EffectInstance.bottom inline crates/horizon-client/src/hud.rs::HudBatch::text_styled underline row, 1 px at pen y plus 8 +BakedSheetGlyph.GlyphInstance.activeRight inline crates/horizon-client/src/hud.rs::HudBatch::text_styled advance including the bold offset +SpecialGlyphs.generate port crates/horizon-client/src/font.rs::Font::white gap: minor: white texel only; the missing-box outline is drawn in HudBatch::text_styled, not generated +BitmapProvider.Definition.validateDimensions missing minor: no check of the bitmap grid; a ragged grid loads +BitmapProvider.Definition.validate missing minor: no check that ascent is at most height +BitmapProvider.Definition.getActualGlyphWidth port crates/horizon-client/src/font.rs::used_width rightmost column with any visible pixel diff --git a/tools/port_map/client2-013.tsv b/tools/port_map/client2-013.tsv new file mode 100644 index 0000000000..4b9b73c566 --- /dev/null +++ b/tools/port_map/client2-013.tsv @@ -0,0 +1,109 @@ +BitmapProvider.ImageDataHolder.gpuData skip:other GPU staging buffer for a sheet; Horizon copies the sheet pixels into its atlas at load (font.rs load_bitmap) +FreeTypeUtil skip:other FreeType library wrapper (getLibrary, error helpers, setVector, destroy); Horizon rasterizes TrueType with ab_glyph (font.rs load_true_type) +UnihexProvider.unpackBitsToBytes port crates/horizon-client/src/font.rs::Font::rasterize_hex gap: minor: rows are written as RGBA into the atlas region, not an IntBuffer; same glyph columns +UnihexProvider.decodeHex port crates/horizon-client/src/font.rs::parse_hex_digit gap: minor: a bad digit skips the whole glyph line (vanilla throws and fails the load) +UnihexProvider.copyUntil inline crates/horizon-client/src/font.rs::parse_hex line splitting on newline and CR; the .hex text is parsed in one pass +UnihexProvider.ByteContents.read inline crates/horizon-client/src/font.rs::parse_hex 8 px rows (32 hex digits) read by parse_hex +UnihexProvider.Definition.loadData port crates/horizon-client/src/font.rs::load_hex reads every .hex entry of the zip and applies size_overrides +UnihexProvider.Dimensions.pack skip:java packs left and right into one int as a map value; Horizon keeps two fields on HexGlyph +UnihexProvider.IntContents.read24 port crates/horizon-client/src/font.rs::parse_hex 6-digit rows (24 bit) handled by parse_hex +UnihexProvider.IntContents.read32 port crates/horizon-client/src/font.rs::parse_hex 8-digit rows (32 bit) handled by parse_hex +UnihexProvider.LineData.mask inline crates/horizon-client/src/font.rs::parse_hex the OR of the rows, used for the glyph width (mask |= row) +UnihexProvider.ShortContents.read port crates/horizon-client/src/font.rs::parse_hex 4-digit rows (16 bit) handled by parse_hex +AbstractLayout.setX skip:ui moves a layout's children with it; Horizon computes positions each frame (dialog_screen.rs Layout::new) +AbstractLayout.setY skip:ui moves a layout's children with it; Horizon computes positions each frame (dialog_screen.rs Layout::new) +AbstractLayout.AbstractChildWrapper.getHeight skip:ui padded child height, layout bookkeeping only +AbstractLayout.AbstractChildWrapper.getWidth skip:ui padded child width, layout bookkeeping only +AbstractLayout.AbstractChildWrapper.setX inline crates/horizon-client/src/dialog_screen.rs::Layout::new gap: minor: only centred alignment (0.5) is modelled; xAlignment and per-child padding are not +AbstractLayout.AbstractChildWrapper.setY inline crates/horizon-client/src/dialog_screen.rs::Layout::new gap: minor: only centred alignment (0.5) is modelled; yAlignment and per-child padding are not +EqualSpacingLayout skip:ui equal-gap layout; its only user is WorldOptionsScreen, placing its buttons; layout only, no behaviour +EqualSpacingLayout.Orientation skip:ui horizontal or vertical helpers of EqualSpacingLayout; layout only +FrameLayout.arrangeElements inline crates/horizon-client/src/dialog_screen.rs::Layout::new gap: minor: frame centring is done for the header title and the footer buttons only; no general minimum size +FrameLayout.addChild skip:ui adds a child to the frame's list; no behaviour of its own +FrameLayout.visitChildren skip:ui visits children for layout; no behaviour of its own +FrameLayout.centerInRectangle port crates/horizon-client/src/dialog_screen.rs::centre gap: minor: one axis at a time through centre; the 2D call is not a separate fn +FrameLayout.alignInRectangle inline crates/horizon-client/src/dialog_screen.rs::Layout::new gap: minor: only the centre alignment (0.5); PauseScreen's 0.25 vertical alignment is not modelled +GridLayout.arrangeElements port crates/horizon-client/src/dialog_screen.rs::columns gap: minor: only the dialog button grids (2 px apart, 22 px rows); general row and column spans and spacing are not modelled +GridLayout.addChild skip:ui adds a cell to the grid's list; no behaviour of its own +GridLayout.visitChildren skip:ui visits children for layout; no behaviour of its own +GridLayout.RowHelper.addChild inline crates/horizon-client/src/dialog_screen.rs::columns gap: minor: rows fill left to right and a full row is cut from the list by columns(); a row that does not fit moves down, as RowHelper does +HeaderAndFooterLayout.visitChildren skip:ui visits children for layout; no behaviour of its own +HeaderAndFooterLayout.arrangeElements port crates/horizon-client/src/dialog_screen.rs::Layout::new header 33 high, body at header + 30 clamped above the footer, footer at the bottom; server_links.rs list_top and lan_screen.rs WorldOptions::top do the same for their screens +HeaderAndFooterLayout.addToHeader skip:ui adds to the header frame; no behaviour of its own +HeaderAndFooterLayout.addToFooter skip:ui adds to the footer frame; no behaviour of its own +HeaderAndFooterLayout.addToContents skip:ui adds to the contents frame; no behaviour of its own +Layout.arrangeElements skip:ui default recursive layout pass; Horizon places each screen's parts directly +LayoutElement.getRectangle skip:ui builds a rectangle from position and size; Horizon uses [f32; 4] arrays +LayoutSettings.LayoutSettingsImpl.padding skip:java builder method for padding values +LinearLayout.addChild skip:ui queues a child into a GridLayout; positions come from the layout pass (dialog_screen.rs Layout::new body column) +LinearLayout.Orientation.setSpacing skip:ui sets the grid spacing of a linear layout; layout only +LinearLayout.Orientation.addChild skip:ui places a child in the grid row or column; layout only +NarrationElementOutput skip:other screen-reader narration; Horizon has no narration output +NarrationThunk skip:other screen-reader narration text; Horizon has no narration output +ScreenNarrationCollector skip:other screen-reader narration collection; Horizon has no narration output +ScreenNarrationCollector.NarrationEntry skip:other screen-reader narration entries; Horizon has no narration output +ScreenNarrationCollector.Output skip:other screen-reader narration output; Horizon has no narration output +ScreenAxis missing minor: keyboard focus moves along the axis (arrow keys, Tab) between widgets; Horizon's menus have no widget focus navigation, Tab only moves between title fields (title.rs tab) +ScreenDirection missing minor: keyboard focus navigation by direction (arrow keys); Horizon's menus have no widget focus navigation +ScreenPosition missing minor: grid position stepping for keyboard focus navigation; not in Horizon's menus +ScreenRectangle.of skip:java builds a rectangle from an axis-ordered index; Horizon uses x and y directly +ScreenRectangle.step missing minor: moves a rectangle one step for keyboard focus navigation; not in Horizon's menus +ScreenRectangle.getLength skip:java width or height accessor +ScreenRectangle.getBoundInDirection missing minor: edge coordinate for keyboard focus navigation; not in Horizon's menus +ScreenRectangle.getBorder missing minor: border strip used by keyboard focus navigation to find the next widget; not in Horizon's menus +ScreenRectangle.overlaps missing minor: overlap test used by keyboard focus navigation; not in Horizon's menus +ScreenRectangle.overlapsInAxis missing minor: per-axis overlap used by keyboard focus navigation; not in Horizon's menus +ScreenRectangle.getCenterInAxis missing minor: centre along one axis for keyboard focus navigation; not in Horizon's menus +ScreenRectangle.intersection missing minor: a nested GUI clip is not intersected with the outer one; paint (screen_draw.rs) replaces the clip on each Scissor draw +ScreenRectangle.intersects skip:other no caller in the 26.3 client GUI code; pure geometry predicate +ScreenRectangle.encompasses skip:other used by GuiRenderState for element batching bounds; Horizon appends quads to the HUD batch without it +ScreenRectangle.containsPoint port crates/horizon-client/src/screen_draw.rs::inside the same half-open point-in-rectangle test +ScreenRectangle.transformAxisAligned skip:other bounds of a rectangle under a GUI pose, for the GUI scissor; Horizon clips the quads themselves (hud.rs HudBatch::clip_since) +ScreenRectangle.transformMaxBounds skip:other bounds of a rotated rectangle under a GUI pose; Horizon clips the quads themselves (hud.rs HudBatch::clip_since) +DynamicAtlasAllocator skip:other slot allocator for the GUI item atlas; Horizon has no item atlas and draws items into the HUD batch (item_render/gui.rs) +GuiItemAtlas.computeTextureSizeFor skip:other atlas texture sizing; Horizon has no item atlas +GuiItemAtlas.tryPrepareFor skip:other atlas space check; Horizon has no item atlas +GuiItemAtlas.getOrUpdate inline crates/horizon-client/src/item_render/gui.rs::draw gap: minor: vanilla draws each item once into an atlas slot and blits it; Horizon re-submits the item and caches its vertices by position (gui_drawn, DrawnKey) +GuiItemAtlas.drawToSlot port crates/horizon-client/src/item_render/gui.rs::draw_now the item is clipped to its 16x16 slot unless oversized +GuiItemAtlas.close skip:other GPU texture release; Horizon has no item atlas +GuiRenderer.render inline crates/horizon-client/src/screen_draw.rs::paint gap: minor: paint has no panorama, blur or GPU upload pass; the frame is one ordered list of draws +GuiRenderer.clearUnusedOversizedItemRenderers skip:other GPU renderer cache cleanup; Horizon's gui_drawn cache is bounded (DRAWN_LIMIT) +GuiRenderer.prepare inline crates/horizon-client/src/screen_draw.rs::paint gap: minor: the prepare step is folded into paint's single pass; no separate mesh build +GuiRenderer.addElementsToMeshes skip:other vertex batching by pipeline and scissor; HudBatch appends quads in order +GuiRenderer.executeDrawRange port crates/horizon-client/src/hud.rs::HudRenderer::draw_range draws a range of the batch with its blend, depth and scissor state +GuiRenderer.addElementToMesh skip:other vertex batching by pipeline and scissor; HudBatch appends quads in order +GuiRenderer.prepareText inline crates/horizon-client/src/screen_draw.rs::paint text is drawn at its own pose through Draw::Text (batch.text_styled) +GuiRenderer.prepareItemElements port crates/horizon-client/src/item_render/gui.rs::draw gap: minor: oversized items are drawn unclipped into the HUD batch, not into a picture-in-picture texture +GuiRenderer.preparePictureInPicture skip:other Horizon calls each picture drawer directly (game/perspective.rs draw_player_preview, item_render/gui.rs draw_banner_result); no pool dispatch +GuiRenderer.preparePictureInPictureState skip:other picture-in-picture pool dispatch; Horizon calls each drawer directly +GuiRenderer.submitBlitFromItemAtlas inline crates/horizon-client/src/item_render/gui.rs::draw the atlas slot blit is the item drawn at its GUI position +GuiRenderer.prepareItemAtlas skip:other item atlas creation and resize; Horizon has no item atlas +GuiRenderer.getGuiScaleInvalidatingItemAtlasIfChanged skip:other item atlas invalidation on GUI scale change; Horizon has no item atlas +GuiRenderer.executeDraw port crates/horizon-client/src/hud.rs::HudRenderer::draw_range one pipeline, vertex buffer, scissor and index range draw +GuiRenderer.scissorChanged skip:other batching compare of scissor rectangles +GuiRenderer.enableScissor port crates/horizon-client/src/hud.rs::HudBatch::clip_since gap: minor: the rectangle arrives already intersected with the outer scissor (GuiGraphicsExtractor); a nested clip replaces the outer one in paint +GuiRenderer.close skip:other GPU resource release +TextureSetup.singleTextureWithLightmap skip:other GPU texture binding with the lightmap; Horizon's lighting is in the mesh +TextureSetup.getSortKey skip:other draw sort key for batching; Horizon appends in order +GuiBookModelRenderer.renderToTexture skip:other no GUI caller in the 26.3 sources (graphics.bookModel is never called) +GuiEntityRenderer.renderToTexture port crates/horizon-client/src/game/perspective.rs::Game::draw_player_preview gap: player: the smithing-table armor stand preview (SmithingScreen) is not drawn; only the player model +GuiProfilerChartRenderer.renderToTexture skip:debug the profiler pie chart (F3 profiler screen) +GuiSkinRenderer.renderToTexture missing player: Options > Skin Customisation has no rotating player model; options_ui.rs lists that entry without a page +OversizedItemRenderer.renderToTexture port crates/horizon-client/src/item_render/gui.rs::draw gap: minor: oversized items are drawn unclipped into the HUD batch, not via a picture-in-picture texture +OversizedItemRenderer.textureIsReadyToBlit skip:other cache check for the picture-in-picture texture; Horizon redraws from the item model +PictureInPictureRenderer.blitTexture skip:other picture-in-picture texture blit; Horizon draws each preview into the batch directly (draw_player_preview, draw_banner_result) +PictureInPictureRenderer.prepareTexturesAndProjection skip:other GPU texture allocation for a picture-in-picture +PictureInPictureRenderer.close skip:other GPU texture release +AccessibilityOnboardingScreen.init missing player: first launch shows the accessibility onboarding (onboardAccessibility defaults to true in options_file.rs) with narrator, Sounds, Accessibility, Language and Continue; Horizon has no such screen (options_fixes.rs only turns it off for old files) +AccessibilityOnboardingScreen.setInitialFocus missing minor: keyboard focus starts on the narrator button; part of the missing onboarding screen +AccessibilityOnboardingScreen.closeAndSetScreen missing player: the Sounds, Accessibility and Language buttons on the onboarding screen; part of the missing screen +AccessibilityOnboardingScreen.extractRenderState missing player: the onboarding screen fades in over 2 s, fades out on Continue, and draws the logo +AccessibilityOnboardingScreen.handleInitialNarrationDelay skip:other screen-reader narration; Horizon has no narration output +AlertScreen missing unsure: no alert screen (title, message, OK) found in Horizon; vanilla callers are world-open, recovery and pack errors (WorldOpenFlows.java, RecoverWorldDataScreen.java, PackSelectionScreen.java) +BackupConfirmScreen missing unsure: no backup question before opening a world that needs one (WorldOpenFlows.java:375, EditWorldScreen.java:111); Horizon's backup is only Make Backup (title.rs) +BanNoticeScreens missing unsure: no ban or suspension notice for a multiplayer ban, forced name change or banned skin; Horizon reads no BanDetails or profile action (Gui.java addInitialScreens) +ChatScreen.removed missing player: unfinished chat text is kept when the chat is interrupted or with Save Chat Drafts on; Horizon's close_chat (game/chat.rs) drops it +ChatScreen.shouldDiscardDraft missing player: decides whether closing chat keeps the typed text as a draft; Horizon has no draft state +ChatScreen.insertText port crates/horizon-client/src/chat.rs::ChatScreen::type_text insert at the cursor; replace=true (setValue) is ChatScreen::suggest_command in Horizon +ChatScreen.formatChat missing minor: a restored draft is drawn grey italic; Horizon has no draft state (see ChatScreen.removed) +ChatScreen.updateNarrationState skip:other screen-reader narration; Horizon has no narration output diff --git a/tools/port_map/client2-014.tsv b/tools/port_map/client2-014.tsv new file mode 100644 index 0000000000..3521027706 --- /dev/null +++ b/tools/port_map/client2-014.tsv @@ -0,0 +1,150 @@ +ConfirmLinkScreen.confirmMessage port crates/horizon-client/src/game/server_links.rs::confirm_link_layout gap: minor: no separate trusted text; the untrusted text is always used +ConfirmLinkScreen.addAdditionalText port crates/horizon-client/src/game/server_links.rs::confirm_link_layout gap: minor: the red warning is always drawn, vanilla draws it only for untrusted links +ConfirmLinkScreen.addButtons port crates/horizon-client/src/game/server_links.rs::Game::confirm_link_click Yes opens the browser, Copy Link copies, No goes back; 100 wide +ConfirmLinkScreen.confirmLinkNow port crates/horizon-client/src/game/server_links.rs::Game::click_link opens the prompt when Prompt on Links is on; gap: minor: the trusted flag is not passed +ConfirmLinkScreen.confirmLink inline crates/horizon-client/src/game/server_links.rs::Game::click_link the button's action, folded into click_link +ConfirmScreen.getNarrationMessage skip:other screen-reader narration; Horizon has no narrator +ConfirmScreen.addMessage port crates/horizon-client/src/confirm_screen.rs::Confirm::new message wrapped to width - 50, 15 rows at most, centred +ConfirmScreen.addButtons port crates/horizon-client/src/confirm_screen.rs::Confirm::answer_at Yes and No buttons; the answer comes from the click position +ConfirmScreen.tick inline crates/horizon-client/src/confirm_screen.rs::Confirm::draw_active the 20-tick delay is a 1000 ms timer kept by the caller; buttons are greyed while it runs +ConnectScreen.connect port crates/horizon-client/src/net.rs::Session::connect background connect on its own thread (Session::start) +ConnectScreen.tick port crates/horizon-client/src/game/remote.rs::Game::tick_remote ticks the pending session while it opens; game.rs tick sends remote sessions here +ConnectScreen.init missing player: no Cancel button while connecting, only the status line; unsure whether Escape aborts the join +ConnectScreen.extractRenderState port crates/horizon-client/src/game/remote.rs::Game::remote_overlay gap: minor: also draws the address line, vanilla draws only the status +CreateBuffetWorldScreen.init missing minor: the buffet biome picker is not in Horizon's create-world flow +CreateBuffetWorldScreen.repositionElements missing minor: layout of the buffet biome picker, which Horizon lacks +CreateBuffetWorldScreen.updateButtonValidity missing minor: Done needs a biome in the buffet picker, which Horizon lacks +CreateBuffetWorldScreen.BiomeList.filterEntries missing minor: biome search of the buffet picker, which Horizon lacks +CreateBuffetWorldScreen.BiomeList.setSelected missing minor: biome selection of the buffet picker, which Horizon lacks +CreateBuffetWorldScreen.BiomeList.Entry.extractContent missing minor: biome row of the buffet picker, which Horizon lacks +CreateFlatWorldScreen.setConfig port crates/horizon-client/src/title/flat.rs::State::apply_share applies the preset or typed share text as the layers; gap: minor: the rows are not rebuilt separately +CreateFlatWorldScreen.init port crates/horizon-client/src/title/flat.rs::buttons Remove Layer, Presets, Done and Cancel buttons +CreateFlatWorldScreen.repositionElements skip:ui layout only; the list is laid out each frame in flat.rs::list_area +CreateFlatWorldScreen.hasValidSelection inline crates/horizon-client/src/title/flat.rs::buttons Remove Layer is enabled only with a layer selected +CreateFlatWorldScreen.DetailsList.populateList inline crates/horizon-client/src/title/flat.rs::draw_layers the layer rows are read from the settings each frame +CreateFlatWorldScreen.DetailsList.resetRows inline crates/horizon-client/src/title/flat.rs::draw_layers nothing to rebuild; rows are drawn from the settings each frame +CreateFlatWorldScreen.DetailsList.deleteLayer port crates/horizon-client/src/title/flat.rs::State::remove_selected removes the selected layer; gap: minor: the next selection after a delete is not checked +CreateFlatWorldScreen.DetailsList.HeaderEntry.extractContent port crates/horizon-client/src/title/flat.rs::draw_layers Layer and Height column titles +CreateFlatWorldScreen.DetailsList.HeaderEntry.getNarration skip:other narration text; Horizon has no narrator +CreateFlatWorldScreen.DetailsList.LayerEntry.extractContent port crates/horizon-client/src/title/flat.rs::draw_layers slot, block name and the Top, Bottom or height label +CreateFlatWorldScreen.DetailsList.LayerEntry.getDisplayItem port crates/horizon-client/src/title/flat.rs::layer_icon water and lava use the bucket sprites +CreateFlatWorldScreen.DetailsList.LayerEntry.blitSlot port crates/horizon-client/src/title/flat.rs::slot slot frame and icon +CreditsAndAttributionScreen.init port crates/horizon-client/src/options_ui.rs::OptionsUi::layout Credits page: Credits, Attribution and Licenses buttons; gap: unsure whether Attribution and Licenses open links here +CreditsAndAttributionScreen.openCreditsScreen port crates/horizon-client/src/game/win.rs::Game::open_win_screen rolls the credits without the poem, then returns here +DatapackLoadFailureScreen.init missing minor: Horizon loads no data packs, so there is no datapack failure screen +DatapackLoadFailureScreen.extractRenderState missing minor: same as init, no datapack failure screen +DeathScreen.exitToTitleScreen port crates/horizon-client/src/game/screens.rs::Game::death_confirm_answer Yes leaves the world; gap: minor: the leave and title hand-off is one close_world call +DeathScreen.extractRenderState port crates/horizon-client/src/game/screens.rs::Game::draw_death_screen gap: minor: no draft-report badge on the title button (reporting service) +DeathScreen.visitText port crates/horizon-client/src/game/screens.rs::Game::draw_death_screen title at double size, cause and score lines +DeathScreen.mouseClicked port crates/horizon-client/src/game/screens.rs::Game::death_click buttons and the title confirm; gap: minor: a click on a link in the death text does nothing +DeathScreen.tick inline crates/horizon-client/src/game/screens.rs::Game::draw_death_screen the 20-tick delay is checked as 1000 ms since death, on draw and on click +DeathScreen.setButtonsActive inline crates/horizon-client/src/game/screens.rs::Game::draw_death_screen buttons are active once the 1000 ms delay has passed +DirectJoinServerScreen.keyPressed port crates/horizon-client/src/game/startup_menu.rs::StartupMenu::title_key gap: player: Enter in the Direct Connection field does not join; title_key only confirms a rename +DirectJoinServerScreen.init port crates/horizon-client/src/multiplayer.rs::Screen::buttons Direct Connection field with Join and Cancel (Screen::Direct arm) +DirectJoinServerScreen.resize skip:ui layout only; Screen::Direct keeps the typed address +DirectJoinServerScreen.onSelect port crates/horizon-client/src/multiplayer.rs::Screen::click Join returns the typed address as Outcome::Connect +DirectJoinServerScreen.removed port crates/horizon-client/src/title.rs::Title::remember_direct_address saves the address to last_server; gap: minor: the options file write is not in this fn +DirectJoinServerScreen.extractRenderState port crates/horizon-client/src/multiplayer.rs::Screen::draw title, label and address field (Screen::Direct arm) +DisconnectedScreen.init port crates/horizon-client/src/multiplayer.rs::Screen::buttons reason text and the disconnected buttons; gap: minor: no Report to Server or open-report-folder button +DisconnectedScreen.getNarrationMessage skip:other narration text; Horizon has no narrator +ErrorScreen.init port crates/horizon-client/src/title.rs::Title::buttons Cancel button of the error mode (Mode::Error arm) +ErrorScreen.extractRenderState port crates/horizon-client/src/title.rs::Title::draw the error title and message (Mode::Error arm) +ErrorScreen.extractBackground port crates/horizon-client/src/title.rs::Title::draw gap: minor: the flat gradient is replaced by the title background +FaviconTexture.forWorld port crates/horizon-client/src/title.rs::Title::icon the world row's icon texture +FaviconTexture.forServer port crates/horizon-client/src/multiplayer.rs::ping_all server icon keyed by address, from the ping +FaviconTexture.clear inline crates/horizon-client/src/multiplayer.rs::ping_all icon dropped when the ping has no favicon +FileFixerAbortedScreen.getNarrationMessage skip:other narration text; Horizon has no narrator +FileFixerAbortedScreen.init missing minor: no upgrade-aborted screen; Horizon has no world-fixer cancel flow +FileFixerAbortedScreen.addButtons missing minor: Back and Report Bug buttons of the aborted-upgrade screen +FileFixerAbortedScreen.keyPressed missing minor: Escape on the aborted-upgrade screen +GenericMessageScreen.init port crates/horizon-client/src/level_loading.rs::draw_progress centred message line while a world opens; gap: minor: no progress bar, the text is the loader's stage line +GenericMessageScreen.repositionElements skip:ui layout only +GenericMessageScreen.extractBackground port crates/horizon-client/src/ui.rs::menu_background the menu background behind the message +GenericWaitingScreen.createWaitingWithoutButton port crates/horizon-client/src/title.rs::Title::start_backup backup runs in the background; gap: minor: no waiting screen blocks input while it runs +GenericWaitingScreen.createWaiting skip:service waiting screen while a report is sent (reporting) +GenericWaitingScreen.createCompleted skip:service result screen after a report (reporting) +GenericWaitingScreen.init skip:service layout of the report waiting screen (reporting) +GenericWaitingScreen.tick skip:service the report button's delay (reporting) +GenericWaitingScreen.extractRenderState missing minor: no waiting text while a backup runs; Horizon shows a toast (title.rs::Title::update_toast) +GenericWaitingScreen.shouldCloseOnEsc skip:service Escape on the report waiting screen (reporting) +GenericWaitingScreen.getNarrationMessage skip:other narration text; Horizon has no narrator +InBedChatScreen.init port crates/horizon-client/src/game/chat.rs::Game::draw_chat_screen Leave Bed button over the chat; its click is in chat_click +InBedChatScreen.onPlayerWokeUp port crates/horizon-client/src/game/chat.rs::Game::tick_in_bed_chat closes the chat, or turns it into the plain chat if text was typed; gap: minor: polled each tick, not on the wake event +LevelLoadingScreen.updateNarratedWidget skip:other narration text; Horizon has no narrator +LevelLoadingScreen.extractChunksForRendering port crates/horizon-client/src/level_loading.rs::draw chunk status colours; gap: minor: the debug red centre square is not drawn +LevelLoadingScreen.getNetherPortalSprite port crates/horizon-client/src/hud_overlays.rs::portal_overlay draws the block/nether_portal sprite +LoadingOverlay.extractRenderState port crates/horizon-client/src/game.rs::Game::render the reload screen's progress bar; gap: player: no Mojang logo and no fade in or out +LoadingOverlay.tick port crates/horizon-client/src/game/server_packs.rs::Game::tick_server_packs finishes the reload; gap: minor: no fade-out before it finishes +LoadingOverlay.isReadyToFadeOut missing minor: no one-second fade-in before the reload can finish +LoadingOverlay.extractProgressBar port crates/horizon-client/src/level_loading.rs::draw_progress 200 by 2 bar; gap: minor: no fade alpha +LoadingOverlay.LogoTexture.loadContents skip:other Horizon ships no Mojang logo (title_edition.rs) +ManageServerScreen.init port crates/horizon-client/src/multiplayer.rs::Screen::buttons name and address fields, resource packs cycle, Done and Cancel (Screen::Edit arm) +ManageServerScreen.resize skip:ui layout only; Screen::Edit keeps its fields +ManageServerScreen.onAdd port crates/horizon-client/src/multiplayer.rs::Screen::click Done saves the name (default if empty) and the address +ManageServerScreen.extractRenderState port crates/horizon-client/src/multiplayer.rs::Screen::draw labels and fields (Screen::Edit arm) +MenuScreens.create port crates/horizon-client/src/game/remote.rs::Game::remote_open_screen server-opened menus; gap: minor: the screen is built from the packet title +MenuScreens.register skip:data registry of menu types; Horizon reads them from tables +MenuScreens.selfTest skip:debug startup check that every menu has a screen +MenuScreens.ScreenConstructor.fromPacket port crates/horizon-client/src/game/remote.rs::Game::remote_open_screen builds the screen from the open-screen packet and sets the container menu +NoticeWithLinkScreen.init missing minor: no symlink warning when a world or pack folder is a symlink +NoticeWithLinkScreen.getNarrationMessage skip:other narration text; Horizon has no narrator +OutOfMemoryScreen.init missing minor: unsure: no out-of-memory screen; Horizon does not handle allocation failures this way +PauseScreen.added skip:service friend list listener on the pause screen (social) +PauseScreen.init port crates/horizon-client/src/game/screens.rs::pause_buttons button layout; the title is drawn in draw_pause_menu +PauseScreen.createPauseMenu port crates/horizon-client/src/game/screens.rs::pause_buttons gap: player: Report Bugs, Feedback and Player Reporting icons are inactive, and no Mods button +PauseScreen.getCustomAdditions port crates/horizon-client/src/game/screens.rs::pause_buttons the server links row (links flag); gap: minor: only the server links dialog, the pause_screen_additions tag is not read +PauseScreen.addCustomDialogButtons port crates/horizon-client/src/game/screens.rs::Game::pause_click Server Links button (id 11) opens the links screen +PauseScreen.extractRenderState port crates/horizon-client/src/game/screens.rs::Game::draw_pause_menu gap: minor: no now-playing toast and no draft-report badge +PauseScreen.rendersNowPlayingToast missing minor: unsure: no check of the music-toast option on the pause screen +PauseScreen.openScreenButton inline crates/horizon-client/src/game/screens.rs::Game::pause_click the Advancements, Stats, Options and World Options actions +PresetFlatWorldScreen.getLayerInfoFromString inline crates/horizon-client/src/title/flat.rs::FlatSettings::parse one layer of the share text, parsed in the same loop +PresetFlatWorldScreen.getLayersInfoFromString port crates/horizon-client/src/title/flat.rs::FlatSettings::parse the share text's layers and biome; malformed text gives None +PresetFlatWorldScreen.save port crates/horizon-client/src/title/flat.rs::FlatSettings::to_share the share text +PresetFlatWorldScreen.init inline crates/horizon-client/src/title/flat.rs::buttons the share box and Select and Cancel buttons +PresetFlatWorldScreen.resize skip:ui layout only; the screen keeps its state +PresetFlatWorldScreen.extractRenderState port crates/horizon-client/src/title/flat.rs::draw_presets title, labels and share box +PresetFlatWorldScreen.updateButtonValidity inline crates/horizon-client/src/title/flat.rs::buttons Select is enabled with a preset or a share text longer than 1 +PresetFlatWorldScreen.PresetsList.setSelected inline crates/horizon-client/src/title/flat.rs::State::select_preset selecting a preset fills the share text +PresetFlatWorldScreen.PresetsList.keyPressed missing minor: unsure: no keyboard selection of a preset found in title/flat.rs +PresetFlatWorldScreen.PresetsList.Entry.extractContent port crates/horizon-client/src/title/flat.rs::draw_presets preset icon and name +PresetFlatWorldScreen.PresetsList.Entry.select port crates/horizon-client/src/title/flat.rs::State::select_preset fills the share text with the preset's layers +PresetFlatWorldScreen.PresetsList.Entry.blitSlot port crates/horizon-client/src/title/flat.rs::slot slot frame and icon +PrivacyConfirmLinkScreen.init skip:service privacy link confirm from the friends and online settings (social) +PrivacyConfirmLinkScreen.addAdditionalText skip:service privacy link confirm from the friends and online settings (social) +PrivacyConfirmLinkScreen.confirmLinkNow skip:service opens privacy links from the friends and online settings (social) +ProgressScreen.extractRenderState port crates/horizon-client/src/game/saving.rs::Game::render_closing_save stage line and bar while the world saves; gap: minor: no header or percent text +RecoverWorldDataScreen. missing minor: no Recover World screen; save.rs only reads level.dat_old as a fallback +RecoverWorldDataScreen.attemptRestore missing minor: no restore from level.dat_old in the menu +RecoverWorldDataScreen.buildInfo missing minor: no issue summary for level.dat and level.dat_old +RecoverWorldDataScreen.collectIssue missing minor: no issue check for level.dat and level.dat_old in the menu +RecoverWorldDataScreen.repositionElements missing minor: layout of the Recover World screen, which Horizon lacks +RecoverWorldDataScreen.getNarrationMessage skip:other narration text; Horizon has no narrator +Screen.extractRenderStateWithTooltipAndSubtitles port crates/horizon-client/src/game.rs::Game::hud_layers draws the current screen's layer and tooltips; unsure about subtitles +Screen.setInitialFocus skip:ui keyboard focus starts on the first widget; Horizon draws its own focus +Screen.isInputCaptured port crates/horizon-client/src/title.rs::Title::has_caret_field gap: minor: only the title screen's text fields +Screen.addWidget skip:ui widget list plumbing; Horizon draws each screen from its own state +Screen.removeWidget skip:ui widget list plumbing; Horizon draws each screen from its own state +Screen.getTooltipFromItem port crates/horizon-client/src/item_tooltip.rs::lines tooltip lines for an item +Screen.defaultHandleGameClickEvent port crates/horizon-client/src/game/books.rs::Game::book_follow_link gap: minor: ShowDialog and custom click events are not handled here +Screen.fadeWidgets missing minor: unsure: no fade of widgets found in title.rs +Screen.extractPanorama port crates/horizon-client/src/gfx/panorama.rs::Panorama::draw the title panorama cube, drawn in the render pass +Screen.extractMenuBackground port crates/horizon-client/src/ui.rs::menu_background the dirt background with blur +Screen.extractMenuBackgroundTexture port crates/horizon-client/src/ui.rs::menu_background gap: minor: only the standard background texture +Screen.isValidCharacterForName missing minor: only the structure block name field uses it, and Horizon has no structure block editor +Screen.scheduleNarration skip:other narration timing; Horizon has no narrator +Screen.shouldRunNarration skip:other narration gate; Horizon has no narrator +Screen.handleDelayedNarration skip:other narration timing; Horizon has no narrator +Screen.runNarration skip:other narration output; Horizon has no narrator +Screen.updateNarratedWidget skip:other narration of widgets; Horizon has no narrator +Screen.findNarratableWidget skip:other narration focus search; Horizon has no narrator +Screen.updateNarratorStatus skip:other narrator option toggle only; Horizon has no narrator output +TitleScreen.tick skip:service Realms notifications and the friend request count (social) +TitleScreen.init port crates/horizon-client/src/title.rs::Title::buttons main menu buttons; the splash text is chosen at load (choose_splash) +TitleScreen.getHorizontalPosition skip:ui layout of the icon row only +TitleScreen.createNormalMenuOptions port crates/horizon-client/src/title.rs::Title::buttons gap: player: Minecraft Realms button is greyed out (service) +TitleScreen.getMultiplayerDisabledReason missing minor: unsure: no ban check; Multiplayer is always enabled in title.rs +TitleScreen.createDemoMenuOptions skip:other demo build only (isDemo); Horizon has no demo mode +TitleScreen.checkDemoWorldPresence skip:other demo world only +TitleScreen.extractRenderState port crates/horizon-client/src/title.rs::Title::draw version line and buttons +TitleScreen.mouseClicked port crates/horizon-client/src/title.rs::Title::click gap: minor: no Realms notification popup to click +TitleScreen.confirmDemo skip:other demo world reset only diff --git a/tools/port_map/client2-015.tsv b/tools/port_map/client2-015.tsv new file mode 100644 index 0000000000..865f460d94 --- /dev/null +++ b/tools/port_map/client2-015.tsv @@ -0,0 +1,149 @@ +WinScreen.wrapCreditsIO port crates/horizon-client/src/win_screen.rs::WinScreen::new reads the credits texts from the resource root; a failed read is logged and skipped +WinScreen.extractRenderState port crates/horizon-client/src/win_screen.rs::WinScreen::draw logo, lines scrolled up, last line stops mid-screen; background and vignette are drawn here too +WinScreen.extractVignette inline crates/horizon-client/src/win_screen.rs::WinScreen::draw credits_vignette sprite stretched over the screen +WinScreen.extractMenuBackground port crates/horizon-client/src/win_screen.rs::WinScreen::menu_background menu background tiled and scrolled at half the text speed +WorldOptionsScreen.init port crates/horizon-client/src/game/lan_screen.rs::Game::open_world_options reads difficulty, lock, LAN port and command/force settings when opened; layout is WorldOptions::buttons +WorldOptionsScreen.createGameRulesButton inline crates/horizon-client/src/game/lan_screen.rs::Game::world_options_click GameRules arm opens the rules view; enabled state is in WorldOptions::active +WorldOptionsScreen.createGameModeButton inline crates/horizon-client/src/game/lan_screen.rs::Game::world_options_click Default and Personal mode arms cycle the mode; enabled state is in WorldOptions::active +WorldOptionsScreen.createAllowCommandsButton inline crates/horizon-client/src/game/lan_screen.rs::Game::world_options_click Cheats arm toggles commands; the hardcore lock is in WorldOptions::active +WorldOptionsScreen.createRestrictionsButton inline crates/horizon-client/src/game/lan_screen.rs::Game::world_options_click Restrictions arm opens the restrictions view +WorldOptionsScreen.multiplayerOptions inline crates/horizon-client/src/game/lan_screen.rs::Game::open_world_options LAN, port, guest command and force values are read at open; rows from WorldOptions::buttons; the LAN toggle is in world_options_click +WorldOptionsScreen.updateGuestCommandAccessButton port crates/horizon-client/src/game/lan_screen.rs::WorldOptions::active command access is enabled only with LAN and commands; the shown value is derived in WorldOptions::label +WorldOptionsScreen.updateForceGameModeButton port crates/horizon-client/src/game/lan_screen.rs::WorldOptions::active enabled only with LAN, no guest commands and not hardcore; gap: minor: vanilla forces the saved flag on when LAN is off or hardcore, Horizon keeps the stored flag and only shows it on +WorldOptionsScreen.hasChanges port crates/horizon-client/src/game/lan_screen.rs::WorldOptions::changed Apply is enabled when a setting differs and the port is valid +WorldOptionsScreen.applyChanges port crates/horizon-client/src/game/lan_screen.rs::Game::apply_world_options sends difficulty and lock changes, applies modes, commands, guest and force settings, then opens or closes LAN +WorldOptionsScreen.repositionElements skip:ui layout only: scroll area and footer placement +WorldOptionsScreen.extractMenuBackground port crates/horizon-client/src/game/lan_screen.rs::Game::draw_options_view in-world background and the list background under the content +WorldOptionsScreen.extractRenderState port crates/horizon-client/src/game/lan_screen.rs::Game::draw_options_view header and footer separators and the widgets +WorldOptionsScreen.onGamemasterPermissionChanged port crates/horizon-client/src/game/lan_screen.rs::Game::remote_world_options_permission losing permission on a server closes the screen; gap: minor: the default and personal modes are not reset as updatePermissionDependentButtons does +WorldOptionsScreen.updatePermissionDependentButtons inline crates/horizon-client/src/game/lan_screen.rs::Game::world_options_click the Cheats arm resets default and personal mode to the saved default when commands turn off; gap: minor: the remote permission path does not do this +WorldOptionsScreen.updatePersonalGameModeButton missing minor: a personal mode changed while the options are open is not shown; Horizon reads the modes once when the screen opens +WorldOptionsScreen.onDefaultGameModeChanged missing minor: a default mode changed while the options are open is not shown; Horizon reads the modes once when the screen opens +WorldOptionsScreen.hasSettingsChanges inline crates/horizon-client/src/game/lan_screen.rs::WorldOptions::changed compares every setting against the values read at open +WorldOptionsScreen.lanPortChanged inline crates/horizon-client/src/game/lan_screen.rs::WorldOptions::snapshot the port is part of the snapshot only while LAN is on +WorldOptionsScreen.changeMultiplayerScope inline crates/horizon-client/src/game/lan_screen.rs::Game::apply_world_options closes LAN and reopens it on the new port when the scope or port changed +WorldOptionsScreen.sendPublishMessage inline crates/horizon-client/src/game/lan_screen.rs::Game::apply_world_options publish started, failed or stopped line in chat via system_message +WorldOptionsScreen.updatePortControlsState inline crates/horizon-client/src/game/lan_screen.rs::Game::world_options_click Lan arm drops port focus; gap: minor: vanilla clears the port text when LAN is switched off and refills it from the saved port when on, Horizon keeps the typed text +WorldOptionsScreen.tryParsePort port crates/horizon-client/src/game/lan_screen.rs::WorldOptions::parsed_port blank uses the suggested port; 1024 to 65535 accepted; gap: minor: no port-in-use check and no error message, only the red colour from draw_options_view +WorldOptionsScreen.setPortError inline crates/horizon-client/src/game/lan_screen.rs::Game::draw_options_view port text turns red when invalid; gap: minor: no error tooltip +WorldOptionsScreen.DifficultyButtons.create inline crates/horizon-client/src/game/lan_screen.rs::Game::open_world_options difficulty read at open; the cycle and lock arms are in world_options_click +WorldOptionsScreen.DifficultyButtons.refresh port crates/horizon-client/src/game/lan_screen.rs::WorldOptions::server_difficulty a server's difficulty and lock update the screen; gap: minor: it also resets the baseline, so Apply no longer sees the change +WorldOptionsScreen.DifficultyButtons.isDifficultyLocked inline crates/horizon-client/src/game/lan_screen.rs::Game::open_world_options locked when the level is locked or hardcore; the lock button's enabled state is in WorldOptions::active +StatsScreen.onStatsUpdated inline crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats the lists are rebuilt from the stored values on every draw; the AwardStats arm in remote.rs only stores them +StatsScreen.setTabActiveStateAndTooltip missing player: an empty Items or Mobs tab stays selectable with no 'No statistics found' tooltip +StatsScreen.repositionElements skip:ui layout only: tab bar placement and header height +StatsScreen.keyPressed missing minor: no keyboard tab switching on the statistics screen; Escape still closes it +StatsScreen.extractRenderState inline crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats footer separator is drawn with the tab bar in draw_stats +StatsScreen.extractMenuBackground inline crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats background drawn at the start of draw_stats +StatsScreen.GeneralStatisticsList.Entry.getValueText port crates/horizon-client/src/game/stats_screen.rs::format value text by the stat's format (time, distance, tenths, plain) +StatsScreen.GeneralStatisticsList.Entry.extractContent port crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats General rows: name left, value right +StatsScreen.GeneralStatisticsList.Entry.getNarration skip:other narration text for screen readers; Horizon has no narrator output +StatsScreen.ItemStatisticsList. port crates/horizon-client/src/game/stats_screen.rs::Game::stat_items items with a non-zero count in any column, block items included +StatsScreen.ItemStatisticsList.getColumn inline crates/horizon-client/src/game/stats_screen.rs::Game::stat_items the column index selects COLUMNS directly +StatsScreen.ItemStatisticsList.getColumnIndex inline crates/horizon-client/src/game/stats_screen.rs::Game::stats_click the sort state keeps the column index, so no reverse lookup is needed +StatsScreen.ItemStatisticsList.sortByColumn inline crates/horizon-client/src/game/stats_screen.rs::Game::stats_click header click cycles descending, ascending, then off +StatsScreen.ItemStatisticsList.sortItems inline crates/horizon-client/src/game/stats_screen.rs::Game::stat_items sorts by the chosen column; gap: minor: equal counts keep insertion order instead of item id order +StatsScreen.ItemStatisticsList.getItemRows skip:java copies the item rows out of the list widget +StatsScreen.ItemStatisticsList.HeaderEntry.extractContent port crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats column header row with the sort arrow on the sorted column +StatsScreen.ItemStatisticsList.HeaderEntry.StatSortButton.extractContents inline crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats slot background and column icon per header cell +StatsScreen.ItemStatisticsList.ItemRow.extractContent port crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats item slot and the block and item stat cells per row +StatsScreen.ItemStatisticsList.ItemRow.extractStat inline crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats cell value right-aligned in its column +StatsScreen.ItemStatisticsList.ItemRow.ItemRowWidget.extractWidgetRenderState inline crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats slot background and item icon +StatsScreen.ItemStatisticsList.ItemRow.ItemRowWidget.extractTooltip missing player: hovering an item in the Items tab shows no item tooltip +StatsScreen.ItemStatisticsList.ItemRowComparator.compare inline crates/horizon-client/src/game/stats_screen.rs::Game::stat_items orders by the sorted column; gap: minor: non-block items count as 0 in block columns (vanilla -1), and ties are not broken by item id +StatsScreen.MobsStatisticsList.MobRow.extractContent port crates/horizon-client/src/game/stats_screen.rs::Game::draw_stats mob name, kills and killed-by lines +StatsScreen.MobsStatisticsList.MobRow.getNarration skip:other narration text for screen readers; Horizon has no narrator output +StatsScreen.StatisticsTab.doLayout skip:ui layout only: list size and position +AdvancementTab.copyPosition inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_cache a rebuilt tree keeps adv_ui.scroll; tab bounds are recomputed in adv_view +AdvancementTab.extractTab port crates/horizon-client/src/game/advancements_screen.rs::Game::draw_advancements tab sprite per position (first, middle, last) and selected state +AdvancementTab.extractIcon port crates/horizon-client/src/game/advancements_screen.rs::Game::draw_advancements tab icon at its side offset +AdvancementTab.extractContents port crates/horizon-client/src/game/advancements_screen.rs::Game::draw_advancements background grid, connection lines and advancement widgets inside the 234 by 113 area +AdvancementTab.extractTooltips port crates/horizon-client/src/game/advancements_screen.rs::Game::adv_draw_hover hovered advancement card; gap: minor: the fade fill is drawn in draw_advancements +AdvancementTab.scroll port crates/horizon-client/src/game/advancements_screen.rs::Game::adv_scroll_by clamps the scroll to the tab bounds on each axis +AdvancementTab.canScrollHorizontally inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_scroll_by scrolls only when the bounds are wider than 234 +AdvancementTab.canScrollVertically inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_scroll_by scrolls only when the bounds are taller than 113 +AdvancementTab.addAdvancement inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_view tasks of each tab are placed in the view's widget list +AdvancementTab.addWidget inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_view tab bounds grown by each widget (28 by 27) +AdvancementTabType.extractRenderState port crates/horizon-client/src/game/advancements_screen.rs::tab_sprite first, middle or last sprite, selected variant +AdvancementTabType.extractIcon port crates/horizon-client/src/game/advancements_screen.rs::icon_offset icon offset per side +AdvancementTabType.getX port crates/horizon-client/src/game/advancements_screen.rs::tab_pos x per side and index +AdvancementTabType.getY port crates/horizon-client/src/game/advancements_screen.rs::tab_pos y per side and index +AdvancementTabType.isMouseOver inline crates/horizon-client/src/game/advancements_screen.rs::Game::advancements_mouse tab hit test with strict edges on click +AdvancementWidget. port crates/horizon-client/src/game/advancements_screen.rs::Game::adv_widget_text title split at 163 px, description wrapped, widget width +AdvancementWidget.createWidget inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_view only advancements with a display get a widget +AdvancementWidget.getMaxProgressWidth inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_widget_text width of the progress text when there is more than one requirement +AdvancementWidget.getMaxWidth inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_widget_text widest line of the wrapped description +AdvancementWidget.findOptimalLines inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_widget_text offsets 0, 10, -10, 25, -25 and the 10 px tolerance +AdvancementWidget.findFirstVisibleParent inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_view nearest displayed ancestor becomes the parent +AdvancementWidget.extractConnectivity port crates/horizon-client/src/game/advancements_screen.rs::Game::draw_advancements connection lines, background pass then the line pass +AdvancementWidget.extractRenderState port crates/horizon-client/src/game/advancements_screen.rs::Game::draw_advancements frame sprite (obtained or not) and the item icon +AdvancementWidget.extractMultilineText inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_draw_hover description lines in the hover card +AdvancementWidget.isMouseOver port crates/horizon-client/src/game/advancements_screen.rs::Game::adv_tick_hover 26 px box hit test; hidden advancements count only when done +AdvancementWidget.attachToParent inline crates/horizon-client/src/game/advancements_screen.rs::Game::adv_view parent link set when the view is built +AdvancementsScreen.init port crates/horizon-client/src/game/advancements_screen.rs::Game::open_advancements opens on the kept tab; gap: minor: no tab pages with < and > buttons, the view shows at most 26 tabs +AdvancementsScreen.repositionElements skip:ui layout only: window position +AdvancementsScreen.removed missing minor: the server is not sent the closed-screen packet when the advancements screen closes +AdvancementsScreen.tick port crates/horizon-client/src/game/advancements_screen.rs::Game::adv_tick_hover hover tracking and card fade; gap: minor: runs from the frame, not the 20 Hz tick +AdvancementsScreen.mouseClicked port crates/horizon-client/src/game/advancements_screen.rs::Game::advancements_mouse left click selects a tab, Done closes; gap: minor: tab pages are not filtered +AdvancementsScreen.keyPressed port crates/horizon-client/src/game.rs::Game::key_pressed the advancements key (L) closes the screen +AdvancementsScreen.extractRenderState port crates/horizon-client/src/game/advancements_screen.rs::Game::draw_advancements window, tabs and contents; gap: minor: no 'page / pages' text +AdvancementsScreen.mouseDragged port crates/horizon-client/src/game/advancements_screen.rs::Game::advancements_drag first movement starts scrolling, later ones move the tree +AdvancementsScreen.mouseScrolled port crates/horizon-client/src/game/advancements_screen.rs::Game::advancements_scroll wheel scrolls 16 px per step +AdvancementsScreen.extractInside port crates/horizon-client/src/game/advancements_screen.rs::Game::draw_advancements 'no advancements' placeholder when there is no tab +AdvancementsScreen.extractWindow port crates/horizon-client/src/game/advancements_screen.rs::Game::draw_advancements window background, tab bar and the selected tab title (window() gives the position) +AdvancementsScreen.extractTooltips port crates/horizon-client/src/game/advancements_screen.rs::Game::draw_advancements tab title tooltips; the card tooltip comes from adv_draw_hover +AdvancementsScreen.onAdvancementsUpdated port crates/horizon-client/src/game/advancements_screen.rs::Game::adv_cache a changed tree rebuilds the positions; tabs and progress are read in adv_view and at draw time +AdvancementsScreen.getAdvancementWidget skip:java lookup of a widget by node; Horizon indexes positions by tree index +DebugOptionsScreen.init missing minor: no F3+F6 debug entry editor; the F3 overlay footer still says 'To edit: press [F3+F6]' (debug_overlay.rs) +DebugOptionsScreen.addProfileButton missing minor: no debug profile buttons (default and performance) +DebugOptionsScreen.repositionElements missing minor: layout of the missing debug entry editor +DebugOptionsScreen.CategoryEntry.extractContent missing minor: category headings of the missing debug entry editor +DebugOptionsScreen.OptionEntry. missing minor: per-entry always, overlay and never buttons of the missing editor +DebugOptionsScreen.OptionEntry.narrateButton skip:other narration text for screen readers +DebugOptionsScreen.OptionEntry.setValue missing minor: sets an entry's status in the missing editor +DebugOptionsScreen.OptionEntry.extractContent missing minor: draws the entry row of the missing editor +DebugOptionsScreen.OptionEntry.refreshEntry missing minor: refreshes the buttons of the missing editor +DebugOptionsScreen.OptionList.updateSearch missing minor: search filter of the missing debug entry editor +GameModeSwitcherScreen.getDefaultSelected port crates/horizon-client/src/game_mode_switcher.rs::GameModeSwitcher::new previous mode, else the opposite of the current one +GameModeSwitcherScreen.init port crates/horizon-client/src/game/gamemode_switcher.rs::Game::open_gamemode_switcher opens with the selection state; slot positions are computed in draw +GameModeSwitcherScreen.extractRenderState port crates/horizon-client/src/game/gamemode_switcher.rs::Game::draw_gamemode_switcher title, help line, slots, selection and hover +GameModeSwitcherScreen.extractBackground port crates/horizon-client/src/game/gamemode_switcher.rs::Game::draw_gamemode_switcher background sprite centred above the slots +GameModeSwitcherScreen.switchToHoveredGameMode port crates/horizon-client/src/game/gamemode_switcher.rs::Game::close_gamemode_switcher applies the selected mode on close when gamemaster and it differs +GameModeSwitcherScreen.keyPressed port crates/horizon-client/src/game/debug_keys.rs::Game::debug_key the switch key cycles the selection +GameModeSwitcherScreen.keyReleased port crates/horizon-client/src/game/debug_keys.rs::Game::debug_key releasing the debug modifier applies and closes +GameModeSwitcherScreen.mouseReleased missing minor: a debug modifier bound to a mouse button does not close the switcher; debug keys are keyboard only +GameModeSwitcherScreen.GameModeIcon.getNext port crates/horizon-client/src/game_mode_switcher.rs::GameModeSwitcher::cycle creative, survival, adventure, spectator, then creative +GameModeSwitcherScreen.GameModeIcon.getFromGameType inline crates/horizon-client/src/game/gamemode_switcher.rs::Game::close_gamemode_switcher compares the chosen mode with the current one before sending +GameModeSwitcherScreen.GameModeSlot.extractWidgetRenderState inline crates/horizon-client/src/game/gamemode_switcher.rs::Game::draw_gamemode_switcher slot, selection frame and icon per mode +GameModeSwitcherScreen.GameModeSlot.extractSlot inline crates/horizon-client/src/game/gamemode_switcher.rs::Game::draw_gamemode_switcher slot sprite +GameModeSwitcherScreen.GameModeSlot.extractSelection inline crates/horizon-client/src/game/gamemode_switcher.rs::Game::draw_gamemode_switcher selection sprite +ButtonListDialogScreen.populateBodyElements port crates/horizon-client/src/dialog_screen.rs::columns action buttons packed into the dialog's columns +ButtonListDialogScreen.updateHeaderAndFooter port crates/horizon-client/src/dialog_screen.rs::Layout::new exit button in the footer, else a 5 pixel footer +DialogControlSet.addInput port crates/horizon-client/src/dialog_screen.rs::input gap: player: single-option and number-range inputs get no control, so their value cannot be changed +DialogControlSet.createDialogButton port crates/horizon-client/src/dialog_screen.rs::Layout::new button width from the dialog; gap: minor: no button tooltips +DialogControlSet.bindAction port crates/horizon-client/src/dialog.rs::Action::click reads the inputs' current values into the action when clicked +DialogControlSet.createActionButton port crates/horizon-client/src/game/dialogs.rs::Game::dialog_click button press runs the action through run_dialog_action +DialogListDialogScreen.createDialogClickAction missing player: a dialog list shows no entry buttons; only its exit button is drawn +DialogScreen.repositionElements skip:ui layout only: body scroll height and warning clamp, both in Layout::new +DialogScreen.createTitleWithWarningButton skip:ui layout only: title row, warning button 10 px right of the title in Layout::new +DialogScreen.makeSureWarningButtonIsInBounds port crates/horizon-client/src/dialog_screen.rs::Layout::in_bounds moves the warning button on screen when it is off +DialogScreen.createWarningButton port crates/horizon-client/src/game/dialogs.rs::Game::dialog_click warning rect opens the warning; gap: minor: no hover tooltip +DialogScreen.handleDialogClickEvent port crates/horizon-client/src/game/dialogs.rs::Game::run_dialog_action gap: player: open-URL, suggest-command and change-page buttons only log and do nothing +DialogScreens.createFromData port crates/horizon-client/src/game/dialogs.rs::Game::show_dialog picks the screen from the dialog's kind +DialogScreens.bootstrap skip:data registers screen factories by codec +ServerLinksDialogScreen.createListActions port crates/horizon-client/src/game/server_links.rs::Game::server_links the server's links +ServerLinksDialogScreen.createDialogClickAction port crates/horizon-client/src/game/server_links.rs::Game::click_link opens the link (prompt or browser by setting) +SimpleDialogScreen.updateHeaderAndFooter port crates/horizon-client/src/dialog_screen.rs::Layout::new main action buttons side by side in the footer, 8 px apart +WaitingForResponseScreen.init port crates/horizon-client/src/dialog_screen.rs::waiting_button Back button starts hidden and inactive +WaitingForResponseScreen.tick port crates/horizon-client/src/dialog_screen.rs::waiting_button Back shows after a second with a countdown label, works after five seconds +DialogBodyHandlers.createBodyElement port crates/horizon-client/src/dialog_screen.rs::Layout::new gap: player: item bodies are dropped (Body::Item gives no element) +DialogBodyHandlers.bootstrap skip:data registers body handlers +DialogBodyHandlers.runActionOnParent missing player: clicking text in a dialog message or item description does nothing; dialog_click only tests fields, checkboxes and buttons +DialogBodyHandlers.ItemHandler.createControls missing player: an item body (icon and description) is not drawn in a dialog +DialogBodyHandlers.PlainMessageHandler.createControls port crates/horizon-client/src/dialog_screen.rs::message centred wrapped text; gap: player: text clicks do nothing +InputControlHandlers.createHandler port crates/horizon-client/src/dialog_screen.rs::input dispatch by control type; unknown types are dropped +InputControlHandlers.bootstrap skip:data registers input handlers +InputControlHandlers.BooleanHandler.addControl port crates/horizon-client/src/dialog_screen.rs::input checkbox with its label; toggled in dialog_click +InputControlHandlers.NumberRangeHandler.addControl missing player: a number-range input (slider) in a dialog has no control +InputControlHandlers.SingleOptionHandler.addControl missing player: a single-option input in a dialog has no control +InputControlHandlers.TextInputHandler.addControl port crates/horizon-client/src/dialog_screen.rs::input gap: player: multiline inputs are drawn as one 20 px line and the line limit is not used diff --git a/tools/port_map/client2-016.tsv b/tools/port_map/client2-016.tsv new file mode 100644 index 0000000000..0e80c83130 --- /dev/null +++ b/tools/port_map/client2-016.tsv @@ -0,0 +1,79 @@ +AbstractFriendsEntryContainerWidget skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +AbstractFriendsTab skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +AddFriendWidget skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +FriendEntry skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +FriendsListConfirmScreen skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +FriendsOverlayScreen skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +FriendsOverlayTabButton skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +FriendsTab skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +IncomingEntry skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +OutgoingEntry skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +PendingTab skip:service Mojang friends service UI (friend list, requests, presence): service. Horizon's Friends button opens its own invite-code screen (game/screens.rs) +EffectsInInventory.extractRenderState port crates/horizon-client/src/game/potions.rs::Game::draw_inventory_effects panel layout and visibility beside the survival and creative inventories; drawn by inventory_effects::paint +EffectsInInventory.extractEffects port crates/horizon-client/src/inventory_effects.rs::paint one row per effect: icon, then text and background +EffectsInInventory.extractBackground port crates/horizon-client/src/inventory_effects.rs::background nine-slice panel background, ambient variant included +EffectsInInventory.extractText port crates/horizon-client/src/inventory_effects.rs::paint name and duration text, clipping with ..., hover tooltip for compact panels +EffectsInInventory.getEffectName inline crates/horizon-client/src/inventory_effects.rs::panels name plus level suffix for amplifier 1-9, folded into panels +EnchantmentNames.getRandomName inline crates/horizon-client/src/game/stations.rs::Game::draw_station galactic word names from the enchant seed (WORDS), drawn in the enchanting option slots +EnchantmentScreen.init skip:ui creates the book model for drawing only; the book itself is not drawn (see extractBook) +EnchantmentScreen.containerTick port crates/horizon-client/src/game/locator_bar.rs::Game::track_experience_display gap: player: keeps the experience bar up while the table is open; the book tick (tickBook) is not run +EnchantmentScreen.extractBook missing player: the enchanting table has no open 3D book; only the flat table is drawn, so the book never opens when an item is placed +EnchantmentScreen.tickBook missing player: the book's open and page-flip animation (driven by the offers) is not run; the table stays flat +GrindstoneScreen.extractBackground inline crates/horizon-client/src/game/stations.rs::Game::draw_station grindstone texture and the error sprite when an input is set and there is no result +HangingSignEditScreen.extractSignBackground port crates/horizon-client/src/game/sign_edit.rs::Game::draw_sign_editor hanging sign board at 4.5x scale +HopperScreen.extractBackground port crates/horizon-client/src/game/machines.rs::Game::draw_machine_screen hopper background for block and minecart hoppers +HorseInventoryScreen.shouldRenderSaddleSlot port crates/horizon-client/src/game/mount_menu.rs::shows_saddle saddle slot shown for every mount except llamas +HorseInventoryScreen.shouldRenderArmorSlot port crates/horizon-client/src/game/mount_menu.rs::shows_body armour slot for horse armour wearers and llamas +InventoryScreen.containerTick missing minor: a survival inventory that stays open when the game mode becomes creative is not switched to the creative screen +InventoryScreen.init inline crates/horizon-client/src/game.rs::Game::key_pressed gap: minor: the creative screen is chosen when E opens the inventory; no switch while a survival screen is already open +InventoryScreen.getRecipeBookButtonPosition port crates/horizon-client/src/game/recipe_book.rs::Game::book_button_pos book button at (leftPos+104, height/2-22), same as the crafting placement +InventoryScreen.extractLabels inline crates/horizon-client/src/game.rs::Game::draw_inventory Crafting title at x 97 in the survival inventory +InventoryScreen.extractRenderState inline crates/horizon-client/src/game.rs::Game::draw_inventory effect panels (draw_inventory_effects), slots and widgets +InventoryScreen.extractBackground inline crates/horizon-client/src/game.rs::Game::draw_inventory background texture and the player preview (draw_inventory_player) +InventoryScreen.extractEntityInInventoryFollowsMouse port crates/horizon-client/src/game/perspective.rs::Game::draw_player_preview gap: minor: player only; mounts use mount_menu.rs::draw_mob_preview. Head and body turn toward the cursor as vanilla +InventoryScreen.mouseReleased inline crates/horizon-client/src/game.rs::Game::inventory_mouse_release release goes through the slot path; a recipe-book press is handled first and has no separate release flag +ItemCombinerScreen.extractBackground inline crates/horizon-client/src/game/stations.rs::Game::draw_station anvil and smithing backgrounds, with the error sprite +JigsawBlockEditScreen missing player: creative operators cannot open a jigsaw block's editor; no jigsaw UI exists in Horizon +LecternScreen.createMenuControls inline crates/horizon-client/src/game/books.rs::Game::draw_book gap: player: Take Book and Done are drawn on every lectern; vanilla shows Take Book only with build permission (mayBuild) +LoomScreen.init skip:ui bakes the banner flag model for the preview; the loom is drawn by screen_layout::loom +LoomScreen.isScrollBarActive port crates/horizon-client/src/game/loom.rs::LoomState::offscreen_rows gap: minor: more than 16 patterns (four rows of four); the displayPatterns check is at the call sites +LoomScreen.extractBannerOnButton port crates/horizon-client/src/game/loom.rs::Game::paint_screen grey square and pattern sprite on each pattern button (Fill and Atlas draws from screen_layout::loom) +LoomScreen.hasClickedOutside port crates/horizon-client/src/game.rs::Game::clicked_outside outside test against the container rectangle, plus the recipe book area +MerchantScreen.postButtonClick port crates/horizon-client/src/game/trading.rs::Game::try_move_items selection hint, move the offer's items, and the SelectTrade packet on a server +MerchantScreen.init skip:ui trade buttons are laid out in draw_merchant and hit-tested in merchant_click +MerchantScreen.extractLabels inline crates/horizon-client/src/game/trading.rs::Game::draw_merchant title with the trader level, Trades and Inventory labels +MerchantScreen.extractBackground inline crates/horizon-client/src/game/trading.rs::Game::draw_merchant villager background and the out-of-stock sprite +MerchantScreen.extractProgressBar inline crates/horizon-client/src/game/trading.rs::Game::draw_merchant experience bar, with the pending trade's XP +MerchantScreen.extractScroller inline crates/horizon-client/src/game/trading.rs::Game::draw_merchant gap: minor: no pointing or resize cursor over the scroller +MerchantScreen.extractContents inline crates/horizon-client/src/game/trading.rs::Game::draw_merchant the seven offer rows from the scroll offset +MerchantScreen.extractButtonArrows inline crates/horizon-client/src/game/trading.rs::Game::draw_merchant trade arrow, and the out-of-stock arrow +MerchantScreen.extractAndDecorateCostA inline crates/horizon-client/src/game/trading.rs::Game::draw_merchant discount: base count struck through, new count drawn (merchant.rs cost_a) +MerchantScreen.mouseScrolled port crates/horizon-client/src/game/trading.rs::Game::merchant_scroll gap: minor: one offer per wheel step; vanilla uses the float scroll amount +MerchantScreen.mouseDragged missing player: dragging the trade list scroller does nothing; only the mouse wheel scrolls the offers +MerchantScreen.mouseClicked port crates/horizon-client/src/game/trading.rs::Game::merchant_click gap: player: clicking the scroller does not start a drag (see mouseDragged); the offer buttons work +MerchantScreen.TradeOfferButton.extractToolTip inline crates/horizon-client/src/game/trading.rs::Game::draw_merchant item tooltip for cost A, cost B or the result under the hovered offer +MinecartCommandBlockEditScreen.populateAndSendPacket missing player: a command block minecart never opens its command editor (minecarts.rs CartKind::CommandBlock is never usable), so its command cannot be set +PageButton.extractContents inline crates/horizon-client/src/game/books.rs::Game::draw_book page arrows with their hover sprites (page_buttons) +ShulkerBoxScreen.extractBackground port crates/horizon-client/src/game/storage.rs::Game::draw_chest_screen shulker box background texture, drawn with the other container screens +SignEditScreen.extractSignBackground port crates/horizon-client/src/game/sign_edit.rs::Game::draw_sign_editor sign board at 3.9x scale, wall signs at 12 rows +SmithingScreen.containerTick missing player: the empty template slot shows one fixed netherite icon; the cycling template icons and the base and addition slot icons are not drawn +SmithingScreen.getTemplateItem missing minor: helper for the slot icons only; Horizon has no template lookup for icons +SmithingScreen.extractRenderState inline crates/horizon-client/src/game/stations.rs::Game::draw_station gap: player: onboarding tooltips for empty slots are missing (see extractOnboardingTooltips) +SmithingScreen.extractBackground inline crates/horizon-client/src/game/stations.rs::Game::draw_station gap: player: the armour stand preview beside the slots is not drawn +SmithingScreen.updateArmorStandPreview missing player: no armour stand preview of the item being upgraded is built, so nothing is drawn beside the slots +SmithingScreen.extractErrorIcon inline crates/horizon-client/src/game/stations.rs::Game::draw_station error sprite at (65, 46) when all inputs are set and there is no result +SmithingScreen.extractOnboardingTooltips inline crates/horizon-client/src/game/stations.rs::Game::draw_station gap: player: only the upgrade error tooltip exists; the missing-template and slot description tooltips are not drawn +StonecutterScreen.extractBackground inline crates/horizon-client/src/game/stations.rs::Game::draw_station background, scroller and the recipe grid +StonecutterScreen.extractTooltip inline crates/horizon-client/src/game/stations.rs::Game::draw_station tooltip of the hovered recipe result +StonecutterScreen.extractButtons inline crates/horizon-client/src/game/stations.rs::Game::draw_station recipe cell sprites: selected and highlighted +StonecutterScreen.extractRecipes inline crates/horizon-client/src/game/stations.rs::Game::draw_station recipe result icons in the grid +StonecutterScreen.mouseDragged missing player: the recipe list scroller cannot be dragged; only the mouse wheel scrolls it +StonecutterScreen.mouseScrolled port crates/horizon-client/src/game/stations.rs::Game::station_scroll one row of recipes per wheel step, only with more than 12 recipes +StonecutterScreen.isScrollBarActive inline crates/horizon-client/src/game/stations.rs::Game::station_scroll more than 12 visible recipes +StonecutterScreen.getOffscreenRows inline crates/horizon-client/src/game/stations.rs::Game::station_scroll rows beyond the 3 visible rows, ceil(recipes/4) - 3 +StructureBlockEditScreen missing player: creative operators cannot open a structure block's editor; no structure block UI exists in Horizon +TestBlockEditScreen missing player: creative operators cannot open a test block's editor; gametest setup has no UI in Horizon +TestInstanceBlockEditScreen missing player: creative operators cannot open a test instance block's editor; gametest setup has no UI in Horizon +BelowOrAboveWidgetTooltipPositioner.positionTooltip missing minor: unsure: widget-anchored tooltip placement (below the widget, flipped above when it does not fit); Horizon places tooltips at the cursor (ui::tooltip) +ClientActivePlayersTooltip.getWidth missing player: hovering a server's player count in the server list shows no online player names; the ping sample is not read (multiplayer.rs) +ClientActivePlayersTooltip.extractImage missing player: no online player heads and names are drawn for a server; the ping sample is not read (multiplayer.rs) diff --git a/tools/port_map/client2-017.tsv b/tools/port_map/client2-017.tsv new file mode 100644 index 0000000000..15704d3d0d --- /dev/null +++ b/tools/port_map/client2-017.tsv @@ -0,0 +1,139 @@ +ClientBundleTooltip.extractEmptyBundleTooltip port crates/horizon-client/src/game/stations.rs::Game::draw_bundle_tooltip empty branch: description, bar and label drawn from the layout +ClientBundleTooltip.extractBundleWithItemsTooltip port crates/horizon-items/src/bundle.rs::tooltip_layout gap: player: the selected item's name box above the grid is not drawn (extractSelectedItemTooltip); grid and bar placement are here +ClientBundleTooltip.getShownItems port crates/horizon-items/src/bundle.rs::Contents::shown shown count (getNumberOfItemsToShow) and the visible items +ClientBundleTooltip.shouldRenderSurplusText inline crates/horizon-items/src/bundle.rs::tooltip_layout overflow cell is the first cell of the first row when items are hidden +ClientBundleTooltip.getAmountOfHiddenItems port crates/horizon-items/src/bundle.rs::Contents::shown hidden count is the second return value +ClientBundleTooltip.extractSlot port crates/horizon-client/src/game/stations.rs::Game::draw_bundle_tooltip slot background or highlight sprite, item, and front highlight +ClientBundleTooltip.extractCount port crates/horizon-client/src/game/stations.rs::Game::draw_bundle_tooltip the +N label in the overflow cell +ClientBundleTooltip.extractSelectedItemTooltip missing player: when a bundle item is selected with the scroll wheel, vanilla shows that item's name in a box above the bundle; Horizon draws only the slot highlight +ClientBundleTooltip.extractProgressbar port crates/horizon-client/src/game/stations.rs::Game::draw_bundle_tooltip fill, border and label sprites at the layout's bar position +ClientBundleTooltip.getEmptyBundleDescriptionTextHeight inline crates/horizon-items/src/bundle.rs::tooltip_layout gap: minor: fixed at two lines (18 px) instead of measuring the wrapped text +ClientBundleTooltip.getProgressBarFillText port crates/horizon-items/src/bundle.rs::tooltip_layout empty and full labels (the label field), drawn by draw_bundle_tooltip +ClientTooltipComponent.create inline crates/horizon-client/src/game/stations.rs::Game::draw_stack_tooltip gap: minor: only bundles and plain text lines get a component here; no ActivePlayersTooltip +DefaultTooltipPositioner.positionTooltip port crates/horizon-client/src/ui.rs::tooltip gap: minor: also clamps y to at least 4, which vanilla does not do; the text height is Horizon's own estimate +MenuTooltipPositioner.positionTooltip missing minor: widget tooltips (WidgetTooltipHolder) sit beside their widget in vanilla; Horizon's ui::tooltip always places tooltips at the cursor +MenuTooltipPositioner.getOffset missing minor: the widget-relative offset of the same missing positioner +TooltipRenderUtil.extractTooltipBackground port crates/horizon-client/src/ui.rs::tooltip background and frame sprites (tooltip/background, tooltip/frame) +TooltipRenderUtil.getBackgroundSprite inline crates/horizon-client/src/ui.rs::tooltip gap: minor: only the default style; no tooltip_style component +TooltipRenderUtil.getFrameSprite inline crates/horizon-client/src/ui.rs::tooltip gap: minor: only the default style; no tooltip_style component +CodeOfConductScreen missing player: a server with code of conduct enabled is accepted at once (net.rs CodeOfConduct reply), so the player never sees the text or the Acknowledge and Disconnect choice +JoinMultiplayerScreen.init port crates/horizon-client/src/multiplayer.rs::Screen::buttons footer buttons and their enabling; server load and LAN scan start are in Screen::list +JoinMultiplayerScreen.repositionElements skip:ui layout only: the list area is computed from the window size each frame (list_area) +JoinMultiplayerScreen.tick inline crates/horizon-client/src/multiplayer.rs::Screen::draw LAN games are read each frame (lan.games()); server pings run on their own threads +JoinMultiplayerScreen.removed port crates/horizon-client/src/lan/discovery.rs::ScanInner::drop dropping the list screen drops its scanner, and the drop stops the LAN scan +JoinMultiplayerScreen.deleteCallback port crates/horizon-client/src/multiplayer.rs::Screen::click delete answer (ids 58, 59) removes the server, clears the selection and saves +JoinMultiplayerScreen.editServerCallback port crates/horizon-client/src/multiplayer.rs::Screen::click edit save (id 50); the list reloads and re-pings after it +JoinMultiplayerScreen.addServerCallback port crates/horizon-client/src/multiplayer.rs::Screen::click gap: minor: no hidden-server unhide (ServerList.unhide); add save (id 50, no index) +JoinMultiplayerScreen.directJoinCallback port crates/horizon-client/src/multiplayer.rs::Screen::click gap: minor: a new address is not saved to the list first, as vanilla does; direct join (id 52) connects +JoinMultiplayerScreen.keyPressed missing minor: no keyboard path on the server list: F5 does not refresh; title.rs shows no key handling for it beyond Escape and text +JoinMultiplayerScreen.join port crates/horizon-client/src/multiplayer.rs::Screen::click returns Outcome::Connect for the selected server, LAN game or friend invite +JoinMultiplayerScreen.onSelectedChange port crates/horizon-client/src/multiplayer.rs::Screen::buttons Join enabled with a selection; Edit and Delete only for a saved server +RestrictionsScreen.createPermissionStatus port crates/horizon-client/src/game/lan_screen.rs::Restrictions::new allowed or denied line per permission, green or red +RestrictionsScreen.repositionElements skip:ui layout only: Restrictions::new sizes the scroll area from the window +SafetyScreen.addFooterButtons missing player: opening Multiplayer skips vanilla's safety warning (Proceed or Back, don't show again); skipMultiplayerWarning is stored in options_file.rs but no screen reads it +ServerReconfigScreen missing player: during a server reconfiguration vanilla shows a waiting screen with a Disconnect button; no such screen found; net.rs only forwards Event::Reconfigured +ServerSelectionList.refreshEntries port crates/horizon-client/src/multiplayer.rs::rows saved servers, LAN header and LAN games as one row list; selection is kept by index +ServerSelectionList.updateOnlineServers inline crates/horizon-client/src/multiplayer.rs::rows no separate list to refresh; rows are rebuilt from the saved servers +ServerSelectionList.updateNetworkServers inline crates/horizon-client/src/multiplayer.rs::rows gap: minor: no narrator announcement when a LAN game is found +ServerSelectionList.LANHeader.extractContent port crates/horizon-client/src/multiplayer.rs::Screen::draw scanning label and loading dots drawn in the list +ServerSelectionList.NetworkServerEntry.extractContent port crates/horizon-client/src/multiplayer.rs::Screen::draw gap: minor: the address is always shown, Hide Server Address (hideServerAddress) is not read; LAN World title, MOTD and address +ServerSelectionList.NetworkServerEntry.keyPressed missing minor: no keyboard join on the server list (Enter) +ServerSelectionList.NetworkServerEntry.join port crates/horizon-client/src/multiplayer.rs::Screen::click LAN game joined by a click on its row (Row::Lan) or a second click on the selected row +ServerSelectionList.NetworkServerEntry.matches skip:java selection is kept by index, so no entry identity match is needed +ServerSelectionList.OnlineServerEntry.extractContent port crates/horizon-client/src/multiplayer.rs::Screen::draw gap: player: no incompatible-version state (protocol mismatch) and no player-name tooltip; every failed ping reads Cannot connect; name, MOTD, ping bars, player count, hover join and move sprites +ServerSelectionList.OnlineServerEntry.refreshStatus port crates/horizon-client/src/multiplayer.rs::ping_tooltip gap: player: no player-name tooltip on the player count (playerList); ping tooltip: ms, no connection or pinging; the bar choice is in Screen::draw +ServerSelectionList.OnlineServerEntry.extractIcon port crates/horizon-client/src/multiplayer.rs::draw_server_icon server icon at 32x32 +ServerSelectionList.OnlineServerEntry.keyPressed missing minor: no keyboard path: Enter does not join and Shift+Up/Down does not move a server +ServerSelectionList.OnlineServerEntry.swap port crates/horizon-client/src/multiplayer.rs::Screen::click icon arrow click swaps the server up or down and saves +ServerSelectionList.OnlineServerEntry.getNarration missing minor: no narrator in Horizon, so no screen-reader text for entries +ServerSelectionList.OnlineServerEntry.matches skip:java selection is kept by index, so no entry identity match is needed +WarningScreen missing player: the warning page vanilla builds for multiplayer (base of SafetyScreen) does not exist in Horizon +AccessibilityOptionsScreen.options port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows ACCESSIBILITY_OPTIONS list, same options in the same order +AccessibilityOptionsScreen.init missing minor: High Contrast is not greyed out without the high_contrast pack, and Rotate with Minecarts is not greyed out without the minecart feature +AccessibilityOptionsScreen.addOptions port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows Narrator row with the Controls button, then the rest of the rows +AccessibilityOptionsScreen.addFooter missing minor: the Accessibility Help link button is absent from the footer +AccessibilityOptionsScreen.isMinecartOptionEnabled missing minor: no minecart feature-flag check; part of the greying-out in init +ChatOptionsScreen.options port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows gap: minor: Chat Delay and Narrator are on the Accessibility page instead; Chat page rows +FontOptionsScreen.options port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows Force Unicode Font and Japanese glyph variants +InWorldGameRulesScreen.initContent port crates/horizon-client/src/game/lan_screen.rs::Game::world_options_click GameRules button: a remote world sends REQUEST_GAMERULE_VALUES (action 2); a local world builds RulesScreen from its rules +InWorldGameRulesScreen.onDone port crates/horizon-client/src/game/lan_screen.rs::Game::close_game_rules Done sends only the changed rules, and only with permission 2 on a remote world +InWorldGameRulesScreen.collectChangedGameRule port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::changes rules whose value differs from the initial one +InWorldGameRulesScreen.hasGameRuleChanged inline crates/horizon-client/src/game_rules_screen.rs::RulesScreen::changes comparison against the initial value +InWorldGameRulesScreen.onGameRuleValuesUpdated port crates/horizon-client/src/game/lan_screen.rs::Game::remote_world_options_rules server values for the rules view (GameRuleValues) +InWorldGameRulesScreen.initializeGameRuleValue inline crates/horizon-client/src/game/lan_screen.rs::Game::remote_world_options_rules each server value parsed into the rules +InWorldGameRulesScreen.onGamemasterPermissionChanged port crates/horizon-client/src/game/lan_screen.rs::Game::remote_world_options_permission gap: minor: closes the rules only for a remote world; vanilla closes on any lost permission +LanguageSelectScreen.addTitle inline crates/horizon-client/src/options_ui.rs::OptionsUi::layout language search box at the top of the Language page +LanguageSelectScreen.setInitialFocus skip:ui focus only: typed text goes to the language search whatever has focus (type_text) +LanguageSelectScreen.addContents port crates/horizon-client/src/options_ui.rs::OptionsUi::layout language rows of the Language page +LanguageSelectScreen.addFooter port crates/horizon-client/src/options_ui.rs::OptionsUi::layout gap: minor: unsure whether the translation warning line is drawn; Font... and Done buttons +LanguageSelectScreen.repositionElements skip:ui layout only: rows are laid out from the window size each frame +LanguageSelectScreen.onDone port crates/horizon-client/src/options_ui.rs::OptionsUi::back Done applies the chosen language (language_choice) +LanguageSelectScreen.LanguageSelectionList.filterEntries port crates/horizon-client/src/options_ui.rs::OptionsUi::layout filter by the search text against name and region (Page::Language) +LanguageSelectScreen.LanguageSelectionList.Entry.extractContent port crates/horizon-client/src/options_ui.rs::OptionsUi::draw the language row's label, centred +LanguageSelectScreen.LanguageSelectionList.Entry.keyPressed missing minor: unsure: no Enter-to-select on language rows found in OptionsUi::key +LanguageSelectScreen.LanguageSelectionList.Entry.mouseClicked port crates/horizon-client/src/options_ui.rs::OptionsUi::click gap: minor: a second click on the chosen language finishes the page, which stands in for double-click; click selects +MouseSettingsScreen.options port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows MOUSE_OPTIONS in vanilla order +OnlineOptionsScreen missing player: Options > Online is greyed out in Horizon, so friends list, friend requests, in-game friend notifications, server listing and Realms notifications cannot be set +OptionsScreen.init port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows gap: player: the Online button is greyed out and Skin Customisation opens no page; main page rows +OptionsScreen.createOnlineButton missing player: the Online button is greyed out, so Options > Online cannot be opened +OptionsScreen.openScreenButton port crates/horizon-client/src/options_ui.rs::OptionsUi::click sub-page buttons push their page (Act::Push) +OptionsSubScreen.init port crates/horizon-client/src/options_ui.rs::OptionsUi::push opening a page resets its scroll, search and page state +OptionsSubScreen.addContents port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows the page's rows +OptionsSubScreen.addFooter inline crates/horizon-client/src/options_ui.rs::OptionsUi::layout Done button at the bottom of each page +OptionsSubScreen.repositionElements skip:ui layout only: rows are laid out from the window size each frame +OptionsSubScreen.onClose port crates/horizon-client/src/options_ui.rs::OptionsUi::back Done and Escape leave the page and apply its results +SkinCustomizationScreen.addOptions missing player: Skin Customisation opens no page in Horizon: no cape, jacket, sleeve, pants or main hand options +SoundOptionsScreen.addOptions port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows gap: minor: no sound device, directional audio or music toast rows; master and per-source volumes, subtitles, music frequency +SoundOptionsScreen.getAllSoundOptionsExceptMaster port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows per-source volume rows (Volume(1..)) +UnsupportedGraphicsWarningScreen missing minor: no GPU warn list in Horizon, so the unsupported-graphics warning never appears when Improved Transparency is turned on +VideoSettingsScreen.qualityOptions port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows gap: player: no Texture Filtering or Max Anisotropy rows; Video page quality rows +VideoSettingsScreen.displayOptions port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows gap: player: no Exclusive Fullscreen or Preferred Graphics Backend rows; framerate, vsync, inactivity FPS, GUI scale, fullscreen, gamma +VideoSettingsScreen.preferenceOptions port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows autosave indicator, vignette, attack indicator, chunk fade-in +VideoSettingsScreen.addOptions port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows gap: player: no exclusive fullscreen row (see displayOptions); Video page sections with Horizon's Graphics buttons first +VideoSettingsScreen.addTitle missing minor: no restart-required line; the option that needs a restart in vanilla (preferred backend) is absent +VideoSettingsScreen.tick missing minor: no anisotropy greying and no restart-required line (see addTitle) +VideoSettingsScreen.removed port crates/horizon-client/src/game/options.rs::Game::apply_ui_settings gap: minor: applied on every apply, not only when the value changed; no anisotropy or filtering to check; mipmap levels applied to the renderer +VideoSettingsScreen.mouseClicked missing minor: no unsupported-graphics warning when Improved Transparency is turned on +VideoSettingsScreen.mouseScrolled missing player: Ctrl with the mouse wheel does not change the GUI scale on the Video page; the wheel only scrolls (scroll_at has no Ctrl check) +VideoSettingsScreen.updateFullscreenButton skip:ui rows are rebuilt from Settings on each layout, so there is no fullscreen widget to set +VideoSettingsScreen.repositionElements missing minor: no exclusive fullscreen mode list to rebuild when the window moves to another monitor +VideoSettingsScreen.updateTransparencyButton skip:ui rows are rebuilt from Settings on each layout, so the transparency row reads its value each frame +ControlsScreen.options port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows gap: player: no Toggle Attack, Toggle Use, Sprint Window, Quit Shortcuts, Ctrl-click or Operator Items rows (the settings exist in options_file.rs); toggle crouch, sprint and auto jump rows +ControlsScreen.addOptions port crates/horizon-client/src/options_ui.rs::OptionsUi::list_rows Mouse Settings and Key Binds buttons, then the toggles; Horizon adds Controller Settings +KeyBindsList.CategoryEntry.extractContent port crates/horizon-client/src/options_ui.rs::OptionsUi::draw category header text +KeyBindsList.KeyEntry. port crates/horizon-client/src/options_ui.rs::OptionsUi::layout change and reset buttons per key row +KeyBindsList.KeyEntry.extractContent port crates/horizon-client/src/options_ui.rs::OptionsUi::draw row name and conflict highlight +KeyBindsList.KeyEntry.refreshEntry port crates/horizon-client/src/options_ui.rs::OptionsUi::layout conflicts from keys.conflicts; reset enabled when the key is not default +KeyBindsList.KeyEntry.updateChangeButtonMessage port crates/horizon-client/src/options_ui.rs::OptionsUi::layout key label, with the > < marker while waiting for a key (selected_key) +KeyBindsScreen.addContents port crates/horizon-client/src/options_ui.rs::OptionsUi::layout the key bind rows +KeyBindsScreen.addFooter port crates/horizon-client/src/options_ui.rs::OptionsUi::layout Reset All and Done buttons +KeyBindsScreen.repositionElements skip:ui layout only +KeyBindsScreen.mouseClicked port crates/horizon-client/src/options_ui.rs::OptionsUi::bind_mouse a mouse button is bound to the key being changed +KeyBindsScreen.extractRenderState port crates/horizon-client/src/options_ui.rs::OptionsUi::layout gap: minor: Reset All drawn in layout; its enabled-when-changed check was not confirmed +PackSelectionModel.getUnselected port crates/horizon-client/src/packs.rs::SelectionModel::list unselected list (list(false)), returned as a slice rather than a stream +PackSelectionModel.getSelected port crates/horizon-client/src/packs.rs::SelectionModel::list selected list (list(true)), returned as a slice rather than a stream +PackSelectionModel.updateRepoSelectedList port crates/horizon-client/src/packs.rs::SelectionModel::commit selection written bottom first, as options.txt stores it +PackSelectionModel.findNewPacks port crates/horizon-client/src/game/packs.rs::Game::refresh_pack_lists gap: minor: rescans when the options open, not while the screen is open (no folder watch) +PackSelectionModel.EntryBase.updateHighContrastOptionInstance missing minor: selecting the high_contrast pack does not switch the High Contrast option on, as vanilla does +PackSelectionModel.EntryBase.move port crates/horizon-client/src/packs.rs::SelectionModel::move_by move up or down within the list when the neighbour is not fixed +PackSelectionScreen.closeWatcher missing minor: no folder watcher to close (see PackSelectionScreen.Watcher) +PackSelectionScreen.setInitialFocus port crates/horizon-client/src/pack_screen.rs::PackScreen::set_focus search box focused on open +PackSelectionScreen.updateFilteredEntries port crates/horizon-client/src/pack_screen.rs::PackScreen::filter filters both lists by the search text +PackSelectionScreen.repositionElements port crates/horizon-client/src/pack_screen.rs::PackScreen::reposition search box and both list boxes +PackSelectionScreen.tick missing minor: no folder poll; packs added while the screen is open appear only when the options reopen (refresh_pack_lists) +PackSelectionScreen.reload port crates/horizon-client/src/pack_screen.rs::PackScreen::populate gap: minor: rebuilds both lists; no rescan here (see findNewPacks) +PackSelectionScreen.copyPacks missing minor: no pack copy into the packs folder; dropped packs are not copied +PackSelectionScreen.onFilesDrop missing minor: dropping pack files onto the screen does nothing; Horizon has no file-drop handling +PackSelectionScreen.getPackIcon port crates/horizon-client/src/pack_screen.rs::load_icon icons decoded once into PackInfo.icon, then bound as textures (icon_texture) +PackSelectionScreen.Watcher missing minor: no WatchService on the packs folder (create, watchDir, pollForChanges) +TransferableSelectionList.keyPressed missing minor: no keyboard entry navigation: keys reach only the search box (type_text, shortcut, erase), not a selected pack +TransferableSelectionList.HeaderEntry.extractContent port crates/horizon-client/src/pack_screen.rs::PackScreen::draw_list header row (Available or Selected) drawn in the list +TransferableSelectionList.PackEntry.getNarration missing minor: no narrator in Horizon +TransferableSelectionList.PackEntry.keyPressed missing minor: no keyboard select or move for a pack (Enter, Shift+Up/Down); nothing calls activate or move_by from keys +TransferableSelectionList.PackEntry.showHoverOverlay port crates/horizon-client/src/pack_screen.rs::PackScreen::overlay overlay shown unless the pack is both fixed and required +TransferableSelectionList.PackEntry.keyboardSelection missing minor: no keyboard selection (see PackEntry.keyPressed) +TransferableSelectionList.PackEntry.handlePackSelection port crates/horizon-client/src/pack_screen.rs::PackScreen::entry_click gap: minor: keyboard path missing (keyboardSelection); icon click selects; an incompatible pack opens the confirmation (open_confirm) first +TransferableSelectionList.PackEntry.shouldTakeFocusAfterInteraction skip:ui focus after a click matters only for keyboard entry navigation, which Horizon does not have +CraftingRecipeBookComponent.isCraftingSlot missing minor: unsure: no crafting-slot check found in the recipe book code +CraftingRecipeBookComponent.fillGhostRecipe port crates/horizon-client/src/recipe_book.rs::Book::ghost crafting branch: shaped recipes placed by place_cells, shapeless in order; result set first +FurnaceRecipeBookComponent.fillGhostRecipe port crates/horizon-client/src/recipe_book.rs::Book::ghost furnace branch: ingredient, and the fuel only when the fuel slot is empty (fuel_empty) diff --git a/tools/port_map/client2-018.tsv b/tools/port_map/client2-018.tsv new file mode 100644 index 0000000000..578913404c --- /dev/null +++ b/tools/port_map/client2-018.tsv @@ -0,0 +1,65 @@ +GhostSlots.extractTooltip missing player: hovering a ghost ingredient or result in the crafting grid or furnace shows no item tooltip; the ghost draw has no tooltip call and draw_slot_tooltip reads only real slots +GhostSlots.GhostSlot.getItem port crates/horizon-client/src/recipe_book_screen.rs::ghost the stack cycles with index % items; empty ghost slots are dropped in Book::ghost +OverlayRecipeComponent.mouseClicked port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click left click places the overlay's recipe; right click and outside clicks close the overlay +OverlayRecipeComponent.OverlayCraftingRecipeButton.calculateIngredientsPositions port crates/horizon-client/src/recipe_book_screen.rs::overlay_cells shaped recipes use the 3x3 grid cells, shapeless ones i % 3 and i / 3; empty ingredients are dropped +OverlayRecipeComponent.OverlayCraftingRecipeButton.getSprite port crates/horizon-client/src/recipe_book_screen.rs::overlay crafting_overlay sprite by craftable and hover +OverlayRecipeComponent.OverlayRecipeButton.createGridPos port crates/horizon-client/src/recipe_book_screen.rs::overlay cell at 3 + 7x, 3 + 7y inside the 24 px button +OverlayRecipeComponent.OverlayRecipeButton.extractWidgetRenderState port crates/horizon-client/src/recipe_book_screen.rs::overlay cells drawn as items at 0.375 scale, each cycling its stacks by index +OverlayRecipeComponent.OverlaySmeltingRecipeButton.getSprite port crates/horizon-client/src/recipe_book_screen.rs::overlay furnace_overlay sprites by craftable and hover +RecipeBookComponent.init inline crates/horizon-client/src/game/recipe_book.rs::Game::ensure_book the book and search box are built on first use; the open flag is Game::book_open and the views come from refresh_book_view +RecipeBookComponent.initVisuals inline crates/horizon-client/src/game/recipe_book.rs::Game::refresh_book_view tabs, buttons and page are rebuilt when inventory, grid, filter, tab, search text or known recipes change; the search box is drawn by screen::draw +RecipeBookComponent.setVisible port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click the book button flips the open flag and sends the settings; closing clears the overlay; gap: minor: a server settings packet sets the flag without clearing the overlay or page +RecipeBookComponent.slotClicked port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click a click on a grid, result or furnace slot clears the ghost recipe and the last placed recipe (is_book_slot) +RecipeBookComponent.selectMatchingRecipes inline crates/horizon-client/src/game/recipe_book.rs::Game::build_view a recipe is selected when it fits the crafting grid; craftable comes from Book::can_craft against book_available +RecipeBookComponent.updateCollections port crates/horizon-client/src/game/recipe_book.rs::Game::build_view collections filtered by the search (Book::collection_matches) and by the craftable filter; callers reset the page on tab and filter changes +RecipeBookComponent.updateTabs inline crates/horizon-client/src/game/recipe_book.rs::Game::build_view tab visibility; positions are in Game::tab_pos and the squeeze in Game::start_new_recipe_animations +RecipeBookComponent.tick port crates/horizon-client/src/game/recipe_book.rs::Game::draw_recipe_book advances the swap time and the squeezes; the inventory-changed refresh is the hash in refresh_book_view +RecipeBookComponent.extractTooltip port crates/horizon-client/src/game/recipe_book.rs::Game::draw_book_panel gap: player: no tooltip for ghost slots (GhostSlots.extractTooltip); the page's recipe tooltip is the ItemTooltip call +RecipeBookComponent.mouseClicked port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click gap: player: in the narrow layout vanilla closes the book after placing a recipe (isOffsetNextToMainGUI is false); Horizon keeps it open +RecipeBookComponent.mouseDragged missing player: dragging across the recipe search box does not select text; EditBox::drag_to is used only by the title screen +RecipeBookComponent.onTabButtonPress port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click a tab click selects the tab and resets the page to 0 +RecipeBookComponent.replaceSelected inline crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click sets recipe_ui.tab, so one tab is selected at a time +RecipeBookComponent.toggleFiltering port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click the craftable filter button flips the filter, sends the settings and resets the page +RecipeBookComponent.keyPressed port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_key search box keys and KeyT focus (Game::recipe_focus_search); gap: player: Enter and Space do not place the last clicked recipe (lastRecipe); gap: player: Escape does not close only the book in the narrow layout +RecipeBookComponent.charTyped port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_key typed text enters the search box from the key event; gap: minor: no separate char event, so no IME text +RecipeBookComponent.preeditUpdated missing minor: no IME composition in the recipe search box +RecipeBookComponent.capturesInput port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_search_focused true while the search box has focus +RecipeBookComponent.checkSearchStringUpdate inline crates/horizon-client/src/game/recipe_book.rs::Game::refresh_book_view the search text is part of the view key, so a change rebuilds the buttons +RecipeBookComponent.pirateSpeechForThePeople missing minor: typing excitedze in the recipe search switches the game to pirate English (en_pt); Horizon has no language switch from the book +RecipeBookComponent.recipesUpdated inline crates/horizon-client/src/game/recipe_book.rs::Game::refresh_book_view new or removed known recipes change client_version, which rebuilds the tabs and buttons +RecipeBookComponent.updateNarration skip:other screen-reader narration; Horizon has no narration output +RecipeBookPage.init inline crates/horizon-client/src/game/recipe_book.rs::Game::button_pos button places (5 per row, 25 px apart) are computed here; arrow places are fixed in recipe_book_click and screen::draw +RecipeBookPage.updateCollections inline crates/horizon-client/src/game/recipe_book.rs::Game::refresh_book_view page count is div_ceil(20) of the buttons; the page is reset to 0 when it is past the end +RecipeBookPage.updateButtonsForPage port crates/horizon-client/src/game/recipe_book.rs::Game::draw_book_panel the page's buttons are skip(page * 20).take(20) +RecipeBookPage.updateArrowButtons port crates/horizon-client/src/recipe_book_screen.rs::draw arrows show when there is more than one page and the page is not the first or the last +RecipeBookPage.mouseClicked port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click page arrows; right click opens the overlay when the button has more than one recipe; left click places +RecipeBookTabButton.extractContents port crates/horizon-client/src/recipe_book_screen.rs::draw tab sprite, the 2 px shift when selected, and the squeeze pose +RecipeBookTabButton.extractIcon port crates/horizon-client/src/recipe_book_screen.rs::draw one or two fake item icons at offsets 9, or 3 and 14, shifted 2 px when selected +RecipeBookTabButton.updateVisibility port crates/horizon-client/src/game/recipe_book.rs::Game::build_view a tab is shown when a collection of its category has a selected recipe +RecipeButton.extractWidgetRenderState port crates/horizon-client/src/recipe_book_screen.rs::draw sprite by craftable and by several recipes, squeeze pose, same-result item under the fake item +RecipeButton.getCurrentRecipe port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click the entry at swap_index % entries +RecipeButton.getTooltipText port crates/horizon-client/src/game/recipe_book.rs::Game::paint_book ItemTooltip: the stack's tooltip, then Right Click for More when there are several recipes +RecipeButton.updateWidgetNarration skip:other screen-reader narration; Horizon has no narration output +RecipeButton.isValidClickButton port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click left and right clicks only; a middle click does nothing here +RecipeButton.ResolvedEntry.selectItem port crates/horizon-client/src/recipe_book_screen.rs::display_stack the stack at offset % count; display_stack first picks the recipe from index % count +RecipeCollection.selectRecipes inline crates/horizon-client/src/game/recipe_book.rs::Game::build_view selected when the recipe fits the crafting grid; craftable from Book::can_craft against the stock +RecipeCollection.getSelectedRecipes inline crates/horizon-client/src/game/recipe_book.rs::Game::build_view entries kept per view: selected ones, and only craftable ones when filtering +AbstractReportScreen skip:service chat and skin abuse reporting screens; Horizon has no reporting +AbstractReportScreen.DiscardReportWarningScreen skip:service abuse report discard warning; Horizon has no reporting +ChatReportScreen skip:service chat abuse reporting screen; Horizon has no reporting +ChatSelectionLogFiller skip:service chat log filler for the report selection; Horizon has no reporting +ChatSelectionScreen skip:service chat message selection for a report; Horizon has no reporting +ChatSelectionScreen.ChatSelectionList skip:service chat message list of the report screen; Horizon has no reporting +ChatSelectionScreen.ChatSelectionList.DividerEntry skip:service divider row of the report chat list; Horizon has no reporting +ChatSelectionScreen.ChatSelectionList.MessageEntry skip:service message row of the report chat list; Horizon has no reporting +ChatSelectionScreen.ChatSelectionList.MessageHeadingEntry skip:service heading row of the report chat list; Horizon has no reporting +NameReportScreen skip:service player name abuse report screen; Horizon has no reporting +ReportPlayerScreen skip:service player abuse report screen; Horizon has no reporting +ReportReasonSelectionScreen skip:service abuse report reason screen; Horizon has no reporting +ReportReasonSelectionScreen.ReasonSelectionList skip:service reason list of the report screen; Horizon has no reporting +ReportReasonSelectionScreen.ReasonSelectionList.Entry skip:service reason row of the report screen; Horizon has no reporting +SkinReportScreen skip:service skin abuse report screen; Horizon has no reporting +PlayerEntry skip:service social list row with report, hide and status; Horizon has no social list +PlayerSocialManager skip:service Mojang social manager: block, friends and message hiding; Horizon has none of it +PresenceHandler skip:service online presence sharing with the service; Horizon has none +RemoteFriendListUpdateHandler skip:service friend list polling and toasts from the service; Horizon has no friend list (its relay is separate) diff --git a/tools/port_map/client2-019.tsv b/tools/port_map/client2-019.tsv new file mode 100644 index 0000000000..90821f5f21 --- /dev/null +++ b/tools/port_map/client2-019.tsv @@ -0,0 +1,150 @@ +SocialInteractionsPlayerList.enableScissor skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.updatePlayerList skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.addOnlinePlayers skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.addSeenPlayers skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.makePlayerEntry skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.updatePlayersFromChatLog skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.collectProfilesFromChatLog skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.sortPlayerEntries skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.updateFiltersAndScroll skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.updateFilteredPlayers skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.addPlayer skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.removePlayer skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsPlayerList.refreshHasDraftReport skip:service social list (hidden and blocked players); Horizon has no Social Interactions screen +SocialInteractionsScreen.getNarrationMessage skip:ui narration text only +SocialInteractionsScreen.init skip:service social screen: tabs, search box, server label; no Horizon counterpart +SocialInteractionsScreen.repositionElements skip:ui layout only +SocialInteractionsScreen.showPage skip:service social: All, Hidden and Blocked tabs +SocialInteractionsScreen.extractBackground skip:service social screen background +SocialInteractionsScreen.extractRenderState skip:service social screen drawing +SocialInteractionsScreen.keyPressed skip:service social keybind closes the screen +SocialInteractionsScreen.checkSearchStringUpdate skip:service social search filter +SocialInteractionsScreen.updateServerLabel skip:service social server label +TelemetryEventWidget.buildContent skip:service telemetry event list (opt-in data); Horizon's telemetry.rs is perf and crash reporting +TelemetryEventWidget.setScrollAmount skip:service telemetry event list (opt-in data); Horizon's telemetry.rs is perf and crash reporting +TelemetryEventWidget.extractContents skip:service telemetry event list (opt-in data); Horizon's telemetry.rs is perf and crash reporting +TelemetryEventWidget.addEventType skip:service telemetry event list (opt-in data); Horizon's telemetry.rs is perf and crash reporting +TelemetryEventWidget.addEventTypeProperties skip:service telemetry event list (opt-in data); Horizon's telemetry.rs is perf and crash reporting +TelemetryEventWidget.ContentBuilder.addLine skip:service telemetry event list builder +TelemetryEventWidget.ContentBuilder.addHeader skip:service telemetry event list builder +TelemetryEventWidget.ContentBuilder.build skip:service telemetry event list builder +TelemetryInfoScreen.getNarrationMessage skip:ui narration text only +TelemetryInfoScreen.init skip:service telemetry info screen: privacy, feedback, opt-in, view data +TelemetryInfoScreen.repositionElements skip:ui layout only +AbstractGameRulesScreen.createAndConfigureSearchBox port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::build search box made with the screen; the search text filters through rows() +AbstractGameRulesScreen.init port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::build title, search box and rule list; Done and Cancel are handled by RulesScreen::click +AbstractGameRulesScreen.repositionElements skip:ui layout only +AbstractGameRulesScreen.filterGameRules port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::rows gap: minor: the scroll reset to the top is in RulesScreen::edited, not here +AbstractGameRulesScreen.BooleanRuleEntry.extractContent port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::draw draws the on/off button of a boolean rule +AbstractGameRulesScreen.CategoryRuleEntry.extractContent port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::draw draws the category header row +AbstractGameRulesScreen.GameRuleEntry.extractLabel port crates/horizon-client/src/game_rules_screen.rs::lines wraps the rule label to 170 px (one or two lines); drawn by RulesScreen::draw +AbstractGameRulesScreen.IntegerRuleEntry.extractContent port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::draw draws the 44 px number box; invalid text turns red in RulesScreen::edited +AbstractGameRulesScreen.RuleList.matchesFilter port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::rows matches id, name, category and description, as rows() does +AbstractGameRulesScreen.RuleList.extractWidgetRenderState port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::draw hovered rule's tooltip: id, description, default +ConfirmExperimentalFeaturesScreen.getNarrationMessage skip:ui narration text only +ConfirmExperimentalFeaturesScreen.init missing minor: confirmation for experimental packs; Horizon has no experimental packs, so it never opens +ConfirmExperimentalFeaturesScreen.repositionElements skip:ui layout only +ConfirmExperimentalFeaturesScreen.DetailsScreen.init missing minor: details of the experimental packs; no such screen in Horizon +ConfirmExperimentalFeaturesScreen.DetailsScreen.repositionElements skip:ui layout only +ConfirmExperimentalFeaturesScreen.DetailsScreen.PackListEntry.extractContent missing minor: draws an experimental pack id and its message; no such list in Horizon +ConfirmExperimentalFeaturesScreen.DetailsScreen.PackListEntry.getNarration skip:ui narration text only +CreateWorldScreen.openFresh port crates/horizon-client/src/title.rs::Title::new_world gap: minor: no resource load for datapack registries before the screen +CreateWorldScreen.testWorld skip:debug TW button, shown only when running in the IDE +CreateWorldScreen.openCreateWorldScreen skip:data loads datapack registries and world presets before the screen; Horizon reads data from tables +CreateWorldScreen.createFromExisting port crates/horizon-client/src/title/create_world.rs::WorldOptions::of_world gap: minor: no datapacks copied; name, seed and mode come from title.rs click_action arm 15 +CreateWorldScreen.init port crates/horizon-client/src/title/create_world.rs::buttons tab bar and footer buttons; tabs are drawn by draw_create +CreateWorldScreen.repositionElements skip:ui layout only +CreateWorldScreen.onCreate port crates/horizon-client/src/title.rs::Title::create_action gap: minor: no experimental or deprecated-data warning before creating +CreateWorldScreen.createWorldAndCleanup inline crates/horizon-client/src/game/startup_menu.rs::StartupMenu::queue_entry gap: minor: temp datapack folder cleanup not modelled (no datapack copy) +CreateWorldScreen.createNewWorld port crates/horizon-client/src/game.rs::Game::open_world gap: minor: the folder is made in WorldRequest::prepare and no datapacks are copied in +CreateWorldScreen.createLevelSettings port crates/horizon-client/src/game/world_loading.rs::WorldRequest::prepare name, mode, difficulty, hardcore and commands written with the new level; gap: minor: debug world (spectator, peaceful) not modelled +CreateWorldScreen.keyPressed missing player: Enter does not create the world; startup_menu title_key only confirms a focused rename box, so Enter on Create New World does nothing +CreateWorldScreen.extractRenderState port crates/horizon-client/src/title/create_world.rs::Title::draw_create draws the footer separator +CreateWorldScreen.extractMenuBackground port crates/horizon-client/src/title/create_world.rs::Title::draw_create draws the tab header strip and menu background +CreateWorldScreen.getOrCreateTempDataPackDir missing minor: temp folder for datapacks chosen at creation; Horizon has no create-time datapack picker +CreateWorldScreen.openExperimentsScreen missing minor: Experiments screen, only in non-stable builds; Horizon's button is disabled and has no screen +CreateWorldScreen.openDataPackSelectionScreen missing player: the Data Packs button is disabled on the More tab, so the datapack picker never opens +CreateWorldScreen.tryApplyNewDataPacks missing player: part of the greyed-out Data Packs picker; picked packs never reach the new world +CreateWorldScreen.applyNewPackConfig missing player: part of the greyed-out Data Packs picker; validates and applies the picked packs +CreateWorldScreen.createDefaultLoadConfig skip:data builds the datapack load config; Horizon loads no datapack set at creation +CreateWorldScreen.removeTempDataPackDir missing minor: cleanup of the temp datapack folder; no such folder in Horizon +CreateWorldScreen.copyBetweenDirs missing minor: copies datapack files into the temp folder; not in Horizon +CreateWorldScreen.createNewWorldDirectory port crates/horizon-client/src/game/world_loading.rs::WorldRequest::prepare makes the world folder and writes level.dat; gap: minor: datapack files not copied in +CreateWorldScreen.createTempDataPackDirFromExistingWorld missing minor: Re-Create copies the old world's datapacks to a temp folder; Horizon's Re-Create does not +CreateWorldScreen.getDataPackSelectionSettings missing minor: temp datapack repository for the picker; no picker in Horizon +CreateWorldScreen.GameTab. port crates/horizon-client/src/title/create_world.rs::buttons Game tab: name box, game mode, difficulty, allow cheats; gap: minor: vanilla's Experiments button sits on this tab in non-stable builds +CreateWorldScreen.MoreTab.openGameRulesScreen port crates/horizon-client/src/title.rs::Title::click_action Game Rules button (arm 29) over a copy of the rules, via RulesScreen::for_creation +CreateWorldScreen.WorldTab. port crates/horizon-client/src/title/create_world.rs::buttons World tab: world type, customize, seed box (field 1), structures and bonus chest; gap: player: only Default and Superflat are offered, vanilla also offers Amplified and other presets +CreateWorldScreen.WorldTab.openPresetEditor port crates/horizon-client/src/title.rs::Title::click_action Customize button (arm 26) opens the Superflat editor (Mode::CreateFlat) +EditWorldScreen.create port crates/horizon-client/src/title.rs::Title::click_action Edit button (arm 13) builds the Edit mode from the list entry; gap: minor: no fixAndGetSummary (file fixing) on open +EditWorldScreen. port crates/horizon-client/src/title.rs::Title::buttons Edit buttons: reset icon, open folder, backup, backups folder, Optimize, Save, Cancel; gap: player: Optimize World is drawn inactive and does nothing +EditWorldScreen.keyPressed port crates/horizon-client/src/game/startup_menu.rs::StartupMenu::title_key Enter confirms a focused name box through confirm_edit; Escape goes back through Title::escape +EditWorldScreen.onRename port crates/horizon-client/src/title/world_management.rs::rename saves the trimmed name to level.dat; Save (arm 35) and Enter reach it, then return to the list +EditWorldScreen.makeBackupAndShowToast port crates/horizon-client/src/title.rs::Title::start_backup gap: minor: the success or failure toast and the return to the list are in Title::poll_backup +EditWorldScreen.extractRenderState port crates/horizon-client/src/title.rs::Title::draw draws the centred Edit World title +ExperimentsScreen.init missing minor: Experiments screen, only in non-stable builds; Horizon has none +ExperimentsScreen.getHumanReadableTitle missing minor: translated title of an experiment pack; Horizon has no experiments +ExperimentsScreen.repositionElements skip:ui layout only +ExperimentsScreen.getNarrationMessage skip:ui narration text only +ExperimentsScreen.onDone missing minor: applies the chosen experiments to the pack repository; none in Horizon +FileFixerProgressScreen.init missing player: Cancel while a world's files are fixed; Horizon fixes them with no progress screen +FileFixerProgressScreen.extractRenderState missing player: part of the file-fix progress screen, which Horizon never shows +FileFixerProgressScreen.extractProgress missing player: part of the file-fix progress screen, which Horizon never shows +FileFixerProgressScreen.extractProgressBar missing player: part of the file-fix progress screen, which Horizon never shows +FileFixerProgressScreen.extractTypeText missing player: part of the file-fix progress screen, which Horizon never shows +FileFixerProgressScreen.extractTypeProgress missing player: part of the file-fix progress screen, which Horizon never shows +FileFixerProgressScreen.extractFileStats missing player: part of the file-fix progress screen, which Horizon never shows +FileFixerProgressScreen.extractScanning missing player: part of the file-fix progress screen, which Horizon never shows +FileFixerProgressScreen.extractFileFixerCount missing player: part of the file-fix progress screen, which Horizon never shows +OptimizeWorldScreen.create missing player: opens from Optimize World, which Horizon draws inactive on purpose; no optimizer exists +OptimizeWorldScreen.init missing player: Cancel of the optimize run; no optimize run exists +OptimizeWorldScreen.extractRenderState missing player: optimize progress (converted, skipped, per-dimension bars); not shown +SelectWorldScreen.init port crates/horizon-client/src/title.rs::Title::buttons Play, Create, Edit, Delete, Re-Create and Back; the list comes from list_worlds in return_to_list +SelectWorldScreen.createFooterButtons port crates/horizon-client/src/title.rs::Title::buttons footer buttons for the selected world: Play, Create, Edit, Delete, Re-Create, Back +SelectWorldScreen.createDebugWorldRecreateButton skip:debug DEBUG recreate button, only with SharedConstants.DEBUG_WORLD_RECREATE +SelectWorldScreen.repositionElements skip:ui layout only +SelectWorldScreen.updateButtonStatus port crates/horizon-client/src/title.rs::Title::buttons gap: minor: buttons enable from the selection only; canEdit, canRecreate, canDelete and the primary action are not modelled +SwitchGrid.Builder.addSwitch skip:java builder for the switch rows +SwitchGrid.Builder.build skip:java layout builder for the switch rows; Horizon draws them in create_world buttons +SwitchGrid.LabeledSwitch.refreshState port crates/horizon-client/src/title/create_world.rs::buttons switch value and enabled flag are read from WorldOptions every frame +SwitchGrid.SwitchBuilder.build port crates/horizon-client/src/title/create_world.rs::buttons structures (27) and bonus chest (28) switches with their enable rules; structures info is in create_tooltip +WorldCreationContext.withSettings skip:java immutable copy-with helper; Horizon's WorldOptions is a plain struct changed in place +WorldCreationContext.withOptions skip:java immutable copy-with helper; Horizon's WorldOptions is changed in place +WorldCreationContext.withDimensions skip:java immutable copy-with helper; Horizon has no dimension settings to copy +WorldCreationContext.validate skip:data validates datapack dimension generators; Horizon has no datapack dimensions +WorldCreationGameRulesScreen.initContent port crates/horizon-client/src/game_rules_screen.rs::RulesScreen::for_creation creation rule list +WorldCreationUiState. port crates/horizon-client/src/title.rs::Title::new_world gap: minor: builds the defaults only; Re-Create and preset starts are in click_action and WorldOptions::of_world +WorldCreationUiState.onChanged skip:ui listener refresh; Horizon's buttons read WorldOptions every frame, bonus chest via WorldOptions::bonus_chest +WorldCreationUiState.setName port crates/horizon-client/src/title.rs::Title::edit_field name text kept in Mode::Create.name; gap: minor: target folder is not recomputed per keystroke (create_tooltip and create_action compute it) +WorldCreationUiState.findResultFolder port crates/horizon-client/src/title.rs::Title::target_folder gap: minor: a blank name gives folder World, vanilla gives New World +WorldCreationUiState.isAllowCommands port crates/horizon-client/src/title/create_world.rs::WorldOptions::commands never in Hardcore, else on for Creative until toggled +WorldCreationUiState.setSeed port crates/horizon-client/src/title.rs::parse_seed gap: minor: seed text is stored in Mode::Create.seed and parsed in create_action +WorldCreationUiState.isBonusChest port crates/horizon-client/src/title/create_world.rs::WorldOptions::bonus_chest never in Hardcore +WorldCreationUiState.tryUpdateDataConfiguration skip:data datapack and feature-flag config; Horizon has neither at creation +WorldCreationUiState.setWorldType port crates/horizon-client/src/title/create_world.rs::WorldOptions::press Superflat toggle (id 25); gap: player: only Default and Superflat exist, vanilla's other presets such as Amplified are missing +WorldCreationUiState.getPresetEditor port crates/horizon-client/src/title/create_world.rs::buttons customize button enabled only for Superflat; the editor opens in title.rs click_action arm 26 +WorldCreationUiState.updatePresetLists skip:data preset lists from the registry; Horizon has two built-in types +WorldCreationUiState.findPreset skip:data registry lookup +WorldCreationUiState.getNonEmptyList skip:data registry tag lookup +WorldCreationUiState.WorldTypeEntry.describePreset port crates/horizon-client/src/title/create_world.rs::buttons world type label: Default or Superflat +WorldCreationUiState.WorldTypeEntry.isAmplified missing player: there is no Amplified world type in Horizon, so its help tooltip never shows +WorldOpenFlows.createFreshLevel port crates/horizon-client/src/game/world_loading.rs::WorldRequest::prepare writes a fresh level.dat; gap: minor: vanilla uses it for the debug recreate and the demo world; no datapack dimensions +WorldOpenFlows.createWorldAccess port crates/horizon-client/src/save.rs::SessionLock::acquire takes the world's session lock as the access; gap: minor: no symlink warning +WorldOpenFlows.createLevelFromExistingSettings port crates/horizon-client/src/game.rs::Game::open_world starts the world with the given settings; gap: minor: no datapack repository is built +WorldOpenFlows.loadWorldStem port crates/horizon-client/src/game/world_loading.rs::WorldRequest::prepare reads level data and generator settings; gap: minor: no datapack load or safe-mode retry +WorldOpenFlows.recreateWorldData port crates/horizon-client/src/title/create_world.rs::WorldOptions::of_world reads the old world's settings for Re-Create; title.rs click_action arm 15 fills name, seed and mode +WorldOpenFlows.loadWorldDataBlocking port crates/horizon-client/src/game/world_loading.rs::WorldLoader::finish waits for the load worker and returns its result +WorldOpenFlows.askForBackup missing player: backup question before opening an old customized or experimental world; Horizon never asks +WorldOpenFlows.confirmWorldCreation skip:data lifecycle warning for experimental or deprecated data; Horizon has no such data +WorldOpenFlows.openWorld port crates/horizon-client/src/game/startup_menu.rs::StartupMenu::queue_entry Play (title.rs arm 10) queues the existing world for loading +WorldOpenFlows.openWorldLoadLevelData port crates/horizon-client/src/game/world_loading.rs::WorldRequest::prepare gap: player: a level.dat that will not read (Level::read gives None) opens with a random seed and the folder name, not the Recover World Data screen +WorldOpenFlows.openWorldCheckVersionCompatibility missing player: no incompatible-version alert and no backup question; Horizon upgrades older worlds and opens newer ones without a check +WorldOpenFlows.createBackupAndOpenWorld missing player: no backup step before opening an old world; the backup button exists only in Edit World +WorldOpenFlows.upgradeAndOpenWorld port crates/horizon-client/src/save.rs::upgrade_older_world runs the file fixes and upgrades on open; gap: player: no file-fix progress screen and no upgrade done, join now confirm +WorldOpenFlows.tryFileFixAndReportErrors port crates/horizon-world/src/datafix/files.rs::fix_world_files runs the file fixer; gap: player: cancel, symlink and failed-cleanup alerts are not shown +WorldOpenFlows.openWorldLoadLevelStem port crates/horizon-client/src/game/world_loading.rs::WorldRequest::prepare loads the level stem and generator; gap: minor: no safe-mode retry +WorldOpenFlows.openWorldCheckWorldStemCompatibility missing player: old-customized and experimental warning before the load; Horizon has none +WorldOpenFlows.openWorldLoadBundledResourcePack missing player: the world's own resources.zip is not applied on open; only an old-world file fix moves it into resourcepacks (datafix fix 4772) +WorldOpenFlows.openWorldCheckDiskSpace missing player: no low-disk-space warning before loading a world +WorldOpenFlows.loadBundledResourcePack missing player: pushes the world's resources.zip as a local pack; Horizon has no such path +WorldOpenFlows.promptBundledPackLoadFailure missing minor: prompt when the world's own pack fails to load; no such pack in Horizon diff --git a/tools/port_map/client2-020.tsv b/tools/port_map/client2-020.tsv new file mode 100644 index 0000000000..509e5ce101 --- /dev/null +++ b/tools/port_map/client2-020.tsv @@ -0,0 +1,32 @@ +WorldSelectionList.pollLevelsIgnoreErrors inline crates/horizon-client/src/title.rs::list_worlds the list is read at once, so there is no pending state; a world whose level.dat fails to read is skipped; the upload filter is Realms only, not ported +WorldSelectionList.extractWidgetRenderState inline crates/horizon-client/src/title.rs::Title::draw the list comes from recent_worlds, re-read at most every 2 seconds, not on every frame +WorldSelectionList.handleNewLevels inline crates/horizon-client/src/title.rs::Title::enter_singleplayer no worlds opens the new-world screen; otherwise the list is Mode::Select; the Realms upload empty entry is skipped +WorldSelectionList.updateFilter inline crates/horizon-client/src/title.rs::Title::visible_worlds the search text is applied at each draw; there is no refill step +WorldSelectionList.loadLevels inline crates/horizon-client/src/title.rs::list_worlds gap: minor: an unreadable saves folder shows no worlds instead of the Unable to load error screen +WorldSelectionList.fillLevels inline crates/horizon-client/src/title.rs::Title::search_changed gap: minor: any search edit clears the selection; vanilla keeps it while its world still matches the filter +WorldSelectionList.filterAccepts port crates/horizon-client/src/title.rs::Title::matches_search +WorldSelectionList.handleLevelLoadFailure inline crates/horizon-client/src/title.rs::Title::failed gap: minor: list_worlds never fails, so the Unable to load screen never shows +WorldSelectionList.setSelected inline crates/horizon-client/src/title.rs::Title::click_action selection is set on the row click path and cleared by search edits +WorldSelectionList.getSelectedOpt inline crates/horizon-client/src/title.rs::Title::click_action the selected world is looked up by index in the Select arms +WorldSelectionList.updateWidgetNarration skip:ui narration only; Horizon has no narrator output +WorldSelectionList.Builder.build skip:java builder for the list constructor +WorldSelectionList.LoadingHeader.extractContent skip:other saves are read at once, so the loading header is never shown +WorldSelectionList.NoWorldsEntry.extractContent skip:service empty entry of the Realms world upload list only +WorldSelectionList.WorldListEntry. port crates/horizon-client/src/title.rs::Title::draw gap: minor: no hover tooltip on overflowing name, folder or info text; the name, folder and date, and mode and version lines are drawn +WorldSelectionList.WorldListEntry.validateIconFile port crates/horizon-client/src/title.rs::Title::icon gap: minor: no symlink check; an icon that is not a decodable 64x64 image shows the unknown-server icon +WorldSelectionList.WorldListEntry.getNarration skip:ui narration text only; Horizon has no narrator output +WorldSelectionList.WorldListEntry.keyPressed missing player: Enter or Space on a selected world does nothing (title Enter only confirms a rename), so keyboard players cannot join from the list +WorldSelectionList.WorldListEntry.canInteract missing player: vanilla greys out locked and incompatible worlds and will not open them; Horizon's list_worlds has no lock or compatibility check, so Play opens every listed world +WorldSelectionList.WorldListEntry.joinWorld port crates/horizon-client/src/title.rs::Title::click_action gap: minor: no warning for a symlinked world before it opens +WorldSelectionList.WorldListEntry.deleteWorld port crates/horizon-client/src/title.rs::Title::click_action gap: minor: no progress screen while the world is deleted +WorldSelectionList.WorldListEntry.doDeleteWorld port crates/horizon-client/src/title.rs::Title::confirm_delete gap: minor: a failed delete shows the error screen, not the world delete failure toast +WorldSelectionList.WorldListEntry.editWorld port crates/horizon-client/src/title.rs::Title::click_action gap: minor: no data-read screen and no access-failure toast +WorldSelectionList.WorldListEntry.recreateWorld port crates/horizon-client/src/title.rs::Title::click_action gap: player: Re-Create takes the name, seed, mode and options into Create; no old-customized-world confirm, no datapack copy, and an unreadable world does nothing instead of showing an alert +WorldSelectionList.WorldListEntry.loadIcon port crates/horizon-client/src/title.rs::Title::icon icon.png is decoded at draw time and cached by modification time; gap: minor: no symlink check +PlayerMenuItem.extractIcon port crates/horizon-client/src/spectator_menu.rs::icon Item::Player arm: face at 2,2 size 12; vanilla ignores brightness, but only enabled players are listed, so no difference +SpectatorMenu.getItems inline crates/horizon-client/src/spectator_menu.rs::SpectatorGui::hotbar the nine-slot loop; each slot is read through SpectatorMenu::item +SpectatorPage.getItem inline crates/horizon-client/src/spectator_menu.rs::SpectatorMenu::item the page bounds check and the empty-slot fallback are folded into item +TeleportToPlayerMenuCategory.extractIcon port crates/horizon-client/src/spectator_menu.rs::icon Item::TeleportToPlayer arm: teleport_to_player sprite at 16x16 with brightness and alpha tint +TeleportToTeamMenuCategory.extractIcon port crates/horizon-client/src/spectator_menu.rs::icon Item::TeleportToTeam arm: teleport_to_team sprite at 16x16 with brightness and alpha tint +TeleportToTeamMenuCategory.TeamSelectionItem.create port crates/horizon-client/src/game/spectator.rs::Game::new_spectator_menu team members come from the tab list, non-spectators only; no members means no entry; the icon is a random member's skin +TeleportToTeamMenuCategory.TeamSelectionItem.extractIcon port crates/horizon-client/src/spectator_menu.rs::icon Item::Team arm: team colour fill at 1,1 to 15,15 scaled by brightness, then the face diff --git a/tools/port_map/final-001.tsv b/tools/port_map/final-001.tsv new file mode 100644 index 0000000000..cd9014aeaa --- /dev/null +++ b/tools/port_map/final-001.tsv @@ -0,0 +1,11 @@ +LocationPredicate.matches port crates/horizon-client/src/advancements.rs::location position, biome, structure, dimension and block checks for advancement criteria +MultiLineEditBox.setValue missing minor: unsure: book pages keep their own text in books.rs; no multi-line box setter found +TransferableSelectionList.PackEntry.mouseClicked inline crates/horizon-client/src/pack_screen.rs::PackScreen::click entry select, move arrows and double-click transfer +CompoundTag.getIntArray skip:java typed NBT accessor; Rust matches Tag::IntArray where it reads one +Component.toFlatList skip:java Horizon Text already keeps flat styled spans +Style.withColor port crates/horizon-client-core/src/text.rs::Text::with_color colour on the text span +MinecraftServer.halt port crates/horizon-client/src/game/integrated.rs::Integrated::halt stop the integrated server and wait for it +DedicatedPlayerList.loadOps inline crates/horizon-server-files/src/user_lists.rs::UserList::load ops.json through the shared user-list loader +ServerStatsCounter.toJson inline crates/horizon-client/src/game/stats.rs::Stats::write stats JSON built while saving +LevelChunk.getFluidState port crates/horizon-world/src/level/mod.rs::Behaviours::fluid fluid state of a block state +Fox.getBreedOffspring inline crates/horizon-client/src/game/creatures.rs::Game::breed the Fox arm: either parent's variant on a coin flip diff --git a/tools/port_map/game-001.tsv b/tools/port_map/game-001.tsv new file mode 100644 index 0000000000..8422cccccc --- /dev/null +++ b/tools/port_map/game-001.tsv @@ -0,0 +1,144 @@ +BlockDestructionProgress.equals skip:java identity by breaker id; Horizon keys cracks by breaker id in a map +BlockDestructionProgress.compareTo inline crates/horizon-client/src/game.rs::Game::cracks the furthest progress on a block wins (max per position) +ChunkGenerationTask.create inline crates/horizon-client/src/streaming.rs::Streaming::schedule_generation a Job::Generate per column; neighbours are held as protos, not a claimed holder cache +ChunkGenerationTask.runUntilWait inline crates/horizon-client/src/streaming.rs::Streaming::update stages advance as worker results come back, not by waiting on futures +ChunkGenerationTask.scheduleNextLayer inline crates/horizon-client/src/streaming.rs::Streaming::update next stage is generate, then decorate, then finish +ChunkGenerationTask.releaseClaim skip:other generation claims keep a chunk from unloading while a task reads it; Horizon checks protos out (None) to the job instead +ChunkGenerationTask.canLoadWithoutGeneration inline crates/horizon-client/src/streaming.rs::ChunkGenerator::load the worker tries a saved final chunk first, else makes (or restores) a proto +ChunkGenerationTask.getCenter skip:java accessor +ChunkGenerationTask.scheduleLayer inline crates/horizon-client/src/streaming.rs::Streaming::update one stage over the 3x3 neighbourhoods +ChunkGenerationTask.getRadiusForLayer inline crates/horizon-client/src/streaming.rs::Ranges::margins pyramid radius is two fixed margins (generate 3, decorate 2) +ChunkGenerationTask.scheduleChunkInLayer inline crates/horizon-client/src/streaming.rs::Streaming::update decorate and finish jobs are queued per column when the 3x3 is ready +ChunkGenerationTask.waitForScheduledLayer skip:java future bookkeeping +ChunkHolder.addSendDependency skip:java future combinator; chunks are only sent once lit (Hub::next_batch) +ChunkHolder.addSaveDependency skip:java future combinator; saving snapshots resident chunks directly +ChunkHolder.blockChanged port crates/horizon-client/src/world.rs::World::set pushes the position to block_log; block entity only changes go through Game::mark_block_entity_updated +ChunkHolder.sectionLightChanged port crates/horizon-client/src/streaming.rs::Streaming::mark_light_changes light_log bit per section; the unsaved mark is world.modified in the Lit result +ChunkHolder.hasChangesToBroadcast inline crates/horizon-client/src/lan/hub.rs::Hub::broadcast_block_changes both broadcast fns return early when their log is empty +ChunkHolder.broadcastBlockEntity port crates/horizon-client/src/lan/hub.rs::Hub::broadcast_block_changes BlockEntityData for changed positions with data the client shows +ChunkHolder.scheduleFullChunkPromotion inline crates/horizon-client/src/game.rs::Game::start_ticking_chunks ticking promotion unpacks saved ticks; ticket levels are read directly +ChunkHolder.demoteFullChunk skip:other only tells the status listener (loading map), which Horizon reads from Streaming::latest_status +ChunkHolder.updateFutures inline crates/horizon-client/src/game.rs::Game::start_ticking_chunks promotion to ticking on level change; demotion needs no work as Levels is read each tick +ChunkLevel.getStatusAroundFullChunk inline crates/horizon-client/src/streaming.rs::Ranges::margins fixed margins around the view instead of a status per distance +ChunkLevel.byStatus skip:other per status ticket levels; Horizon streams by distance (Ranges), not by level +ChunkLevel.fullStatus port crates/horizon-client/src/ticket_levels.rs::Levels::loaded_ticking 31 entity ticking, 32 block ticking, 33 full as predicates on Levels +ChunkLoadCounter.track inline crates/horizon-client/src/game.rs::Game::level_load_progress gap: minor: counts only the player's 7x7 chunks, not the initial forced or ticketed chunks +ChunkLoadCounter.pendingChunks inline crates/horizon-client/src/game.rs::Game::level_load_progress counts lit chunks in the 7x7 around the player +ChunkMap. inline crates/horizon-client/src/streaming.rs::Streaming::new worker pool and generator; light engine and POI storage are set up elsewhere +ChunkMap.isChunkTracked inline crates/horizon-client/src/lan/hub.rs::Hub::broadcast_block_changes sent_chunks.contains: in the view and already sent +ChunkMap.getChunkQueueLevel inline crates/horizon-client/src/streaming.rs::Ranges::priority job priority is a distance from the centre, not a ticket level +ChunkMap.getChunkDebugData skip:debug chunk debug renderer text +ChunkMap.getChunkRangeFuture inline crates/horizon-client/src/streaming.rs::Streaming::update neighbourhood waits: 3x3 protos to decorate, 3x3 decorated to finish, 3x3 lit to mesh +ChunkMap.debugFuturesAndCreateReportedException skip:debug crash report detail +ChunkMap.prepareEntityTickingChunk inline crates/horizon-client/src/game/creatures.rs::Game::entity_ticking_area entity ticking comes from the ticket levels alone +ChunkMap.updateChunkScheduling inline crates/horizon-client/src/streaming.rs::Streaming::update columns past every kept range are unloaded in the update scan +ChunkMap.onLevelChange port crates/horizon-client/src/streaming.rs::Streaming::requeue queued jobs are ranked again when the centre moves +ChunkMap.close port crates/horizon-client/src/streaming.rs::Streaming::drop waits for saves in flight +ChunkMap.saveAllChunks port crates/horizon-client/src/game.rs::Game::save_level modified chunks only; wait picks the flushing form +ChunkMap.hasWork port crates/horizon-client/src/streaming.rs::Streaming::busy +ChunkMap.processUnloads inline crates/horizon-client/src/game.rs::Game::update_world unloaded columns: modified ones saved, entities and block entities dropped +ChunkMap.saveChunksEagerly inline crates/horizon-client/src/streaming.rs::Streaming::receive_generated gap: minor: fresh chunks are marked modified and written at the next autosave or unload, not within a few ticks +ChunkMap.scheduleUnload inline crates/horizon-client/src/game.rs::Game::update_world save if modified, unload entities and block entities +ChunkMap.promoteChunkMap skip:java copy of the holder map for the main thread; Horizon has one chunk map +ChunkMap.scheduleChunkLoad port crates/horizon-client/src/streaming.rs::ChunkGenerator::load the Generate job loads a saved final chunk, else a proto is made +ChunkMap.handleChunkLoadFailure inline crates/horizon-client/src/save.rs::SaveDir::load gap: minor: an unreadable record gives None and the column is generated again with no failure report +ChunkMap.createEmptyChunk inline crates/horizon-client/src/streaming.rs::Streaming::schedule_generation no saved data: the Generate job makes a fresh proto +ChunkMap.markPosition skip:java chunk type cache for isExistingChunkFull +ChunkMap.applyStep inline crates/horizon-client/src/streaming.rs::Streaming::new the worker loop runs the Generate, Decorate and Finish jobs +ChunkMap.scheduleGenerationTask port crates/horizon-client/src/streaming.rs::Streaming::schedule_generation +ChunkMap.runGenerationTask port crates/horizon-client/src/streaming.rs::Streaming::submit queues the job by distance +ChunkMap.onChunkReadyToSend inline crates/horizon-client/src/lan/hub.rs::Hub::next_batch queued columns are sent once lit +ChunkMap.prepareAccessibleChunk inline crates/horizon-client/src/streaming.rs::Streaming::update a column finishes once its 3x3 is decorated +ChunkMap.allChunksWithAtLeastStatus inline crates/horizon-client/src/game.rs::Game::level_load_progress only the loading screen asks: lit chunks around the player +ChunkMap.saveChunkIfNeeded inline crates/horizon-client/src/game.rs::Game::save_level the modified set stands for the unsaved flag; autosave every 6000 ticks +ChunkMap.isExistingChunkFull inline crates/horizon-client/src/streaming.rs::Streaming::save_checked_out protos exist only for columns not final, so one cannot overwrite a final chunk +ChunkMap.markChunkPendingToSend port crates/horizon-client/src/lan/hub.rs::requeue chunks entering the view join the guest's queue, closest first +ChunkMap.getChunkToSend inline crates/horizon-client/src/lan/hub.rs::Hub::next_batch only lit columns are taken +ChunkMap.dumpChunks skip:debug CSV chunk dump +ChunkMap.printFuture skip:debug +ChunkMap.readChunk port crates/horizon-client/src/save.rs::SaveDir::read_record reads the raw record (pending writes first); the upgrade is done when the world opens +ChunkMap.upgradeChunkTag inline crates/horizon-world/src/datafix/files.rs::upgrade_world_data chunks are upgraded all at once when an older world opens, not as each is read +ChunkMap.getChunkDataFixContextTag inline crates/horizon-world/src/datafix/files.rs::upgrade_world_data dimension id and generator type passed to update_chunk per dimension folder +ChunkMap.forEachBlockTickingChunk port crates/horizon-client/src/ticket_levels.rs::Levels::random_ticking simulation entity ticking and a ticking chunk +ChunkMap.anyPlayerCloseEnoughForSpawningInternal inline crates/horizon-client/src/game/natural_spawning.rs::Game::spawning_chunks any non-spectator within 128 blocks of the chunk centre +ChunkMap.playerIsCloseEnoughForSpawning port crates/horizon-client/src/game/natural_spawning.rs::close_enough_for_spawning +ChunkMap.playerIsCloseEnoughTo inline crates/horizon-client/src/game.rs::GameLevel::can_spread_fire_around the only caller (fire spread radius), spectators left out +ChunkMap.skipPlayer missing minor: spectators always hold chunk tickets; the spectators_generate_chunks rule is stored but never read, so with it off a spectator still loads and generates chunks +ChunkMap.updatePlayerStatus inline crates/horizon-client/src/game.rs::Game::update_chunk_tickets tickets follow every player in the level each tick; a leaving guest's view is dropped by the hub +ChunkMap.updateChunkTracking port crates/horizon-client/src/lan/hub.rs::Hub::update_view a new centre or view distance (center reset) re-applies the view +ChunkMap.applyChunkTrackingView inline crates/horizon-client/src/lan/hub.rs::Hub::update_view SetChunkCacheCenter, then forget and queue the difference +ChunkMap.getPlayers inline crates/horizon-client/src/lan/hub.rs::Hub::broadcast_block_changes guests whose sent_chunks hold the column; the border form is in broadcast_light_changes +ChunkMap.removeEntity inline crates/horizon-client/src/lan/entity.rs::Entities::tick entities gone from the level leave each client in one RemoveEntities at the next tick +ChunkMap.sendToTrackingPlayers port crates/horizon-client/src/lan/containers.rs::Hub::broadcast_near gap: minor: goes to guests within a fixed distance, not to the clients that track the entity +ChunkMap.sendToTrackingPlayersFiltered port crates/horizon-client/src/lan/containers.rs::Hub::broadcast_near gap: minor: only an except-one-guest filter, same distance rule +ChunkMap.sendToTrackingPlayersAndSelf port crates/horizon-client/src/lan/containers.rs::Hub::critical_hit the one caller, the crit animation, goes to the attacker and the clients tracking them +ChunkMap.isTrackedByAnyPlayer skip:debug only the debug synchronizers ask +ChunkMap.forEachEntityTrackedBy skip:debug only the debug synchronizers ask +ChunkMap.resendBiomesForChunks missing minor: /fillbiome changes the host's biomes but sends guests no ChunksBiomes packet, so LAN guests keep the old biome tints until their chunks are sent again +ChunkMap.waitForLightBeforeSending inline crates/horizon-client/src/lan/hub.rs::Hub::next_batch columns are sent only once lit +ChunkMap.forEachReadyToSendChunk skip:debug only the debug synchronizers ask +ChunkMap.TrackedEntity.equals skip:java identity by entity id; Horizon keys tracked entities by id +ChunkMap.TrackedEntity.sendToTrackingPlayers port crates/horizon-client/src/lan/containers.rs::Hub::broadcast_near gap: minor: distance based, see ChunkMap.sendToTrackingPlayers +ChunkMap.TrackedEntity.sendToTrackingPlayersAndSelf port crates/horizon-client/src/lan/containers.rs::Hub::critical_hit the attacker plus clients tracking them +ChunkMap.TrackedEntity.sendToTrackingPlayersFiltered port crates/horizon-client/src/lan/containers.rs::Hub::broadcast_near gap: minor: only an except-one-guest filter +ChunkMap.TrackedEntity.broadcastRemoved inline crates/horizon-client/src/lan/entity.rs::Entities::tick removed entities leave each client in one RemoveEntities +ChunkMap.TrackedEntity.removePlayer inline crates/horizon-client/src/lan/entity.rs::Entities::tick the client's tracker loses entities out of range or level +ChunkMap.TrackedEntity.updatePlayers port crates/horizon-client/src/lan/entity.rs::Entities::tick gap: player: broadcastToPlayer is not checked, so a spectating player is tracked and shown to non-spectators as a normal player model +ChunkResult.error skip:java result type +ChunkTaskDispatcher.hasWork port crates/horizon-client/src/streaming.rs::Streaming::busy +ChunkTaskDispatcher.onLevelChange port crates/horizon-client/src/streaming.rs::Streaming::requeue +ChunkTaskDispatcher.release missing minor: queued jobs of a column that lost its ticket are not cancelled; their results are dropped on arrival (Done::Proto range check) +ChunkTaskDispatcher.submit port crates/horizon-client/src/streaming.rs::Streaming::submit +ChunkTaskDispatcher.pollTask inline crates/horizon-client/src/streaming.rs::Streaming::new each worker pops the most urgent job +ChunkTaskDispatcher.scheduleForExecution inline crates/horizon-client/src/streaming.rs::Streaming::new the worker runs the job it popped +ChunkTaskPriorityQueue.resortChunkTasks port crates/horizon-client/src/streaming.rs::rank_again +ChunkTaskPriorityQueue.release missing minor: no per column release; queued jobs stay queued until run +ChunkTaskPriorityQueue.pop inline crates/horizon-client/src/streaming.rs::Streaming::new BinaryHeap pop in the worker loop +ChunkTracker.checkNeighborsAfterUpdate inline crates/horizon-client/src/ticket_levels.rs::compute closed form: ticket level plus Chebyshev distance, rebuilt when sources change +ChunkTracker.getComputedLevel inline crates/horizon-client/src/ticket_levels.rs::compute lowest ticket level plus distance, per cell +ChunkTracker.computeLevelFromNeighbor inline crates/horizon-client/src/ticket_levels.rs::compute one level per chunk of distance +ChunkTrackingView.difference inline crates/horizon-client/src/lan/hub.rs::Hub::update_view forget what left (scan of sent chunks), queue what entered (requeue) +ChunkTrackingView.isWithinDistance port crates/horizon-client/src/lan/hub.rs::tracked buffer 2 form; the buffer 1 form is streaming.rs in_view_distance +ChunkTrackingView.Positioned.squareIntersects skip:java optimisation that picks a box scan over a full enumeration +ChunkTrackingView.Positioned.contains port crates/horizon-client/src/lan/hub.rs::tracked +ChunkTrackingView.Positioned.forEach inline crates/horizon-client/src/lan/hub.rs::requeue loops the view's square around the centre +ColumnPos.asLong skip:java key packing +DemoMode missing minor: no demo mode: no demo timer, intro hints or five day limit (the dedicated server's --demo argument is accepted and ignored) +DistanceManager.runAllUpdates port crates/horizon-client/src/ticket_levels.rs::ChunkTickets::update levels rebuilt when sources change +DistanceManager.addPlayer inline crates/horizon-client/src/game.rs::Game::update_chunk_tickets players are tickets sources each tick +DistanceManager.removePlayer inline crates/horizon-client/src/game.rs::Game::update_chunk_tickets a player gone from the level drops out of the sources +DistanceManager.getChunkLevel port crates/horizon-client/src/ticket_levels.rs::Levels::get loading and simulation level as a pair +DistanceManager.updateSimulationDistance inline crates/horizon-client/src/game.rs::Game::update_chunk_tickets simulation_distance read from settings into Sources +DistanceManager.hasPlayersNearby inline crates/horizon-client/src/mobs/spawn_selection.rs::SpawnState::players_near the 8 chunk gate; the 5 chunk shortcut is skipped +DistanceManager.forEachEntityTickingChunk inline crates/horizon-client/src/ticket_levels.rs::Levels::random_ticking the simulation half of the random tick test +DistanceManager.FixedPlayerDistanceChunkTracker.setLevel inline crates/horizon-client/src/ticket_levels.rs::compute cells are filled by compute +DistanceManager.FixedPlayerDistanceChunkTracker.havePlayer inline crates/horizon-client/src/game.rs::Game::update_chunk_tickets players' chunks are the sources +DistanceManager.PlayerTicketTracker.onLevelChange inline crates/horizon-client/src/ticket_levels.rs::compute loading level 31 plus distance beyond the view; no throttle of 4 loads at a time +DistanceManager.PlayerTicketTracker.updateViewDistance inline crates/horizon-client/src/ticket_levels.rs::reach view distance sets how far the loading levels reach +DistanceManager.PlayerTicketTracker.runAllUpdates inline crates/horizon-client/src/ticket_levels.rs::ChunkTickets::update +GenerationChunkHolder.scheduleChunkGenerationTask inline crates/horizon-client/src/streaming.rs::Streaming::schedule_generation the scan picks the columns that need a job +GenerationChunkHolder.applyStep inline crates/horizon-client/src/streaming.rs::Streaming::update queues the Decorate and Finish jobs +GenerationChunkHolder.updateHighestAllowedStatus inline crates/horizon-client/src/streaming.rs::Ranges::requested how far a column may be generated follows the ranges +GenerationChunkHolder.replaceProtoChunk inline crates/horizon-client/src/streaming.rs::Streaming::receive_generated the proto becomes the final column +GenerationChunkHolder.rescheduleChunkTask inline crates/horizon-client/src/streaming.rs::GenerationScan::candidates wanted columns are scanned again when ranges change +GenerationChunkHolder.getOrCreateFuture skip:java per status future +GenerationChunkHolder.failAndClearPendingFuturesBetween skip:java future bookkeeping +GenerationChunkHolder.failAndClearPendingFuture skip:java future bookkeeping +GenerationChunkHolder.completeFuture skip:java future bookkeeping +GenerationChunkHolder.findHighestStatusWithPendingFuture skip:java future bookkeeping +GenerationChunkHolder.acquireStatusBump skip:java future bookkeeping +GenerationChunkHolder.isStatusDisallowed inline crates/horizon-client/src/streaming.rs::Ranges::requested +GenerationChunkHolder.increaseGenerationRefCount skip:other keeps a chunk from saving or unloading under a task; Horizon checks protos out to the job instead +GenerationChunkHolder.decreaseGenerationRefCount skip:other see increaseGenerationRefCount +GenerationChunkHolder.getChunkIfPresentUnchecked skip:java future lookup; Horizon reads protos and world.chunks +GenerationChunkHolder.getLatestChunk skip:java future lookup; Horizon reads protos and world.chunks +GenerationChunkHolder.getPersistedStatus skip:other Horizon tells proto and final apart by type (SavedProto or Chunk) +GenerationChunkHolder.getAllFutures skip:debug +GenerationChunkHolder.getLatestStatus port crates/horizon-client/src/streaming.rs::Streaming::latest_status furthest step reached, for the loading map +LoadingChunkTracker.getLevel port crates/horizon-client/src/ticket_levels.rs::Levels::get the loading half of the pair +LoadingChunkTracker.setLevel inline crates/horizon-client/src/ticket_levels.rs::ChunkTickets::update levels are rebuilt when sources change +PlayerSpawnFinder.scheduleNext port crates/horizon-client/src/world_spawn.rs::PlayerSpawnFinder::next_candidate coprime stride order, then the suggestion +PlayerSpawnFinder.scheduleCandidate port crates/horizon-client/src/game/initial_spawn.rs::Game::search_spawn gap: minor: the in-frame path waits for the candidate's chunk, but a server tick (LAN, dedicated) passes over candidates in unloaded chunks, so a new guest can stand on another spot of the radius +SectionTracker.checkNeighborsAfterUpdate inline crates/horizon-client/src/game/raids.rs::sections_to_village its only subclass is the POI village distance: Chebyshev distance in sections, 7 when farther than 6 +SectionTracker.getComputedLevel inline crates/horizon-client/src/game/raids.rs::sections_to_village +SectionTracker.computeLevelFromNeighbor inline crates/horizon-client/src/game/raids.rs::sections_to_village diff --git a/tools/port_map/game-002.tsv b/tools/port_map/game-002.tsv new file mode 100644 index 0000000000..6d46ccecf1 --- /dev/null +++ b/tools/port_map/game-002.tsv @@ -0,0 +1,146 @@ +ServerBossEvent.setProgress inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events UpdateProgress goes out when a viewer's last-sent progress differs +ServerBossEvent.setColor inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events UpdateStyle when colour or overlay differ from what the viewer was sent +ServerBossEvent.setOverlay inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events same UpdateStyle packet as the colour +ServerBossEvent.setDarkenScreen inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events UpdateProperties when darken, music or fog differ +ServerBossEvent.setPlayBossMusic inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events same UpdateProperties packet +ServerBossEvent.setCreateWorldFog inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events same UpdateProperties packet +ServerBossEvent.setName inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events UpdateName when the name differs +ServerBossEvent.broadcast inline crates/horizon-client/src/game/bossbars.rs::Game::publish_boss_bars bars are re-evaluated each tick per viewer and the diff is sent to that viewer only +ServerBossEvent.addPlayer inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events a viewer in the bar's player list gets an Add the first time it is wanted +ServerBossEvent.removePlayer inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events a Remove goes out once the viewer no longer wants the bar +ServerBossEvent.removeAllPlayers inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events every viewer gets its Remove from the same diff +ServerBossEvent.setVisible inline crates/horizon-client/src/game/bossbars.rs::Game::boss_events_for a hidden bar is left out of the wanted list, so the diff sends Remove or Add +ServerChunkCache. inline crates/horizon-client/src/streaming.rs::Streaming::new builds the chunk map and workers per level; forced chunks are loaded in world_loading, no ticket storage object +ServerChunkCache.storeInCache skip:java 4-entry last-chunk lookup cache +ServerChunkCache.getChunk inline crates/horizon-client/src/streaming.rs::Streaming::update chunks load and generate asynchronously around tickets; callers read World::chunks and skip an absent column, nothing waits for a target status +ServerChunkCache.getChunkNow port crates/horizon-client/src/world.rs::World::loaded World::chunks.get is the lookup itself +ServerChunkCache.clearCache skip:java clears the 4-entry lookup cache +ServerChunkCache.getChunkFuture inline crates/horizon-client/src/streaming.rs::Streaming::update no futures; the worker pool delivers columns to the game thread +ServerChunkCache.getChunkFutureMainThread inline crates/horizon-client/src/streaming.rs::Streaming::update no futures or per-request tickets; streaming loads by distance +ServerChunkCache.hasChunk port crates/horizon-client/src/world.rs::World::loaded +ServerChunkCache.getChunkForLighting inline crates/horizon-client/src/streaming.rs::Streaming::light_region the 3x3 region handed to the light workers +ServerChunkCache.runDistanceManagerUpdates inline crates/horizon-client/src/game.rs::Game::update_chunk_tickets ticket levels are recomputed once a tick from players and forced chunks +ServerChunkCache.isPositionTicking port crates/horizon-client/src/game.rs::GameLevel::is_ticking_chunk block-ticking ticket level and a lit chunk with its neighbours loaded +ServerChunkCache.close inline crates/horizon-client/src/game.rs::Game::save_game save with wait; nothing else to close +ServerChunkCache.broadcastChangedChunks port crates/horizon-client/src/lan/hub.rs::Hub::broadcast_block_changes block changes grouped by section as BlockUpdate or SectionBlocksUpdate, plus block entity data; light is in broadcast_light_changes +ServerChunkCache.getFullChunk inline crates/horizon-client/src/game/natural_spawning.rs::Game::spawn_natural the spawn pass reads World::chunks directly +ServerChunkCache.blockChanged inline crates/horizon-client/src/world.rs::World::set World::set pushes the position onto block_log, which the hub drains +ServerChunkCache.onLightUpdate port crates/horizon-client/src/lan/hub.rs::Hub::broadcast_light_changes light_log read once a tick; sent to players for whom the column is on the tracked border +ServerChunkCache.addTicketAndLoadWithRadius inline crates/horizon-client/src/streaming.rs::Streaming::update spawn search and guest placement wait for streaming to produce the columns; no ticket-with-radius future +ServerChunkCache.MainThreadExecutor.pollTask inline crates/horizon-client/src/streaming.rs::Streaming::update chunk-system updates run once per tick (streaming and ticket levels); there is no task queue to poll +ServerEntity.isFullPrecisionEncodingRequired missing minor: item entities against a wall are sent as quantised deltas, so a LAN guest can see them off by a hair; TNT and happy ghast use the precise flag instead +ServerEntity.handleMinecartPosRot skip:other only for the experimental minecart_improvements movement; Horizon has the old minecart movement only +ServerEntity.addPairing inline crates/horizon-client/src/lan/entity.rs::Entities::tick a viewer that starts tracking an entity gets ServerEntity::pairing bundled, and the entity is remembered per viewer +ServerEntityGetter.getNearestPlayer inline crates/horizon-client/src/game/lan_mobs.rs::player_for each mob is given one player (the host or a guest) per tick, then Mob::can_target_player and friends apply the TargetingConditions +ServerEntityGetter.getNearestEntity inline crates/horizon-client/src/mobs/world.rs::MobWorld::nearby nearest-first scan used by NearestTarget; the goal applies its own conditions +ServerEntityGetter.getNearbyPlayers inline crates/horizon-client/src/mobs/goals.rs::Mob::can_target_player gap: minor: tests only the one player a mob sees, so a phantom or iron golem with several players around considers one of them +ServerEntityGetter.getNearbyEntities inline crates/horizon-client/src/mobs/world.rs::MobWorld::nearby each caller (breed partner, raider shout, fox, evoker) scans this and filters itself +ServerLevel. inline crates/horizon-client/src/game/world_loading.rs::WorldRequest::prepare +ServerLevel.getNextEntityId inline crates/horizon-client/src/game/lan_entities.rs::Game::lan_assign_ids one counter, Game::next_entity, bumped wherever an entity is created; ids for guests' tracking are given here +ServerLevel.tickCustomSpawners inline crates/horizon-client/src/game/creatures.rs::Game::tick_creatures wandering trader, patrol, cat and siege spawners; the phantom spawner runs from stats.rs +ServerLevel.tickThunder port crates/horizon-client/src/game/lightning.rs::Game::tick_thunder +ServerLevel.findLightningTargetAround port crates/horizon-client/src/game/lightning.rs::Game::lightning_target +ServerLevel.announceSleepStatus port crates/horizon-client/src/game/lan_actions.rs::Game::announce_sleep_status +ServerLevel.getMoonBrightness inline crates/horizon-client/src/game/creatures.rs::Game::local_difficulty MOON_BRIGHTNESS table indexed by day / 24000 mod 8 +ServerLevel.prepareWeather port crates/horizon-client/src/save.rs::Level::read rain and thunder levels start at 1 when the saved flags are on +ServerLevel.advanceWeatherCycle port crates/horizon-client/src/world.rs::Weather::tick_timers level fade in tick_levels, game events broadcast in Hub::sync_world +ServerLevel.resetWeatherCycle inline crates/horizon-client/src/game.rs::Game::tick_sleep the sleep-through-night reset; /weather sets the timers itself +ServerLevel.tickFluid port crates/horizon-world/src/level/tick.rs::Behaviours::scheduled_step +ServerLevel.tickBlock port crates/horizon-world/src/level/tick.rs::Behaviours::scheduled_step +ServerLevel.tickPassenger inline crates/horizon-client/src/game/passengers.rs::Game::sync_riders a rider whose vehicle is gone or dead lets go; others are re-seated each tick +ServerLevel.updateNeighboursOnBlockSet inline crates/horizon-world/src/level/update.rs::Sim::set_block_limit neighbour and comparator updates inside setBlock +ServerLevel.mayInteract inline crates/horizon-client/src/game/lan_actions.rs::Game::under_spawn_protection gap: minor: spawn protection only for guests on a dedicated server; the world border check is a stub that always allows (hand.rs within_border), so blocks can be built and broken outside the border +ServerLevel.save port crates/horizon-client/src/game.rs::Game::save_level +ServerLevel.saveLevelData port crates/horizon-client/src/game.rs::Game::save_game_metadata +ServerLevel.getEntities inline crates/horizon-client/src/game/commands.rs::Game::select_in_level entity selectors filter the per-kind entity lists +ServerLevel.addDuringTeleport inline crates/horizon-client/src/game/nether_portal.rs::Game::resolve_arrivals mobs, items, boats and carts waiting for a portal exit join the level with the chunk entities; players go through Hub::respawn +ServerLevel.addPlayer inline crates/horizon-client/src/lan/hub.rs::Hub::place_new_player +ServerLevel.addEntity inline crates/horizon-client/src/game/creatures.rs::Game::spawn_mob mobs go in Game::mobs; other kinds have their own lists +ServerLevel.tryAddFreshEntityWithPassengers inline crates/horizon-client/src/game/persist.rs::Game::load_mob gap: minor: loads a mob with its Passengers but never refuses a UUID that is already loaded, so /summon with a fixed UUID twice makes two +ServerLevel.unload inline crates/horizon-client/src/game.rs::Game::update_world unloaded columns drop their block entities and names, and their entities are saved out +ServerLevel.playSeededSound port crates/horizon-client/src/lan/containers.rs::Hub::sound +ServerLevel.sendBlockUpdated inline crates/horizon-client/src/world.rs::World::set gap: minor: mobs never re-plan a path when a block on it changes (Mob::recompute_path is never triggered by a block update), so they walk a stale path until a goal re-paths +ServerLevel.neighborChanged port crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed +ServerLevel.getDestroyType port crates/horizon-client/src/game.rs::Game::explode mob explosions pick Keep, Destroy or DestroyWithDecay from mob_griefing and mob_explosion_drop_decay +ServerLevel.doBlockEvent inline crates/horizon-world/src/level/pistons.rs::Behaviours::run_block_events +ServerLevel.getEntityInAnyDimension missing minor: owner and target references resolve in the level in place only; parked levels are not searched +ServerLevel.getEntityOrPart inline crates/horizon-client/src/game/lan_actions.rs::Game::look_at_entity an entity id from a guest finds a mob, an object or a tracked dragon part or end crystal +ServerLevel.findClosestBiome3d port crates/horizon-client/src/vanillagen.rs::VanillaTerrain::find_closest_biome +ServerLevel.getWorldBorder inline crates/horizon-client/src/game/scoreboard.rs::Game::load_scoreboard the border is unpacked from saved data next to the scoreboard +ServerLevel.setChunkForced port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_forceload +ServerLevel.updatePOIOnBlockStateChange inline crates/horizon-client/src/poi_sections.rs::PoiSections::get no incremental records: a section's POI list is rebuilt from its blocks after any edit +ServerLevel.saveDebugReport skip:debug +ServerLevel.dumpEntities skip:debug +ServerLevel.dumpBlockEntityTickers skip:debug +ServerLevel.clearBlockEvents skip:debug test hook for game tests +ServerLevel.getWatchdogStats skip:server server watchdog crash text +ServerLevel.getTypeCount skip:debug +ServerLevel.onStructureStartsAvailable skip:other Horizon computes structure starts from the seed (VanillaTerrain) instead of caching them in a StructureCheck +ServerLevel.waitForEntities skip:other entities load together with their chunk, so a spawning player has nothing to wait for +ServerLevel.areEntitiesActuallyLoadedAndTicking skip:debug only game tests call it +ServerLevel.anyPlayerCloseEnoughForSpawning port crates/horizon-client/src/game/natural_spawning.rs::close_enough_for_spawning +ServerLevel.fillReportDetails skip:debug crash report detail +ServerLevel.EntityCallbacks.onCreated inline crates/horizon-client/src/lan/waypoints.rs::Waypoints::track players are the only waypoint transmitters Horizon has +ServerLevel.EntityCallbacks.onDestroyed inline crates/horizon-client/src/lan/waypoints.rs::Waypoints::untrack gap: minor: dead non-player entities keep their scores and team membership (vanilla Scoreboard.entityRemoved clears them), so a sidebar can keep listing a dead mob +ServerLevel.EntityCallbacks.onTrackingStart inline crates/horizon-client/src/lan/entity.rs::Entities::tick an entity is tracked the first tick the level lists it; guests, waypoints and the sleep list are polled each tick, and there are no separate dragon part entities +ServerLevel.EntityCallbacks.onTrackingEnd inline crates/horizon-client/src/lan/entity.rs::Entities::tick an entity the level stops listing is removed from every viewer (RemoveEntities) +ServerPlayer. port crates/horizon-client/src/lan/guest.rs::Guest::new +ServerPlayer.readAdditionalSaveData port crates/horizon-client/src/save.rs::Level::read_player gap: minor: entered_nether_pos, last_explosion_impact_pos, spawn_extra_particles_on_fall, post_effects and previousPlayerGameType are not read back (kept verbatim in player_extra); recipe book, respawn, shoulders, warden tracker and raid omen are read +ServerPlayer.addAdditionalSaveData port crates/horizon-client/src/save.rs::Level::player_tag gap: minor: writes only what it reads; the keys above come from the loaded file unchanged +ServerPlayer.saveParentVehicle missing player: quitting while riding a horse, boat or minecart and reloading leaves the player standing next to the mount instead of still riding it +ServerPlayer.loadAndSpawnParentVehicle missing player: the player is never put back on its mount when the world loads +ServerPlayer.saveEnderPearls missing minor: a thrown ender pearl is not saved at all, so one in flight or hanging in a stasis chamber is gone after a reload +ServerPlayer.loadAndSpawnEnderPearl missing minor: ender pearls are not loaded back (spawn_loaded_entities has no pearl) +ServerPlayer.setExperiencePoints port crates/horizon-client/src/game/commands.rs::Game::change_experience /xp set points +ServerPlayer.setExperienceLevels port crates/horizon-client/src/game/commands.rs::Game::change_experience /xp set levels +ServerPlayer.onLeaveCombat skip:other the client ignores ClientboundPlayerCombatEnd +ServerPlayer.playShoulderEntityAmbientSound missing player: a parrot on the player's shoulder never chirps or imitates nearby mobs (1 in 200 ticks in vanilla) +ServerPlayer.respawnEntityOnShoulder port crates/horizon-client/src/game/shoulders.rs::Game::remove_entities_on_shoulder +ServerPlayer.trackStartFallingPosition missing player: the Caves & Cliffs (fall_from_height) and Who Needs Rockets? (fall_after_explosion) advancements can never be earned; neither trigger exists +ServerPlayer.trackEnteredOrExitedLavaOnVehicle missing player: the This Boat Has Legs advancement (ride_entity_in_lava) can never be earned; the trigger does not exist +ServerPlayer.updateScoreForCriteria port crates/horizon-client/src/game/scoreboard.rs::Game::tick_scoreboard health, food, air, armor, xp and level objectives +ServerPlayer.tellNeutralMobsThatIDied missing player: wolves, bees, piglins and other mobs that were angry at the player stay angry after the player dies and respawns (vanilla forgives them with forgive_dead_players) +ServerPlayer.handleTeamKill inline crates/horizon-client/src/game/scoreboard.rs::Game::score_player_killed teamkill and killedByTeam objectives +ServerPlayer.hurtServer port crates/horizon-client/src/game/lan_pvp.rs::Game::pvp_land pvp rule and team friendly fire gate the damage +ServerPlayer.addPostEffect port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_posteffect gap: minor: one list shared by every target, nothing is sent to guests (an empty list at join) and no post shader is drawn +ServerPlayer.clearPostEffects port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_posteffect gap: minor: same as addPostEffect +ServerPlayer.removePostEffect port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_posteffect gap: minor: same as addPostEffect +ServerPlayer.forceSetRotation inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_rotate /rotate turns a guest through a teleport packet with the new rotation +ServerPlayer.triggerDimensionChangeTriggers port crates/horizon-client/src/game/advancements.rs::Game::trigger_dimension gap: player: nether_travel is never triggered (entered_nether_pos is not kept), so the Subspace Bubble advancement can never be earned +ServerPlayer.broadcastToPlayer missing player: a spectator (the host or a LAN guest) is shown to the other players as an ordinary visible player; vanilla hides spectators from everyone who is not a spectator +ServerPlayer.startSleeping port crates/horizon-client/src/game.rs::Game::try_sleep +ServerPlayer.isReachableBedBlock inline crates/horizon-client/src/game.rs::Game::try_sleep the reachable closure: 3 blocks sideways, 2 vertically +ServerPlayer.isInvulnerableTo inline crates/horizon-client/src/game/lan_mobs.rs::Game::lan_guest_views gap: minor: only the not-loaded part (a guest that has not sent PlayerLoaded is ignored by mobs); no invulnerability while a dimension change is pending +ServerPlayer.openNautilusInventory missing player: sneak-using a tamed nautilus does nothing, so its saddle and armour screen never opens (items can only be put on by right-clicking with them) +ServerPlayer.doCloseContainer inline crates/horizon-client/src/lan/containers.rs::Hub::container_closed the carried stack and a crafting grid go back through Game::guest_carried_back and lan_crafting_closed +ServerPlayer.rideTick inline crates/horizon-client/src/game/stats.rs::Game::movement_stats only the riding statistics part; the ride itself is per vehicle +ServerPlayer.didNotMove inline crates/horizon-client/src/game/stats.rs::Game::movement_stats a move of 0 cm counts nothing +ServerPlayer.awardStat port crates/horizon-client/src/game/stats.rs::Stats::add scoreboard objectives follow in stat_criteria_changed +ServerPlayer.resetStat port crates/horizon-client/src/game/stats.rs::Stats::reset +ServerPlayer.disconnect inline crates/horizon-client/src/game/lan.rs::Game::tick_lan HostEvent::Left: the guest is saved and removed; its mount just loses its rider (no eject or forced wake) +ServerPlayer.transferInventoryXpAndScore inline crates/horizon-client/src/game/screens.rs::Game::respawn_player keep_inventory keeps the inventory and Experience::respawn keeps xp +ServerPlayer.onEffectUpdated port crates/horizon-client/src/lan/hub.rs::Hub::sync_effects gap: player: the effects_changed trigger does not exist, so How Did We Get Here? and A Furious Cocktail can never be earned +ServerPlayer.onEffectsRemoved port crates/horizon-client/src/lan/hub.rs::Hub::sync_effects gap: player: same missing effects_changed trigger +ServerPlayer.magicCrit port crates/horizon-client/src/lan/containers.rs::Hub::critical_hit +ServerPlayer.onUpdateAbilities inline crates/horizon-client/src/lan/hub.rs::Hub::set_game_mode abilities go out with a game mode change; there is no other way abilities change +ServerPlayer.createCommandSourceStack inline crates/horizon-client/src/game/lan_actions.rs::Game::run_guest_command the guest is swapped in as the acting player and its permission level applied +ServerPlayer.sendSystemMessage port crates/horizon-client/src/lan/hub.rs::Hub::system_message +ServerPlayer.sendBuildLimitMessage missing player: placing a block at the build limit fails silently; the red build.tooHigh / build.tooLow overlay never shows +ServerPlayer.getIpAddress skip:server for ban-ip and the dedicated server log +ServerPlayer.updateOptions port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox the ClientInformation arm: view distance, main hand, model parts and chat visibility are kept +ServerPlayer.clientInformation skip:java getter +ServerPlayer.sendServerStatus port crates/horizon-client/src/lan/hub.rs::Hub::place_new_player ServerData with the motd and icon at join +ServerPlayer.updateInvisibilityStatus missing player: a spectator is not made invisible, so others see a full player model flying around +ServerPlayer.drop port crates/horizon-client/src/game.rs::Game::player_drop item_dropped and drop statistics, thrower set +ServerPlayer.calculateGameModeForNewPlayer port crates/horizon-client/src/game/lan.rs::Game::lan_guest_joined default or forced game mode for a new or force-gamemode guest +ServerPlayer.storeGameTypes port crates/horizon-client/src/save.rs::Level::player_tag gap: minor: playerGameType only; previousPlayerGameType is not saved, so the game mode switcher forgets the previous mode after a reload +ServerPlayer.shouldFilterMessageTo skip:service text filtering +ServerPlayer.mayInteract inline crates/horizon-client/src/game/lan_actions.rs::Game::under_spawn_protection gap: minor: same as ServerLevel.mayInteract, no world border check +ServerPlayer.onItemPickup missing player: the You've Got a Friend in Me advancement (thrown_item_picked_up_by_player) can never be earned +ServerPlayer.getChatSession skip:java getter with an expiry check +ServerPlayer.startRiding inline crates/horizon-client/src/game/lan_entities.rs::Game::lan_riding gap: minor: SetPassengers goes out, but the mount's active effects (a horse's speed) are not sent to the rider +ServerPlayer.removeVehicle inline crates/horizon-client/src/game/lan_entities.rs::Game::lan_riding gap: minor: SetPassengers updates, vehicle effects are not removed from the rider +ServerPlayer.getKnownSpeed port crates/horizon-client/src/game/spear.rs::Game::known_player_movement gap: minor: ignores the vehicle's speed for a rider who is not steering +ServerPlayer.onEquippedItemBroken port crates/horizon-client/src/game.rs::Game::damage_held gap: minor: only the held item counts toward the broken statistic; breaking armour is not counted +ServerPlayer.registerAndUpdateEnderPearlTicket missing minor: a thrown ender pearl does not keep its chunk loaded; none are saved either +ServerPlayer.debugSubscriptions skip:debug debug subscriptions for the debug screen diff --git a/tools/port_map/game-003.tsv b/tools/port_map/game-003.tsv new file mode 100644 index 0000000000..a97af7a779 --- /dev/null +++ b/tools/port_map/game-003.tsv @@ -0,0 +1,146 @@ +ServerPlayerGameMode.changeGameModeForPlayer port crates/horizon-client/src/game/commands.rs::Game::set_game_mode same-mode check, mode switch, abilities; a LAN guest goes through Hub::set_game_mode which also broadcasts PlayerInfo UPDATE_GAME_MODE +ServerPlayerGameMode.setGameModeForPlayer port crates/horizon-client/src/game/game_mode.rs::Game::set_mode previous mode kept, GameType.updatePlayerAbilities applied +ServerPlayerGameMode.isInRangeOfGround missing minor: spectator to creative near the ground keeps flying (set_mode always sets flying); vanilla stops flying within 1 block of the ground +ServerPlayerGameMode.incrementDestroyProgress inline crates/horizon-client/src/game/lan_actions.rs::Game::guest_dig_feedback gap: minor: crack stage is the client formula (progress*10 - 1), so other players' cracks on a guest's block run one stage behind the server value +ServerPlayerGameMode.debugLogging skip:debug DEBUG_BLOCK_BREAK logging +ServerPlayerGameMode.abortDestroyBlock inline crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet the abort action clears the guest's crack for everyone; no separate check of a mismatched destroy position +ServerPlayerGameMode.destroyAndAck missing minor: guest_destroy ignores whether the break succeeded, so a refused break never sends the block back to the guest +SimulationChunkTracker.getLevel port crates/horizon-client/src/ticket_levels.rs::Map::get simulation level per chunk, 33 where no ticket reaches +SimulationChunkTracker.setLevel inline crates/horizon-client/src/ticket_levels.rs::compute the whole level map is rebuilt from player and forced tickets instead of updated per node +ThreadedLevelLightEngine.checkBlock inline crates/horizon-client/src/light.rs::relight a block change relights resident chunks directly, no queued PRE_UPDATE task +ThreadedLevelLightEngine.updateChunkStatus skip:other Horizon lights a chunk in one flood fill and keeps no per-section light state to reset +ThreadedLevelLightEngine.updateSectionStatus skip:other no per-section empty flags: light_chunk reads the sections directly +ThreadedLevelLightEngine.propagateLightSources inline crates/horizon-client/src/light.rs::light_chunk light_chunk seeds sky and block sources and propagates in one pass +ThreadedLevelLightEngine.setLightEnabled skip:other no light-enabled flag per chunk; relight only touches resident, lit chunks +ThreadedLevelLightEngine.queueSectionData inline crates/horizon-client/src/light.rs::update_light_layers light layers from a chunk packet are applied at once, not queued +ThreadedLevelLightEngine.addTask skip:java light thread task queue +ThreadedLevelLightEngine.retainData skip:other no retained light data between chunk statuses +ThreadedLevelLightEngine.initializeLight skip:other INITIALIZE_LIGHT step marks sections and enables light; Horizon has no such step before light_chunk +ThreadedLevelLightEngine.lightChunk port crates/horizon-client/src/light.rs::light_chunk the chunk's light is computed from its 3x3 neighbourhood +ThreadedLevelLightEngine.tryScheduleUpdate skip:java schedules the light thread +ThreadedLevelLightEngine.runUpdate inline crates/horizon-client/src/light.rs::relight runLightUpdates: decrease then increase passes, run directly on the changed positions +ThreadedLevelLightEngine.waitForPendingTasks skip:java future that completes when the light queue drains +ThrottlingChunkTaskDispatcher.popTasks skip:other caps chunk tasks in flight; Horizon schedules chunk work on its own worker pool +ThrottlingChunkTaskDispatcher.getDebugStatus skip:debug +Ticket.toString skip:debug +Ticket.decreaseTicksLeft missing minor: only player and forced tickets exist; timed tickets (ender pearl 40, portal 300, post teleport 5 ticks) are not modelled +Ticket.isTimedOut missing minor: no timed tickets, so nothing expires +WorldGenRegion. port crates/horizon-world/src/worldgen/feature_region.rs::TerrainRegion::new the region is a set of chunks around the centre +WorldGenRegion.hasChunk port crates/horizon-world/src/worldgen/feature_region.rs::TerrainRegion::chunk a chunk is there when it is in the region's index, not by the step's dependency distance +WorldGenRegion.warnIfReadOutsideWriteZone skip:debug logs an unsafe terrain read +WorldGenRegion.findBlocksIn skip:debug only adds the read-zone warnings to the default scan +WorldGenRegion.isWithinWriteZone port crates/horizon-world/src/worldgen/write_zone.rs::GenerationWriteZone::contains_block Chebyshev distance to the centre chunk within the write radius +LevelLoadProgressTracker.start inline crates/horizon-client/src/level_loading.rs::progress weights 10 + player chunks; no persistent (forced) chunk stage +LevelLoadProgressTracker.update inline crates/horizon-client/src/level_loading.rs::progress player chunks ready out of 49 +LevelLoadProgressTracker.finishSegment inline crates/horizon-client/src/level_loading.rs::progress the 10 weight prepare segment counts as finished +LevelLoadProgressTracker.tracksStage inline crates/horizon-client/src/level_loading.rs::progress both stages tracked, player chunks included +LevelLoadProgressTracker.updateProgress port crates/horizon-client/src/level_loading.rs::progress +LoggingLevelLoadListener skip:server console progress log of a dedicated server or integrated server start +CompoundContainer.isEmpty port crates/horizon-client/src/game/machines.rs::Containers::is_empty a View of both halves is empty when every slot is +CompoundContainer.contains inline crates/horizon-client/src/game/block_entity_anims.rs::Game::container_openers_changed an opener of a chest counts on both halves (chest_other_half) +CompoundContainer.getItem port crates/horizon-client/src/game/machines.rs::Containers::get View::locate picks the half; lan/menu.rs Slots::get does the same for a guest's menu +CompoundContainer.removeItem inline crates/horizon-client/src/game/machines.rs::Game::slot_mut_at View::locate picks the half, the caller splits the stack +CompoundContainer.removeItemNoUpdate inline crates/horizon-client/src/game/machines.rs::Game::slot_mut_at View::locate picks the half, the caller takes the slot +CompoundContainer.setItem inline crates/horizon-client/src/game/machines.rs::Game::slot_mut_at View::locate picks the half +CompoundContainer.stillValid port crates/horizon-client/src/lan/menu.rs::Slots::still_valid both halves must still be there and in reach +CompoundContainer.canPlaceItem port crates/horizon-client/src/game/machines.rs::Containers::can_place +Container.countItem port crates/horizon-client/src/mobs/villager.rs::Ext::count_item villager inventory only, the one caller that needs it +Container.hasAnyOf inline crates/horizon-client/src/mobs/villager.rs::Ext::take_half_stack TradeWithVillager's hasAnyOf gate: no wanted item in the inventory, nothing is thrown +Container.hasAnyMatching inline crates/horizon-client/src/mobs/villager_brain.rs::harvest_tick Villager.hasFarmSeeds gate: a farmer sows only when a plantable seed is in the inventory; the hopper checks of chiseled bookshelf and jukebox canTakeItem are not ported as such +Container.getSlot port crates/horizon-client/src/game/commands/items.rs::Game::slot_get /item and execute if items read a container slot by number +Container.ContainerIterator.next skip:java iterator over slots +ContainerHelper.removeItem skip:java NonNullList helper: split a stack in a slot Vec; block entities take from their own slot vector +ContainerHelper.takeItem skip:java NonNullList helper: Option::take on the slot +ContainerHelper.clearOrCountMatchingItems port crates/horizon-client/src/game/commands.rs::Game::clear_matching /clear over main, armor, offhand, crafting grid and carried stack, count only when max is 0 +DifficultyInstance.isHard skip:other no vanilla caller (isHarderThan is what Illusioner uses) +DifficultyInstance.getSpecialMultiplier port crates/horizon-client/src/mobs/spawning.rs::LocalDifficulty::special +DifficultyInstance.calculateDifficulty port crates/horizon-client/src/mobs/spawning.rs::LocalDifficulty::new +InteractionHand.asArm inline crates/horizon-client/src/mobs/ai.rs::Mob::swing_hand off_hand != main arm left; the player's first person hand does the same in game.rs hand_geometry +InteractionResult.Success.heldItemTransformedTo skip:java result builder; Horizon replaces the held item in place where the item is used +ItemStackWithSlot.isValidInContainer port crates/horizon-client-core/src/block_entity_nbt.rs::items slots outside 0..size are dropped when items load +LockCode.canUnlock missing minor: a lock on a container is kept in the NBT but never checked, so any player opens a command-locked chest +RandomSequences.createSequence port crates/horizon-items/src/random_sequences.rs::create +RandomSequences.DirtyMarkingRandomSource.nextInt skip:java marks the saved data dirty on each draw; Horizon saves every sequence +RandomSequences.DirtyMarkingRandomSource.equals skip:java wrapper equality +RandomizableContainer.trySaveLootTable port crates/horizon-client-core/src/block_entity_nbt.rs::Fields::loot_table saves LootTable and a non-zero seed, no items +SimpleContainer.getItem port crates/horizon-world/src/simple_container.rs::SimpleContainer::get library port; villager and piglin inventories are plain vectors +SimpleContainer.removeAllItems port crates/horizon-world/src/simple_container.rs::SimpleContainer::remove_all +SimpleContainer.removeItemNoUpdate port crates/horizon-world/src/simple_container.rs::SimpleContainer::remove_no_update +SimpleContainer.setItem port crates/horizon-world/src/simple_container.rs::SimpleContainer::set +SimpleContainer.isEmpty port crates/horizon-world/src/simple_container.rs::SimpleContainer::is_empty +SimpleContainer.fillStackedContents skip:other no vanilla caller: SimpleContainer is never a recipe input +SimpleContainer.toString skip:debug +SimpleContainer.moveItemToEmptySlots inline crates/horizon-world/src/simple_container.rs::SimpleContainer::add second pass of addItem; the villager's Ext::add_item does the same +SimpleContainer.moveItemToOccupiedSlotsWithSameType inline crates/horizon-world/src/simple_container.rs::SimpleContainer::add first pass of addItem +SimpleContainer.moveItemsBetweenStacks inline crates/horizon-world/src/simple_container.rs::SimpleContainer::add the grow and shrink with the per-stack limit +SimpleContainer.fromItemList inline crates/horizon-client/src/entity_nbt.rs::mob_from_nbt villager and piglin Inventory read; the piglin list is cut to 8 instead of re-added +SimpleContainer.storeAsItemList inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt villager and piglin Inventory written +Stopwatches.unpack port crates/horizon-client/src/stopwatches.rs::Stopwatches::load +Stopwatches.pack port crates/horizon-client/src/stopwatches.rs::Stopwatches::save +Stopwatches.add port crates/horizon-client/src/stopwatches.rs::Stopwatches::create +Stopwatches.update port crates/horizon-client/src/stopwatches.rs::Stopwatches::restart only the restart update that /stopwatch uses +Stopwatches.remove port crates/horizon-client/src/stopwatches.rs::Stopwatches::remove +Stopwatches.isDirty inline crates/horizon-client/src/stopwatches.rs::Stopwatches::save save skips the write when not dirty and empty +AttributeType.ofInterpolated skip:data attribute type definitions and codecs +AttributeType.ofNotInterpolated skip:data attribute type definitions and codecs +AttributeType.createModifierCodec skip:data attribute type definitions and codecs +AttributeType.checkAllowedModifier skip:data attribute type definitions and codecs +BedRule.Rule.test inline crates/horizon-client/src/game.rs::Game::try_sleep WHEN_DARK is the hard-coded night or thunder check; ALWAYS and NEVER are in bed_sets_spawn and the dimension checks +EnvironmentAttribute.valueCodec skip:data codec +EnvironmentAttribute.Builder.build skip:data attribute definition builder +EnvironmentAttributeMap.applyModifier inline crates/horizon-client/src/environment.rs::Environment::value gap: minor: only multiply and override are applied for visual tracks; and, or and maximum tracks are read by their own users +EnvironmentAttributeMap.equals skip:java +EnvironmentAttributeMap.Builder.modify skip:java builder +EnvironmentAttributeMap.Builder.build skip:java builder +EnvironmentAttributeMap.Entry.createCodec skip:data codec +EnvironmentAttributeMap.Entry.createFullCodec skip:data codec +EnvironmentAttributeProbe.reset skip:other Horizon keeps no probe state: render recomputes the biome weights each frame +EnvironmentAttributeProbe.getValue inline crates/horizon-client/src/game.rs::Game::render sky, fog and water fog are blended from Gaussian biome weights at the camera each frame, no last and new tick lerp +EnvironmentAttributeProbe.ValueProbe.getValueFromLevel inline crates/horizon-client/src/environment.rs::Environment::sample dimension and timeline value; biome blend is applied by the caller +EnvironmentAttributeProbe.ValueProbe.tick port crates/horizon-client/src/biome.rs::gaussian_weights the per-tick Gaussian sampling of noise biomes around the camera +EnvironmentAttributeProbe.ValueProbe.get inline crates/horizon-client/src/game.rs::Game::render per frame values from the camera position instead of lerping last and new tick values +EnvironmentAttributeReader.getValue skip:java overload that centres a BlockPos and passes no interpolator +EnvironmentAttributeSystem.bakeLayerSampler skip:java folds leading constant layers into the base value; load_dimension builds its base values the same way +EnvironmentAttributeSystem.addStaticLayers inline crates/horizon-client/src/environment.rs::Environment::load_dimension dimension attributes become base values; biome attributes are read in Biomes::load +EnvironmentAttributeSystem.addDynamicLayers inline crates/horizon-client/src/environment.rs::Environment::sample gap: minor: only day.json drives the visual attributes here; moon, early game and villager timelines are read by moon, patrol and villager code; weather is apply_weather +EnvironmentAttributeSystem.addBiomeLayer inline crates/horizon-client/src/biome.rs::Biomes::load biome attributes (sky, fog, water fog, spawns, fire burnout) are read into BiomeInfo +EnvironmentAttributeSystem.addBiomeLayerForAttribute inline crates/horizon-client/src/biome.rs::blend_rgb gap: minor: only sky, fog and water fog colours are blended by Gaussian weights; other biome attributes use the biome at the position +EnvironmentAttributeSystem.getDimensionValue inline crates/horizon-client/src/environment.rs::Environment::sample +EnvironmentAttributeSystem.getValue inline crates/horizon-client/src/environment.rs::Environment::sample the sky state at a time of day; other attributes are read straight from dimension and biome data by their users +EnvironmentAttributeSystem.getConstantBaseValue skip:debug VisibleForTesting +EnvironmentAttributeSystem.isAffectedByPosition skip:debug VisibleForTesting +EnvironmentAttributeSystem.Builder.addConstantLayer inline crates/horizon-client/src/environment.rs::Environment::load_dimension dimension attributes inserted as base values +EnvironmentAttributeSystem.Builder.addConstantEntry inline crates/horizon-client/src/environment.rs::Environment::load_dimension +EnvironmentAttributeSystem.Builder.addLayer skip:java builder +EnvironmentAttributeSystem.Builder.addTimelineLayer inline crates/horizon-client/src/environment.rs::Environment::load_dimension day timeline tracks loaded (overworld only) +EnvironmentAttributeSystem.Builder.addTimelineLayerForAttribute inline crates/horizon-client/src/environment.rs::Environment::load_dimension one Track per attribute with its easing +EnvironmentAttributeSystem.ValueSampler.getDimensionValue inline crates/horizon-client/src/environment.rs::Environment::sample no per-tick cache: recomputed per frame +EnvironmentAttributeSystem.ValueSampler.getValue inline crates/horizon-client/src/environment.rs::Environment::sample +EnvironmentAttributeSystem.ValueSampler.computeValuePositional inline crates/horizon-client/src/game.rs::Game::render base, timeline track, then biome blend, then weather, in that order +EnvironmentAttributeSystem.ValueSampler.computeValueNotPositional inline crates/horizon-client/src/environment.rs::Environment::sample base then timeline track +EnvironmentAttributes.register skip:data attribute registry +WeatherAttributes.addBuiltinLayers port crates/horizon-client/src/environment.rs::apply_weather rain and thunder layers over sky, fog, cloud, sky light, stars and sunrise; bees staying in the hive is bees_stay_in_hive in bee.rs +WeatherAttributes.addLayer port crates/horizon-client/src/environment.rs::apply_weather rain level minus thunder level, then thunder level, each lerped over the value +BooleanModifier.apply port crates/horizon-client/src/patrol.rs::BoolTrack::apply and, or, xor, nand, nor, xnor over the timeline track value +ColorModifier.BlendToGray.lerp skip:other only lerps blend_to_gray arguments between keyframes; no timeline uses that modifier +ListModifier.Append.apply inline crates/horizon-client/src/animate_tick.rs::AnimateTables::load a biome's ambient_particles list is its argument over an empty dimension list +MobSpawnSettingsModifier.Overlay.apply inline crates/horizon-client/src/biome.rs::Biomes::load no dimension sets natural_mob_spawns, so the overlay over the empty default is the biome's own spawns +ClockTimeMarker.getRepetitionCount skip:other no vanilla caller +ClockTimeMarker.resolveTimeToMoveTo port crates/horizon-client/src/game/chat.rs::TimeMarker::resolve +ClockTimeMarker.occursAt port crates/horizon-client/src/game/chat.rs::TimeMarker::occurs_at +ClockTimeMarker.durationToNext inline crates/horizon-client/src/game/chat.rs::TimeMarker::resolve the d > 0 else period + d step +ServerClockManager.init inline crates/horizon-client/src/game/chat.rs::Game::build_registries overworld timelines and their time markers are loaded; the End clock runs with game time +ServerClockManager.getInstance skip:java clock lookup; Horizon keeps one overworld clock in ui.clock +ServerClockManager.setTotalTicks inline crates/horizon-client/src/game/commands.rs::Game::cmd_time /time set +ServerClockManager.moveToTimeMarker inline crates/horizon-client/src/game/commands.rs::Game::cmd_time /time set : not found, already there, or moved +ServerClockManager.addTicks inline crates/horizon-client/src/game/commands.rs::Game::cmd_time /time add, floored at 0 +ServerClockManager.isAtTimeMarker inline crates/horizon-client/src/game/raids.rs::Game::tick_village_siege ROLL_VILLAGE_SIEGE marker as tick 18000 of the day +ServerClockManager.commandTimeMarkersForClock inline crates/horizon-client/src/game/chat.rs::Game::build_registries markers with show_in_commands go to the command suggestions +ServerClockManager.ServerClockInstance.loadFrom port crates/horizon-client/src/game/chat.rs::Game::load_world_state total ticks come from level data; rate, partial tick and paused are read from world_clocks.dat +ServerClockManager.ServerClockInstance.packState port crates/horizon-client/src/game/chat.rs::Game::save_world_state +WorldClocks.bootstrap skip:data clock registration +CombatTracker.getMessageForAssistedFall port crates/horizon-client/src/game/combat.rs::Game::combat_death_message_for gap: minor: the .item variants of the fell assist and finish messages are never used, a named weapon is not shown +CombatTracker.getCombatDuration skip:other only feeds ClientboundPlayerCombatEndPacket, which the client ignores +DamageSource.getLocalizedDeathMessage port crates/horizon-client/src/game/combat.rs::Game::combat_death_message_for gap: minor: no .item variant when the killer holds a custom-named item +DamageSource.isCreativePlayer port crates/horizon-world/src/damage_source.rs::DamageSource::creative_player +DamageSource.is port crates/horizon-world/src/damage_source.rs::DamageSource::in_tag tag form; the key form is is_type diff --git a/tools/port_map/game-004.tsv b/tools/port_map/game-004.tsv new file mode 100644 index 0000000000..455c614e11 --- /dev/null +++ b/tools/port_map/game-004.tsv @@ -0,0 +1,105 @@ +DamageSources.source port crates/horizon-client/src/mobs.rs::Damage::of a damage source is a DamageKind plus attacker and origin; type ids are resolved in creatures.rs::damage_type +DamageSources.fireball inline crates/horizon-client/src/game/creatures.rs::damage_type gap: minor: always minecraft:fireball; an owner-less fireball is not the unattributed_fireball type (death message and loot condition only) +DamageSources.explosion inline crates/horizon-client/src/game/creatures.rs::damage_type gap: minor: player_explosion only when the attacker is the player; vanilla uses it whenever a causing entity exists (a creeper too), so that death message differs +FallLocation.blockToFallLocation port crates/horizon-client/src/game/combat.rs::Game::fall_location gap: minor: trapdoors are not mapped to the ladder location, they count as other_climbable +AbsorptionMobEffect.onEffectStarted port crates/horizon-client/src/game/potions.rs::Game::add_player_effect gap: minor: sets absorption to max(current, 4 * (level + 1)) for the player only; a mob given absorption gets none +BadOmenMobEffect.applyEffectTick port crates/horizon-client/src/game/raids.rs::Game::omen_tick bad omen in a village (not peaceful, not spectator, raid below max omen level) becomes Raid Omen 600 ticks and records the position +HealOrHarmMobEffect.applyEffectTick port crates/horizon-client/src/effects.rs::periodic instant_health heals 4 << level, instant_damage hurts 6 << level (magic); mobs get the instant effects from splash and cloud code in potions.rs +HungerMobEffect.applyEffectTick port crates/horizon-client/src/effects.rs::periodic exhaustion 0.005 * (level + 1) every tick, applied by Game::apply_effect_action; player only like vanilla +InfestedMobEffect.spawnSilverfish inline crates/horizon-client/src/game/potions.rs::Game::infested_hurt the 10 percent roll and the one or two silverfish thrown along the look direction, with the hurt sound; player and mobs +MobEffect.getOrCreateDescriptionId skip:java lazy translation key cache +MobEffect.setBlendDuration inline crates/horizon-client/src/screen_effects.rs::ScreenEffects::tick_spinning nausea fades in over 150 and out over 20 ticks, darkness over 22 (sculk.rs::Game::tick_darkness_blend); no other effect blends +MobEffect.removeAttributeModifiers inline crates/horizon-client/src/game/potions.rs::Game::apply_effect_status modifiers that left the effect list are removed in the diff loop; horizon-world has an unused kernel (effect_attribute_modifiers.rs::remove_modifiers) +MobEffectInstance. port crates/horizon-client/src/effects.rs::EffectInstance::new gap: minor: no showIcon flag, so an effect given without particles still shows its icon; amplifier clamps to 0..255 +MobEffectInstance.asDetails inline crates/horizon-client/src/effects.rs::PlayerEffects::saved gap: minor: only id, duration, amplifier, ambient and particles are saved; hidden_effect is lost on save and show_icon is written as the particles flag +MobEffectInstance.setDetailsFrom inline crates/horizon-client/src/effects.rs::EffectInstance::downgrade copies the hidden effect's details when the active one runs out; the copy constructor is Clone +MobEffectInstance.isShorterDurationThan port crates/horizon-client/src/effects.rs::EffectInstance::shorter_than +MobEffectInstance.withScaledDuration inline crates/horizon-client/src/game/potions.rs::Game::cloud_affect the scaled closure: floor(duration * 0.25) at least 1 for lingering clouds; fixed 0.25, the stack's potion_duration_scale is not read +MobEffectInstance.mapDuration inline crates/horizon-client/src/game/potions.rs::Game::splash the (int)(scale * duration + 0.5) of a splash potion on mobs (splash_on_player for the player); potions are never infinite +MobEffectInstance.tickDownDuration port crates/horizon-client/src/effects.rs::EffectInstance::tick_down hidden effect ticks down too; infinite stays +MobEffectInstance.downgradeToHiddenEffect port crates/horizon-client/src/effects.rs::EffectInstance::downgrade gap: minor: mobs keep no hidden effects (Mob::add_effect drops a weaker one) +MobEffectInstance.onMobRemoved inline crates/horizon-client/src/game/potions.rs::Game::effects_on_death oozing, weaving and wind charged run when their bearer dies (player or mob) +MobEffectInstance.onMobHurt inline crates/horizon-client/src/game/potions.rs::Game::infested_hurt only infested reacts to being hurt; called for the player from combat.rs and for mobs from creatures.rs +MobEffectInstance.toString skip:debug debug string +MobEffectInstance.equals skip:java value equality +MobEffectInstance.hashCode skip:java value hash +MobEffectInstance.BlendState.setImmediate missing minor: skipBlending is never used, so nausea and darkness restored on login or a dimension change fade in from 0 instead of starting full +MobEffectInstance.BlendState.getFactor port crates/horizon-client/src/game/fov.rs::Game::nausea_blend lerp of the previous and current factor; darkness has its own in sculk.rs::Game::darkness_blend +MobEffectInstance.Details.create skip:java record constructor for the codec +MobEffectUtil.hasDigSpeed inline crates/horizon-client/src/effects.rs::PlayerEffects::mining_speed_factor haste or conduit power, also read for the arm swing duration in first_person.rs +MobEffectUtil.hasWaterBreathing inline crates/horizon-client/src/effects.rs::PlayerEffects::status water_breathing flag is water breathing or conduit power; breath of the nautilus is its own flag read with it +MobEffectUtil.shouldEffectsRefillAirsupply inline crates/horizon-client/src/motion/environment.rs::MotionWorld::tick_environment_damage air refills unless only breath of the nautilus is active +OozingMobEffect.onMobRemoved inline crates/horizon-client/src/game/potions.rs::Game::effects_on_death oozing branch: two slimes, fewer when slimes within 2 blocks fill max_entity_cramming +OozingMobEffect.spawnSlimeOffspring inline crates/horizon-client/src/game/potions.rs::Game::effects_on_death size 2 slime half a block up +PoisonMobEffect.applyEffectTick port crates/horizon-client/src/effects.rs::periodic one magic damage while above 1 health; mobs in mobs/ai.rs::tick_effects +PoisonMobEffect.shouldApplyEffectTickThisTick port crates/horizon-client/src/effects.rs::periodic 25 >> level interval +RegenerationMobEffect.applyEffectTick port crates/horizon-client/src/effects.rs::periodic heal 1; mobs in mobs/ai.rs::tick_effects +RegenerationMobEffect.shouldApplyEffectTickThisTick port crates/horizon-client/src/effects.rs::periodic 50 >> level interval +SaturationMobEffect.applyEffectTick port crates/horizon-client/src/effects.rs::periodic Eat(level + 1, 1.0) applied to the food data; player only +WeavingMobEffect.onMobRemoved inline crates/horizon-client/src/game/potions.rs::Game::effects_on_death weaving branch (killed, player or mob_griefing) calls spawn_cobwebs_around: 2 or 3 cobwebs among 15 random blocks +WitherMobEffect.shouldApplyEffectTickThisTick port crates/horizon-client/src/effects.rs::periodic 40 >> level interval; mobs in mobs/ai.rs::tick_effects take magic damage, not wither damage +AbstractInterpolationHandler.adjustInterpolationPosition skip:other Horizon moves a remote entity only through SteppedInterpolation, so there is no client-side move to add to the target +AbstractInterpolationHandler.adjustInterpolationTargetFromDeltas skip:other same: the entity never drifts from where the interpolation put it (see SteppedInterpolation::interpolate) +AbstractInterpolationHandler.setLastPositionAndRotation skip:other only feeds the delta adjustments above; Horizon's own last is the start of the step +AgeableMob.mobInteract missing player: using a golden dandelion on a baby animal does nothing; vanilla uses one up, locks its growth and plays a sound (no golden_dandelion handling exists) +AgeableMob.canUseGoldenDandelion missing player: part of the golden dandelion age lock, which is not implemented +AgeableMob.setAgeLockedData missing player: nothing toggles AgeLocked in play (only NBT, buckets and spawn data set it); golden dandelion age lock is missing +AgeableMob.setAgeLocked missing player: golden dandelion consumption, persistence and the use or unuse sound are missing +AgeableMob.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: minor: the baby flag is sent (baby_index); the age-locked entry is not +AgeableMob.getAge inline crates/horizon-client/src/game/remote_mob_data.rs::apply m.extra.age on the host; a remote client only learns baby or not (-1 or 0) +AgeableMob.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt Age, ForcedAge and AgeLocked for creatures +AgeableMob.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt Age, ForcedAge and AgeLocked +AgeableMob.makeAgeLockedParticle missing player: no age lock particle timer, so no pause or reset growth particles; the particle types exist +AnimationState.getTimeInMillis port crates/horizon-client/src/mobs/keyframes.rs::AnimationState::millis +AreaEffectCloud.defineSynchedData port crates/horizon-client/src/game/lan_entities.rs::Game::lan_projectile_states radius, waiting and particle for dragon breath and lingering clouds; read back in remote_projectiles.rs::remote_projectile_data +AreaEffectCloud.setRadius inline crates/horizon-client/src/game/potions.rs::Game::tick_potion_cloud radius shrinks per tick and per victim; the 0..32 clamp is not needed +AreaEffectCloud.refreshDimensions inline crates/horizon-client/src/game/potions.rs::Game::tick_potion_cloud the affected box is rebuilt from the radius every check +AreaEffectCloud.readAdditionalSaveData missing minor: clouds are not loaded from a world; a saved lingering or breath cloud, or one made by /summon, does not appear +AreaEffectCloud.addAdditionalSaveData missing minor: a lingering potion or dragon breath cloud is gone after the world is saved and reopened +AreaEffectCloud.get skip:other entity data component accessor for predicates and /data; Horizon keeps the cloud's potion in PotionCloud +AreaEffectCloud.applyImplicitComponents inline crates/horizon-client/src/game/potions.rs::Game::splash the lingering branch takes the thrown stack's potion effects and colour; potion_duration_scale is fixed at 0.25 +AreaEffectCloud.applyImplicitComponent inline crates/horizon-client/src/game/potions.rs::Game::splash same: potion contents only +Avatar.defineSynchedData port crates/horizon-client/src/lan/hub.rs::Hub::player_state gap: minor: skin parts (entry 16) are sent, the main hand entry is not +Avatar.isModelPartShown missing player: Options > Skin Customization is an inactive button and the client always sends all parts shown, so hat, jacket, sleeve and pants layers and the cape cannot be switched off +Bucketable.loadDefaultDataFromBucketTag port crates/horizon-client/src/entity_nbt.rs::load_bucket_data NoAI, Silent, NoGravity, Glowing, Invulnerable, PersistenceRequired and Health +ContainerUser.getLivingEntity skip:java interface cast; every container user here is the player +ConversionTracker.addAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt gap: minor: DrownedConversionTime and InWaterTime are kept from the loaded NBT or written as -1; live progress is not saved, and skeletons write no FreezingTime or StrayConversionTime +ConversionTracker.readAdditionalSaveData missing minor: saved conversion and affliction times are not read, a half converted zombie or skeleton starts over +ConversionTracker.doConversion inline crates/horizon-client/src/mobs/undead.rs::custom_server_ai_step gap: player: zombie to drowned, husk to zombie and skeleton to stray (and piglin to zombified piglin, hoglin to zoglin) spawn a fresh mob through SpawnKind, so the old one's armor, held items, name tag and effects are lost +ConversionType.convertCommon port crates/horizon-client/src/game/zombie_villagers.rs::carry_over gap: player: only villager and zombie villager conversions use it (dispenser.rs::Game::convert_mob copies the flags for shearing); drowned, stray, zombified piglin, zoglin, frog and witch conversions go through SpawnKind and carry only baby, yaw and persistence, so name tag, armor, held items and effects are lost; absorption and tags are never copied +ConversionType.copyComponent inline crates/horizon-client/src/game/zombie_villagers.rs::carry_over gap: minor: the custom name is kept by carry_over and convert_mob, custom data is not +Crackiness.byFraction port crates/horizon-client/src/mobs/golem.rs::crackiness iron golem thresholds 0.75, 0.5, 0.25; wolf armor 0.95, 0.69, 0.32 are in mobs/render.rs::layers and mobs.rs::Mob::hurt_wolf_armor +Crackiness.byDamage inline crates/horizon-client/src/mobs/render.rs::layers wolf armor: durability left over max picks the crack texture; a golem uses its health instead +Display.onSyncedDataUpdated port crates/horizon-client/src/game/text_displays.rs::RemoteTextDisplay::metadata gap: minor: text displays a remote server sends only; start delay, duration and render state flags are tracked, width and height do not update a culling box +Display.createTransformation port crates/horizon-client/src/text_display.rs::TextDisplayData::matrix translation, left rotation, scale, right rotation plus the billboard turn +Display.tick port crates/horizon-client/src/game/text_displays.rs::RemoteTextDisplay::tick start tick, interpolation duration and render state refresh; text displays only +Display.defineSynchedData port crates/horizon-client/src/text_display.rs::TextDisplayData::default the defaults of entries 8 to 22 +Display.readAdditionalSaveData missing player: display entities saved in a world (made by commands, datapacks or maps) are not loaded; the local game has no display entity, only a remote server's text displays are drawn +Display.setTransformation missing minor: server side setter; there is no local display entity +Display.addAdditionalSaveData missing minor: no local display entity to save +Display.getInterpolatedBoundingBox missing minor: no culling box, a text display is drawn whenever it is within its view range, even off screen +Display.setBrightnessOverride missing minor: server side setter; there is no local display entity +Display.getBrightnessOverride inline crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry the packed sky and block light of the brightness entry (-1 uses the world light) +Display.calculateInterpolationProgress port crates/horizon-client/src/game/text_displays.rs::RemoteTextDisplay::progress +Display.updateCulling missing minor: width and height are stored but no culling box is built +Display.shouldRenderAtSqrDistance inline crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry view_range * 64 * view scale squared +Display.getTeamColor missing minor: glow_color_override is read but a glowing display gets no coloured outline +Display.createFreshRenderState inline crates/horizon-client/src/game/text_displays.rs::RemoteTextDisplay::tick the copy of the synced data when no interpolation runs +Display.createInterpolatedRenderState inline crates/horizon-client/src/game/text_displays.rs::RemoteTextDisplay::tick the new start state is the old one blended to the last drawn progress (TextDisplayData::blend) +Display.BlockDisplay missing player: block displays a server or map places are not drawn at all (only text displays are); command and datapack made decorations are invisible +Display.ItemDisplay missing player: item displays (floating items, custom models on servers) are not drawn at all; only text displays are +Display.TextDisplay.defineSynchedData port crates/horizon-client/src/text_display.rs::TextDisplayData::default text, line width 200, background 0x40000000, opacity -1, flags 0 +Display.TextDisplay.onSyncedDataUpdated port crates/horizon-client/src/game/text_displays.rs::RemoteTextDisplay::metadata the text render state ids mark the render state dirty +Display.TextDisplay.loadFlag missing minor: reads the NBT booleans into the style flags; no local display entity +Display.TextDisplay.readAdditionalSaveData missing player: text displays saved in a world are not loaded (see Display.readAdditionalSaveData) +Display.TextDisplay.storeFlag missing minor: writes the style flags to NBT; no local display entity +Display.TextDisplay.addAdditionalSaveData missing minor: no local display entity to save +Display.TextDisplay.updateRenderSubState port crates/horizon-client/src/text_display.rs::TextDisplayData::blend text, line width, opacity and background blend over the interpolation +Display.TextDisplay.createFreshTextRenderState inline crates/horizon-client/src/game/text_displays.rs::RemoteTextDisplay::tick the plain copy of the synced text data +Display.TextDisplay.createInterpolatedTextRenderState port crates/horizon-client/src/text_display.rs::TextDisplayData::blend opacity and the four background channels interpolate linearly +Display.TextDisplay.cacheDisplay inline crates/horizon-client/src/game/text_displays.rs::Game::text_display_geometry the split lines and widths are cached in RemoteTextDisplay::lines until the render state changes +Display.TransformationInterpolator.get port crates/horizon-client/src/text_display.rs::TextDisplayData::blend translation and scale lerp, both rotations slerp +DropChances.filterDefaultValues missing minor: drop_chances is neither read from nor written to a mob's NBT, so custom drop chances (guaranteed drops) are lost on save and reload +DropChances.toEnumMap missing minor: the saved drop_chances map is not read back, a loaded mob uses the default 0.085 or species rule +DropChances.withEquipmentChance port crates/horizon-client/src/mobs/finalize.rs::set_drop_chance stored per slot in extra.drop_chances +ElytraAnimationState.tick port crates/horizon-client/src/mobs.rs::ElytraAnimation::tick diff --git a/tools/port_map/game-005.tsv b/tools/port_map/game-005.tsv new file mode 100644 index 0000000000..69f216ae4d --- /dev/null +++ b/tools/port_map/game-005.tsv @@ -0,0 +1,143 @@ +Entity. port crates/horizon-client/src/mobs.rs::Mob::new mobs only; players and projectiles are built by their own structs and the synched defaults (flags, air, name, pose) are plain fields +Entity.isColliding missing minor: a block change that a server refuses and that comes back inside the player does not snap the player to the position of the prediction (end_predictions only restores the block) +Entity.canInteractWithLevel inline crates/horizon-client/src/mobs/leash.rs::tick gap: minor: only the leashed mob's life is tested (a dead one drops its lead); a dead or spectating holder is not tested +Entity.unRide inline crates/horizon-client/src/game/consume.rs::Game::stop_riding gap: minor: the player's half (boat, minecart or mob mount); riders of a vehicle are let go by sync_riders and eject_boat_mobs +Entity.addTag port crates/horizon-client/src/game/commands/data.rs::Game::cmd_tag the tag.add branch, with the 1024 tag limit +Entity.equals skip:java entities are compared by id +Entity.setRot skip:other plain setter; yaw and pitch are struct fields and the callers clamp pitch +Entity.reapplyPosition skip:other bounding boxes are built from the position on demand (Mob::bounds_at), so there is no cached box to refresh +Entity.commonTick inline crates/horizon-client/src/mobs/ai.rs::tick previous position and rotations saved and age counted; invulnerable time counts down in base_tick; remote mobs do the same in tick_remote_entities +Entity.computeSpeed missing minor: lastKnownSpeed is not tracked; only FollowPlayerRiddenEntityGoal reads it and Horizon has no such goal +Entity.setSharedFlagOnFire inline crates/horizon-client/src/game/lan.rs::mob_meta gap: minor: flag 0x01 is fire_ticks > 0, packed only when a mob goes to LAN guests; HasVisualFire is not read +Entity.isSupportedBy inline crates/horizon-client/src/game/piston_push.rs::Game::push_body supporting block equal to the piston's block decides whether the body rides it +Entity.getBlockBounciness port crates/horizon-world/src/movement_restitution.rs::restitute_movement gap: player: only the player uses block restitution; mobs ignore it (Mob::do_move says so), so a mob falling on a slime block or bed does not bounce +Entity.addMovementThisTick port crates/horizon-world/src/entity_move.rs::MovementHistory::add the 100 record cap merges the first two (MovementHistory); only the player keeps a history, mobs and arrows sweep from the start to the end of their move +Entity.hasMovedHorizontallyRecently missing player: dolphins never swim after and escort a boat or nautilus a player is steering (no FollowPlayerRiddenEntityGoal); nothing tracks horizontal speed +Entity.isStateClimbable port crates/horizon-world/src/movement_emission.rs::climbable climbable tag or powder snow +Entity.applyPistonMovementRestriction port crates/horizon-world/src/piston_movement.rs::PistonMovement::limit the 0.51 per axis budget for each game tick +Entity.getAvailableSpaceBelow missing player: /gamemode creative from spectator while standing on the ground leaves the player flying (vanilla drops flight when the ground is under 1 block away and nothing blocks the box) +Entity.collectAllColliders inline crates/horizon-world/src/world_movement.rs::resolve_world_movement entity, border and block shapes are gathered inside resolve_world_movement; no standalone list exists, and the creative flight drop on a mode switch has no use of it +Entity.collideWithShapes port crates/horizon-core/src/geometry/movement.rs::resolve_shape_movement axis step order from the movement size +Entity.debugBlockIntersection skip:debug debug subscription for the entity block intersection overlay +Entity.gameEvent port crates/horizon-client/src/game/sculk.rs::Game::post_entity_event gap: minor: only players and mobs (Who) can be the source +Entity.playMuffledStepSound port crates/horizon-client/src/motion.rs::MotionWorld::walking_step_sound gap: minor: players only; mobs play the floor block's own step and never the muffled floor sound under snow, carpet or other combination blocks +Entity.shouldPlayAmethystStepSound port crates/horizon-client/src/motion.rs::MotionWorld::walking_step_sound gap: minor: only the player chimes on crystal blocks; mobs walking on amethyst stay silent +Entity.isInClouds missing minor: a happy ghast above the cloud layer should heal a point a second like in rain; only rain and snow count +Entity.updateSwimming port crates/horizon-world/src/swimming.rs::update the player goes through update_player; mobs never swim in the player sense +Entity.doWaterSplashEffect port crates/horizon-world/src/water_splash.rs::water_splash sound, bubbles, splashes and the splash event; arrows, mobs and the player each supply a world +Entity.spawnSprintParticle port crates/horizon-client/src/game/client_ambient.rs::Game::sprint_dust gap: minor: players only; sprinting cats and ocelots raise no block dust +Entity.absSnapTo skip:other plain position setter; Horizon writes position and previous position fields directly +Entity.absSnapRotationTo skip:other plain rotation setter with the pitch clamp; teleports clamp pitch where they set it +Entity.snapTo skip:other plain position and rotation setter; spawns and teleports write the fields (Mob::new, teleport_to_in_level) +Entity.setOldPos skip:other previous position is a field set where a move starts +Entity.setOldRot skip:other previous rotations are fields set where a tick starts +Entity.distanceToSqr port crates/horizon-client/src/mobs.rs::Mob::distance_sqr plain arithmetic; other entities use Vec3::distance_squared +Entity.hurtOrSimulate skip:other dispatches to the server or client hurt; the owning side applies damage directly +Entity.calculateViewQuaternion inline crates/horizon-client/src/game/enchant_effects.rs::Game::apply_piercing_effect its only use is the lunge enchantment's apply_impulse, which rotates the direction by pitch then yaw by hand +Entity.getPosition port crates/horizon-core/src/geometry/mod.rs::Vec3::lerp previous to current position at the frame fraction +Entity.awardKillScore inline crates/horizon-client/src/game.rs::Game::tick the player's death branch fires entity_killed_player with the killer's type +Entity.shouldRender port crates/horizon-client/src/game/entity_culling.rs::Game::entity_in_view range from default_range (64 times the mean box edge times the view scale), then the frustum +Entity.saveAsPassenger port crates/horizon-client/src/game/persist.rs::Game::mob_tree_nbt a mob with its riders nested under Passengers; players and unsaved types are skipped +Entity.getEncodeId inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt the id written is the mob type's id +Entity.dropAllLeashConnections port crates/horizon-client/src/game/dispenser.rs::Game::shear_off_leashes gap: minor: only shears (dispenser) reach it; a firework used while gliding does not drop the player's leads +Entity.canCollideWith inline crates/horizon-client/src/game/ghasts.rs::Game::player_entity_collisions gap: player: the entities a player cannot walk through are boats and happy ghasts only; a live shulker (canBeCollidedWith) is not solid, so the player walks through it +Entity.getDefaultPassengerAttachmentPoint inline crates/horizon-client/src/mobs/riders.rs::passenger_attachment one point per vehicle, so the passenger index is always 0; default is the top of the box +Entity.ejectPassengers port crates/horizon-client/src/game/boat_passengers.rs::Game::eject_boat_mobs gap: minor: boats place riders at the dismount spot; when a mob vehicle dies sync_riders only lets go and the rider stays where it was seated +Entity.removeVehicle inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::dismount_mob gap: minor: no entity_dismount game event, so sculk sensors and wardens never hear a dismount +Entity.moveOrInterpolateTo port crates/horizon-world/src/stepped_interpolation.rs::SteppedInterpolation::interpolate_to living entities from a server; boats and other objects use a three tick Lerp +Entity.getHandHoldingItemAngle missing player: the rocket of a gliding player starts at the player's position, not 0.5 block out toward the hand holding it, so its trail sparks and burst start slightly off +Entity.teleportToPortalDestination inline crates/horizon-client/src/game/nether_portal.rs::Game::send_through_portal may_enter checks isAllowedToEnterPortal; the entity is saved and waits for its dimension +Entity.getDimensionChangingDelay inline crates/horizon-client/src/game/nether_portal.rs::Game::set_portal_cooldown 300 ticks for entities (ENTITY_DELAY) and 10 for the player (PLAYER_DELAY); a vehicle with the player on it never goes through a portal +Entity.getSharedFlag inline crates/horizon-client/src/game/lan.rs::mob_meta flags are separate fields (fire_ticks, invisible, glowing, sprinting) packed into the shared flags byte only for LAN guests +Entity.setSharedFlag inline crates/horizon-client/src/game/lan.rs::mob_meta flags are separate fields (fire_ticks, invisible, glowing, sprinting) packed into the shared flags byte only for LAN guests +Entity.handleOnInsideBubbleColumn port crates/horizon-client/src/motion/fluids.rs::bubble_column_push mobs, items and pearls; arrows have Ticker::bubble_column and the player motion/environment.rs +Entity.removeAction skip:other a mob's custom name is kept as plain text, so it carries no click event; other entities show the stored CustomName as it is +Entity.getName inline crates/horizon-client/src/game/commands.rs::Game::target_name custom name, else the entity type's translated name +Entity.toString skip:debug debug text +Entity.isInvulnerableToBase port crates/horizon-world/src/damage_immunity.rs::base_immunity removed, invulnerable without bypass, fire immune and fall immune +Entity.copyPosition inline crates/horizon-client/src/game/dispenser.rs::Game::convert_mob a converted mob takes the old one's position, previous position and yaws; zombie villager cures copy them in zombie_villagers.rs +Entity.restoreFrom inline crates/horizon-client/src/game/nether_portal.rs::Game::resolve_arrivals the saved tag is loaded again as the new entity; PortalCooldown is written into the tag +Entity.teleport inline crates/horizon-client/src/game/commands.rs::Game::teleport_to_in_level gap: minor: same level /tp only; a riding mob is not dismounted first, so it is pulled back onto its vehicle next tick (the player does stop riding); portals use send_through_portal +Entity.teleportSameDimension inline crates/horizon-client/src/game/commands.rs::Game::teleport_to_in_level position, rotation and old rotation set; riders are re-seated each tick by position_riders +Entity.teleportSpectators inline crates/horizon-client/src/game/commands/extra.rs::Game::tick_camera a spectator's camera follows its target every tick, so no teleport is needed; it drops when the target is gone +Entity.calculatePassengerTransition inline crates/horizon-client/src/game/passengers.rs::Game::position_riders riders are seated on the vehicle each tick instead of getting an offset transition +Entity.sendTeleportTransitionToPlayers inline crates/horizon-client/src/lan/entity.rs::ServerEntity::send_changes a LAN guest sees a teleport as an EntityPositionSync when the jump is too big for a relative move; no ClientboundTeleportEntity is sent +Entity.forceSetRotation inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_rotate /rotate goes through teleport_to_in_level with the new rotation; the pitch of non-players is not clamped separately +Entity.placePortalTicket skip:other only one dimension is loaded at a time; arriving entities wait in portals.arrivals until resolve_arrivals can place them +Entity.canTeleport inline crates/horizon-client/src/game/end.rs::Game::tick_end leaving the End before the credits have been seen shows the credits instead of teleporting the player +Entity.fillCrashReportCategory skip:debug crash report detail +Entity.teleportPassengers inline crates/horizon-client/src/game/passengers.rs::Game::position_riders riders and riders of riders are seated again each tick +Entity.teleportRelative port crates/horizon-client/src/landing.rs::MotionWorld::teleport_relative gap: minor: only the player is lifted out of a block that turns solid; mobs are lifted by trample_farmland, dropped items and boats are not +Entity.fixupDimensions inline crates/horizon-client/src/mobs/living.rs::Mob::dims dimensions are worked out on each call from size, age and pose, so there is nothing to refresh after a slime resize +Entity.createHoverEvent inline crates/horizon-client/src/game/lan_trading.rs::Game::merchant_display_name gap: minor: only players and merchants get the show_entity hover in chat; other mobs' names have none +Entity.getInterpolatedBoundingBox port crates/horizon-client/src/game/entity_culling.rs::Game::standing_entity_in_view the box is moved to the interpolated position for culling +Entity.getFirstPassenger inline crates/horizon-client/src/game/passengers.rs::Game::sync_riders gap: minor: one rider id per mob vehicle (extra.rider), not a list +Entity.hasPassenger inline crates/horizon-client/src/game/passengers.rs::Game::sync_riders gap: minor: one rider id per mob vehicle (extra.rider); the player's vehicle is Game.riding +Entity.hasIndirectPassenger skip:other vehicle chains are seated by position_riders in three passes; no separate lookup is kept +Entity.isLocalInstanceAuthoritative port crates/horizon-client/src/motion.rs::MotionWorld::locally_authoritative the player's server copy is not authoritative; mobs are always local +Entity.isLocalClientAuthoritative inline crates/horizon-client/src/motion.rs::MotionWorld::locally_authoritative a ridden vehicle's passenger check is not modelled; the player's own copy decides +Entity.isClientAuthoritative skip:other the host trusts its own player and LAN guests send their moves; there is no per-vehicle authority switch +Entity.canSimulateMovement port crates/horizon-client/src/motion.rs::MotionWorld::can_simulate gap: minor: always true; the move simulation type of vehicles and remote entities is not consulted +Entity.getCollisionHorizontalEscapeVector inline crates/horizon-client/src/game/boats.rs::Game::boat_dismount_target gap: minor: only boats use the formula; horses and striders use a plain side offset in dismount_mount +Entity.getControlledVehicle skip:other the player's vehicle is Game.riding and a mob's is extra.mount; the controlling passenger is the first rider +Entity.createCommandSourceStackForNameResolution inline crates/horizon-client/src/game/scoreboard.rs::Game::resolve_component gap: minor: selector and score parts resolve from the player's view, not from the entity's own position and no-permission source +Entity.touchingUnloadedChunk missing minor: fall damage checks and spectator ticks are not skipped at the edge of the loaded area (a LAN guest there) +Entity.getX skip:other plain position field read +Entity.getRandomY skip:other a random point in the box; each effect draws its own offsets inline +Entity.getZ skip:other plain position field read +Entity.getRandomZ skip:other a random point in the box; each effect draws its own offsets inline +Entity.getRopeHoldPosition inline crates/horizon-client/src/game/leads.rs::Game::leash_geometry a mob holder's end is its interpolated position plus 0.7 of its eye height; a player's comes from player_rope_hold +Entity.setYRot skip:other plain setter; yaw is a struct field +Entity.getClientPosition port crates/horizon-world/src/stepped_interpolation.rs::SteppedInterpolation::heading the interpolation target while it runs, else the entity's position +Entity.shouldBeSaved inline crates/horizon-client/src/game/persist.rs::Game::put_mob_passengers riders are saved inside their vehicle's tag, not on their own +Entity.lerpPositionAndRotationStep inline crates/horizon-client/src/game/remote.rs::Game::tick_remote_entities boats, minecarts, projectiles and orbs of a server step toward the packet's target by 1/steps each tick +Entity.applyImplicitComponents missing player: a spawn egg (or armor stand, boat or minecart item) renamed in an anvil spawns an unnamed entity; the item's custom_name and custom_data are not applied +Entity.applyImplicitComponent missing player: custom_name and custom_data of a placed item are not applied to the new entity; only name tags (name_mob) and buckets copy data +Entity.applyImplicitComponentIfPresent missing player: same gap, no item component reaches a freshly spawned entity +EntityAttachments.scale missing minor: attachment points are not scaled with the mob, so riders on a resized or baby vehicle sit at the unscaled height +EntityAttachments.getNullable inline crates/horizon-client/src/game/names.rs::Game::name_tags gap: minor: only the NAME_TAG default (0.5 above the height) is used, so a sniffer's tag (offset 2.05) sits 0.2 too high +EntityAttachments.get inline crates/horizon-client/src/mobs/riders.rs::passenger_attachment the point is always found: the species table or the top of the box +EntityAttachments.getAverage inline crates/horizon-client/src/mobs/riders.rs::passenger_attachment gap: minor: one point; a sheared harness or sulfur cube item spawns at the mob's centre or box top rather than at the average passenger point +EntityAttachments.getClamped inline crates/horizon-client/src/mobs/riders.rs::seat the point turned by the vehicle's yaw and added to its position +EntityAttachments.Builder.attach inline crates/horizon-client/src/mobs/riders.rs::passenger_attachment attachment points are a match per species; only PASSENGER (and VEHICLE in vehicle_attachment) exist +EntityAttachments.Builder.build inline crates/horizon-client/src/mobs/riders.rs::passenger_attachment the fallback when a species has no point is the top of its box +EntityDimensions.scale port crates/horizon-client/src/mobs/living.rs::Mob::dims baby and size scaling multiply width, height and eye height; gap: minor: attachments are not scaled +EntityDimensions.withEyeHeight skip:data eye heights come from entity-types.json (MobType.eye_height) +EntityDimensions.withAttachments inline crates/horizon-client/src/mobs/riders.rs::passenger_attachment attachments are a per species table, not part of a dimensions value +EntityEquipment.isEmpty port crates/horizon-world/src/player_inventory.rs::PlayerInventory::is_empty +EntityEquipment.tick port crates/horizon-world/src/inventory_tick.rs::tick_equipment gap: minor: the player's worn items only; items on mobs never tick +EntityEquipment.dropAll port crates/horizon-client/src/game.rs::Game::drop_inventory the main inventory, armour and off hand are thrown in random directions on death (Inventory.dropAll) +EntityFluidInteraction.getOrCreateTrackerFor inline crates/horizon-world/src/entity_fluid.rs::EntityFluidInteraction::update a tracker is found or pushed while scanning the box +EntityFluidInteraction.getCurrentAccumulatorFor inline crates/horizon-world/src/entity_fluid.rs::EntityFluidInteraction::new one accumulator is made per fluid tag up front +EntityFluidInteraction.CurrentAccumulator.accumulate port crates/horizon-world/src/entity_fluid.rs::CurrentAccumulator::accumulate +EntityFluidInteraction.Tracker.reset inline crates/horizon-world/src/entity_fluid.rs::EntityFluidInteraction::update the retain_mut at the start: idle trackers are dropped, the others get height 0 and eyes outside +EntityReference.of skip:java references are plain UUID arrays (extra.owner), looked up when used +EntityReference.getEntity port crates/horizon-client/src/game/persist.rs::Game::mob_by_uuid gap: minor: mobs only; the player is matched by its own UUID where it is needed +EntityReference.resolve skip:java class cast check +EntityReference.store inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt Owner is written as an int array UUID +EntityReference.readWithOldOwnerConversion inline crates/horizon-client/src/entity_nbt.rs::mob_from_nbt gap: minor: Owner is read as an int array UUID; an owner saved as a player name (worlds from before 1.7.6) is not converted +EntityReference.equals skip:java UUIDs are compared directly +world/entity/EntitySelector.withinDistance port crates/horizon-client/src/game/dragon.rs::Game::fight_has_players the 192 block range around (0, 128, 0) for the dragon fight's valid players +EntityType.appendCustomEntityStackConfig missing player: a spawn egg or entity item with an entity_data component spawns a plain entity (use_spawn_egg ignores the stack's data) +EntityType.updateCustomEntityTag missing player: entity_data of a spawn egg (from /give or a data pack) is never loaded into the spawned entity +EntityType.getDescription inline crates/horizon-client/src/game/commands.rs::Game::target_name entity.. translated +EntityType.toShortString skip:debug only a game test message uses it +EntityType.loadPassengersRecursive port crates/horizon-client/src/game/persist.rs::Game::load_mob riders under Passengers are loaded after the vehicle; gap: minor: only mobs Horizon simulates, the rest stay in the tag +EntityType.loadEntitiesRecursive inline crates/horizon-client/src/game/persist.rs::Game::load_mob chunk entity lists, spawner and structure entities load each tag through load_mob +EntityType.loadStaticEntity port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt a bad tag gives None instead of an exception +EntityType.Builder.passengerAttachments inline crates/horizon-client/src/mobs/riders.rs::passenger_attachment per species heights for the vehicles that carry riders; others use the box top +EntityType.Builder.attach inline crates/horizon-client/src/mobs/riders.rs::passenger_attachment only the PASSENGER point is a table; NAME_TAG and the rest use their defaults +EntityType.Builder.build skip:data entity types are read from entity-types.json (MobType) instead of being built in code +EquipmentSlot.isArmor skip:other slots are plain head, chest, legs and feet constants; armour logic names them (Ctx::armor) +EquipmentSlot.byName skip:data slot names are read as strings from data and NBT +EquipmentSlotGroup.bySlot inline crates/horizon-client/src/game/lan_attributes.rs::slot_in_group a slot is its own group, so the test is group == slot +EquipmentTable.createForAllSlots skip:data drop chances come from the loot and entity data +ExperienceOrb. port crates/horizon-client/src/game.rs::Game::add_orb random yaw and motion from the orb's own random; gap: minor: no rough direction offset and no unstuck when the orb spawns inside a block +ExperienceOrb.unstuckIfPossible missing minor: an orb that spawns inside blocks (a kill in a 1x1 hole) is not nudged to the nearest free spot; Mob has free_position, orbs do not use it +ExperienceOrb.awardWithDirection port crates/horizon-client/src/game.rs::Game::spawn_orbs gap: minor: the rough direction is never given, so xp bottle orbs do not fly away from the surface they hit +ExperienceOrb.setUnderwaterMovement port crates/horizon-client/src/entities.rs::ExperienceOrb::tick gap: minor: tests the block at the orb's feet, not its eyes +ExperienceOrb.hurtServer missing player: xp orbs have no health, so explosions, fire and lava never destroy them (vanilla orbs have 5) +ExperienceOrb.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::orb_to_nbt gap: minor: Health is always written as 5 +ExperienceOrb.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::orbs_from_nbt gap: minor: Health is ignored +HumanoidArm.getOpposite inline crates/horizon-client/src/game.rs::Game::hand_geometry the main hand's side is main_hand_left; the off hand is the other side +InsideBlockEffectApplier.StepBasedCollector.flushStep port crates/horizon-client/src/arrows.rs::Collector::flush gap: minor: the arrow collector has four effects (clear freeze, fire, lava, extinguish); the player's collector sits in motion.rs check_inside_blocks diff --git a/tools/port_map/game-006.tsv b/tools/port_map/game-006.tsv new file mode 100644 index 0000000000..0e106b6404 --- /dev/null +++ b/tools/port_map/game-006.tsv @@ -0,0 +1,150 @@ +Interaction.defineSynchedData missing minor: no interaction entity; /summon interaction (the invisible click hitbox map makers use) creates nothing +Interaction.readAdditionalSaveData missing minor: no interaction entity; /summon interaction (the invisible click hitbox map makers use) creates nothing +Interaction.addAdditionalSaveData missing minor: no interaction entity; /summon interaction (the invisible click hitbox map makers use) creates nothing +Interaction.onSyncedDataUpdated missing minor: no interaction entity; /summon interaction (the invisible click hitbox map makers use) creates nothing +Interaction.skipAttackInteraction missing minor: no interaction entity; /summon interaction (the invisible click hitbox map makers use) creates nothing +Interaction.interact missing minor: no interaction entity; /summon interaction (the invisible click hitbox map makers use) creates nothing +Interaction.getLastAttacker missing minor: no interaction entity; /summon interaction (the invisible click hitbox map makers use) creates nothing +Interaction.getTarget missing minor: no interaction entity; /summon interaction (the invisible click hitbox map makers use) creates nothing +Leashable.isLeashed inline crates/horizon-client/src/mobs/leash.rs::tick the mob's extra.leash being set +Leashable.setDelayedLeashHolderId inline crates/horizon-client/src/game/remote.rs::Game::handle_packet SetEntityLink arm: a holder id not known yet waits as Holder::Mob until the knot arrives (remote_objects.rs) +Leashable.readLeashData port crates/horizon-client/src/mobs/leash.rs::pending_from_nbt holder UUID or fence knot block; kept pending until the holder exists +Leashable.restoreLeashFromSave port crates/horizon-client/src/game/leads.rs::Game::tick_leads holder found by UUID or knot block; a lead whose holder never shows up drops as an item after 100 ticks +Leashable.angularFriction inline crates/horizon-client/src/mobs/leash.rs::tick gap: minor: ground friction is a fixed 0.6 times 0.91, not the friction of the block under the mob (ice, slime) +Leashable.checkElasticInteractions port crates/horizon-client/src/mobs/leash.rs::tick gap: minor: one attachment pair only; no quad leash for a horse, llama or sniffer led by a happy ghast +Leashable.getHolderMovement port crates/horizon-client/src/mobs/leash.rs::holder_state gap: minor: the holder's velocity is always used; a NoAI mob holder should count as standing still +Leashable.computeElasticInteraction port crates/horizon-client/src/mobs/leash.rs::spring gap: minor: single attachment pair (0, 0.5, 0.5 of the mob and 0, 0.5, 0 of the holder); the four-point version is missing +Leashable.createQuadLeashOffsets missing player: a horse, llama or sniffer led by a happy ghast is drawn with one rope to its middle and pulled by one spring instead of four ropes to its corners +Leashable.leashableLeashedTo port crates/horizon-client/src/game/leads.rs::Game::player_leashed only the player as holder (box of 32); mob and knot holders are filtered inline in interact_leash and interact_knot +Leashable.leashableInArea port crates/horizon-client/src/game/leads.rs::Game::player_leashed the 32 block box around a point, for the player's lead only +Leashable.Wrench.torqueFromForce inline crates/horizon-client/src/mobs/leash.rs::spring lever.z * force.x - lever.x * force.z +Leashable.Wrench.accumulate inline crates/horizon-client/src/mobs/leash.rs::tick gap: minor: one wrench only, nothing to sum (quad leash would need it) +Leashable.Wrench.scale inline crates/horizon-client/src/mobs/leash.rs::tick gap: minor: the 0.25 scale of a quad connection is not applied +LightningBolt.powerLightningRod port crates/horizon-client/src/game/lightning.rs::Game::strike_rod powered for 8 ticks with level event 3002 along the rod +LightningBolt.getStrikePosition inline crates/horizon-client/src/game/lightning.rs::Game::strike_rod the block 1e-6 below the bolt, also used by bolt_clear_copper +LightningBolt.spawnFire port crates/horizon-client/src/game/lightning.rs::Game::bolt_fire gap: minor: no fire_spread_radius_around_player check (canSpreadFireAround) +LightningBolt.randomWalkCleaningCopper inline crates/horizon-world/src/level/tick.rs::Sim::lightning_clear_copper 3 to 5 walks of up to 8 steps +LightningBolt.randomStepCleaningCopper inline crates/horizon-world/src/level/tick.rs::Sim::lightning_clear_copper up to 10 random blocks in the 3x3x3 cube, first weathering copper steps back +LightningBolt.shouldRenderAtSqrDistance missing player: a bolt more than 64 blocks away (scaled by Entity Distance) is still drawn; vanilla shows only the sky flash and thunder there +LinearInterpolationHandler.create inline crates/horizon-client/src/game/remote.rs::Game::move_entity 3 steps (lerps.insert); only on a server's client, a local game moves boats, carts and bobbers directly +LinearInterpolationHandler.startInterpolating inline crates/horizon-client/src/game/remote.rs::Game::move_entity target kept as Lerp with steps 3 +LinearInterpolationHandler.doInterpolate inline crates/horizon-client/src/game/remote.rs::Game::tick_remote_entities gap: minor: position eased by 1/steps; a boat's or minecart's yaw is set at once, not eased +LivingEntity. port crates/horizon-client/src/mobs.rs::Mob::new box, health, random yaw +LivingEntity.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: player: arrow count (12) and stinger count (13) are never kept or sent, so arrows and bee stingers do not stay stuck in a body +LivingEntity.hasLandedInLiquid missing minor: the fall impulse context (mace, wind charge) is not cleared when a server player lands in a liquid +LivingEntity.getScale missing minor: no scale attribute, so /attribute scale and datapack scaling do nothing; baby and slime sizes come from tables in Mob::dims +LivingEntity.shouldDropLoot inline crates/horizon-client/src/mobs/drops.rs::die mob_drops rule and not a baby (monsters excepted) +LivingEntity.decreaseAirSupply port crates/horizon-client/src/motion/environment.rs::MotionWorld::tick_environment_damage gap: minor: only the player reads oxygen_bonus (Respiration); a mob loses one air per tick in ai.rs::base_tick +LivingEntity.setLastHurtByPlayer inline crates/horizon-client/src/mobs.rs::Mob::damage_with ai.last_hurt_by_player = 100 on a player's hit; Game::last_hurt_by_player records which guest +LivingEntity.onRemoval skip:other waypoint tracking is per player (game/lan_waypoints.rs); no mob has a transmit range +LivingEntity.updateDirtyEffects skip:java dirty flag; Horizon reads invisibility and swirls from the effect list when needed +LivingEntity.updateInvisibilityStatus inline crates/horizon-client/src/mobs/trader.rs::is_invisible the Invisibility effect hides the model (render_with) and sets flag 0x20 in lan.rs::mob_meta +LivingEntity.areAllEffectsAmbient inline crates/horizon-client/src/game/client_ambient.rs::Game::effect_swirls a server's flag is kept in Extra::effect_ambient; a mob's own effects are never ambient +LivingEntity.removeAllEffects port crates/horizon-client/src/effects.rs::PlayerEffects::clear player; a mob's list is cleared in commands/effects.rs::cmd_effect_clear +LivingEntity.removeEffect inline crates/horizon-client/src/game/commands/effects.rs::Game::cmd_effect_clear retain by effect id for the player and for mobs +LivingEntity.onEffectUpdated port crates/horizon-client/src/game/potions.rs::Game::apply_effect_status gap: minor: player only; a mob's attributes follow just speed and slowness, strength, weakness, resistance and absorption do nothing on mobs +LivingEntity.onEffectsRemoved port crates/horizon-client/src/game/potions.rs::Game::apply_effect_status gap: minor: same as onEffectUpdated, mobs only react to speed and slowness +LivingEntity.refreshDirtyAttributes port crates/horizon-client/src/game/potions.rs::Game::apply_effect_status modifiers diffed against the applied list each tick (player) +LivingEntity.dealDefaultKnockback inline crates/horizon-client/src/mobs.rs::Mob::damage_with gap: minor: pushed away from the source position; an arrow uses its previous position, vanilla uses its flight direction +LivingEntity.applyItemBlocking port crates/horizon-client/src/health.rs::Environment::apply_blocking gap: minor: the player's shield only; a mob holding a shield never blocks +LivingEntity.playSecondaryHurtSound port crates/horizon-client/src/game/enchant_effects.rs::Game::thorns enchant.thorns.hit at the attacker +LivingEntity.resolveMobResponsibleForDamage inline crates/horizon-client/src/mobs.rs::Mob::damage_with gap: minor: last_hurt_by comes from any attacker; no #no_anger damage types or wind charge exemption +LivingEntity.resolvePlayerResponsibleForDamage inline crates/horizon-client/src/mobs.rs::Mob::damage_with gap: player: damage by a player's tamed wolf or cat sets no last-hurt-by-player, so the kill drops no experience and no player-only loot +LivingEntity.breakItem port crates/horizon-client/src/game.rs::Game::damage_held gap: player: a tool or armor breaking shows no crumb particles and always plays entity.item.break, not the item's own break sound +LivingEntity.dropExperience inline crates/horizon-client/src/mobs/drops.rs::die orbs only when a player killed it and mob_drops is on +LivingEntity.getKnockback port crates/horizon-client/src/game/enchant_effects.rs::Game::extra_knockback gap: minor: the Knockback enchant halved (player and gear::knockback for mobs); the attack_knockback attribute counts only for the hoglin shove +LivingEntity.dropFromLootTable port crates/horizon-client/src/mobs/drops.rs::roll the entity table from its random sequence +LivingEntity.dropFromEntityInteractLootTable port crates/horizon-client/src/game/creatures.rs::Game::interact_armadillo brush/armadillo is the only table that uses it +LivingEntity.dropFromShearingLootTable port crates/horizon-client/src/game/creatures.rs::Game::interact_mob sheep by colour; snow golem and bogged in interact_shear_mob; gap: minor: a mooshroom's five mushrooms are hardcoded, not read from the table +LivingEntity.getFallDamageSound inline crates/horizon-client/src/mobs/living.rs::Mob::cause_fall_damage gap: minor: monsters use the generic small and big fall sounds, not entity.hostile.small_fall and big_fall +LivingEntity.getHitbox missing minor: a rider's hitbox is not cut at its seat height; spear and melee tests use the full box +LivingEntity.postPiercingAttack port crates/horizon-client/src/game/enchant_effects.rs::Game::post_piercing_attack Lunge's dash after a jab +LivingEntity.trapdoorUsableAsLadder port crates/horizon-world/src/climbing.rs::on_climbable gap: minor: the player only; a mob's ai.rs::on_climbable checks the climbable tag at its feet, no open trapdoor over a ladder +LivingEntity.isAlive port crates/horizon-client/src/mobs.rs::Mob::alive not removed and health above zero +LivingEntity.setIgnoreFallDamageFromCurrentImpulse port crates/horizon-world/src/living_fall.rs::ImpulseContext::set_ignore_fall_damage 40 ticks of grace +LivingEntity.getDamageAfterArmorAbsorb inline crates/horizon-client/src/mobs.rs::Mob::damage_with the player's armor wear is health.rs hurt_armor; the mob path also scales armor effectiveness (breach) +LivingEntity.getKillCredit port crates/horizon-client/src/game/creatures.rs::Game::mob_died the last player to hit it, else the last mob +LivingEntity.getTicksSinceLastKineticHitFeedback port crates/horizon-client/src/game/spear.rs::Game::kinetic_hit_ticks +LivingEntity.swapHandItems missing player: on a server another player's hand swap (entity event 55) is not handled, so their held items do not swap on screen +LivingEntity.getAttackRangeWith inline crates/horizon-client/src/game.rs::Game::reach gap: minor: only the spear's range is item specific (spear.rs REACH); other items use the default reach +LivingEntity.getActiveItem inline crates/horizon-client/src/game.rs::Game::held the selected slot; the item in use is the `using` slot +LivingEntity.getItemInHand inline crates/horizon-client/src/game.rs::Game::held main hand; the off hand is the `offhand` field +LivingEntity.setItemInHand inline crates/horizon-client/src/game.rs::Game::held_mut main hand cell +LivingEntity.getArmorCoverPercentage port crates/horizon-client/src/game/creatures.rs::Game::player_view armor_cover is worn pieces over 4; a LAN guest's is 0 +LivingEntity.shouldTravelInFluid inline crates/horizon-client/src/mobs/living.rs::Mob::travel in liquid, affected by fluids and not standing on a fluid it walks on (lava walkers) +LivingEntity.isInShallowFluid inline crates/horizon-client/src/mobs/living.rs::Mob::travel_in_fluid lava depth against fluid_jump_threshold +LivingEntity.updateFallFlyingMovement port crates/horizon-client/src/motion.rs::MotionWorld::travel_fall_flying gap: minor: the player only; a mob with a glider never fall-flies +LivingEntity.travelRidden inline crates/horizon-client/src/mobs/living.rs::Mob::travel the rider's input is applied in equine.rs ridden_step, nautilus.rs ridden and happy_ghast.rs ridden +LivingEntity.calculateEntityAnimation port crates/horizon-client/src/mobs/living.rs::Mob::update_walk_animation a passenger's legs are held still at draw time (anim.rs) +LivingEntity.causeExtraKnockback port crates/horizon-client/src/game/enchant_effects.rs::Game::extra_knockback_direction target pushed along the attacker's yaw; the attacker stops sprinting +LivingEntity.wasRecentlyStabbed inline crates/horizon-client/src/game/spear.rs::Game::spear_tick Game::stabbed with contact_cooldown_ticks +LivingEntity.rememberStabbedEntity inline crates/horizon-client/src/game/spear.rs::Game::spear_tick stabbed.insert(key, now) +LivingEntity.stabbedEntities missing player: the count of mobs stabbed in one use is never read, so the Spear Many Mobs advancement (spear_mobs trigger) never unlocks +LivingEntity.stabAttack port crates/horizon-client/src/game/spear.rs::Game::stab_mob gap: minor: the player's spear only; mobs never stab +LivingEntity.handleHandSwap skip:other the LAN server sends both changed slots in one SetEquipment instead of entity event 55; clients end in the same state +LivingEntity.handleEquipmentChanges port crates/horizon-client/src/lan/entity.rs::ServerEntity::send_equipment changed slots as one SetEquipment +LivingEntity.updatePlayersWithNewEquipment inline crates/horizon-client/src/lan/entity.rs::ServerEntity::send_equipment the packet is built there +LivingEntity.tickHeadTurn port crates/horizon-client/src/game/remote.rs::remote_player_head_turn another player's body turn; mobs use Mob::body_rotation_tick in control.rs +LivingEntity.canGlide inline crates/horizon-client/src/motion.rs::MotionWorld::player_movement_tick_with_fluids gap: minor: the player only (airborne, not flying, a worn glider, no Levitation); mobs never glide +LivingEntity.onItemPickup port crates/horizon-client/src/game/sulfur_cubes.rs::Game::sulfur_cube_pickups gap: player: only a sulfur cube's pickup fires thrown_item_picked_up_by_entity; a piglin picking up gold you threw does not, so Oh Shiny never unlocks +LivingEntity.isUsingItem inline crates/horizon-client/src/game/remote.rs::RemoteInputWorld::using_item Game::using is Some; a mob's is extra.using_item above 0 +LivingEntity.setLivingEntityFlag skip:java bit accessor; the flags are built when synced in game/lan.rs::mob_meta +LivingEntity.getTicksUsingItem inline crates/horizon-client/src/game.rs::Game::tick_used_item the tick count kept in Game::using +LivingEntity.stopUsingItem inline crates/horizon-client/src/game.rs::Game::tick_used_item using is set to None when the use ends or the stack is gone +LivingEntity.getLocalBoundsForPose missing minor: no pose boxes; a dismounting rider uses mounts.rs::dismount_mount's own side checks +LivingEntity.wouldNotSuffocateAtTargetPose port crates/horizon-client/src/pose.rs::MotionWorld::can_fit_pose gap: minor: the player's poses only; a camel changing pose is not checked +LivingEntity.canUsePortal port crates/horizon-client/src/game/nether_portal.rs::Game::mob_can_use_portal gap: minor: a sleeping mob is not excluded +LivingEntity.setPosToBed port crates/horizon-client/src/mobs/villager.rs::start_sleeping x and z at the bed's centre, y 0.6875 above its base (sleep height plus 0.125) +LivingEntity.entityEventForEquipmentBreak missing player: on a server another player's or mob's tool or armor breaking (entity events 47 to 52, 65 and 68) makes no break sound or crumbs +LivingEntity.onEquippedItemBroken inline crates/horizon-client/src/game.rs::Game::damage_held gap: minor: the stack is cleared and modifiers rebuilt, but no break event is broadcast +LivingEntity.stopLocationBasedEffects inline crates/horizon-client/src/game.rs::Game::apply_equipment all applied modifiers are dropped and re-added from what is worn +LivingEntity.getEquipmentSlotForItem inline crates/horizon-client/src/game/mob_gear.rs::Game::equip_from_table resolveSlot: the item's equip_slot, else the main hand +LivingEntity.createEquipmentSlotAccess inline crates/horizon-client/src/game/commands/items.rs::Game::slots_named /item slot names such as armor.head and weapon.mainhand +LivingEntity.getEquipmentSlot inline crates/horizon-client/src/game/commands/items.rs::Game::slots_named slot names instead of the numeric ids 98, 99 and 100 and up +LivingEntity.getSlot port crates/horizon-client/src/game/commands/items.rs::Game::slots_named for the player and mobs through /item +LivingEntity.recreateFromPacket port crates/horizon-client/src/game/remote.rs::Game::spawn_entity position, head yaw and movement of a server's mob +LivingEntity.getPassengerRidingPosition port crates/horizon-client/src/mobs/riders.rs::seat gap: minor: attachments are a table by species, not scaled by getScale and the age scale +LivingEntity.lerpHeadRotationStep inline crates/horizon-client/src/game/remote.rs::Game::tick_remote_entities head_lerps: 1/steps of the way to the target yaw +LivingEntity.isInvulnerableTo inline crates/horizon-client/src/mobs.rs::Mob::damage_with gap: minor: the invulnerable flag only; no enchantment damage immunity +Mob.getMoveControl port crates/horizon-client/src/mobs/control.rs::Mob::move_control_tick a mount is steered through riders.rs::drive +Mob.getNavigation inline crates/horizon-client/src/mobs/riders.rs::drive a rider's vehicle carries it along; each mob keeps its own ai.nav +Mob.getControllingPassenger inline crates/horizon-client/src/mobs/riders.rs::drive extra.rider steers the vehicle with its goals +Mob.asValidTarget port crates/horizon-client/src/mobs/monster_brain.rs::can_attack creative and spectator players are never valid; goal mobs use goals.rs Mob::can_target_player +Mob.spawnAnim port crates/horizon-client/src/game/spawner.rs::Game::poof_particles the poof of a mob a spawner made (event 20 on a server's client) +Mob.updateControlFlags inline crates/horizon-client/src/mobs/riders.rs::drive gap: minor: a mob in a boat keeps its JUMP goals +Mob.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt gap: minor: drop_chances and DeathLootTable survive only from the loaded raw tag +Mob.burnUndead inline crates/horizon-client/src/mobs/ai.rs::ai_step gap: player: a helmeted undead never loses helmet durability in sunlight (vanilla 0 to 1 per burn tick), so the helmet never wears out or breaks +Mob.pickUpItem missing player: mobs do not pick up dropped gear: zombies, skeletons, husks, drowned (tridents), vindicators, pillagers and wither skeletons that can pick up loot ignore items on the ground; only piglins, villagers, pandas, allays and sulfur cubes pick things up +Mob.attemptToShearEquipment inline crates/horizon-client/src/game/ghasts.rs::Game::interact_happy_ghast gap: minor: the happy ghast harness and the llama carpet (llamas.rs interact_llama) only +Mob.shearItem inline crates/horizon-client/src/game/ghasts.rs::Game::interact_happy_ghast the item is dropped and the shears lose 1 durability +Mob.canReplaceCurrentItem missing minor: only used by loot pickup (Mob.pickUpItem, missing) +Mob.compareArmor missing minor: only used by loot pickup (Mob.pickUpItem, missing); spawn gear is picked in finalize.rs Ctx::armor +Mob.compareWeapons missing minor: only used by loot pickup (Mob.pickUpItem, missing) +Mob.getApproximateAttributeWith missing minor: only used by loot pickup (Mob.pickUpItem, missing) +Mob.canReplaceEqualItem missing minor: only used by loot pickup (Mob.pickUpItem, missing) +Mob.checkSpawnObstruction port crates/horizon-client/src/mobs/spawn_selection.rs::spawn_obstruction no liquid in the box and no blocking entity +Mob.hasValidEquippableItemForSlot skip:other Horizon stores only items that fit a slot, so there is nothing to validate +Mob.createEquipmentParams inline crates/horizon-client/src/game/mob_gear.rs::Game::equip_from_table EntityLootParams with the mob as `this` +Mob.getEquipmentForSlot inline crates/horizon-client/src/mobs/finalize.rs::Ctx::armor the material table leather, copper, golden, chainmail, iron, diamond by piece +Mob.enchantSpawnedEquipment port crates/horizon-client/src/game/mob_gear.rs::Game::enchant_slot chance times the local difficulty's special multiplier +Mob.usePlayerItem port crates/horizon-client/src/game.rs::Game::consume_held gap: minor: no use_remainder (a bottle or bowl a food item leaves behind) +Mob.setNoAi skip:java plain field Mob::no_ai, synced as a flag in game/lan.rs::mob_meta +Mob.setLeftHanded skip:java plain field Extra::left_handed, synced in game/lan.rs::mob_meta +Mob.setAggressive skip:java plain field Ai::aggressive, synced in game/lan.rs::mob_meta +Mob.isNoAi skip:java plain field Mob::no_ai +Mob.isLeftHanded skip:java plain field Extra::left_handed +Mob.getAttackBoundingBox inline crates/horizon-client/src/mobs/goals.rs::Mob::within_melee_range gap: minor: a mounted mob's box is not merged with its mount's +Mob.removeAfterChangingDimensions inline crates/horizon-client/src/game/nether_portal.rs::Game::send_through_portal the mob moves through as NBT with its leash dropped, so nothing is left to clear +Mob.onAttributeUpdated missing minor: follow_range set at runtime does not resize the pathfinder's visited-node cap (set once in Mob::new) +Mob.registerDebugValues skip:debug debug overlay values +Mob.swingForAttack port crates/horizon-client/src/mobs/ai.rs::Mob::swing_hand_modified swing duration with haste, conduit power and mining fatigue +NeutralMob.addPersistentAngerSaveData port crates/horizon-client/src/mobs/bee.rs::to_nbt gap: minor: anger_end_time only; angry_at (the target's UUID) is not saved, and wolves and polar bears keep a tick counter instead +NeutralMob.readPersistentAngerSaveData port crates/horizon-client/src/mobs/bee.rs::from_nbt gap: minor: anger_end_time only; angry_at is not read, so a reloaded angry bee picks its target again +NeutralMob.isAngryAtAllPlayers missing minor: the universal_anger game rule has no effect (default off) +NeutralMob.playerDied missing player: the forgive_dead_players rule (on by default) is not implemented, so bees, wolves and polar bears stay angry at a player who died and respawned +OminousItemSpawner.tick port crates/horizon-client/src/game/ominous_item_spawner.rs::Game::tick_ominous_item_spawners count, then server and client parts +OminousItemSpawner.tickServer inline crates/horizon-client/src/game/ominous_item_spawner.rs::Game::tick_ominous_item_spawners the about-to-spawn sound 36 ticks ahead, then the item +OminousItemSpawner.tickClient inline crates/horizon-client/src/game/ominous_item_spawner.rs::Game::tick_ominous_item_spawners particles every 5 game ticks +OminousItemSpawner.spawnItem port crates/horizon-client/src/game/ominous_item_spawner.rs::Game::spawn_ominous_item item entity or projectile, level event 3021 and entity_place game event +OminousItemSpawner.spawnProjectile inline crates/horizon-client/src/game/ominous_item_spawner.rs::Game::spawn_ominous_item gap: minor: power and uncertainty come from projectile(); the spawner is not set as the projectile's owner +PathfinderMob.checkSpawnRules port crates/horizon-client/src/mobs/ai.rs::walk_target_value natural spawning needs a walk target value of 0 or more diff --git a/tools/port_map/game-007.tsv b/tools/port_map/game-007.tsv new file mode 100644 index 0000000000..0a3711cd38 --- /dev/null +++ b/tools/port_map/game-007.tsv @@ -0,0 +1,149 @@ +PortalProcessor.processPortalTeleportation port crates/horizon-client/src/nether_portal.rs::process decayTick when not inside, else counts up to the transition time; the time comes from the players_nether_portal_* rules for the player +PositionMoveRotation.of skip:java record factory for teleport packets; Horizon builds the packet fields directly (lan/hub.rs teleport_relative) +PostSpawnProcessor.andThen skip:java closure chaining +Relative.rotation skip:java EnumSet builder; Horizon uses the bit constants in horizon-protocol packets/play.rs relative +Relative.position skip:java EnumSet builder; bit constants +Relative.direction skip:java EnumSet builder; bit constants +Relative.isSet skip:java bit test; callers test the flag bits with & +Relative.unpack skip:java the packet keeps the packed int and tests bits directly +Relative.pack skip:java the packet keeps the packed int +SlotAccess.forEquipmentSlot inline crates/horizon-client/src/game/commands/items.rs::slot_set gap: minor: no validator, so /item replace puts any item in a chest, legs or feet slot; vanilla refuses an item that does not belong there +SlotProvider.getSlotsFromRange inline crates/horizon-client/src/game/commands/items.rs::slots_named slot names and ranges resolve to slots there; missing slots are skipped +SpawnPlacements.register skip:data placements and heightmaps are read from tables (MobData) +SpawnPlacements.getPlacementType port crates/horizon-client/src/mobs/data.rs::placement on ground when unregistered +SteppedInterpolationHandler.create port crates/horizon-world/src/stepped_interpolation.rs::new client branch only: remote.rs builds one per living entity with its type's update interval; the server tracker is not created (see SteppedInterpolationTracker) +SteppedInterpolationHandler.InterpolationData.advance port crates/horizon-world/src/stepped_interpolation.rs::advance +SteppedInterpolationHandler.InterpolationData.addDelta skip:other remote mobs are only moved by interpolation here, so there is no predicted movement to fold in (see SteppedInterpolation::interpolate) +SteppedInterpolationHandler.InterpolationData.addRotation skip:other same: no client-side movement of remote mobs to adjust the target for +SteppedInterpolationHandler.InterpolationData.addStep port crates/horizon-world/src/stepped_interpolation.rs::add_step +SteppedInterpolationHandler.InterpolationData.addSteps port crates/horizon-world/src/stepped_interpolation.rs::add_steps +SteppedInterpolationHandler.InterpolationData.getInterpolationTicks inline crates/horizon-world/src/stepped_interpolation.rs::add_steps the sum of the steps' ticks +SteppedInterpolationHandler.InterpolationData.getNewPositionAndRotation port crates/horizon-world/src/stepped_interpolation.rs::new_position +SteppedInterpolationTracker.addStep missing minor: a LAN host never builds stepped paths; lan/entity.rs always sends a linear position, so guests interpolate each move over the update interval instead of replaying the host's step timing +SteppedInterpolationTracker.updateTracking missing minor: same, the host keeps no tracked steps +SteppedInterpolationTracker.getPositionPath missing minor: same, only PositionPath::Linear is sent +SulfurCubeArchetype.AttributeEntry.add skip:data archetype attribute modifiers are constants in mobs/sulfur_cube.rs ARCHETYPES +SulfurCubeArchetype.AttributeEntry.multiply skip:data same table +SulfurCubeArchetypes.bootstrap skip:data the 12 archetypes are the ARCHETYPES table in mobs/sulfur_cube.rs +SulfurCubeArchetypes.archetype skip:data builds the speed, bounce, friction and drag modifiers; values are in the table +SulfurCubeArchetypes.contactDamage skip:data hot archetype's contact damage is a field of the table +SulfurCubeArchetypes.register skip:data registry registration +TamableAnimal.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: minor: the flags byte (sitting 1, tame 4) is sent to LAN guests; the owner UUID data is never sent +TamableAnimal.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt Owner and Sitting for wolf, cat and parrot +TamableAnimal.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt an owner makes the mob tame (wolf health 40), Sitting restored; old owner-name conversion is not done +TamableAnimal.handleEntityEvent port crates/horizon-client/src/particles.rs::entity_event_burst event 7 hearts, event 6 smoke, seven particles +TamableAnimal.isTame skip:other a field: Mob.extra.tame +TamableAnimal.setInSittingPose skip:other a field: Mob.extra.sitting, set by the sit goals and interactions +TamableAnimal.tame inline crates/horizon-client/src/game/creatures.rs::interact_mob wolf (bone, 1 in 3), cat and parrot set tame, owner and sitting in their interaction arms, then the tame_animal trigger; mobs/tame.rs::set_tame gives the wolf 40 health +TamableAnimal.getTeam missing minor: a tame pet does not take its owner's scoreboard team (mob_team looks up only the pet's own UUID), so team glow colour, collision rules and friendly fire ignore the owner's team +TamableAnimal.die missing player: when a tamed wolf, cat or parrot dies its owner gets no chat death message; death_message only covers the player's own death +TamableAnimal.shouldTryTeleportToOwner port crates/horizon-client/src/mobs/tame.rs::owner_distance_sqr the >= 144 comparison is made in TamePanic::tick and the follow-owner goal +TamableAnimal.teleportToAroundBlockPos port crates/horizon-client/src/mobs/tame.rs::teleport_to_owner ten tries within 3 blocks; the walkable test is approximated (solid block below, not leaves, no fluid, no collision) +WalkAnimationState.speed port crates/horizon-client/src/mobs/anim.rs::state speed(partial) lerps previous and current limb speed, capped at 1; the plain speed() is the limb_speed field +ActivityData.createPriorityPairs inline crates/horizon-client/src/mobs/villager_brain.rs::packages each behaviour's priority is written out where it is added +Brain.provider skip:data builder of the sensor list and activity supplier; each species builds its brain in init or make_brain +Brain. port crates/horizon-client/src/mobs/villager_brain.rs::make_brain sensors' first scans delayed by draws from the mob's random, packed memories restored, schedule applied; the warden's is Brain::new plus delay_sensors +Brain.getMemorySlot skip:java memories are typed Slot fields of Memories, no map lookup +Brain.setMemoryInternal port crates/horizon-client/src/mobs/villager_brain.rs::set_ttl Slot::set_ttl stores a value with its time to live; set_list erases an empty list +Brain.getMemoryInternal skip:java typed fields, an unregistered memory cannot be asked for +Brain.forEach inline crates/horizon-client/src/mobs/villager_brain.rs::pack the visitor over the memories is Memories::pack and the debug describe +Brain.isMemoryValue skip:java compare Slot::get() with the value +Brain.checkMemory port crates/horizon-client/src/mobs/villager_brain.rs::present VALUE_PRESENT is Slot::present, VALUE_ABSENT is its negation, REGISTERED is always true for a field +Brain.getRunningBehaviors inline crates/horizon-client/src/mobs/villager_brain.rs::tick behaviours are kept in start order and tick loops over those whose B::running is true +Brain.getActiveNonCoreActivity skip:other Brain.activity is the one non-core activity +Brain.eraseMemoriesForOtherActivitesThan port crates/horizon-client/src/mobs/warden_brain.rs::set_active leaving an activity erases the memory it needed (warden); villager activities erase none in vanilla +Brain.setActiveActivityToFirstValid port crates/horizon-client/src/mobs/warden_brain.rs::update_activity the same loop is in animal_brain.rs and monster_brain.rs update_activity +Brain.addActivity inline crates/horizon-client/src/mobs/villager_brain.rs::packages behaviours are listed per brain in vanilla's start order (priority, hash slot, insertion) instead of a priority map +Brain.tickEachRunningBehavior inline crates/horizon-client/src/mobs/villager_brain.rs::tick the second loop of tick +Brain.activityRequirementsAreMet port crates/horizon-client/src/mobs/villager_brain.rs::requirements_met hard-coded memory conditions per activity +Brain.isEmptyCollection port crates/horizon-client/src/mobs/villager_brain.rs::set_list only list memories can be empty +Brain.isBrainDead skip:other mobs without a brain are told apart by species (monster_brain::has_brain) or an empty brain Option +Attribute.Sentiment.getStyle port crates/horizon-client/src/item_tooltip.rs::attribute_color burning time and fall damage are negative, gravity, scale and waypoint ranges neutral, the rest positive +AttributeInstance.getModifiers port crates/horizon-world/src/attributes/mod.rs::modifiers player and LAN attribute maps; a mob's own attributes (mobs/finalize.rs) keep a plain list +AttributeInstance.addOrUpdateTransientModifier port crates/horizon-world/src/attributes/mod.rs::add_or_update_transient +AttributeInstance.addOrReplacePermanentModifier port crates/horizon-world/src/attributes/mod.rs::add_or_replace_permanent mobs: mobs/finalize.rs add_modifier +AttributeInstance.addPermanentModifier port crates/horizon-world/src/attributes/mod.rs::add_permanent +AttributeInstance.addPermanentModifiers port crates/horizon-world/src/attributes/mod.rs::add_permanent_many +AttributeInstance.removeModifier port crates/horizon-world/src/attributes/mod.rs::remove a mob's own attributes (mobs/finalize.rs) have no remove +AttributeInstance.removeModifiers port crates/horizon-world/src/attributes/mod.rs::remove_all +AttributeInstance.replaceFrom port crates/horizon-world/src/attributes/mod.rs::replace_from +AttributeInstance.pack port crates/horizon-world/src/attributes/mod.rs::pack +AttributeInstance.apply port crates/horizon-world/src/attributes/mod.rs::apply +AttributeMap.onAttributeModified port crates/horizon-world/src/attribute_map.rs::modified Dirty::dirty in attributes/live.rs calls it +AttributeMap.getBaseValue port crates/horizon-world/src/attribute_map.rs::value ValueKind::Base +AttributeMap.addTransientAttributeModifiers port crates/horizon-world/src/attributes/live.rs::add_effect_modifiers effect modifiers: remove the old one by id, then add transient +AttributeMap.removeAttributeModifiers port crates/horizon-world/src/attributes/live.rs::remove_effect_modifiers +AttributeMap.assignAllValues missing minor: the player is rebuilt from default attributes when respawning out of the End (PlayerMotion::new), so values and modifiers carried by keepAll are lost +AttributeMap.assignBaseValues missing minor: base values changed by /attribute (max_health, speed) reset on respawn because the player's attributes are rebuilt +AttributeMap.assignPermanentModifiers missing minor: permanent modifiers added by /attribute are lost on respawn out of the End +AttributeMap.resetBaseValue port crates/horizon-world/src/attribute_map.rs::reset_base live.rs reset_base for the player +AttributeMap.apply inline crates/horizon-client/src/entity_nbt.rs::mob_from_nbt gap: minor: of a mob's saved attributes only max_health, movement_speed, attack_damage, jump_strength bases and the follow range bonus are read; other bases and permanent modifiers are dropped, and a player's saved attributes are kept raw in player_extra but not applied on load +AttributeSupplier.getAttributeInstance port crates/horizon-world/src/attributes/entity.rs::instance EntityAttributes::instance +AttributeSupplier.createInstance port crates/horizon-world/src/attributes/entity.rs::create Catalog::create builds the entity's attributes from the type's default bases +AttributeSupplier.Builder.create skip:data defaults come from the attribute catalog export (Catalog::from_json) +AttributeSupplier.Builder.add skip:data same catalog export +DefaultAttributes.validate skip:debug logs entity types without attributes in an IDE +RangedAttribute.sanitizeValue port crates/horizon-world/src/attributes/mod.rs::sanitize Definition::sanitize clamps and gives the minimum for NaN +AcquirePoi.create port crates/horizon-client/src/mobs/villager_brain.rs::acquire the behaviour runs in B::try_start; POI tickets are counted from other villagers' memories +AcquirePoi.findPathToPois inline crates/horizon-client/src/mobs/villager_brain.rs::try_start create_path_to_any over the found POIs with the largest valid range +AcquirePoi.JitteredLinearRetry.markAttempt port crates/horizon-client/src/mobs/villager_brain.rs::mark +AcquirePoi.JitteredLinearRetry.isStillValid inline crates/horizon-client/src/mobs/villager_brain.rs::try_start retries.retain(now - previous < 400) +AnimalMakeLove.checkExtraStartConditions inline crates/horizon-client/src/mobs/animal_brain.rs::try_start in love and a partner from breed_partner +AnimalMakeLove.canStillUse inline crates/horizon-client/src/mobs/animal_brain.rs::tick_or_stop gap: minor: partner_panicking is stubbed to false, so a partner that starts panicking does not end the pairing +AnimalMakeLove.stop port crates/horizon-client/src/mobs/animal_brain.rs::do_stop +AnimalMakeLove.hasBreedTargetOfRightType inline crates/horizon-client/src/mobs/animal_brain.rs::can_mate_with same-species test on the remembered partner +AnimalPanic.checkExtraStartConditions inline crates/horizon-client/src/mobs/animal_brain.rs::try_start hurt by a panic cause, or already panicking +AnimalPanic.start port crates/horizon-client/src/mobs/animal_brain.rs::try_start panic memory set, walk target cleared, navigation stopped +AnimalPanic.lookForWater missing minor: a burning brain animal (sniffer, ghastling) does not run for nearby water; goal-driven animals do (goals.rs Panic) +BabyFollowAdult.create port crates/horizon-client/src/mobs/animal_brain.rs::trigger B::FollowAdult for the ghastling; goat, camel, armadillo, axolotl and hoglin babies use the FollowParent goal +BackUpIfTooClose.create missing player: a crossbow piglin stays put when you come within 5 blocks; vanilla backs away strafing while it keeps aiming +BecomePassiveIfMemoryPresent.create port crates/horizon-client/src/mobs/piglin.rs::hoglin_repellent a hoglin near a repellent is pacified for 200 ticks and drops its target +Behavior.hasRequiredMemories inline crates/horizon-client/src/mobs/villager_brain.rs::try_start each behaviour tests its memories at the top of its try_start arm +BehaviorUtils.entityIsVisible port crates/horizon-client/src/mobs/villager_brain.rs::visible_contains also copied in the animal, warden and monster brains +BehaviorUtils.setWalkAndLookTargetMemoriesToEachOther port crates/horizon-client/src/mobs/animal_brain.rs::lock_gaze_and_walk this mob's half; the partner's half comes with the breed-target event +BehaviorUtils.isOtherTargetMuchFurtherAwayThanCurrentAttackTarget missing minor: piglins, hoglins and zoglins switch to any attacker, even one more than 4 blocks further than their current target +BehaviorUtils.getNearestTarget skip:other picks among attack, avoid and nearest targets for PiglinAi and HoglinAi; those mobs choose with goals (NearestTarget, HurtByTarget) +BehaviorUtils.getTargetNearestMe skip:other same helper +BehaviorUtils.getLivingEntityFromUUIDMemory skip:other the ANGRY_AT memory is the mob's anger and target fields +BehaviorUtils.getRandomSwimmablePos inline crates/horizon-client/src/mobs/goals.rs::can_use Stroll with swim: default_random_pos retried up to 10 times until the spot is water +CelebrateVillagersSurvivedRaid.checkExtraStartConditions inline crates/horizon-client/src/mobs/villager_brain.rs::try_start raid victory and no blocks above +CelebrateVillagersSurvivedRaid.canStillUse inline crates/horizon-client/src/mobs/villager_brain.rs::tick_or_stop 600 ticks while a raid is nearby +CelebrateVillagersSurvivedRaid.stop inline crates/horizon-client/src/mobs/villager_brain.rs::do_stop back to the scheduled activity +CelebrateVillagersSurvivedRaid.tick inline crates/horizon-client/src/mobs/villager_brain.rs::tick_or_stop celebrate sound 1 in 100, a firework 1 in 200 with open sky +CelebrateVillagersSurvivedRaid.getFirework port crates/horizon-client/src/game/raids.rs::villager_firework burst rocket in a random dye colour, flight 0 to 2 +ChargeAttack.canStillUse port crates/horizon-client/src/mobs/nautilus.rs::still_ok tame, distance limits 12 and 11, line of sight and the cooldown +ChargeAttack.dealDamageToTarget inline crates/horizon-client/src/mobs/nautilus.rs::tick attack damage sent as an AttackPlayer or AttackMob event on contact +ChargeAttack.dealKnockBack inline crates/horizon-client/src/mobs/nautilus.rs::tick push along the nautilus's facing scaled by speed with speed and slowness effects +CopyMemoryWithExpiry.create inline crates/horizon-client/src/mobs/piglin.rs::register_piglin gap: minor: zombified avoidance is the AvoidEntity goal; baby piglins do not run from wither skeletons or the wither and never ride baby hoglins +CountDownCooldownTicks.canStillUse inline crates/horizon-client/src/mobs/animal_brain.rs::tick_or_stop the CountDown arms run while the cooldown is above 0; the goat's ram cooldown is ram_cooldown in goat.rs +CrossbowAttack.checkExtraStartConditions port crates/horizon-client/src/mobs/illager.rs::can_use gap: minor: the piglin uses RangedCrossbowAttackGoal's check (holding a crossbow, live target), with no line of sight or attack range test +CrossbowAttack.canStillUse port crates/horizon-client/src/mobs/illager.rs::can_continue +CrossbowAttack.crossbowAttack port crates/horizon-client/src/mobs/illager.rs::tick gap: minor: the goal's state machine; piglins also walk to within 8 blocks and move at half speed while charged, the brain behaviour stands and shoots +DismountOrSkipMounting.create missing minor: piglins never ride; baby piglins do not climb on baby hoglins +DismountOrSkipMounting.isVehicleValid missing minor: same, no piglin riding +DoNothing.tryStart port crates/horizon-client/src/mobs/warden_brain.rs::try_start B::DoNothing starts a random timer +DoNothing.tickOrStop port crates/horizon-client/src/mobs/warden_brain.rs::tick_or_stop stops when the timer ends +EntityTracker.currentPosition port crates/horizon-client/src/mobs/villager_brain.rs::position Tracker::position, eyes or feet +EntityTracker.currentBlockPosition port crates/horizon-client/src/mobs/villager_brain.rs::block Tracker::block +EntityTracker.isVisibleBy port crates/horizon-client/src/mobs/villager_brain.rs::tracker_visible +EraseMemoryIf.create skip:other generic builder; its uses (breeding hoglins and axolotls drop their target, piglins near zombified drop theirs) are only approximated by goal priorities +FollowTemptation.canStillUse inline crates/horizon-client/src/mobs/animal_brain.rs::tick_or_stop tempting player present, no breed target, not panicking +FollowTemptation.stop port crates/horizon-client/src/mobs/animal_brain.rs::do_stop cooldown 100, walk and look targets cleared +GateBehavior.getRequiredMemories skip:java memory registration, memories are fields +GateBehavior.hasRequiredMemories inline crates/horizon-client/src/mobs/villager_brain.rs::try_start Gate::try_start checks its absent memory and each child's +GateBehavior.debugString skip:debug +GiveGiftToHero.checkExtraStartConditions inline crates/horizon-client/src/mobs/villager_brain.rs::try_start hero visible, countdown to the next gift +GiveGiftToHero.start inline crates/horizon-client/src/mobs/villager_brain.rs::try_start interaction target set, look at the hero +GiveGiftToHero.canStillUse inline crates/horizon-client/src/mobs/villager_brain.rs::tick_or_stop hero visible and no gift given yet +GiveGiftToHero.stop inline crates/horizon-client/src/mobs/villager_brain.rs::do_stop next gift in 600 to 6600 ticks, memories cleared +GiveGiftToHero.throwGift port crates/horizon-client/src/game/raids.rs::throw_gift loot table by profession (villager::gift_table), tossed toward the player +GiveGiftToHero.isWithinThrowingDistance inline crates/horizon-client/src/mobs/villager_brain.rs::tick_or_stop block distance under 5 +GoAndGiveItemsToTarget.start inline crates/horizon-client/src/mobs/allay.rs::tick the Deliver goal flies to the deposit +GoAndGiveItemsToTarget.tick port crates/horizon-client/src/game/allays.rs::allay_deliver within 3 blocks of the deposit it throws one item a tick +GoAndGiveItemsToTarget.canThrowItemToTarget inline crates/horizon-client/src/mobs/allay.rs::can_use holding something and a deposit (note block or tame owner) exists +GoToClosestVillage.create port crates/horizon-client/src/mobs/villager_brain.rs::try_start B::GoToClosestVillage arm: five tries at a land position closer to a village +GoToPotentialJobSite.checkExtraStartConditions inline crates/horizon-client/src/mobs/villager_brain.rs::try_start idle, work or play activity +GoToPotentialJobSite.tick inline crates/horizon-client/src/mobs/villager_brain.rs::tick_or_stop walk to the potential job site +GoToPotentialJobSite.stop inline crates/horizon-client/src/mobs/villager_brain.rs::do_stop potential job site forgotten +GoToTargetLocation.create port crates/horizon-client/src/mobs/warden_brain.rs::try_start B::GoToDisturbance with getNearbyPos; the piglin's celebrate (mobs/piglin.rs Celebrate::walk) walks straight to the spot without the random offset +GoToWantedItem.create port crates/horizon-client/src/mobs/piglin.rs::can_use the game picks the gold (game/piglins.rs) and the goal walks to it at speed 1.0 diff --git a/tools/port_map/game-008.tsv b/tools/port_map/game-008.tsv new file mode 100644 index 0000000000..cbb4bffe83 --- /dev/null +++ b/tools/port_map/game-008.tsv @@ -0,0 +1,147 @@ +HarvestFarmland.checkExtraStartConditions inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start HarvestFarmland arm: mob_griefing, farmer, then the 3x3x3 scan for ripe crops and bare farmland (farmland_around) and one pick by the level random (pick_farmland) +HarvestFarmland.start inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start sets look and walk target (speed 0.5, close 1) once now is past next_ok +HarvestFarmland.stop inline crates/horizon-client/src/mobs/villager_brain.rs::B::do_stop clears look and walk target, worked = 0, next_ok = now + 40 +InsideBrownianWalk.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start InsideBrownianWalk arm: shuffled 3x3x3 cells, first one without sky view that is solid; a solid-block test stands in for loadedAndEntityCanStandOn +InteractWith.of inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start InteractWith arm for villager, breeding villager and cat targets (range 8, speed 0.5, stop distance 2); piglins and piglin brutes use goals instead +InteractWithDoor.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start gap: player: only the villager brain opens doors on its path (interact_with_doors); copper golems and piglins never do, so a golem's route through a wooden door ends against the closed door and a piglin cannot path through one +InteractWithDoor.areOtherMobsComingThroughDoor inline crates/horizon-client/src/mobs/villager_brain.rs::close_doors villagers within 2 blocks of the door whose path has the door as previous or next node +InteractWithDoor.isMobComingThroughDoor inline crates/horizon-client/src/mobs/villager_brain.rs::close_doors read from the other villager's Seen.path_nodes +InteractWithDoor.isDoorTooFarAway inline crates/horizon-client/src/mobs/villager_brain.rs::close_doors 3 block test; no dimension compare needed +InteractWithDoor.rememberDoorToClose port crates/horizon-client/src/mobs/villager_brain.rs::remember_door +JumpOnBed.checkExtraStartConditions inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start baby with a nearest bed remembered +JumpOnBed.start inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start target bed, 100 ticks to reach it, 3 to 6 jumps from the level random +JumpOnBed.stop inline crates/horizon-client/src/mobs/villager_brain.rs::B::do_stop resets bed, time left, jumps and cooldown +JumpOnBed.canStillUse inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop baby, bed still a bed, not tired of walking or jumping +JumpOnBed.tick inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop counts time to reach the bed, then jumps every 5 ticks while on the bed +JumpOnBed.startWalkingTowardsBed inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start walk target on the bed, speed 0.5, close 0 +JumpOnBed.nearBed inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start the nearest-bed memory must be present +JumpOnBed.onOrOverBed inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop the on_or_over test on the block and the one below +JumpOnBed.tiredOfWalking inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop folded into the still-using test +JumpOnBed.tiredOfJumping inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop folded into the still-using test +LocateHidingPlace.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start LocateHidingPlace arm: a home POI within close+1 and near, else a shuffled one within the radius, else the home memory; sets the hiding place and walks there when far +LongJumpMidJump.stop missing player: frogs and goats never long jump (no long-jump activity; only the frog's jump animation exists), so there is no leap, landing thud or jump cooldown +LongJumpToPreferredBlock.start missing player: frogs never aim their jumps at lily pads or big dripleaf, since they never long jump +LongJumpToPreferredBlock.getJumpCandidate missing minor: the lily pad and big dripleaf preference of the frog's missing long jump +LongJumpToRandomPos.defaultAcceptableLandingSpot missing minor: landing test of the missing long jump (the breeze has its own jump in monster_brain.rs) +LongJumpToRandomPos.checkExtraStartConditions missing player: frogs and goats never start a long jump (goats leaping between ledges, frogs hopping across water and lily pads) +LongJumpToRandomPos.canStillUse missing minor: part of the missing frog and goat long jump +LongJumpToRandomPos.start missing player: no long jump for frogs and goats, so no 40 tick crouch and leap +LongJumpToRandomPos.tick missing player: no leap velocity or jump sound for frogs and goats +LongJumpToRandomPos.pickCandidate missing minor: landing spot search of the missing long jump +LongJumpToRandomPos.getJumpCandidate missing minor: weighted pick of the missing long jump +LongJumpToRandomPos.isAcceptableLandingPosition missing minor: landing test of the missing long jump +LongJumpToRandomPos.calculateOptimalJumpVector missing minor: jump angle search of the missing frog and goat long jump (the breeze has its own in breeze.rs) +LookAndFollowTradingPlayerSink.followPlayer port crates/horizon-client/src/mobs/villager_brain.rs::follow_trading_player +LookAtTargetSink.canStillUse inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop the look target must still be visible; the sniffer, warden and breeze brains have the same arm +LookAtTargetSink.tick inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop look_at the target position +MeleeAttack.create inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start creaking arm (target in reach and visible, attack cooldown); the warden and axolotl brains have their own; gap: minor: piglins, piglin brutes, hoglins and zoglins attack through the MeleeAttack goal in goals.rs, whose chase and cooldown rhythm differ from the brain behaviour +Mount.create missing player: a baby piglin never walks to and climbs onto a baby hoglin (PiglinAi's ride target); no brain or goal sends a mob to mount another, jockeys only come from spawning +MoveToSkySeeingSpot.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start MoveToSkySeeingSpot arm: only when the villager cannot see the sky and has no walk target +MoveToSkySeeingSpot.getOutdoorPosition inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start ten random spots within 10, 3 and 10 blocks, the first with no blocks above (no_blocks_above) +MoveToTargetSink.checkExtraStartConditions inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start MoveToTargetSink arm: retry cooldown, reached, compute_path; the sniffer, ghastling, golem, breeze, creaking and warden brains carry the same arm +MoveToTargetSink.canStillUse inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop gap: minor: no spectator check (see isWalkTargetSpectator) +MoveToTargetSink.start inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start the arm sets the PATH memory and starts the navigation as it starts +MoveToTargetSink.isWalkTargetSpectator missing minor: a walk target on a player in spectator mode is not dropped +PlayTagWithOtherKids.chaseKid inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start PlayTag arm sets interaction, look and walk target (speed 0.6, close 1) +PlayTagWithOtherKids.findSomeoneBeingChased missing minor: a kid always chases the first friend it sees instead of joining a chase of a kid that has 1 to 5 chasers (Seen.interaction_target is recorded but unused) +PlayTagWithOtherKids.checkHowManyChasersEachFriendHas missing minor: no chaser counts; see findSomeoneBeingChased +PlayTagWithOtherKids.isFriendChasingMe missing player: a kid that is being chased never runs off to a spot in the village, so the tag game is only chasing +PoiCompetitorScan.selectWinner inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start PoiCompetitorScan arm: higher xp keeps the site, a tie goes to the later villager; gap: minor: only this villager's own site is erased, a losing neighbour loses its site in its own scan +PoiCompetitorScan.competesForSameJobsite inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start same job site and profession; the profession is compared with this villager's, not with the site's POI type +PrepareRamNearestTarget.start port crates/horizon-client/src/mobs/goat.rs::Ram::start picks the nearest allowed target and chooses a ramming spot +PrepareRamNearestTarget.canStillUse port crates/horizon-client/src/mobs/goat.rs::Ram::can_continue prepare phase: a candidate whose target lives, within the 160 tick time out +PrepareRamNearestTarget.getEdgeOfBlock inline crates/horizon-client/src/mobs/goat.rs::prepare_tick half block offset by the sign toward the target +RandomStroll.fly inline crates/horizon-client/src/mobs/animal_brain.rs::B::trigger Stroll arm with fly set (ghastling); only without a walk target +RandomStroll.strollFlyOrSwim inline crates/horizon-client/src/mobs/animal_brain.rs::B::trigger Stroll arm: only without a walk target, walk target set or erased; the monster and warden brains have their own stroll arms +RandomStroll.getTargetSwimPos inline crates/horizon-client/src/mobs/animal_brain.rs::axolotl_action SwimStroll arm: the (1,1) to (10,7) tiers with the fallback +RandomStroll.getTargetFlyPos inline crates/horizon-client/src/mobs/animal_brain.rs::B::trigger air_and_water_random_pos along the view vector, -2 vertical offset +SetClosestHomeAsWalkTarget.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start ClosestHomeAsWalkTarget arm: nearest home within 48, five per batch, 40 tick cache +SetEntityLookTarget.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start LookAtEntity arm (species, player or mob category within 8); the sniffer and warden brains have their own; piglins use the LookAtPlayer goal +SetEntityLookTargetSometimes.create inline crates/horizon-client/src/mobs/animal_brain.rs::B::trigger LookAtPlayerSometimes arm (copper golem, axolotl); the creaking has its own in monster_brain.rs; goal mobs use LookAtPlayer goals +SetEntityLookTargetSometimes.Ticker.tickDownAndCheck inline crates/horizon-client/src/mobs/animal_brain.rs::B::trigger the tick counter inside the arm +SetLookAndInteract.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start SetLookAndInteract arm: nearest visible player within 4 blocks becomes interaction and look target +SetWalkTargetAwayFrom.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start WalkAwayFrom arm (nearest hostile and last attacker within 6 blocks, away 16 and 7, ten tries); piglins and hoglins use AvoidEntity and repellent goals +SetWalkTargetFromAttackTargetIfTargetOutOfReach.create inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start SetWalkTargetFromAttackTarget arm (the warden and axolotl brains have the same); piglins and hoglins chase with the MeleeAttack goal +SetWalkTargetFromBlockMemory.create port crates/horizon-client/src/mobs/villager_brain.rs::walk_to_block_memory +SetWalkTargetFromLookTarget.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start WalkToLookTarget arm; the sniffer, ghastling and breeze brains have the same +ShowTradesToPlayer.canStillUse inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop ShowTrades arm: still near the player, look time left and an interaction target +ShowTradesToPlayer.start inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start looks at the player, look time 40 +ShowTradesToPlayer.stop inline crates/horizon-client/src/mobs/villager_brain.rs::B::do_stop clears the interaction target; the held item is never touched because no offer is ever shown +ShowTradesToPlayer.findItemsToDisplay missing player: a villager facing a player who holds an item matching one of its trade costs never holds up the matching offer, and the look time never extends to 900 ticks +ShowTradesToPlayer.updateDisplayItems missing minor: the offer list matched against the player's item, part of findItemsToDisplay +ShowTradesToPlayer.playerItemStackMatchesCostOfOffer missing minor: cost match of findItemsToDisplay +ShowTradesToPlayer.lookAtTarget inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop the arm sets the look target to the player on start and each tick +ShowTradesToPlayer.displayCyclingItems missing player: with several matching offers the villager would swap the item in its hand every 40 ticks; it never holds anything +ShufflingList.add inline crates/horizon-client/src/mobs/warden_brain.rs::activities the idle RunOne list is built with its weights (stroll 2, do nothing 1); the other brains list weights where their packages are built +ShufflingList.WeightedEntry.setRandom inline crates/horizon-client/src/mobs/warden_brain.rs::B::try_start the RunOne arm draws -pow(random, 1/weight) per entry and sorts; the villager, animal and monster brains do the same in their gates +SleepInBed.checkExtraStartConditions inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start SleepInBed arm: not riding, 100 ticks since waking, bed within 2 blocks and free +SleepInBed.canStillUse inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop rest activity, above the bed by 0.4 and within 1.14 of it +SleepInBed.stop inline crates/horizon-client/src/mobs/villager_brain.rs::B::do_stop wakes the villager and sets next_ok = now + 40 +SpearApproach.ableToAttack missing minor: part of the missing spear approach of PiglinAi +SpearApproach.checkExtraStartConditions missing player: a piglin holding a golden spear (about 1 in 20 adult piglins) just walks up and melees; vanilla first closes in on the target to 10 blocks while holding the spear +SpearApproach.start missing minor: sets the approach status of the missing spear routine +SpearApproach.canStillUse missing minor: part of the missing spear approach +SpearApproach.farEnough missing minor: part of the missing spear approach +SpearApproach.tick missing minor: part of the missing spear approach +SpearAttack.ableToAttack missing minor: part of the missing spear charge of PiglinAi +SpearAttack.getKineticWeaponUseDuration missing minor: part of the missing spear charge +SpearAttack.checkExtraStartConditions missing minor: part of the missing spear charge +SpearAttack.start missing player: a piglin holding a golden spear (about 1 in 20 adult piglins) just walks up and melees; vanilla raises the spear (startUsingItem) and charges at the target +SpearAttack.canStillUse missing minor: part of the missing spear charge +SpearAttack.tick missing player: a piglin holding a golden spear (about 1 in 20 adult piglins) just walks up and melees; vanilla charges along a line, then repositions 6 to 7 blocks away and charges again; no mob uses a spear's kinetic attack here +SpearAttack.stop missing minor: part of the missing spear charge +SpearRetreat.ableToAttack missing minor: part of the missing spear retreat +SpearRetreat.checkExtraStartConditions missing player: a piglin holding a golden spear (about 1 in 20 adult piglins) just walks up and melees; vanilla backs off 9 to 11 blocks after a charge +SpearRetreat.start missing minor: part of the missing spear retreat +SpearRetreat.canStillUse missing minor: part of the missing spear retreat +SpearRetreat.tick missing minor: part of the missing spear retreat +SpearRetreat.stop missing minor: part of the missing spear retreat +StartAttacking.create inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start StartAttacking arm (nearest attackable, last attacker, active creaking's player); piglins and hoglins choose targets with NearestTarget goals +StartCelebratingIfTargetDead.create port crates/horizon-client/src/mobs/piglin.rs::celebrate gap: minor: the forgive_dead_players game rule is not read (a dead player target is always dropped, which is the default) +StayCloseToTarget.create port crates/horizon-client/src/mobs/allay.rs::StayClose::can_use gap: minor: starts when the deposit is more than 4 blocks away instead of 16, so an allay hovers closer to its player than vanilla +StopAttackingIfTargetInvalid.create inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start StopAttacking arm (creaking, breeze); the warden has its own arm without the tired check; piglins and hoglins drop targets in their target goals +StopAttackingIfTargetInvalid.isTiredOfTryingToReachTarget inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start cant_reach_walk_target_since older than 200 ticks +StrollAroundPoi.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start StrollAroundPoi arm: stays within max_dist of the POI, strolls (8, 6) every 180 ticks +StrollToPoi.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start StrollToPoi arm: job site within 10 blocks, every 80 ticks +StrollToPoiList.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start StrollToPoiList arm: random secondary job site every 100 ticks while near the job site +Swim.checkExtraStartConditions port crates/horizon-client/src/mobs/villager_brain.rs::swim_condition water deeper than the jump threshold, or lava; the sniffer, warden, breeze and creaking brains have copies +Swim.tick inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop jumps with chance 0.8 each tick +TradeWithVillager.start inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start lock_gaze: look at and walk to the other villager +TradeWithVillager.figureOutWhatIAmWillingToTrade inline crates/horizon-client/src/mobs/villager.rs::meet wares the neighbour's profession requests and mine does not; worked out on every meeting tick, not once at start +TransportItemsBetweenContainers.start inline crates/horizon-client/src/mobs/transport_items.rs::Transport::try_start sets below_surface (setCanPathToTargetsBelowSurface); the not-leashed start test is in the same fn +TransportItemsBetweenContainers.canStillUse inline crates/horizon-client/src/mobs/transport_items.rs::Transport::tick_or_stop no cooldown, not panicking, not leashed +TransportItemsBetweenContainers.onTravelToTarget inline crates/horizon-client/src/mobs/transport_items.rs::Transport::tick the Travelling branch: queue within 3 blocks when another golem has the chest, interact within range, else walk +TransportItemsBetweenContainers.walkTowardsTarget port crates/horizon-client/src/mobs/transport_items.rs::Transport::walk_towards +TransportItemsBetweenContainers.startOnReachedTargetInteraction inline crates/horizon-client/src/mobs/transport_items.rs::Transport::tick reached_interaction picks the interaction state, then the state becomes Interacting +TransportItemsBetweenContainers.onTargetInteraction inline crates/horizon-client/src/mobs/transport_items.rs::Transport::on_reached look at the chest, stop in place, open the chest on tick 1, sound on tick 9, close on tick 60 +TransportItemsBetweenContainers.doReachedTargetInteraction port crates/horizon-client/src/mobs/transport_items.rs::reached_interaction the dispatch on empty hand and chest contents; the 60 tick call is in on_reached +TransportItemsBetweenContainers.getTransportTarget port crates/horizon-client/src/mobs/transport_items.rs::Transport::find_target +TransportItemsBetweenContainers.isTargetValidToPick inline crates/horizon-client/src/mobs/transport_items.rs::Transport::find_target search box, container, wanted block and not visited +TransportItemsBetweenContainers.isContainerLocked missing minor: a chest's lock is not stored or checked (only set by commands) +TransportItemsBetweenContainers.getPositionToReachTargetFrom inline crates/horizon-client/src/mobs/transport_items.rs::Transport::valid_travelling_path path end or the golem's own position, raised by half its height +TransportItemsBetweenContainers.getConnectedTargets port crates/horizon-client/src/mobs/transport_items.rs::connected the other half of a double chest +TransportItemsBetweenContainers.getTargetSearchArea inline crates/horizon-client/src/mobs/transport_items.rs::Transport::find_target box of 32 across and 8 up and down (1 and 1 on a mount) +TransportItemsBetweenContainers.isPositionAlreadyVisited inline crates/horizon-client/src/mobs/transport_items.rs::Transport::find_target either half in the visited or unreachable memory +TransportItemsBetweenContainers.setVisitedBlockPos port crates/horizon-client/src/mobs/transport_items.rs::Transport::set_visited +TransportItemsBetweenContainers.markVisitedBlockPosAsUnreachable port crates/horizon-client/src/mobs/transport_items.rs::Transport::mark_unreachable +TransportItemsBetweenContainers.isWantedBlock port crates/horizon-client/src/mobs/transport_items.rs::wanted copper chests with an empty hand, chests and trapped chests when carrying +TransportItemsBetweenContainers.isWithinTargetDistance port crates/horizon-client/src/mobs/transport_items.rs::within +TransportItemsBetweenContainers.targetIsReachableFromPosition inline crates/horizon-client/src/mobs/transport_items.rs::Transport::valid_travelling_path reach and a clear line to a side of the chest +TransportItemsBetweenContainers.canSeeAnyTargetSide port crates/horizon-client/src/mobs/transport_items.rs::sees_a_side +TransportItemsBetweenContainers.matchesLeavingItemsRequirement port crates/horizon-client/src/mobs/transport_items.rs::leaving_ok +TransportItemsBetweenContainers.hasItemMatchingHandItem inline crates/horizon-client/src/mobs/transport_items.rs::leaving_ok same item id in any slot +TransportItemsBetweenContainers.pickUpItems port crates/horizon-client/src/mobs/transport_items.rs::Transport::pick_up +TransportItemsBetweenContainers.putDownItem port crates/horizon-client/src/mobs/transport_items.rs::Transport::put_down +TransportItemsBetweenContainers.pickupItemFromContainer inline crates/horizon-client/src/mobs/transport_items.rs::Transport::pick_up up to 16 of the first non-empty slot +TransportItemsBetweenContainers.addItemsToContainer inline crates/horizon-client/src/mobs/transport_items.rs::Transport::put_down fills an empty slot or tops up a matching stack, slot by slot +TransportItemsBetweenContainers.stopTargetingCurrentTarget port crates/horizon-client/src/mobs/transport_items.rs::Transport::stop_targeting +TransportItemsBetweenContainers.clearMemoriesAfterMatchingTargetFound port crates/horizon-client/src/mobs/transport_items.rs::Transport::clear_memories +TransportItemsBetweenContainers.enterCooldownAfterNoMatchingTargetFound port crates/horizon-client/src/mobs/transport_items.rs::Transport::enter_cooldown +TransportItemsBetweenContainers.TransportItemTarget.tryCreatePossibleTarget inline crates/horizon-client/src/mobs/transport_items.rs::Transport::find_target the target is built from container() when a chest has one +TransportItemsBetweenContainers.TransportItemTarget.getBlockEntityContainer port crates/horizon-client/src/mobs/transport_items.rs::container gap: minor: a cat sitting on the chest does not block it, only a redstone conductor above does +TriggerGate.triggerGate inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start TriggerGate arm: shuffle by weight, then each trigger until one fires; the piglin's idle gate is part of its goals +TryFindLand.create inline crates/horizon-client/src/mobs/goals.rs::Stroll::can_use gap: minor: a frog in water strolls to a random land spot within 15 blocks (water-avoiding stroll, every tick) instead of the nearest standable block within 6 to 8 with a 60 tick cooldown, at speed 1.0 not 1.5 +TryFindLand.canStandOn missing minor: no scan for the nearest solid land next to water; see TryFindLand.create +TryFindLandNearLiquid.create missing minor: only runs in the frog's lay-spawn activity, which needs a pregnant frog (see TryLaySpawnOnFluidNearLand) +TryFindLiquid.create port crates/horizon-client/src/mobs/animal_brain.rs::axolotl_action FindLiquid arm: nearest water within 6, speed 0.15, re-run every 40 ticks +TryLaySpawnOnFluidNearLand.create missing player: frogs cannot be bred (no slimeball feeding, no pregnancy), so no frogspawn is ever laid by a frog and no tadpoles follow diff --git a/tools/port_map/game-009.tsv b/tools/port_map/game-009.tsv new file mode 100644 index 0000000000..5ca1ed53ad --- /dev/null +++ b/tools/port_map/game-009.tsv @@ -0,0 +1,125 @@ +UseBonemeal.checkExtraStartConditions missing minor: the B::UseBonemeal arm only checks for bone meal in the inventory and then always returns false, so a farmer carrying bone meal never grows a crop with it +UseBonemeal.canStillUse missing minor: no bone meal session is ever started +UseBonemeal.pickNextTarget missing minor: no random pick of a growing crop in the 3x3x3 around the villager +UseBonemeal.validPos missing minor: no crop-not-fully-grown test +UseBonemeal.start missing minor: villager never holds bone meal or walks to a crop +UseBonemeal.setCurrentCropAsTarget missing minor: no look and walk target on a crop +UseBonemeal.tick missing minor: never uses a bone meal item on a crop (no growCrop, no bonemeal effect) +ValidateNearbyPoi.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start ValidatePoi arm: within 16 blocks the memory is erased when the POI is gone or the bed is occupied by someone else +ValidateNearbyPoi.bedIsOccupiedByVillager inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start no ticket to release: POI tickets are counted from the villagers' memories (tickets), so erasing the memory frees the bed +VillageBoundRandomStroll.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start VillageBoundStroll arm: land pos inside a village, else a pos towards the section closest to a village +VillagerGoalPackages.getCorePackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages gap: minor: GoToWantedItem (priority 5) never starts (arm returns false), so villagers do not walk to dropped items within 4 blocks; they only pick up what lands at their feet +VillagerGoalPackages.validateBedPoi inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start AcquirePoi home arm keeps only beds that are not occupied, after the 5-candidate cap like vanilla +VillagerGoalPackages.getWorkPackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages gap: minor: the UseBonemeal entry never starts (see UseBonemeal) +VillagerGoalPackages.getPlayPackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages baby villagers: look, tag, run-one of interact, stroll, jump on bed, do nothing +VillagerGoalPackages.getRestPackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages walk home, validate home, sleep, homeless run-one, minimal look +VillagerGoalPackages.getMeetPackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages bell gate, trades, gift, validate meeting point, trade gate, full look +VillagerGoalPackages.getIdlePackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages run-one of interactions and strolls, gift, trades, trade gate, make love gate, full look +VillagerGoalPackages.getPanicPackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages calm down, walk away from hostile and from the attacker at 0.75, stroll 2x2 +VillagerGoalPackages.getPreRaidPackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages ring bell, meeting point or stroll at 0.75, reset raid status +VillagerGoalPackages.getRaidPackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages sky spot or stroll once the raid is won, celebrate, hide while the raid is active +VillagerGoalPackages.getHidePackage inline crates/horizon-client/src/mobs/villager_brain.rs::packages set hidden state, locate hiding place at 0.625, minimal look +VillagerGoalPackages.getFullLookBehavior port crates/horizon-client/src/mobs/villager_brain.rs::look_full +VillagerGoalPackages.getMinimalLookBehavior port crates/horizon-client/src/mobs/villager_brain.rs::look_minimal +VillagerGoalPackages.raidExistsAndActive inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start RaidSequence arm (victory false): the villager's raid status is RAID or PRE_RAID instead of looking up the raid at its position +VillagerGoalPackages.raidExistsAndNotVictory inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start RaidSequence arm (victory true): the villager's raid status is victory (the vanilla name is misleading, it tests isVictory) +VillagerMakeLove.canStillUse inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop MakeLove arm stops once now passes the birth time or breeding is no longer possible +VillagerMakeLove.start inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start MakeLove arm: lock gaze, birth time 275 + random 50 ticks; entity event 18 has no villager effect +VillagerMakeLove.canReach inline crates/horizon-client/src/mobs/villager_brain.rs::take_bed_for_child bed counts only if create_path_to gets there +VillagerMakeLove.breed port crates/horizon-client/src/game/villagers.rs::Game::villager_birth baby villager at the parent, ages 6000 and -24000, hearts for the child +VillagerMakeLove.giveBedToChild inline crates/horizon-client/src/game/villagers.rs::Game::villager_birth child.extra.bed is the taken bed +WorkAtPoi.checkExtraStartConditions inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start WorkAtPoi arm: 300 tick gap, 1 in 2 roll, within 1.73 of the job site +WorkAtPoi.canStillUse inline crates/horizon-client/src/mobs/villager_brain.rs::B::tick_or_stop WorkAtPoi arm: job site still within 1.73 +YieldJobSite.create inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start gap: minor: the villager gives up its potential site but the nearby villager is not handed the walk and look target or the potential site; it finds the site through its own job search +YieldJobSite.nearbyWantsJobsite inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start tests the nearby villager's profession against the site type and its job site memory +YieldJobSite.canReachPos inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start gap: minor: no path test to the site for the nearby villager +BehaviorBuilder.create skip:java declarative behaviour builder; Horizon codes each behaviour as an arm of B +BehaviorBuilder.sequence inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start RaidSequence arm is the only sequence the villager packages use +BehaviorBuilder.triggerIf skip:java condition combinator; each use is ported in place (raid conditions in RaidSequence) +BehaviorBuilder.Instance.point skip:java applicative plumbing of the behaviour DSL +BehaviorBuilder.Instance.lift1 skip:java applicative plumbing of the behaviour DSL +BehaviorBuilder.Instance.map skip:java applicative plumbing of the behaviour DSL +BehaviorBuilder.Instance.ap2 skip:java applicative plumbing of the behaviour DSL +BehaviorBuilder.Instance.ap3 skip:java applicative plumbing of the behaviour DSL +BehaviorBuilder.Instance.ap4 skip:java applicative plumbing of the behaviour DSL +MemoryCondition.Absent.createAccessor skip:java memory accessor of the behaviour DSL; memories are Slot fields +MemoryCondition.Present.createAccessor skip:java memory accessor of the behaviour DSL; memories are Slot fields +Digging.start inline crates/horizon-client/src/mobs/warden_brain.rs::B::try_start Digging arm: dig pose and sound on ground, else agitated sound and the warden is removed +Roar.start inline crates/horizon-client/src/mobs/warden_brain.rs::B::try_start Roar arm: sound delay 25, walk target cleared, look at target, roaring pose, anger +20 +Roar.stop inline crates/horizon-client/src/mobs/warden_brain.rs::B::do_stop Roar arm: standing pose, roar target becomes the attack target +SetRoarTarget.create inline crates/horizon-client/src/mobs/warden_brain.rs::B::try_start SetRoarTarget arm: entity_angry_at and can_target, clears cant-reach memory +Sniffing.stop inline crates/horizon-client/src/mobs/warden_brain.rs::B::do_stop Sniffing arm: standing pose, anger +35 for a target within 6x20, disturbance location +SonicBoom.checkExtraStartConditions inline crates/horizon-client/src/mobs/warden_brain.rs::B::try_start SonicBoom arm: target within 15 horizontally and 20 vertically +BodyRotationControl.rotateBodyIfNecessary inline crates/horizon-client/src/mobs/control.rs::Mob::body_rotation_tick rotate_if_necessary on body_yaw in the standing-still branches +BodyRotationControl.rotateHeadIfNecessary inline crates/horizon-client/src/mobs/control.rs::Mob::body_rotation_tick rotate_if_necessary on head_yaw in the moving branch +BodyRotationControl.isMoving inline crates/horizon-client/src/mobs/control.rs::Mob::body_rotation_tick moved distance squared against ARRIVED +LookControl.clampHeadRotationToBody inline crates/horizon-client/src/mobs/control.rs::Mob::look_control_tick head yaw clamped to the body while the navigation is not done +LookControl.getXRotD port crates/horizon-client/src/mobs/control.rs::Mob::look_angles +LookControl.getYRotD port crates/horizon-client/src/mobs/control.rs::Mob::look_angles +MoveControl.isWalkable port crates/horizon-client/src/mobs/control.rs::Mob::walkable gap: minor: tests the static path type of the cell, not the mob's own node evaluator +SmoothSwimmingLookControl.tick port crates/horizon-client/src/mobs/control.rs::Mob::look_control_tick SmoothSwimming arm of the look kinds +SmoothSwimmingMoveControl.getTurningSpeedFactor inline crates/horizon-client/src/mobs/control.rs::Mob::move_control_tick SmoothSwimming arm: 1 - clamp((left - 10) / 50) +BegGoal.canContinueToUse port crates/horizon-client/src/mobs/tame.rs::Beg::can_continue +BegGoal.playerHoldingInteresting port crates/horizon-client/src/mobs/tame.rs::Beg::interesting gap: minor: untamed wolves beg only for bones and tamed wolves only for food; in 26.3 a bone or food interests any wolf +BreathAirGoal.givesAir inline crates/horizon-client/src/mobs/goals.rs::BreathAir::find_air no fluid or a bubble column, and land-pathfindable +BreedGoal.getFreePartner inline crates/horizon-client/src/mobs/goals.rs::Breed::can_use gap: minor: the partner's isPanicking is not tested (Seen has no panic flag) +CatLieOnBlockGoal.isValidTarget port crates/horizon-client/src/mobs/tame.rs::can_lie_on +CatSitOnBlockGoal.isValidTarget port crates/horizon-client/src/mobs/tame.rs::can_sit_on +ClimbOnTopOfPowderSnowGoal.canUse missing player: rabbits, foxes, silverfish and endermites that end up in powder snow never jump to climb out (goal missing from their registration) +DolphinJumpGoal.waterIsClear inline crates/horizon-client/src/mobs/dolphin.rs::Jump::can_use water at each step and not a blocks_dolphin_jump block +DolphinJumpGoal.surfaceIsClear inline crates/horizon-client/src/mobs/dolphin.rs::Jump::can_use two air blocks above each step +DolphinJumpGoal.canContinueToUse port crates/horizon-client/src/mobs/dolphin.rs::Jump::can_continue +DoorInteractGoal.start inline crates/horizon-client/src/mobs/doors.rs::RaiderOpenDoor::start passed and the door direction only feed canContinueToUse, which both subclasses override, so they are not kept +DoorInteractGoal.tick inline crates/horizon-client/src/mobs/doors.rs::BreakDoor::tick the passed-the-door dot test is not kept: BreakDoor and RaiderOpenDoor both override can_continue +FollowFlockLeaderGoal missing player: cod, salmon and tropical fish spawn in groups but do not follow a leader, so schools do not swim together (leader id is stored, no goal uses it) +FollowMobGoal missing player: parrots do not fly after and hover near other mobs (priority 3 goal missing from register_parrot) +FollowPlayerRiddenEntityGoal missing player: dolphins do not swim after a boat or nautilus a player is riding +GoalSelector.removeAllGoals port crates/horizon-client/src/mobs/goals.rs::GoalSelector::clear gap: minor: clear() takes no predicate and does not stop running goals +GoalSelector.removeGoal missing minor: no per-goal removal; callers (cat and ocelot trust, zombie break door, skeleton weapon swap, skeleton horse trap) fold the condition into can_use +GoalSelector.goalContainsAnyFlags inline crates/horizon-client/src/mobs/goals.rs::GoalSelector::tick flags & disabled +GoalSelector.goalCanBeReplacedForAllFlags inline crates/horizon-client/src/mobs/goals.rs::GoalSelector::tick the replaceable check over the locked slots +GoalSelector.tickRunningGoals port crates/horizon-client/src/mobs/goals.rs::GoalSelector::tick_running +GolemRandomStrollInVillageGoal missing player: iron golems stroll with a plain water-avoiding stroll, so they do not stay in the village or go towards villagers who want a golem +MeleeAttackGoal.checkAndPerformAttack inline crates/horizon-client/src/mobs/goals.rs::MeleeAttack::tick reset cooldown, swing, do_hurt_target +MeleeAttackGoal.canPerformAttack inline crates/horizon-client/src/mobs/goals.rs::MeleeAttack::tick cooldown over, within melee range, line of sight +MoveBackToVillageGoal missing player: iron golems outside a village never walk back towards it (goal missing from register_iron) +MoveThroughVillageGoal missing player: zombies never walk purposefully through a village towards its POIs and doors at night; they only wander (priority 6 goal missing; the raider variant is separate) +MoveToBlockGoal.moveMobToBlock inline crates/horizon-client/src/mobs/goals.rs::MoveToBlock::start nav_move_to the block centre one up +MoveTowardsTargetGoal.start inline crates/horizon-client/src/mobs/golem.rs::MoveTowardsTarget::can_use gap: minor: paths straight to the target entity in can_use, not to a random position towards it chosen in canUse +OpenDoorGoal.canContinueToUse port crates/horizon-client/src/mobs/doors.rs::RaiderOpenDoor::can_continue the raider goal does not close the door after, so it never continues +PanicGoal.findRandomPosition inline crates/horizon-client/src/mobs/goals.rs::Panic::can_use default_random_pos(5, 4) +PanicGoal.lookForWater inline crates/horizon-client/src/mobs/goals.rs::Panic::can_use burning (or turtle) mob not inside a solid cell looks for water by Manhattan distance +PathfindToRaidGoal.canContinueToUse port crates/horizon-client/src/mobs/raider.rs::PathfindToRaid::can_continue +RandomStandGoal.playStandSound port crates/horizon-client/src/mobs/equine.rs::RandomStand::start gap: player: plays the angry neigh and only for horses; vanilla plays the ambient sound (baby variants too), so skeleton and zombie horses rear silently, and donkeys and mules never get the goal (can_rear) although only llamas are barred from rearing +RangedCrossbowAttackGoal.canContinueToUse port crates/horizon-client/src/mobs/illager.rs::CrossbowAttack::can_continue gap: minor: holding test looks at the main hand only +RangedCrossbowAttackGoal.isValidTarget inline crates/horizon-client/src/mobs/illager.rs::CrossbowAttack::can_use alive target; also rejects creative players +RemoveBlockGoal.getPosWithBlock inline crates/horizon-client/src/mobs/undead.rs::AttackTurtleEgg::tick own cell, below, west, east, north, south, two below +RunAroundLikeCrazyGoal.start inline crates/horizon-client/src/mobs/equine.rs::RunAroundLikeCrazy::can_use navigation to the random spot starts in can_use +SpearUseGoal missing player: zombies and zombified piglins holding a spear do not run the charge, jab and retreat routine; they only melee +SpearUseGoal.SpearUseState missing player: see SpearUseGoal +StrollThroughVillageGoal.tick missing minor: foxes do not wander through villages at night (goal missing; no village-bound step) +StrollThroughVillageGoal.moveRandomly missing minor: foxes do not wander through villages at night +TemptGoal.shouldFollow inline crates/horizon-client/src/mobs/goals.rs::Tempt::can_use main hand or off hand holds a tempt item +TemptGoal.ForNonPathfinders.navigateTowards port crates/horizon-client/src/mobs/happy_ghast.rs::GhastTempt::tick random fraction of the way to the player's eye as the wanted position; the sulfur cube has its own SulfurTempt +WaterAvoidingRandomFlyingGoal.getPosition inline crates/horizon-client/src/mobs/goals.rs::Stroll::can_use gap: minor: a flying stroll (the wither) draws a plain default_random_pos(10, 7), not HoverRandomPos along its view direction and AirAndWaterRandomPos +WaterAvoidingRandomStrollGoal.getPosition inline crates/horizon-client/src/mobs/goals.rs::Stroll::can_use water_avoiding arm: land pos, or default pos with the probability +WrappedGoal.canBeReplacedBy inline crates/horizon-client/src/mobs/goals.rs::GoalSelector::tick interruptable and lower priority number +WrappedGoal.start inline crates/horizon-client/src/mobs/goals.rs::GoalSelector::tick running flag then goal.start +WrappedGoal.equals skip:java identity of wrapped goals in a set +HurtByTargetGoal.alertOthers inline crates/horizon-client/src/mobs/goals.rs::HurtByTarget::start gap: player: the owner and team checks are missing, so when a wolf is hurt every wolf nearby is called, including other owners' and the player's own tamed wolves +NonTameRandomTargetGoal.canUse inline crates/horizon-client/src/mobs/tame.rs::register wolf and cat target goals use NearestTarget with when(!tame) +NonTameRandomTargetGoal.canContinueToUse inline crates/horizon-client/src/mobs/goals.rs::NearestTarget::can_continue gap: minor: the prey filter is not re-tested while hunting, only the base TargetGoal rules +ResetUniversalAngerTargetGoal.wasHurtByPlayer missing minor: the universal_anger rule is never read by mobs (default off) +ResetUniversalAngerTargetGoal.getNearbyMobsOfSameType missing minor: the universal_anger rule is never read by mobs (default off) +TargetGoal.canAttack inline crates/horizon-client/src/mobs/goals.rs::NearestTarget::can_use gap: minor: no isWithinHome test and no must-reach path test +TargetGoal.canReach missing minor: must_reach is stored by reach() but never read, so polar bears, llamas and endermen pick targets they cannot path to +GossipContainer.getGossipEntries port crates/horizon-client/src/gossip.rs::Gossips::entries +GossipContainer.add port crates/horizon-client/src/gossip.rs::Gossips::add +GossipContainer.remove port crates/horizon-client/src/gossip.rs::Gossips::remove gap: minor: only the per-target, per-type removal; the by-amount and all-targets overloads have no caller +GossipContainer.putAll skip:java map copy; Gossips is Clone +GossipContainer.mergeValuesForAddition inline crates/horizon-client/src/gossip.rs::Gossips::add sum above the max keeps the larger of max and old +GossipContainer.EntityGossips.weightedValue port crates/horizon-client/src/gossip.rs::Gossips::reputation all types summed; no type filter, as every caller passes all types +GossipContainer.EntityGossips.makeSureValueIsntTooLowOrTooHigh inline crates/horizon-client/src/gossip.rs::Gossips::add clamps to the max and forgets values below 2 +ExpirableValue.of inline crates/horizon-client/src/mobs/villager_brain.rs::Slot::set_ttl Slot holds the value and its time to live (set or set_ttl) +ExpirableValue.toString skip:debug +ExpirableValue.codec skip:java memory serialisation; Horizon packs memories in its own save format diff --git a/tools/port_map/game-010.tsv b/tools/port_map/game-010.tsv new file mode 100644 index 0000000000..f40d99136e --- /dev/null +++ b/tools/port_map/game-010.tsv @@ -0,0 +1,131 @@ +MemoryMap.equals skip:java map equality of a built memory set +MemoryMap.iterator skip:java iteration over a built memory set, used for the brain's debug and save output +MemoryModuleType.register skip:data registry of memory types; Horizon declares each memory as a field of the brain's Memories struct +MemorySlot.set port crates/horizon-client/src/mobs/villager_brain.rs::Slot::set Slot::set and set_ttl store the value with the time to live (NEVER when none) +MemorySlot.toString skip:debug text for the brain debug dump +MemorySlot.visit skip:debug Brain.Visitor feeds the debug screen only +NearestVisibleLivingEntities.findAll inline crates/horizon-client/src/mobs/villager_brain.rs::sense_babies walks the visible list with the cached line-of-sight test; only the villager babies sensor needs the whole list here +NearestVisibleLivingEntities.find skip:java Stream view of findAll, no caller in the game code +AmphibiousPathNavigation.createPathFinder inline crates/horizon-client/src/mobs/nav.rs::Navigation::evaluator NavKind::Amphibious picks Evaluator::Amphibious (prefers_shallow false) +AmphibiousPathNavigation.getTempMobPos port crates/horizon-client/src/mobs/nav.rs::Mob::temp_mob_pos x, half the height up, z +AmphibiousPathNavigation.canMoveDirectly port crates/horizon-client/src/mobs/nav.rs::Mob::can_move_directly only while in liquid, clear to the target without fluid blocking +AmphibiousPathNavigation.isStableDestination inline crates/horizon-client/src/mobs/nav.rs::Mob::stable_destination Amphibious arm: the block below is not air +FlyingPathNavigation.createPathFinder inline crates/horizon-client/src/mobs/nav.rs::Navigation::evaluator NavKind::Flying picks Evaluator::Fly +FlyingPathNavigation.canUpdatePath port crates/horizon-client/src/mobs/nav.rs::Mob::can_update_path gap: minor: always true, vanilla is false for a flier that rides something +GroundPathNavigation.createPathFinder inline crates/horizon-client/src/mobs/nav.rs::Navigation::evaluator NavKind::Ground picks Evaluator::Walk +GroundPathNavigation.canUpdatePath port crates/horizon-client/src/mobs/nav.rs::Mob::can_update_path gap: minor: on ground or in liquid, the isPassenger case is missing +GroundPathNavigation.getTempMobPos port crates/horizon-client/src/mobs/nav.rs::Mob::temp_mob_pos x, surface_y, z +GroundPathNavigation.createPath port crates/horizon-client/src/mobs/nav.rs::Mob::create_path_to unloaded chunk gives no path, then the surface search (surface_position) unless below_surface +GroundPathNavigation.getSurfaceY port crates/horizon-client/src/mobs/nav.rs::Mob::surface_y floats up through water up to 16 blocks when the mob can float, else floor(y + 0.5) +GroundPathNavigation.trimPath missing player: avoid_sun is set by the skeleton's RestrictSun goal but nothing truncates the path at the first sunlit node, so skeletons chasing you by day walk out into the sun instead of stopping in the shade +PathNavigation.getGroundY inline crates/horizon-client/src/mobs/nav.rs::Mob::nav_tick target y is the floor level under the node unless the block below is air +PathNavigation.shouldTargetNextNodeInDirection port crates/horizon-client/src/mobs/nav.rs::Mob::should_target_next_node +PathNavigation.trimPath missing minor: path nodes on a cauldron are not raised one block, so a route ending on a cauldron (leatherworker job site) aims into the block +PathNavigation.isClearForMovementBetween port crates/horizon-client/src/mobs/world.rs::MobWorld::clear_between collider clip from start to the target raised by half the height, optionally stopped by fluids +PathNavigation.shouldRecomputePath missing player: nothing asks a mob to re-path when a block changes near its route (recompute_path is only reached after its own delay flag), so a mob keeps walking an old path into a wall you just placed or past one you removed +WallClimberNavigation.createPath port crates/horizon-client/src/mobs/nav.rs::Mob::create_path_to WallClimber kind remembers climb_to before the ground path +WallClimberNavigation.tick inline crates/horizon-client/src/mobs/nav.rs::Mob::nav_tick WallClimber branch: with no path it keeps steering at climb_to until within the body width +WaterBoundPathNavigation.createPathFinder inline crates/horizon-client/src/mobs/nav.rs::Navigation::evaluator Evaluator::Swim with breach for the dolphin; can_pass_doors stays true (minor) +WaterBoundPathNavigation.canUpdatePath port crates/horizon-client/src/mobs/nav.rs::Mob::can_update_path breach (dolphin) or in liquid +WaterBoundPathNavigation.getTempMobPos port crates/horizon-client/src/mobs/nav.rs::Mob::temp_mob_pos x, half the height up, z +AdultSensor.doTick inline crates/horizon-client/src/mobs/animal_brain.rs::tick_sensors gap: player: Sensor::AdultSameType arm (axolotl only); goats, camels, hoglins and farm animals follow parents through FollowParent goals, but baby armadillos have no follow-the-adult behaviour +AdultSensor.setNearestVisibleAdult inline crates/horizon-client/src/mobs/animal_brain.rs::tick_sensors closest visible adult of the same species into nearest_visible_adult +AdultSensorAnyType.setNearestVisibleAdult port crates/horizon-client/src/mobs/animal_brain.rs::sense_adult closest visible adult of a #followable_friendly_mobs species (happy ghast) +AxolotlAttackablesSensor.isMatchingEntity port crates/horizon-client/src/mobs/animal_brain.rs::sense_axolotl_attackables within 8 blocks, in water, always-hostile or hunt target tag, alive and not playing dead +AxolotlAttackablesSensor.isHuntTarget inline crates/horizon-client/src/mobs/animal_brain.rs::sense_axolotl_attackables hunt target tag only while there is no hunting cooldown +BreezeAttackEntitySensor.doTick port crates/horizon-client/src/mobs/monster_brain.rs::sense_breeze_attack nearest living entity that is not a creative or spectator player and is attackable +FrogAttackablesSensor.isMatchingEntity port crates/horizon-client/src/mobs/frog.rs::server_ai_step every 20 ticks: nearest size-1 slime or magma cube within 10 blocks in line of sight +FrogAttackablesSensor.isUnreachableAttackTarget inline crates/horizon-client/src/mobs/frog.rs::server_ai_step skips ids in the unreachable list (up to five, 100 ticks each) +GolemSensor.checkForNearbyGolem port crates/horizon-client/src/mobs/villager_brain.rs::sense_golem an iron golem in the nearby list sets golem_detected_recently for 599 ticks +HoglinSpecificSensor.doTick inline crates/horizon-client/src/mobs/piglin.rs::hoglin_repellent gap: player: only the repellent scan is kept; adult piglin and adult hoglin counts are not sensed, so adult hoglins do not keep 8 blocks from adult piglins and do not flee them when outnumbered +HurtBySensor.doTick port crates/horizon-client/src/mobs/villager_brain.rs::sense_hurt last damage source forgotten after 40 ticks, attacker kept while alive +IsInWaterSensor.doTick inline crates/horizon-client/src/mobs/frog.rs::Croak::can_use no IS_IN_WATER memory; frog goals read fluids.in_water directly +MobSensor.doTick inline crates/horizon-client/src/mobs/armadillo.rs::server_ai_step armadillo scare sensor every fifth tick: cannot stay rolled up clears the danger, else scan +MobSensor.checkForMobsNearby port crates/horizon-client/src/mobs/armadillo.rs::scared_by_someone undead, last attacker or a sprinting or riding player within 7 by 2 blocks +MobSensor.mobDetected inline crates/horizon-client/src/mobs/armadillo.rs::server_ai_step danger = 80 ticks +NearestBedSensor.doTick port crates/horizon-client/src/mobs/villager_brain.rs::sense_bed babies only, at most four new beds per scan, 40 tick cache, path must reach the bed +NearestItemSensor.doTick port crates/horizon-client/src/game/piglins.rs::Game::piglin_pickups gap: player: villagers never walk to dropped food or seeds (only what lands within a block is picked up) and piglins and allays scan their own items without the line-of-sight test +NearestLivingEntitySensor.doTick port crates/horizon-client/src/mobs/brain.rs::living_near alive entities within the follow range box, nearest first, stored with a fresh visible list +NearestVisibleLivingEntitySensor.getNearestEntity inline crates/horizon-client/src/mobs/animal_brain.rs::sense_axolotl_attackables find_closest over the visible list with the sensor's own test +PiglinBruteSpecificSensor.doTick inline crates/horizon-client/src/mobs/piglin.rs::register_brute gap: minor: nemesis is a NearestTarget on wither skeletons only, the wither boss is not one; alerting nearby adult piglins is HurtByTarget alert +PiglinSpecificSensor.requires skip:java declares which memories the sensor fills +PiglinSpecificSensor.doTick inline crates/horizon-client/src/mobs/piglin.rs::ai_step gap: minor: only the repellent scan (every 20 ticks) is a sensor; nemesis, baby hoglin or piglin, player holding gold and adult counts are not remembered, so piglins do not attack wither skeletons or look at a player holding gold from 14 blocks +PiglinSpecificSensor.isValidRepellent port crates/horizon-client/src/mobs/piglin.rs::find_piglin_repellent #piglin_repellents, a soul campfire only while lit +PlayerSensor.doTick port crates/horizon-client/src/mobs/villager_brain.rs::sense_players gap: minor: one player only, no other players of a shared level +SecondaryPoiSensor.doTick port crates/horizon-client/src/mobs/villager_brain.rs::sense_secondary farmer: farmland within 4 by 2 blocks; no other profession has secondary blocks +Sensor.updateTargetingConditionRanges inline crates/horizon-client/src/mobs/monster_brain.rs::targetable each test reads the mob's follow range at call time, so no shared range needs updating +Sensor.isEntityAttackableIgnoringLineOfSight inline crates/horizon-client/src/mobs/goals.rs::Mob::can_target_player check_sight false for the player; mob targets are tested per goal +Sensor.rememberPositives inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start positives_left counter of the breeze's StopAttackingIfTargetInvalid (100 ticks after the last attackable moment) +TemptingSensor.doTick port crates/horizon-client/src/mobs/animal_brain.rs::sense_tempting gap: minor: range test ignores the visibility scaling (sneaking), a rider of any kind blocks the tempt +TemptingSensor.playerHoldingTemptation inline crates/horizon-client/src/mobs/animal_brain.rs::sense_tempting main or off hand holds food of the animal +VillagerBabiesSensor.isVillagerBaby inline crates/horizon-client/src/mobs/villager_brain.rs::sense_babies visible baby villagers +VillagerHostilesSensor.isMatchingEntity port crates/horizon-client/src/mobs/villager_brain.rs::sense_hostiles closest visible hostile within its own acceptable distance +VillagerHostilesSensor.isClose port crates/horizon-client/src/mobs/villager_brain.rs::hostile_distance the acceptable distance table (8, 10, 12, 15 blocks) +WardenEntitySensor.doTick port crates/horizon-client/src/mobs/warden_brain.rs::sense_entities living list then nearest attackable: a player first, else another entity +WardenEntitySensor.getClosest inline crates/horizon-client/src/mobs/warden_brain.rs::sense_entities two passes over the sorted list with warden::can_target +TargetingConditions.copy skip:java clone of a builder object +TargetingConditions.test port crates/horizon-client/src/mobs/goals.rs::Mob::player_in_range gap: minor: the player case (range scaled by visibility, line of sight, ally and creative checks); mob targets are tested in NearestTarget and each brain's targetable and attackable +DefaultRandomPos.generateRandomPosTowardDirection port crates/horizon-client/src/mobs/goals.rs::Mob::default_check Mob::toward builds the position, default_check does limits, home, stable and malus +GoalUtils.mobRestricted port crates/horizon-client/src/mobs/goals.rs::Mob::restricted home within radius + distance + 1, then isRestricted +GoalUtils.hasMalus inline crates/horizon-client/src/mobs/goals.rs::Mob::default_check path type malus must be 0, also in land_up_out_of_solid +LandRandomPos.getPosTowards missing player: iron golems wander anywhere; GolemRandomStrollInVillageGoal and MoveBackToVillageGoal are not registered, so a golem does not drift toward villagers who want it or back to the village +LandRandomPos.getPosInDirection inline crates/horizon-client/src/mobs/goals.rs::Mob::land_random_pos_away only the away direction is wired; the toward call has no user +LandRandomPos.movePosUpOutOfSolid port crates/horizon-client/src/mobs/goals.rs::Mob::land_up_out_of_solid up out of solid, then no water and no malus +RandomPos.generateRandomDirection port crates/horizon-client/src/mobs/goals.rs::Mob::random_direction +RandomPos.generateRandomDirectionWithinRadians port crates/horizon-client/src/mobs/goals.rs::Mob::random_direction_within +RandomPos.generateRandomPos port crates/horizon-client/src/mobs/goals.rs::Mob::pick_best best of ten by walk target value +PoiManager.remove inline crates/horizon-client/src/poi_sections.rs::PoiSections::get no stored records: a section's POI list is rebuilt from its blocks after an edit +PoiManager.findAll port crates/horizon-client/src/mobs/villager_brain.rs::pois_in_range square then sphere filter over the sections' POI blocks, nearest first, with a caller filter +PoiManager.findAllWithType port crates/horizon-client/src/mobs/villager_brain.rs::pois_in_range returns (type, pos) pairs; only HAS_SPACE occupancy (any-occupancy searches use homes_in_range) +PoiManager.findAllClosestFirstWithType port crates/horizon-client/src/mobs/villager_brain.rs::pois_in_range sorted by squared distance +PoiManager.getRandom inline crates/horizon-client/src/mobs/raider.rs::MoveThroughVillage::can_use random home within 48 blocks not visited lately (the raider's village walk); the villager's hiding place picks its own home +PoiManager.release inline crates/horizon-client/src/mobs/villager_brain.rs::PoiMem::clear tickets are derived from the villagers' job, potential job, home and meeting memories, so clearing the memory frees the ticket +PoiManager.getType port crates/horizon-client/src/mobs/villager_brain.rs::poi_at POI kind of the block at a position, from the per-state table +PoiManager.getDebugPoiInfo skip:debug debug screen data +PoiManager.setDirty inline crates/horizon-client/src/mobs/villager_brain.rs::village_sections village distances are recomputed per query from the taken village POIs, no dirty tracking +PoiManager.checkConsistencyWithBlocks inline crates/horizon-client/src/poi_sections.rs::PoiSections::get the list is always built from the blocks, so it cannot drift +PoiManager.updateFromSection inline crates/horizon-client/src/poi_sections.rs::PoiSections::get scans the section's blocks for POI states +PoiManager.ensureLoadedAndValid skip:other no separate POI storage: POIs come from the loaded chunks' blocks +PoiManager.DistanceTracker.setLevel inline crates/horizon-client/src/mobs/villager_brain.rs::sections_to_village Chebyshev distance in sections to a village section (max 7), computed when asked +PoiRecord.acquireTicket inline crates/horizon-client/src/mobs/villager_brain.rs::tickets a taken ticket is a villager memory pointing at the POI, counted per search +PoiRecord.releaseTicket inline crates/horizon-client/src/mobs/villager_brain.rs::tickets see PoiManager.release +PoiRecord.isOccupied inline crates/horizon-client/src/mobs/villager_brain.rs::village_sections a POI counts as occupied (village centre) when a villager memory holds it +PoiRecord.equals skip:java position equality +PoiRecord.Packed.unpack skip:other POI files are not loaded into records; keep_claims reads free_tickets only to carry claims across a save +PoiSection.pack port crates/horizon-client/src/save.rs::encode_poi Valid and Records with free_tickets at the max (claims kept by keep_claims) +PoiSection.getRecords inline crates/horizon-client/src/poi_sections.rs::PoiSections::get per-section POI list, type filter done by poi_matches +PoiSection.add inline crates/horizon-client/src/poi_sections.rs::PoiSections::get list rebuilt from the blocks +PoiSection.remove inline crates/horizon-client/src/poi_sections.rs::PoiSections::get list rebuilt from the blocks +PoiSection.getFreeTickets skip:debug deprecated, debug only +PoiSection.release inline crates/horizon-client/src/mobs/villager_brain.rs::tickets see PoiManager.release +PoiSection.refresh inline crates/horizon-client/src/poi_sections.rs::PoiSections::get invalid sections need no refresh, the list is rebuilt from blocks +PoiSection.Packed.unpack skip:other POI files are not loaded into sections +PoiTypes.registerBlockStates inline crates/horizon-client/src/save.rs::poi_type state to POI type and max tickets in one match +PoiTypes.bootstrap port crates/horizon-client/src/save.rs::poi_type every POI type with its tickets (bed heads of the sixteen colours only, cauldrons, bell 32); villagers use villager_brain::state_poi for jobs, beds and bell +Bat.isFlapping missing minor: a flying bat's wing beat (every 10 ticks) sends no FLAP vibration, so sculk sensors do not hear it +Bat.getAmbientSound inline crates/horizon-client/src/mobs/ai.rs::play_ambient Species::Bat arm: a resting bat is silent three times in four +Bat.isResting port crates/horizon-client/src/mobs/bat.rs::resting +Bat.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt BatFlags byte, bit 0 resting +Bat.checkBatSpawnRules inline crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::Bat arm: below the surface, coin flip, light against nextInt(4), bats_spawnable_on, mob rules +Dolphin.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize S::Dolphin arm: full air, pitch 0, baby chance 0.1 +Dolphin.defineSynchedData skip:java synched fields: got_fish is extra.flag and moistness extra.counter +Dolphin.addAdditionalSaveData missing minor: GotFish and Moistness are not written, a saved dolphin forgets the fish it was fed +Dolphin.readAdditionalSaveData missing minor: GotFish and Moistness are not read, a loaded dolphin starts with no fish and full moistness +Dolphin.registerGoals port crates/horizon-client/src/mobs/aquatic.rs::register gap: player: no MoveToItemGoal and PlayWithItemsGoal (dolphins ignore items thrown in the water), no FollowPlayerRiddenEntityGoal for boats or nautiluses, no AvoidEntity of guardians, and HurtByTarget does not skip guardians +Dolphin.createAttributes skip:data attributes come from the entity tables +Dolphin.canAttack missing minor: a baby dolphin that is hurt can still pick the attacker as target +Dolphin.pickUpItem missing player: a dolphin never picks up an item dropped in the water (pick_up_loot is set but nothing takes the item) +Dolphin.handleEntityEvent port crates/horizon-client/src/particles.rs::entity_event_burst event 38: seven happy villager particles about the dolphin +Dolphin.closeToNextPos inline crates/horizon-client/src/mobs/dolphin.rs::SwimToTreasure::tick nav target within 12 blocks (144 squared) of the dolphin +Dolphin.DolphinSwimWithPlayerGoal.canContinueToUse port crates/horizon-client/src/mobs/dolphin.rs::SwimWithPlayer::can_continue player still swimming and within 16 blocks +Dolphin.ItemGoal.drop missing player: a dolphin holding an item (from a dispenser) never throws it back with the 40 tick pickup delay +Dolphin.ItemGoal.dropItem missing player: the held item is never dropped by the dolphin +Dolphin.MoveToItemGoal.canUse missing player: dolphins do not notice items floating within 8 blocks +Dolphin.MoveToItemGoal.stop missing player: no item to drop after playing, no cooldown +Dolphin.MoveToItemGoal.start missing player: dolphins do not swim to floating items or play the play sound +AbstractChestedHorse.dropEquipment port crates/horizon-client/src/mobs/drops.rs::die the stored items and then a chest drop, chested cleared +AbstractChestedHorse.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt ChestedHorse and Items for llamas, donkeys and mules +AbstractChestedHorse.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt ChestedHorse and Items, 15 slots +AbstractChestedHorse.getSlot missing minor: slot 499 (horse.chest) is not mapped for mobs, so /item replace entity ... horse.chest does nothing +AbstractChestedHorse.equipChest port crates/horizon-client/src/game/mounts.rs::Game::interact_mount tamed donkey or mule takes a held chest with the sound; llamas do it in game/llamas.rs interact_llama +AbstractChestedHorse.playChestEquipsSound inline crates/horizon-client/src/game/mounts.rs::Game::interact_mount entity.donkey.chest with the random pitch; llamas play entity.llama.chest in llama_sound diff --git a/tools/port_map/game-011.tsv b/tools/port_map/game-011.tsv new file mode 100644 index 0000000000..1af860b72d --- /dev/null +++ b/tools/port_map/game-011.tsv @@ -0,0 +1,148 @@ +AbstractHorse.registerGoals port crates/horizon-client/src/mobs/equine.rs::register same goals and priorities; llamas and camels have their own register +AbstractHorse.addBehaviourGoals inline crates/horizon-client/src/mobs/equine.rs::register float, mount panic and tempt come after the rest; none for a skeleton horse, no panic for a zombie horse +AbstractHorse.getFlag skip:other bit accessor of the synched flags; Horizon keeps tame, eating (flag) and standing (counter) as separate fields +AbstractHorse.setFlag skip:other bit accessor of the synched flags; the bits are packed for sync in lan.rs::mob_meta and read back in equine::set_flags +AbstractHorse.canUseSlot port crates/horizon-client/src/game/dispenser.rs::mob_can_use_slot saddle needs an alive tamed adult; horses, zombie horses, skeleton horses and llamas allow every slot +AbstractHorse.equipBodyArmor inline crates/horizon-client/src/game/mounts.rs::Game::interact_mount gap: minor: right click armours only a tamed Horse, a tamed zombie horse needs the inventory screen +AbstractHorse.canDispenserEquipIntoSlot port crates/horizon-client/src/game/dispenser.rs::mob_can_dispenser_equip body and saddle of a tamed mount +AbstractHorse.modifyTemper inline crates/horizon-client/src/game/mounts.rs::Game::feed_mount temper raised by the food, clamped to the maximum; a buck adds 5 in equine.rs RunAroundLikeCrazy::tick +AbstractHorse.eating inline crates/horizon-client/src/game/mounts.rs::Game::feed_mount gap: minor: the mouth stays open 3 ticks instead of 30; a foal uses the adult eat sound +AbstractHorse.causeFallDamage inline crates/horizon-client/src/mobs/living.rs::Mob::cause_fall_damage gap: player: a landing horse makes no thud (entity.horse.land) and its rider takes no fall damage (position_rider zeroes the rider's fall distance); the mount's own damage uses its safe distance of 6 +AbstractHorse.createInventory skip:java resizes a SimpleContainer; Horizon sizes the chest items when the menu opens +AbstractHorse.getEquipSound port crates/horizon-client/src/game/dispenser.rs::equip_sound saddle slot plays entity.horse.saddle +AbstractHorse.hurtServer missing player: a hurt horse never rears up (vanilla: one hit in three makes it stand for a second) +AbstractHorse.playStepSound inline crates/horizon-client/src/mobs/ai.rs::step_sound gap: player: horses play the block's own step sound at 15%, never entity.horse.step, step_wood, step_baby or the gallop of a ridden horse; snow above the block is not used either +AbstractHorse.isWoodSoundType missing minor: part of playStepSound, horses on wood use the block's wood step instead of horse.step_wood +AbstractHorse.fedFood port crates/horizon-client/src/game/mounts.rs::Game::feed_mount handleEating and consuming one item; llamas use llamas.rs feed_llama +AbstractHorse.doPlayerRide inline crates/horizon-client/src/game/mounts.rs::Game::interact_mount clears eating and standing, turns the player to the mount and mounts; the block at the end of interact_mount +AbstractHorse.isImmobile missing player: a grazing or rearing horse is not held still, goals and strolling keep running while its head is down or it is up on its hind legs +AbstractHorse.dropEquipment inline crates/horizon-client/src/mobs/drops.rs::die the equine branch drops the inventory items and a donkey's or mule's chest +AbstractHorse.followMommy missing minor: no Bred flag, so a bred foal is not pulled to its mother from 16 blocks; the FollowParent goal covers 8 +AbstractHorse.openMouth inline crates/horizon-client/src/game/mounts.rs::Game::feed_mount gap: minor: sets the mouth animation to 1 once, it closes after 3 ticks; the mouth flag is not synced to a remote client +AbstractHorse.setStanding inline crates/horizon-client/src/mobs/equine.rs::make_mad the 20 tick rear; RandomStand::start and the jump in tick_mount_riding set the same counter +AbstractHorse.standIfPossible inline crates/horizon-client/src/mobs/equine.rs::make_mad rears only species that can; the hurt caller is missing (see hurtServer) +AbstractHorse.tickRidden port crates/horizon-client/src/mobs/equine.rs::ridden_step yaw and half the pitch follow the rider; no gallop counter (see playStepSound) +AbstractHorse.addPassenger inline crates/horizon-client/src/game/mounts.rs::Game::interact_mount gap: minor: the rider's yaw is snapped to the mount's on mounting, the pitch is not +AbstractHorse.getRiddenInput port crates/horizon-client/src/mobs/equine.rs::ridden_step gap: player: no allowStandSliding, so a horse that lands from a small hop while still rearing ignores the keys until the rear ends +AbstractHorse.executeRidersJump port crates/horizon-client/src/mobs/equine.rs::ridden_step gap: minor: the impulse is jump strength times the bar only, no honey block factor and no Jump Boost +AbstractHorse.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt gap: minor: Tame, Temper and Owner are read; EatingHaystack and Bred are not +AbstractHorse.setOffspringAttribute port crates/horizon-client/src/mobs/equine.rs::inherit max health, jump strength and speed from both parents +AbstractHorse.onPlayerJump inline crates/horizon-client/src/game/mounts.rs::Game::tick_mount_riding gap: minor: the rear and the jump sound; donkeys and mules play the horse jump sound +AbstractHorse.handleEntityEvent port crates/horizon-client/src/particles.rs::entity_event_burst events 7 and 6: hearts and smoke around the mount +AbstractHorse.positionRider port crates/horizon-client/src/game/mounts.rs::Game::position_rider gap: minor: the rider's body yaw is not copied from the mount +AbstractHorse.generateMaxHealth port crates/horizon-client/src/mobs/finalize.rs::Ctx::horse_health +AbstractHorse.generateJumpStrength port crates/horizon-client/src/mobs/finalize.rs::Ctx::horse_jump +AbstractHorse.generateSpeed port crates/horizon-client/src/mobs/finalize.rs::Ctx::horse_speed +AbstractHorse.getSlot missing minor: /item cannot reach the mount slots horse.0 to horse.14 +AbstractHorse.getControllingPassenger port crates/horizon-client/src/mobs/equine.rs::controlled gap: player: a tamed skeleton horse obeys its rider without a saddle +AbstractHorse.getDismountLocationInDirection inline crates/horizon-client/src/game/mounts.rs::Game::dismount_mount gap: minor: no climb up to 0.75 above the mount and no pose boxes, only block checks beside the mount +AbstractHorse.getDismountLocationForPassenger port crates/horizon-client/src/game/mounts.rs::Game::dismount_mount gap: minor: tries both sides by the mount's yaw then on top, not by the rider's main arm +AbstractHorse.getPassengerAttachmentPoint inline crates/horizon-client/src/game/mounts.rs::Game::position_rider gap: player: the rider does not slide back and up when the horse rears; the seat is a fixed point per species +Donkey.canMate inline crates/horizon-client/src/mobs/goals.rs::Breed::can_use gap: player: only Horse, Donkey and llamas get the tame, full health, not ridden rules, so two tame mules in love also breed (a mule never mates in vanilla) +Donkey.getBreedOffspring inline crates/horizon-client/src/game/creatures.rs::Game::breed donkey with horse gives a mule, donkey with donkey a donkey; attributes from both parents +Horse.randomizeAttributes port crates/horizon-client/src/mobs/finalize.rs::Ctx::horse health, speed and jump strength +Horse.setVariantAndMarkings inline crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize coat and markings drawn at spawn; breeding mixes them in creatures.rs Game::breed +Horse.setVariant missing minor: only reached by the horse/variant item component, which Horizon does not read +Horse.get missing minor: the horse/variant component is never reported +Horse.applyImplicitComponent missing minor: a horse/variant component on a spawn egg or item is ignored +Horse.playGallopSound missing player: a ridden horse never plays the gallop and the now and then breathing sound +Horse.mobInteract inline crates/horizon-client/src/game/mounts.rs::Game::interact_mount gap: player: an untamed horse with a non-food item in hand is mounted instead of rearing angrily, a full health tame horse with food in hand is mounted instead of ignoring the click, and a golden dandelion on a foal does nothing +Horse.canMate inline crates/horizon-client/src/mobs/goals.rs::Breed::can_use horse or donkey partner, both tame, adult, not ridden and at full health +Horse.hurtArmor missing player: horse armour never loses durability when the horse is hurt (the body item is only an id) +Horse.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize random coat and markings, the herd shares the coat +Llama.setStrength inline crates/horizon-client/src/mobs/llama.rs::from_nbt strength kept in variant2 and clamped to 1 to 5 where it is set (from_nbt, offspring, apply_offspring) +Llama.setRandomStrength inline crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize one in 25 llamas rolls up to 5, else up to 3 +Llama.addAdditionalSaveData port crates/horizon-client/src/mobs/llama.rs::to_nbt Variant and Strength +Llama.readAdditionalSaveData port crates/horizon-client/src/mobs/llama.rs::from_nbt +Llama.registerGoals port crates/horizon-client/src/mobs/llama.rs::register goals and targets as vanilla, path length 40 +Llama.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta strength and variant entries +Llama.get missing minor: the llama/variant component is never reported +Llama.applyImplicitComponent missing minor: a llama/variant component on an item is ignored +Llama.playChestEquipsSound inline crates/horizon-client/src/game/llamas.rs::Game::interact_llama entity.llama.chest when a chest is added +Llama.canMate inline crates/horizon-client/src/mobs/goals.rs::Breed::can_use any llama or trader llama, both tame, adult, not ridden and at full health +Llama.causeFallDamage inline crates/horizon-client/src/mobs/living.rs::Mob::cause_fall_damage gap: player: the rider of a llama takes no fall damage; the llama's own damage uses its safe distance of 6 +Llama.leaveCaravan inline crates/horizon-client/src/mobs/caravan.rs::FollowCaravan::stop clears the caravan head +Llama.LlamaHurtByTargetGoal.canContinueToUse port crates/horizon-client/src/mobs/llama.rs::LlamaHurtBy::can_continue stops after a spit +Mule.playChestEquipsSound inline crates/horizon-client/src/game/mounts.rs::Game::interact_mount gap: minor: a mule uses the donkey's chest sound (entity.donkey.chest) instead of entity.mule.chest +SkeletonHorse.checkSkeletonHorseSpawnRules inline crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules spawner reasons need light or a light ignoring reason, otherwise the animal rules +SkeletonHorse.randomizeAttributes port crates/horizon-client/src/mobs/finalize.rs::Ctx::horse jump strength only +SkeletonHorse.getSwimSound missing minor: a skeleton horse in water uses the generic swim sound, no skeleton_horse.swim, step_water or gallop_water +SkeletonHorse.playSwimSound missing minor: the swim volume rules for a skeleton horse are not applied +SkeletonHorse.playJumpSound missing minor: no skeleton_horse.jump_water in water; the horse jump sound plays on land +SkeletonHorse.aiStep inline crates/horizon-client/src/game/passengers.rs::Game::tick_skeleton_traps a trap horse that is not persistent is removed after 18000 ticks +SkeletonHorse.addAdditionalSaveData port crates/horizon-client/src/mobs/riders.rs::write_nbt SkeletonTrap and SkeletonTrapTime +SkeletonHorse.readAdditionalSaveData port crates/horizon-client/src/mobs/riders.rs::read_nbt +SkeletonHorse.setTrap inline crates/horizon-client/src/game/passengers.rs::Game::spring_skeleton_trap the trap flag is a plain bool polled every tick instead of adding and removing a goal; lightning.rs sets it +SkeletonTrapGoal.canUse inline crates/horizon-client/src/game/passengers.rs::Game::tick_skeleton_traps a living player within 10 blocks +SkeletonTrapGoal.tick port crates/horizon-client/src/game/passengers.rs::Game::spring_skeleton_trap lightning, a skeleton on the horse and three more horse and skeleton pairs +SkeletonTrapGoal.createHorse inline crates/horizon-client/src/game/passengers.rs::Game::spring_skeleton_trap tame, adult, invulnerable for 60 ticks, persistent +TraderLlama.makeNewLlama inline crates/horizon-client/src/mobs/llama.rs::apply_offspring the baby trader llama is persistent +TraderLlama.registerGoals port crates/horizon-client/src/mobs/llama.rs::register adds panic 2.0, the defend trader target and the zombie and illager targets +TraderLlama.canDespawn port crates/horizon-client/src/mobs/caravan.rs::trader_llama_tick despawn delay counts down unless tamed, led by something else, ridden, age locked or persistent +TraderLlama.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize an event spawn is an adult; a lone one gets no baby chance +ZombieHorse.randomizeAttributes port crates/horizon-client/src/mobs/finalize.rs::Ctx::horse jump strength and speed +ZombieHorse.generateZombieHorseJumpStrength inline crates/horizon-client/src/mobs/finalize.rs::Ctx::horse 0.5 plus three draws of 0.0667 +ZombieHorse.generateZombieHorseSpeed inline crates/horizon-client/src/mobs/finalize.rs::Ctx::horse (9 plus three draws) over 42.16 +ZombieHorse.addBehaviourGoals inline crates/horizon-client/src/mobs/equine.rs::register float and tempt with the zombie horse food, no mount panic +ZombieHorse.mobInteract inline crates/horizon-client/src/game/mounts.rs::Game::interact_mount gap: player: an untamed zombie horse with a non-food item in hand is mounted instead of rearing angrily, and food that is not eaten still mounts it +Cat.getSoundSet missing player: cats always use the classic adult sounds, so royal cats and kittens never get their own voices +Cat.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: minor: variant, lying, relax and collar are sent, the sound variant entry is not +Cat.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt variant, sound_variant and CollarColor +Cat.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt +Cat.getAmbientSound inline crates/horizon-client/src/mobs/ai.rs::play_ambient gap: minor: purr when in love and the stray sound are used; a tame cat never plays the purreow variant +Cat.createAttributes skip:data attributes come from entity-types.json +Cat.handleLieDown inline crates/horizon-client/src/mobs/tame.rs::cat_ai_step gap: minor: purring and the amounts are ported; lying on top of a sleeping player (a 0.15 shift of the pose) is not tracked +Cat.updateLieDownAmount port crates/horizon-client/src/mobs/tame.rs::cat_amounts +Cat.updateRelaxStateOneAmount port crates/horizon-client/src/mobs/tame.rs::cat_amounts +Cat.canMate inline crates/horizon-client/src/mobs/goals.rs::Breed::can_use gap: minor: a sitting cat is refused as a mate, vanilla only needs both tame and in love +Cat.removeWhenFarAway missing minor: wild cats never despawn for distance (vanilla: untamed and older than 2400 ticks) +Cat.reassessTameGoals inline crates/horizon-client/src/mobs/tame.rs::register_cat the player avoidance goal is conditioned on being untamed instead of added and removed +Cat.tryToTame inline crates/horizon-client/src/game/creatures.rs::Game::interact_mob one chance in three, then tame, sitting, event 7 or 6 and the advancement trigger +Cat.CatAvoidEntityGoal.canContinueToUse inline crates/horizon-client/src/mobs/goals.rs::AvoidEntity::can_continue gap: minor: a cat tamed mid flight finishes its escape path, the untamed check is only in can_use +Cat.CatRelaxOnOwnerGoal.spaceIsOccupied port crates/horizon-client/src/mobs/tame.rs::CatRelaxOnOwner::occupied another lying or relaxing cat within 2 blocks of the spot +Cat.CatRelaxOnOwnerGoal.canContinueToUse port crates/horizon-client/src/mobs/tame.rs::CatRelaxOnOwner::can_continue tame, not sitting, owner still asleep and the spot free +Cat.CatTemptGoal.canScare missing player: a cat lured with fish is scared by every move or look of the player; vanilla lets a randomly picked player (re-rolled every 500 to 600 ticks) approach without scaring it +CatVariants.bootstrap skip:data variant registry bootstrap; Horizon reads cat variants from data +Ocelot.registerGoals missing player: an ocelot only panics, wanders and looks at players (the generic animal goals): it is not lured by fish, does not run from players, never stalks chickens or baby turtles and has no breed goal +Ocelot.customServerAiStep port crates/horizon-client/src/mobs/tame.rs::cat_anim crouch and sprint pose from the move speed, shared with cats; nothing sets those speeds for an ocelot yet +Ocelot.removeWhenFarAway missing minor: ocelots never despawn for distance (vanilla: not trusting and older than 2400 ticks) +Ocelot.createAttributes skip:data attributes come from entity-types.json +Ocelot.mobInteract missing player: raw fish never makes a wild ocelot trust the player (no one in three roll, no hearts or smoke); it only puts the ocelot in love mode +Ocelot.handleEntityEvent port crates/horizon-client/src/particles.rs::entity_event_burst events 41 and 40: hearts and smoke; nothing sends them without the trust roll +Ocelot.reassessTrustingGoals missing player: no player avoidance goal, so wild ocelots never run from the player +Ocelot.checkSpawnObstruction port crates/horizon-client/src/mobs/spawn_selection.rs::spawn_obstruction unobstructed, no liquid, at or above sea level on grass or leaves +Ocelot.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize baby chance 1.0 for a group +Ocelot.getLeashOffset missing minor: the lead ties at full eye height instead of half of it (leash::leash_offset only special-cases llamas) +Ocelot.OcelotAvoidEntityGoal.canUse missing player: see reassessTrustingGoals, no avoidance goal exists +Ocelot.OcelotAvoidEntityGoal.canContinueToUse missing minor: see reassessTrustingGoals, no avoidance goal exists +Ocelot.OcelotTemptGoal.canScare missing minor: see registerGoals, an ocelot has no tempt goal +AbstractFish.registerGoals port crates/horizon-client/src/mobs/aquatic.rs::register panic, player avoidance and random swimming; gap: player: creative players do not scare fish (the avoid goal skips them, vanilla only skips spectators) +AbstractFish.mobInteract port crates/horizon-client/src/game/creatures.rs::Game::interact_mob a water bucket scoops the fish with its bucket data +AbstractFish.FishSwimGoal.canUse inline crates/horizon-client/src/mobs/aquatic.rs::register Stroll::swimming(1.0, 40) is the random swim goal; fish never follow a leader so it always applies +AbstractSchoolingFish.registerGoals missing player: no follow the flock leader goal, so cod, salmon and tropical fish spawn in groups but swim about alone instead of schooling +AbstractSchoolingFish.isFollower missing minor: part of the missing schooling; the leader id is kept at spawn but never read +AbstractSchoolingFish.canBeFollowed missing minor: part of the missing schooling +AbstractSchoolingFish.tick missing minor: part of the missing schooling (school size reset when alone) +AbstractSchoolingFish.inRangeOfLeader missing minor: part of the missing schooling +AbstractSchoolingFish.pathToLeader missing minor: part of the missing schooling +AbstractSchoolingFish.addFollowers missing minor: part of the missing schooling +AbstractSchoolingFish.finalizeSpawn inline crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize gap: player: the group shares a leader id and variant, but followers never use it to follow the leader +Pufferfish.readAdditionalSaveData missing minor: PuffState is not read or written, a reloaded pufferfish starts deflated +Pufferfish.registerGoals port crates/horizon-client/src/mobs/aquatic.rs::register the puff goal at priority 1 +Pufferfish.touch missing minor: only the player is stung (inside ai_step); other mobs touching a puffed fish are not hurt or poisoned +Salmon.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta size entry, small 0 to large 2 +Salmon.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::read_variant the type tag, medium by default +Salmon.get inline crates/horizon-client/src/entity_nbt.rs::bucket_components the size component goes onto the bucket +Salmon.applyImplicitComponent port crates/horizon-client/src/entity_nbt.rs::load_bucket_data size from the bucket +Salmon.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize weighted size: small 30, medium 50, large 15 +TropicalFish.packVariant inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt packed pattern, base colour and pattern colour; lan.rs::mob_meta packs it for sync +TropicalFish.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta packed variant entry +TropicalFish.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::read_variant an unknown pattern is a kob +TropicalFish.setPattern inline crates/horizon-client/src/entity_nbt.rs::load_bucket_data only reached from the bucket's pattern component +TropicalFish.setBaseColor inline crates/horizon-client/src/entity_nbt.rs::load_bucket_data only reached from the bucket's base colour component +TropicalFish.setPatternColor inline crates/horizon-client/src/entity_nbt.rs::load_bucket_data only reached from the bucket's pattern colour component +TropicalFish.get inline crates/horizon-client/src/entity_nbt.rs::bucket_components pattern and both colours go onto the bucket +TropicalFish.applyImplicitComponents port crates/horizon-client/src/entity_nbt.rs::load_bucket_data pattern and colours from the bucket +TropicalFish.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize one in ten is a lone random fish, else a shared common variant +TropicalFish.checkTropicalFishSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules water below and above, and any height biomes or the surface rule +TropicalFish.Pattern.addToTooltip missing player: a tropical fish bucket shows no pattern and colour lines in its tooltip diff --git a/tools/port_map/game-012.tsv b/tools/port_map/game-012.tsv new file mode 100644 index 0000000000..dc0e051f76 --- /dev/null +++ b/tools/port_map/game-012.tsv @@ -0,0 +1,132 @@ +Fox.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: minor: the type and the flags byte are synced; the two trusted-entity slots are not +Fox.canEat inline crates/horizon-client/src/mobs/fox.rs::ai_step held food (eats), no target, on the ground and awake, tested in the eating block of ai_step +Fox.populateDefaultEquipmentSlots port crates/horizon-client/src/mobs/finalize.rs::Ctx::populate gap: player: only Ctx::finalize (structure mobs, probe) calls it; natural spawns and eggs use spawning.rs::finalize, so a fresh fox never holds an emerald, egg, rabbit foot or the like in its mouth +Fox.handleEntityEvent missing player: no item crumbs at the mouth of a fox that is eating (event 45 is never broadcast or handled; only the sound plays) +Fox.createAttributes skip:data attributes come from entity-types.json +Fox.checkFoxSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules foxes_spawnable_on below and bright enough +Fox.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize gap: player: live spawns (natural, spawner, summon) run spawning.rs::finalize, which has no fox arm beyond pick-up-loot: every spawned fox is red (no snow foxes in snowy biomes), no baby packs, no item in the mouth; Ctx::finalize is only reached by structure mobs and the probe +Fox.setTargetGoals port crates/horizon-client/src/mobs/fox.rs::register red and snow target orders as goals gated on the variant read when the goal runs +Fox.get missing minor: the fox/variant component is not reported (species_variant_component has no fox) +Fox.applyImplicitComponent missing minor: a fox/variant component on a spawn egg or item is ignored +Fox.getTrustedEntities missing player: foxes trust nobody, so a fox you bred still runs from you (the avoid-player goal has no trust test) and never defends you +Fox.addTrustedEntity missing player: foxes bred or hatched by a player never trust them (breeding does not pass the breeder on) +Fox.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt gap: player: only Type is read; Trusted, Sleeping, Sitting and Crouching are dropped, so a reload wakes a sleeping fox and forgets trust +Fox.clearTrusted missing minor: trust is not modelled, so there is nothing to clear +Fox.getFlag port crates/horizon-client/src/mobs/fox.rs::flag flags byte in extra.ints[0] +Fox.canHoldItem missing player: part of the missing item pickup (a fox never reaches the point of choosing what to hold) +Fox.spitOutItem missing minor: the old item is never spat out with entity.fox.spit because pickup is missing +Fox.dropItemStack missing minor: the rest of a stack is never dropped by a fox because pickup is missing +Fox.pickUpItem missing player: foxes never pick up dropped items (food, emeralds, anything): the loot pickup of Mob.aiStep exists only for pandas (game/pandas.rs) and the fox search-for-items goal is absent +Fox.getHeadRollAngle port crates/horizon-client/src/mobs/fox.rs::pose head_roll_angle from the interested angle at the partial tick +Fox.setTarget missing minor: only clears the defending flag, which is never set +Fox.playAmbientSound missing player: no night screech at double volume (entity.fox.screech) when no player is near +Fox.getAmbientSound missing player: a sleeping fox makes no sleep sound and foxes never screech at night; the ambient is always entity.fox.ambient +Fox.getLeashOffset missing minor: a lead hangs at the default offset (eye height), not at 0.55 of it +Fox.FoxAlertableEntitiesSelector.test inline crates/horizon-client/src/mobs/fox.rs::alertable gap: minor: any other mob counts even when asleep or sneaking, and a trusted mob is not skipped (no trust) +Fox.FoxEatBerriesGoal.onReachedTarget inline crates/horizon-client/src/mobs/fox.rs::EatBerries::tick after 40 ticks at the bush, picks when mob griefing is on +Fox.FoxEatBerriesGoal.pickGlowBerry port crates/horizon-client/src/mobs/fox.rs::EatBerries::pick CaveVines.use: one glow berry, berries off, pick sound with 0.8 to 1.2 pitch +Fox.FoxEatBerriesGoal.pickSweetBerries port crates/horizon-client/src/mobs/fox.rs::EatBerries::pick 1 or 2 berries (+1 when fully grown), the first into the mouth when empty, age back to 1 +Fox.FoxFollowParentGoal.canContinueToUse inline crates/horizon-client/src/mobs/fox.rs::Wrap::can_continue defending is never set, so only the follow-parent test remains +Fox.FoxLookAtPlayerGoal.canContinueToUse inline crates/horizon-client/src/mobs/fox.rs::Wrap::can_continue Wrap with the faceplanted or interested test on both can_use and can_continue +Fox.FoxMeleeAttackGoal.checkAndPerformAttack inline crates/horizon-client/src/mobs/goals.rs::MeleeAttack::tick gap: player: the generic melee strike runs; a fox bite plays no entity.fox.bite sound +Fox.FoxPanicGoal.shouldPanic inline crates/horizon-client/src/mobs/fox.rs::register Panic(2.2); the not-defending test is moot because defending is never set +Fox.FoxPounceGoal.canContinueToUse inline crates/horizon-client/src/mobs/fox.rs::Pounce::can_continue target alive, still in the air or tilted 15 degrees, not faceplanted +Fox.FoxStrollThroughVillageGoal.canFoxMove missing minor: the stroll-through-village goal is not ported (foxes never follow village streets); fox.rs::can_move is Fox.canMove, a different test +Frog.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta variant holder and tongue target id +Frog.getTongueTarget inline crates/horizon-client/src/mobs/frog.rs::ShootTongue::tick gap: minor: the target lives in Frog.State; FrogLookControl.resetXRotOnTick has no frog case, so the head pitch is zeroed while the tongue is out +Frog.get port crates/horizon-client/src/mobs/finalize.rs::species_variant_component frog/variant component +Frog.applyImplicitComponent missing minor: a frog/variant component on a spawn egg or item is ignored +Frog.customServerAiStep port crates/horizon-client/src/mobs/frog.rs::server_ai_step the attackables sensor and memory timers; the activities run as goals +Frog.getBreedOffspring missing player: frogs never breed: they take slimeballs and show hearts, but no breed goal exists, so no frogspawn is ever laid by frogs +Frog.spawnChildFromBreeding missing player: frogs never become pregnant (IS_PREGNANT) and so never lay frogspawn; no breed goal means this is never reached, and the generic baby spawn would be wrong for frogs anyway +Frog.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize variant by biome (live spawns set it in spawning.rs::finalize from biome.frog); the long jump cooldown it sets has no user +Frog.createAttributes skip:data attributes come from entity-types.json +Frog.checkFrogSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules frogs_spawnable_on below and bright enough +Frog.FrogNodeEvaluator.getStart missing minor: frogs use the plain ground navigation, so a frog in water does not start its path at the box corner +Frog.FrogNodeEvaluator.getPathType missing minor: blocks above #frog_prefer_jump_to (lily pad, dripleaf) are not treated as open path nodes +Frog.FrogPathNavigation.canCutCorner missing minor: no frog navigation, so water-border nodes allow cutting corners +Frog.FrogPathNavigation.createPathFinder missing player: frogs path as plain walkers (NavKind::Ground, no water malus 4 or trapdoor block); vanilla frogs use amphibious pathing with a shallow-water preference +FrogAi.initCoreActivity inline crates/horizon-client/src/mobs/frog.rs::register gap: minor: Panic runs at 1.25 where AnimalPanic uses 2.0; look and move sinks are the controls +FrogAi.initIdleActivity inline crates/horizon-client/src/mobs/frog.rs::register gap: player: Croak, stroll and look at player (6 blocks) are there; no slimeball tempt, no mate search and no TryFindLand +FrogAi.initSwimActivity inline crates/horizon-client/src/mobs/frog.rs::register gap: minor: no separate swim activity: Float and the land stroll stand in, no random swim at 0.75 or find-land +FrogAi.initLaySpawnActivity missing player: pregnant frogs never walk to water and lay frogspawn (no pregnancy, no TryLaySpawnOnFluidNearLand) +FrogAi.initJumpActivity missing player: frogs never long jump (no leap onto lily pads or big dripleaf) +FrogAi.initTongueActivity inline crates/horizon-client/src/mobs/frog.rs::register ShootTongue goal at priority 2 with the attack-target checks inside it +FrogAi.isAcceptableLandingSpot missing minor: landing test of the missing frog long jump +FrogVariants.bootstrap skip:data variant registry; the biome to variant choice is BiomeFacts.frog +FrogVariants.register skip:data registry registration +ShootTongue.checkExtraStartConditions inline crates/horizon-client/src/mobs/frog.rs::ShootTongue::can_use path check with the unreachable memory, not croaking; the sensor only offers edible targets +ShootTongue.canStillUse inline crates/horizon-client/src/mobs/frog.rs::ShootTongue::can_continue phase not done, no panic, target alive, 100 tick timeout +ShootTongue.eatEntity inline crates/horizon-client/src/mobs/frog.rs::ShootTongue::tick CatchAnimation arm: frog eat sound at 2.0, then the frog's attack on the target +ShootTongue.canPathfindToTarget inline crates/horizon-client/src/mobs/frog.rs::ShootTongue::can_use a path to the target that ends within 1.75 blocks +ShootTongue.addUnreachableTargetToMemory inline crates/horizon-client/src/mobs/frog.rs::ShootTongue::can_use up to 5 ids kept 100 ticks in Frog.State.unreachable +Tadpole.customServerAiStep inline crates/horizon-client/src/mobs/aquatic.rs::register gap: player: panic and swim-around goals stand in for the brain; tadpoles do not follow a player holding a slimeball and do not look at players +Tadpole.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt Age and AgeLocked +Tadpole.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt Age and AgeLocked +Tadpole.setAgeLockedData missing player: a golden dandelion on a tadpole does nothing (no age lock use anywhere in the game) +Tadpole.saveToBucketTag port crates/horizon-client/src/entity_nbt.rs::bucket_data Age and AgeLocked for axolotls and tadpoles +Tadpole.loadFromBucketTag port crates/horizon-client/src/entity_nbt.rs::load_bucket_data Age and AgeLocked +Tadpole.ageUp inline crates/horizon-client/src/mobs/aquatic.rs::ai_step gap: player: at 24000 the frog is spawned fresh with yaw and persistence only (SpawnSetup), so a named, silenced or glowing tadpole turns into a plain frog; the grow-up sound is 1.0 loud, not 0.15 +TadpoleAi.initCoreActivity inline crates/horizon-client/src/mobs/aquatic.rs::register panic at 2.0 as a goal +TadpoleAi.initIdleActivity inline crates/horizon-client/src/mobs/aquatic.rs::register gap: player: swims around with speed 1.0 every 40 ticks (vanilla 0.5); no look at player, no slimeball tempt +Goat.createHorn missing player: a horn dropped by a ramming goat is always the default ponder horn (no random instrument from the goat's uuid, regular or screaming set) and drops with a flat upward push +Goat.createAttributes skip:data attributes come from entity-types.json +Goat.ageBoundaryReached port crates/horizon-client/src/mobs/finalize.rs::age_boundary_reached attack damage 1 for a baby, 2 for an adult +Goat.getBreedOffspring missing player: a baby goat is never screaming (not inherited from a parent, no 2 percent roll on the breeding path) and starts with no ram or long jump cooldown +Goat.customServerAiStep inline crates/horizon-client/src/mobs/ai.rs::generic goals stand in for the brain: Float, Panic 2.0, Breed, Tempt, FollowParent, stroll and look (ai.rs generic) and the ram goal (goat.rs); the ram cooldown counts down in goat.rs::tick +Goat.setYHeadRot missing minor: the head is not clamped to 15 degrees of the body on every set; only walking and body turning limit it (control.rs) +Goat.playEatingSound port crates/horizon-client/src/mobs/ai.rs::eating_sound screaming or normal eat sound at 0.8 to 1.2 pitch +Goat.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize gap: player: live spawns use spawning.rs::finalize, which has no goat arm: no screaming goats (2 percent), no one-horned goats (10 percent), and no starting ram cooldown, so a fresh goat can ram at once; Ctx::finalize only runs for structure mobs and the probe +Goat.getDefaultDimensions port crates/horizon-client/src/mobs/living.rs::Mob::dims baby box (0.45 x 0.65, eye 0.59375) from the species table; no long jump pose scale since there is no long jump +Goat.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt IsScreamingGoat, HasLeftHorn, HasRightHorn +Goat.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt IsScreamingGoat and both horn flags (default true) +Goat.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: minor: the two horn flags are synced; the screaming flag is not +Goat.checkGoatSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules goats_spawnable_on below and bright enough +GoatAi.initMemories port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize gap: player: the long jump (600 to 1200) and ram (600 to 6000) cooldowns are only set by Ctx::finalize (structure mobs, probe); live spawns and babies start at zero, so goats ram right after spawning +GoatAi.initCoreActivity inline crates/horizon-client/src/mobs/ai.rs::generic Float (swim), Panic 2.0 and the cooldown count in goat.rs::tick +GoatAi.initIdleActivity inline crates/horizon-client/src/mobs/ai.rs::generic Breed, Tempt 1.25 with goat_food, FollowParent 1.25, stroll, look at player 6; no walk-to-look-target or do-nothing options +GoatAi.initLongJumpActivity missing player: goats never long jump between ledges (no crouch and leap) +GoatAi.initRamActivity port crates/horizon-client/src/mobs/goat.rs::register the Ram goal: prepare four to seven blocks away, wait 20 ticks head down, charge, knock back or break a horn +CopperGolem.createAttributes skip:data attributes come from entity-types.json +CopperGolem.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta weather state and golem state +CopperGolem.canTurnToStatue inline crates/horizon-client/src/mobs/copper_golem.rs::update_weathering oxidized, standing in air, 0.0058 chance per tick on the level random +CopperGolem.playHeadSpinSound port crates/horizon-client/src/mobs/copper_golem.rs::ai_step gap: minor: local spin sound by oxidation level, but a silent golem still plays it +CopperGolem.getLeashOffset missing minor: a lead hangs at the default offset, not 0.75 of the eye height with no forward shift +CopperGolemAi.initCoreActivity port crates/horizon-client/src/mobs/animal_brain.rs::copper_golem_package gap: player: panic 1.5, look sink, move sink and the two countdowns are there; InteractWithDoor is not, so a golem's path through a closed wooden door ends at the door +CopperGolemAi.initIdleActivity port crates/horizon-client/src/mobs/animal_brain.rs::copper_golem_package transport between chests, look at player, then stroll (2 blocks) or do nothing while the transport cooldown runs +CopperGolemAi.getTargetReachedInteractions inline crates/horizon-client/src/mobs/transport_items.rs::reached_interaction pickup, pickup nothing, place, place nothing from what the chest holds and what the golem carries +CopperGolemAi.onReachedTargetInteraction inline crates/horizon-client/src/mobs/transport_items.rs::Transport::on_reached tick 1 opens the chest and sets the state, tick 9 plays the sound, tick 60 closes it +IronGolem.registerGoals port crates/horizon-client/src/mobs/golem.rs::register_iron gap: player: no MoveBackToVillageGoal and no village-bound stroll (plain stroll 0.6), so golems wander off from their village; no ResetUniversalAngerTargetGoal +IronGolem.createAttributes skip:data attributes come from entity-types.json +IronGolem.doPush missing minor: a hostile mob that bumps the golem is not picked as its target one time in 20 (the golem still finds enemies by its nearest-target goal) +IronGolem.canSpawnSprintParticle missing player: a walking iron golem kicks up no dust of the block under its feet (sprint dust is drawn for players only, client_ambient.rs) +IronGolem.canAttack missing player: a golem the player built can still attack the player after being hit (player-created is not tracked) +IronGolem.addAdditionalSaveData missing minor: PlayerCreated and the persistent anger are not written from the live mob +IronGolem.readAdditionalSaveData missing minor: PlayerCreated and the persistent anger are not read +IronGolem.hurtServer missing player: no entity.iron_golem.damage crack sound when damage moves the golem to a new crackiness level (the crack texture does change, mobs/golem.rs::crackiness) +IronGolem.isPlayerCreated missing player: the player-created flag does not exist, so every golem acts like a village golem toward the player +IronGolem.setPlayerCreated missing player: building a golem from iron blocks does not mark it player-created (game/golems.rs create_triggered) +IronGolem.getLeashOffset missing minor: a lead hangs at the default offset, not 0.875 of the eye height +SnowGolem.registerGoals port crates/horizon-client/src/mobs/golem.rs::register_snow gap: minor: stroll uses the 0.001 water-avoiding probability, vanilla 1.0000001E-5 +SnowGolem.shear port crates/horizon-client/src/game/creatures.rs::Game::interact_shear_mob shear sound, pumpkin off, shearing loot table at eye height +SnowGolem.hasPumpkin inline crates/horizon-client/src/game/creatures.rs::Game::interact_shear_mob extra.sheared is the inverse; synced as DATA_PUMPKIN_ID in lan.rs and remote_mob_data.rs +SnowGolem.setPumpkin inline crates/horizon-client/src/game/creatures.rs::Game::interact_shear_mob sets extra.sheared (shears, dispenser); nothing puts a pumpkin back, as in vanilla +SnowGolem.getLeashOffset missing minor: a lead hangs at the default offset, not 0.75 of the eye height +HappyGhast.setServerStillTimeout inline crates/horizon-client/src/mobs/happy_ghast.rs::still_tick gap: minor: the timeout is extra.counter (10 when a player stands on top, 10 on dismount, 0 on boarding); the one-off position sync to tracking players is not sent, lan.rs only marks the entity precise +HappyGhast.registerGoals port crates/horizon-client/src/mobs/happy_ghast.rs::setup adult goals: float, tempt (harness and food for a bare adult, food otherwise), float around within 16 +HappyGhast.adultGhastSetup port crates/horizon-client/src/mobs/happy_ghast.rs::setup adult half: ghast move control, happy ghast look control, goals, brain stopped +HappyGhast.babyGhastSetup port crates/horizon-client/src/mobs/happy_ghast.rs::setup baby half: flying move control, baby flying navigation, no goals, timeout cleared +HappyGhast.ageBoundaryReached port crates/horizon-client/src/mobs/happy_ghast.rs::ensure_setup reruns setup when the age sign changes +HappyGhast.createAttributes skip:data attributes come from entity-types.json +HappyGhast.getAmbientSoundInterval port crates/horizon-client/src/mobs.rs::Mob::ambient_interval six times as long while it carries a rider +HappyGhast.canUseSlot inline crates/horizon-client/src/game/ghasts.rs::Game::interact_happy_ghast a harness goes only on a living adult without one; shears take it off +HappyGhast.addPassenger inline crates/horizon-client/src/game/ghasts.rs::Game::interact_happy_ghast gap: minor: goggles-down sound for the first rider and the still timeout reset; only one rider fits (vanilla four) and the timeout is always 0 +HappyGhast.getControllingPassenger port crates/horizon-client/src/mobs/happy_ghast.rs::controlled gap: minor: harnessed adult with a rider; the not-on-still-timeout test is missing +HappyGhast.tickRidden port crates/horizon-client/src/mobs/happy_ghast.rs::ridden turns 8 percent toward the rider's yaw, pitch half of theirs, head and body follow +HappyGhast.getHappyGhastRestrictionRadius port crates/horizon-client/src/mobs/happy_ghast.rs::restriction_radius 64 for a bare adult, 32 otherwise +HappyGhast.checkRestriction inline crates/horizon-client/src/mobs/happy_ghast.rs::ai_step home set to the block when unleashed and unridden and stale or the radius changed +HappyGhast.continuousHeal inline crates/horizon-client/src/mobs/happy_ghast.rs::ai_step gap: minor: one point every 600 ticks, every 20 in precipitation; the in-clouds fast heal is missing +HappyGhast.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: minor: STAYS_STILL is synced; IS_LEASH_HOLDER is not tracked (no leashHolderTime), so the harness rope layer is never drawn +HappyGhast.syncStayStillFlag port crates/horizon-client/src/game/lan.rs::mob_meta STAYS_STILL shown while the timeout is above zero (on_still_timeout) +HappyGhastAi.initCoreActivity port crates/horizon-client/src/mobs/animal_brain.rs::ghastling_package swim, flying panic 2.0, look sink, move sink and the tempt cooldown countdown +HappyGhastAi.initIdleActivity port crates/horizon-client/src/mobs/animal_brain.rs::ghastling_package tempt with look in the eyes, follow a player, follow an adult, then fly stroll or walk to the look target diff --git a/tools/port_map/game-013.tsv b/tools/port_map/game-013.tsv new file mode 100644 index 0000000000..1394402128 --- /dev/null +++ b/tools/port_map/game-013.tsv @@ -0,0 +1,147 @@ +AbstractNautilus.createAttributes skip:data attributes come from the entity type table (MobType in mobs.rs) +AbstractNautilus.checkNautilusSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::Nautilus arm: sea level minus 25 to minus 5, water below and above; only the nautilus has a placement (the zombie nautilus has none of its own) +AbstractNautilus.canUseSlot port crates/horizon-client/src/game/dispenser.rs::mob_can_use_slot saddle and body slots need an alive, grown, tame nautilus +AbstractNautilus.canDispenserEquipIntoSlot port crates/horizon-client/src/game/dispenser.rs::mob_can_dispenser_equip body and saddle always, other slots only with pick_up_loot +AbstractNautilus.getControllingPassenger port crates/horizon-client/src/mobs/nautilus.rs::controlled a rider on a saddled nautilus +AbstractNautilus.getRiddenSpeed inline crates/horizon-client/src/mobs/nautilus.rs::ridden 0.0325 in water, 0.02 on land, times the movement speed +AbstractNautilus.doPlayerRide inline crates/horizon-client/src/game/nautili.rs::Game::interact_nautilus the player mounts it and a lead breaks; there is no home to clear +AbstractNautilus.getNautilusRestrictionRadius missing player: a tame nautilus is never kept near its home, so it wanders off instead of staying within 32 blocks (16 for a baby or a saddled one) +AbstractNautilus.checkRestriction missing player: no home is set for a tame nautilus (ai.home is only set by leads and happy ghasts), see getNautilusRestrictionRadius +AbstractNautilus.applyEffects inline crates/horizon-client/src/mobs/nautilus.rs::ai_step the rider gets breath of the nautilus for 60 ticks, refreshed every 40 ticks +AbstractNautilus.onPlayerJump inline crates/horizon-client/src/game/mounts.rs::Game::charge_mount_jump the charged jump scale (0.4 to 1.0) goes to the rider input; the dash itself waits for its cooldown in nautilus::ridden +AbstractNautilus.executeRidersJump port crates/horizon-client/src/mobs/nautilus.rs::dash gap: minor: the push leaves out getBlockSpeedFactor (soul sand, honey); 1.2 in water, 0.5 on land, 40 tick cooldown, dashing flag +AbstractNautilus.handleStartJump inline crates/horizon-client/src/mobs/nautilus.rs::ai_step the dash sound plays on the tick after the dash (scratch[1]); no game event +AbstractNautilus.onSyncedDataUpdated skip:other client mirror of the dash flag; the nautilus keeps its own dash clock (anim_ticks) and the jump bar uses mount.jump_ticks +AbstractNautilus.tryToTame inline crates/horizon-client/src/game/nautili.rs::Game::interact_nautilus one chance in three, taming particles (event 7 or 6), eating sound, tame_animal trigger +AbstractNautilus.hurtServer port crates/horizon-client/src/mobs/nautilus.rs::after_hurt angry at the attacker for 400 ticks +AbstractNautilus.finalizeSpawn port crates/horizon-client/src/mobs/nautilus.rs::finalize attack target cooldown of 2400 to 3600 ticks +AbstractNautilus.getEquipSound port crates/horizon-client/src/mobs/nautilus.rs::saddle_sound underwater saddle sound while the eyes are in water +AbstractNautilus.createInventory skip:other getInventoryColumns is 0, so the container has no slots; saddle and armour are equipment slots +AbstractNautilus.openCustomInventoryScreen missing player: shift-use on a tame nautilus or the inventory key while riding opens no screen (has_mount_menu is horses and llamas only), so a saddle or armour put on cannot be taken off again +AbstractNautilus.getSlot skip:other slots 500 and up address the container, which has no slots; every other slot is an equipment slot +AbstractNautilus.isAggravated missing minor: only feeds canBeLeashed; a charging or angry nautilus can still be leashed (leash.rs::can_be_leashed has no nautilus arm) +Nautilus.customServerAiStep inline crates/horizon-client/src/mobs/nautilus.rs::register the goal list replaces the brain and its fight or idle switch; the cooldowns tick in ai_step +Nautilus.getAmbientSound port crates/horizon-client/src/mobs/nautilus.rs::sounds by age and by under water or on land +Nautilus.getHurtSound port crates/horizon-client/src/mobs/nautilus.rs::sounds by age and by under water or on land +Nautilus.getDeathSound port crates/horizon-client/src/mobs/nautilus.rs::sounds by age and by under water or on land +Nautilus.playEatingSound port crates/horizon-client/src/mobs/nautilus.rs::eat_sound baby and adult eat sounds +Nautilus.baseTick inline crates/horizon-client/src/mobs/ai.rs::base_tick gap: minor: dries out on land through the generic needs_water air supply, so the damage is the drown kind instead of dry_out and a no-AI nautilus dries out too +NautilusAi.initCoreActivity inline crates/horizon-client/src/mobs/nautilus.rs::register panic 1.6 is a goal; the look and move sinks are the generic controls and the cooldown counters tick in ai_step +NautilusAi.initIdleActivity inline crates/horizon-client/src/mobs/nautilus.rs::register gap: minor: breed 0.4, tempt 1.3, swim stroll 1.0 and the target pick (find_target), but SetWalkTargetFromLookTarget is not ported so it does not swim toward what it looks at +NautilusAi.initFightActivity inline crates/horizon-client/src/mobs/nautilus.rs::register the Charge goal: speed 0.6, 12 block range, 80 tick cooldown, only without love or temptation +NautilusAi.isHostileTarget inline crates/horizon-client/src/mobs/nautilus.rs::find_target a pufferfish in water (the nautilus_hostiles tag holds only the pufferfish) +ZombieNautilus.defineSynchedData skip:other synched variant id; Horizon keeps the variant in extra.variant and reads it from the server's data in remote_mob_data.rs +ZombieNautilus.get skip:other reads the variant as a data component for predicates; Horizon has no entity component view +ZombieNautilus.applyImplicitComponent missing minor: a spawn egg's zombie_nautilus_variant component is not applied (only bucketed mobs read item components) +ZombieNautilus.finalizeSpawn port crates/horizon-client/src/mobs/nautilus.rs::finalize warm variant in warm oceans (coral), temperate elsewhere +ZombieNautilusAi.initCoreActivity inline crates/horizon-client/src/mobs/nautilus.rs::register zombie arm: no panic; the cooldown counters tick in ai_step +ZombieNautilusAi.initIdleActivity inline crates/horizon-client/src/mobs/nautilus.rs::register zombie arm: tempt 0.9, swim stroll 1.0, target pick in find_target; no breeding +ZombieNautilusAi.initFightActivity inline crates/horizon-client/src/mobs/nautilus.rs::register zombie arm: Charge goal at speed 0.5 +ZombieNautilusVariants.bootstrap skip:data variant registry; Horizon has VARIANTS in nautilus.rs and the biome tag in spawning.rs +Panda.setMainGene skip:other stores the gene id in extra.variant; the random re-roll for an id above 6 never happens +Panda.setHiddenGene skip:other stores the gene id in extra.variant2; the random re-roll for an id above 6 never happens +Panda.defineSynchedData skip:other synched flags, counters and genes; Horizon keeps them in the panda State and extra.variant, extra.variant2 +Panda.getFlag port crates/horizon-client/src/mobs/panda.rs::flag reads the DATA_ID_FLAGS bit (set_flag writes it) +Panda.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt MainGene and HiddenGene by name +Panda.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt MainGene and HiddenGene by name, normal when absent +Panda.addEatingParticles inline crates/horizon-client/src/mobs/panda.rs::handle_eating eat sound every 5 ticks and the six crumbs (crumbs) +Panda.updateSitAmount inline crates/horizon-client/src/mobs/panda.rs::update_amounts sit amount rises 0.15 and falls 0.19 a tick +Panda.updateOnBackAnimation inline crates/horizon-client/src/mobs/panda.rs::update_amounts on-back amount, same steps +Panda.updateRollAmount inline crates/horizon-client/src/mobs/panda.rs::update_amounts roll amount, same steps +Panda.pickUpItem inline crates/horizon-client/src/game/pandas.rs::Game::tick_pandas takes a panda_eats_from_ground item at its feet with an empty mouth (mob_griefing), guaranteed drop +Panda.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize random main and hidden genes, set_attributes, ageable group with 0.2 chance +Panda.setGeneFromParents port crates/horizon-client/src/mobs/panda.rs::inherit genes from both parents with a 1 in 32 re-roll each; the one-parent branch is never reached +Panda.PandaAttackGoal.canUse port crates/horizon-client/src/mobs/panda.rs::When::can_use melee attack only while can_perform_action +Panda.PandaAvoidGoal.canUse port crates/horizon-client/src/mobs/panda.rs::When::can_use avoid goals only for a worried panda that can perform an action +Parrot.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize random one of five variants, ageable group without babies +Parrot.createAttributes skip:data attributes (health 6, flying speed 0.4) come from the entity type table +Parrot.createNavigation missing player: parrots have no flying navigation or flying move control, so they walk and hop on the ground and never fly to where a path through the air would lead +Parrot.calculateFlapping missing player: flap and flap_speed are only updated for chickens, so a parrot's wings never flap and a falling one does not glide (no 0.6 fall damping) +Parrot.checkParrotSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::Parrot arm: parrots_spawnable_on below and bright enough +Parrot.onFlap missing player: no parrot wing sound while flying (entity.parrot.fly at 0.15), no next_flap tracking +Parrot.hurtServer missing player: a hit does not stand a sitting tame parrot up (only wolves reset sitting in tame::after_hurt) +Parrot.get skip:other reads the variant as a data component for predicates; Horizon has no entity component view +Parrot.applyImplicitComponent missing minor: a spawn egg's parrot_variant component is not applied (only bucketed mobs read item components) +Parrot.defineSynchedData skip:other synched variant id; Horizon keeps it in extra.variant (remote_mob_data.rs reads index 20) +Parrot.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::read_variant Variant clamped to 0 to 4, red and blue when absent +Parrot.getLeashOffset port crates/horizon-client/src/mobs/leash.rs::leash_offset gap: minor: no parrot arm, the lead attaches at the full eye height instead of 0.5 times it +Parrot.ParrotWanderGoal.getPosition missing player: parrots wander with a ground stroll (Stroll::avoiding_water in tame.rs::register_parrot), so they never pick a flying position or one of the tree perches +Parrot.ParrotWanderGoal.getTreePos missing player: parrots never fly to settle on leaves or logs nearby +ShoulderRidingEntity.setEntityOnShoulder port crates/horizon-client/src/game/shoulders.rs::Game::parrot_to_shoulder saved into the first free ShoulderEntityLeft or ShoulderEntityRight slot, the parrot removed from the world +Pig.registerGoals port crates/horizon-client/src/mobs/farm.rs::register Species::Pig arm: float, panic 1.25, breed, two temptations (stick and food), follow parent, stroll, look +Pig.getControllingPassenger port crates/horizon-client/src/mobs/equine.rs::controlled saddled and the rider holds the carrot on a stick (r.steer) +Pig.onSyncedDataUpdated missing minor: a server's pig does not start its client boost timer from the synched boost time (remote.rs reads only the strider's flag there) +Pig.defineSynchedData skip:other synched boost time, variant and sound variant; Horizon keeps them in extra.counter, extra.counter2, extra.variant and extra.sound_variant +Pig.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt variant, sound_variant and the saddle in equipment +Pig.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt variant, sound_variant and the saddle +Pig.mobInteract port crates/horizon-client/src/game/mounts.rs::Game::interact_steerable gap: player: a saddled pig is mounted even when the click holds its food, so it cannot be fed or bred while saddled (vanilla feeds first) +Pig.canUseSlot port crates/horizon-client/src/game/dispenser.rs::mob_can_use_slot the saddle needs an alive, grown pig +Pig.canDispenserEquipIntoSlot port crates/horizon-client/src/game/dispenser.rs::mob_can_dispenser_equip the saddle always +Pig.getEquipSound port crates/horizon-client/src/game/dispenser.rs::equip_sound entity.pig.saddle +Pig.thunderHit port crates/horizon-client/src/game/lightning.rs::Game::thunder_hit a pig outside peaceful becomes a zombified piglin +Pig.tickRidden inline crates/horizon-client/src/mobs/equine.rs::ridden_step turns to the rider's yaw, pitch halved, and ticks the boost +Pig.getRiddenSpeed inline crates/horizon-client/src/mobs/equine.rs::ridden_step movement speed times 0.225 times the boost factor +Pig.getBreedOffspring port crates/horizon-client/src/game/creatures.rs::Game::breed gap: minor: the baby always takes the first parent's variant, not either parent's by a coin +Pig.getLeashOffset port crates/horizon-client/src/mobs/leash.rs::leash_offset gap: minor: no pig arm, the lead attaches at the full eye height instead of 0.6 times it +Pig.getSoundSet inline crates/horizon-client/src/mobs/ai.rs::voice_sounds gap: player: only the baby or adult classic sounds are used, so mini and big sound variants (chosen and saved) never sound different +Pig.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize variant by biome tag and a random sound variant +PigSoundVariants.bootstrap skip:data sound variant registry; names come from finalize::sound_variants +PigVariants.bootstrap skip:data variant registry; the variants and their biome tags are read from data +PigVariants.register skip:data registry helper +PolarBear.createAttributes skip:data attributes come from the entity type table +PolarBear.checkPolarBearSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::PolarBear arm: ice-block rule in the alternate biomes, animal rule elsewhere +PolarBear.getDefaultDimensions missing minor: the box of a rearing bear stays its normal height (vanilla grows it up to twice with the stand animation); Mob::dims has no polar bear arm +PolarBear.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize ageable group with chance 1.0 (the PolarBear and Ocelot arm) +Rabbit.getJumpPower port crates/horizon-client/src/mobs/rabbit.rs::jump_scale 0.5 toward higher ground or a wall, 0.2 when slow, else 0.3, divided by 0.42 (jump_power adds the block factor and boost) +Rabbit.getJumpCompletion skip:other only copied into the render state; the rabbit model plays the hop and idle keyframe animations instead +Rabbit.setSpeedModifier inline crates/horizon-client/src/mobs/rabbit.rs::prepare_move speed modifier is zeroed on the ground and restored in flight; 1.5 in water +Rabbit.setJumping inline crates/horizon-client/src/mobs/rabbit.rs::start_jumping the jump sound with pitch times 0.8 plays when the hop starts +Rabbit.defineSynchedData skip:other synched variant id; Horizon keeps it in extra.variant (remote_mob_data.rs reads index 18) +Rabbit.facePoint inline crates/horizon-client/src/mobs/rabbit.rs::server_ai_step yaw set toward the wanted or next path point before each hop +Rabbit.setLandingDelay inline crates/horizon-client/src/mobs/rabbit.rs::server_ai_step 10 ticks, 3 when the speed modifier is 2.2 or more +Rabbit.createAttributes skip:data attributes come from the entity type table +Rabbit.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt RabbitType; MoreCarrotTicks is kept at 0 +Rabbit.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt gap: minor: RabbitType (0 to 5 and 99) is read but MoreCarrotTicks is ignored +Rabbit.playAttackSound missing minor: only the killer bunny plays it and Horizon has no killer bunny behaviour +Rabbit.getBreedOffspring port crates/horizon-client/src/game/creatures.rs::Game::breed gap: minor: the baby always takes the first parent's coat; vanilla takes either parent's and one time in 20 rolls a biome coat +Rabbit.setVariant inline crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize gap: player: variant 99 (killer bunny, from /summon or a save) only changes the texture: no armour 8, no plus 5 attack, no melee or target goals, no Killer Bunny name +Rabbit.get skip:other reads the variant as a data component for predicates; Horizon has no entity component view +Rabbit.applyImplicitComponent missing minor: a spawn egg's rabbit variant component is not applied (only bucketed mobs read item components) +Rabbit.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize variant by biome, shared across a group +Rabbit.getRandomRabbitVariant inline crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize white and white splotched in snowy biomes, gold in deserts, else brown, salt or black +Rabbit.checkRabbitSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::Rabbit arm: rabbits_spawnable_on below and bright enough +Rabbit.getLeashOffset port crates/horizon-client/src/mobs/leash.rs::leash_offset gap: minor: no rabbit arm, the lead attaches at the full eye height instead of 0.6 times it +Rabbit.shouldPlayIdleAnimation inline crates/horizon-client/src/mobs/rabbit.rs::base_tick idle timeout over, not leashed, not no-AI +Rabbit.RabbitAvoidEntityGoal.canUse missing player: rabbits never run from players (8 blocks), wolves (10) or monsters (4); no avoid goal is registered for them +Rabbit.RaidGardenGoal.canUse missing player: rabbits never walk to ripe carrot crops to eat them; no MoreCarrotTicks clock +Rabbit.RaidGardenGoal.tick missing player: a rabbit never eats carrots, so ripe carrots are never knocked back or destroyed by rabbits +Rabbit.RaidGardenGoal.isValidTarget missing player: part of the missing carrot raid (a ripe carrot on a crop-supporting block) +Sheep.registerGoals port crates/horizon-client/src/mobs/farm.rs::register Species::Sheep arm: float, panic, breed, tempt, follow parent, eat block, stroll, look +Sheep.aiStep inline crates/horizon-client/src/mobs/ai.rs::species_ai_step the eat animation counter (anim_ticks) counts down each tick +Sheep.handleEntityEvent missing player: on a server a sheep never lowers its head to eat grass, because entity event 10 is not handled in remote.rs (the integrated server sets anim_ticks itself) +Sheep.getHeadEatPositionScale inline crates/horizon-client/src/mobs/anim.rs::setup Anim::Sheep arm: head drops by 9 times the scale (halved for a lamb) +Sheep.getHeadEatAngleScale inline crates/horizon-client/src/mobs/anim.rs::setup Anim::Sheep arm: head tilt of pi/5 with the nibbling wobble +Sheep.mobInteract port crates/horizon-client/src/game/creatures.rs::Game::interact_mob shears drop the shearing loot and set sheared; a dye recolours an unsheared sheep +Sheep.setColor skip:other stores the 4-bit wool colour in extra.color +Sheep.get skip:other reads the colour as a data component for predicates; Horizon has no entity component view +Sheep.applyImplicitComponent missing minor: a spawn egg's sheep colour component is not applied (only bucketed mobs read item components) +Sheep.isSheared skip:other reads extra.sheared +Sheep.setSheared skip:other writes extra.sheared (shear, regrow and nbt set it directly) +Sheep.getBreedOffspring port crates/horizon-client/src/game/creatures.rs::Game::breed gap: player: a lamb always takes the first parent's wool colour; vanilla mixes the two (red and blue give purple) +Sheep.ate port crates/horizon-client/src/mobs/ai.rs::ate wool grows back and a lamb ages up by 60 seconds +Sheep.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize wool colour from the biome's climate (farm::sheep_color) +SheepColorSpawnRules.commonColors inline crates/horizon-client/src/mobs/farm.rs::sheep_color the 82 weight tail: the common colour, pink one time in 500 +SheepColorSpawnRules.getSheepColorConfiguration port crates/horizon-client/src/mobs/farm.rs::sheep_color the three climate tables; the climate itself is picked in Ctx::finalize from the warm and cold farm animal tags +SheepColorSpawnRules.weighted inline crates/horizon-client/src/mobs/farm.rs::sheep_color the weighted pick is the running subtraction over the list +Sniffer.defineSynchedData skip:other synched state and seed drop tick; Horizon keeps them in extra.variant and extra.ints[3] +Sniffer.onPathfindingStart inline crates/horizon-client/src/mobs/sniffer.rs::ai_step water is passable only while it is on fire or in water; reset each tick (the onPathfindingDone part) +Sniffer.canPlayDiggingSound inline crates/horizon-client/src/game/sniffers.rs::Game::tick_sniffer_effects gap: minor: the digging sound is a one-shot at the start of the dig (SnifferSoundInstance is not kept), so it is not cut off when the sniffer stops or gets a target +Sniffer.resetAnimations inline crates/horizon-client/src/mobs/sniffer.rs::animate all five animation states stop when the state changes +Sniffer.onScentingStart inline crates/horizon-client/src/mobs/animal_brain.rs::transition scenting sound, pitch 1.3 for a baby +Sniffer.onDiggingStart inline crates/horizon-client/src/mobs/animal_brain.rs::transition the seed drop is set 120 ticks ahead (ints[3]); the client sound starts from the animation in tick_sniffer_effects +Sniffer.dropSeed inline crates/horizon-client/src/mobs/sniffer.rs::ai_step gift loot sniffer_digging at the head block and the drop seed sound when the tick comes +Sniffer.storeExploredPosition inline crates/horizon-client/src/mobs/animal_brain.rs::B::do_stop the on-pos is put in front of the explored list, kept to 20 +Sniffer.jumpFromGround missing minor: no sniffer arm in Mob::jump_from_ground, so its hop gets no extra forward nudge (moveRelative 0.1 forward) when it moves slowly +Sniffer.mobInteract inline crates/horizon-client/src/mobs/ai.rs::eating_sound the eating sound plays when the food was taken (feed_animal) +Sniffer.playEatingSound port crates/horizon-client/src/mobs/ai.rs::eating_sound Species::Sniffer arm: entity.sniffer.eat with pitch 0.8 to 1.2 +Sniffer.playSearchingSound port crates/horizon-client/src/game/sniffers.rs::Game::tick_sniffer_effects searching sound every 20 ticks while searching, near any player diff --git a/tools/port_map/game2-001.tsv b/tools/port_map/game2-001.tsv new file mode 100644 index 0000000000..92da52b462 --- /dev/null +++ b/tools/port_map/game2-001.tsv @@ -0,0 +1,140 @@ +WorldGenRegion.getChunk port crates/horizon-world/src/worldgen/feature_region.rs::TerrainRegion::chunk gap: minor: returns the resident chunk without the generating-step status check +GossipContainer.getReputation port crates/horizon-client/src/gossip.rs::Gossips::reputation Villager calls it with all types only; the type predicate is fixed to all +Frog.travelInWater missing minor: frog water travel (getSpeed() move, 0.9 drag on all axes) absent; generic fluid travel used instead +HappyGhast.HappyGhastBodyRotationControl.clientTick port crates/horizon-client/src/mobs/happy_ghast.rs::ridden gap: minor: vehicle yaw sync only; the super.clientTick step is Mob::body_rotation_tick in control.rs +SnifferAi.initCoreActivity inline crates/horizon-client/src/mobs/animal_brain.rs::sniffer_package core behaviour list (Swim, panic, sink, cooldown) built here +SnifferAi.initSniffingActivity inline crates/horizon-client/src/mobs/animal_brain.rs::sniffer_package Searching entry here; memory gates in update_activity +SnifferAi.initDigActivity inline crates/horizon-client/src/mobs/animal_brain.rs::sniffer_package Digging and FinishedDigging entries here; memory gates in update_activity +SnifferAi.initIdleActivity inline crates/horizon-client/src/mobs/animal_brain.rs::sniffer_package idle entries (make love with reset, temptation, look sink, RunOne) here +SnifferAi.Digging.canStillUse port crates/horizon-client/src/mobs/animal_brain.rs::B::tick_or_stop +SnifferAi.Digging.stop port crates/horizon-client/src/mobs/animal_brain.rs::B::do_stop +SnifferAi.FinishedDigging.stop port crates/horizon-client/src/mobs/animal_brain.rs::B::do_stop +SnifferAi.Searching.canStillUse port crates/horizon-client/src/mobs/animal_brain.rs::B::tick_or_stop +SnifferAi.Searching.stop port crates/horizon-client/src/mobs/animal_brain.rs::B::do_stop +SnifferAi.Sniffing.stop port crates/horizon-client/src/mobs/animal_brain.rs::B::do_stop +Squid.registerGoals port crates/horizon-client/src/mobs/aquatic.rs::register +Squid.rotateVector inline crates/horizon-client/src/mobs/aquatic.rs::spawn_ink xRot then yRot of the body angles, inside spawn_ink +Squid.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize S::Squid arm: ageable group with 0.05 baby chance +Turtle.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta HAS_EGG and LAYING_EGG as entity metadata 18 and 19 +Turtle.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt home_pos and has_egg written here +Turtle.checkTurtleSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::Turtle arm: below sea+4, sand, bright +Turtle.createAttributes skip:data attribute values come from data tables (attr lookups in mobs.rs) +Turtle.getAmbientSound port crates/horizon-client/src/mobs/ai.rs::ambient_sound gap: player: turtles in water or babies also play the land ambient (no Turtle arm; vanilla is silent there) +Turtle.playStepSound port crates/horizon-client/src/mobs/ai.rs::step_sound shamble and shamble_baby at 0.15 volume +Turtle.travelInWater missing player: turtles swim with generic fluid travel (0.02 base speed, 0.8 vertical drag), not 0.1 move speed, 0.9 drag and the slow sink +Turtle.TurtleLayEggGoal.isValidTarget port crates/horizon-client/src/mobs/turtle.rs::LayEgg::valid air above and sand below +Wolf.registerGoals port crates/horizon-client/src/mobs/tame.rs::register +Wolf.getSoundSet missing player: wolves always use the classic voice; the sound variant (big, puglin, sad, angry...) never changes what plays +Wolf.createAttributes skip:data attribute values come from data tables +Wolf.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta flags, variant and anger metadata sent to clients +Wolf.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt CollarColor written +Wolf.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt CollarColor, sound_variant and anger read +Wolf.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize gap: minor: sound variant is stored but no sound uses it (see getSoundSet) +Wolf.getAmbientSound port crates/horizon-client/src/mobs/ai.rs::ambient_sound gap: minor: angry is taken as having a target, not the anger timer +Wolf.die missing minor: a wolf that dies mid-shake keeps its shake state; Horizon never resets the wet and shake fields on death +Wolf.getHeadRollAngle port crates/horizon-client/src/mobs/tame.rs::anim +Wolf.applyTamingSideEffects port crates/horizon-client/src/mobs/tame.rs::set_tame +Wolf.hurtArmor port crates/horizon-client/src/mobs.rs::Mob::hurt_wolf_armor +Wolf.tryToTame port crates/horizon-client/src/game/creatures.rs::Game::interact_mob one in three tames; particles 7 or 6, sit, stop nav, clear target +Wolf.handleEntityEvent inline crates/horizon-client/src/mobs/tame.rs::ai_step entity events 8 and 56 folded into the shake state in ai_step +Wolf.getLeashOffset port crates/horizon-client/src/mobs/leash.rs::leash_offset gap: player: wolf rope point uses full eye height, not 0.6 times it +Wolf.checkWolfSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::Wolf arm: wolves_spawnable_on and bright +Wolf.WolfAvoidEntityGoal.avoidLlama port crates/horizon-client/src/mobs/tame.rs::AvoidLlamas::can_use strength check inside can_use +WolfVariants.bootstrap skip:data registry bootstrap; variants are the WOLF_VARIANTS table in tame.rs +DragonFlightHistory.copyFrom skip:java ring-buffer copy; Horizon keeps the flight history as fields of Dragon +EndCrystal.defineSynchedData missing player: multiplayer guests never get a crystal's beam target, and the base is always shown (remote spawn ignores show_bottom) +EndCrystal.addAdditionalSaveData port crates/horizon-client/src/game/dragon.rs::Crystal::to_nbt +EndCrystal.readAdditionalSaveData port crates/horizon-client/src/game/dragon.rs::Crystal::from_nbt +EndCrystal.hurtClient missing unsure: client hit check that refuses dragon-sourced hits; not found in destroy_crystal +EndCrystal.shouldRenderAtSqrDistance skip:ui render culling so beams draw from farther away; drawing only +EnderDragon. port crates/horizon-client/src/game/dragon.rs::Dragon::new gap: minor: parts are placed later by Game::place_dragon_parts +EnderDragon.isFlapping port crates/horizon-client/src/game/dragon.rs::Game::tick_dragon +EnderDragon.onFlap port crates/horizon-client/src/game/dragon.rs::Game::tick_dragon flap sound at volume 5 and pitch 0.8 plus random 0.3 +EnderDragon.defineSynchedData port crates/horizon-client/src/game/dragon.rs::Game::lan_dragon_state DATA_PHASE defined as hovering on the wire +EnderDragon.tickPart port crates/horizon-client/src/game/dragon.rs::Game::place_dragon_parts +EnderDragon.getHeadYOffset inline crates/horizon-client/src/game/dragon.rs::Game::place_dragon_parts sitting gives -1, else flight sample 5 minus sample 0 +EnderDragon.knockBack port crates/horizon-client/src/game/dragon.rs::Game::dragon_contacts wing zones push players +EnderDragon.handleKillingBlow inline crates/horizon-client/src/game/dragon.rs::Dragon::hurt health set to 1 and dying phase unless sitting +EnderDragon.knockback missing minor: a hit does not push the dragon back outside the sitting phase +EnderDragon.findClosestNode port crates/horizon-client/src/game/dragon.rs::Game::closest_node +EnderDragon.findPath port crates/horizon-client/src/game/dragon.rs::Game::dragon_path +EnderDragon.reconstructPath inline crates/horizon-client/src/game/dragon.rs::Game::dragon_path path list built inside the A* search +EnderDragon.addAdditionalSaveData port crates/horizon-client/src/game/dragon.rs::Dragon::to_nbt +EnderDragon.readAdditionalSaveData port crates/horizon-client/src/game/dragon.rs::Dragon::from_nbt gap: minor: sitting_damage_received is not read back +EnderDragon.getHeadLookVector port crates/horizon-client/src/game/dragon.rs::Game::dragon_look +EnderDragon.onSyncedDataUpdated port crates/horizon-client/src/game/dragon.rs::Game::remote_dragon_data phase set on guests from synced data +EnderDragon.recreateFromPacket port crates/horizon-client/src/game/dragon.rs::Game::spawn_remote_dragon gap: minor: part entity ids are not assigned; parts are indexes +EnderDragonPart.hurtServer port crates/horizon-client/src/game/dragon.rs::Dragon::hurt hits route to the dragon with the part index +EnderDragonPart.is skip:other identity check; parts are SPECIAL+index ids in special_targets +DragonStrafePlayerPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Strafe arm: charge, sight, fireball, then holding pattern +DragonStrafePlayerPhase.findNewTarget port crates/horizon-client/src/game/dragon.rs::Game::ring_path +DragonStrafePlayerPhase.navigateToNextPathNode port crates/horizon-client/src/game/dragon.rs::Game::next_target +DragonStrafePlayerPhase.begin inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase fresh strafe state (charge 0, no path or target) when entered +EnderDragonPhaseManager.getPhase skip:java lazy cache of phase instances; Horizon phases are enum values +WitherBoss.createNavigation port crates/horizon-client/src/mobs/wither.rs::register gap: minor: door and float flags are not set here +WitherBoss.registerGoals port crates/horizon-client/src/mobs/wither.rs::register +WitherBoss.defineSynchedData port crates/horizon-client/src/game/lan.rs::mob_meta gap: minor: only heads A and B are synced; the invulnerable counter is sent at index 19, the slot of target C +WitherBoss.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt Invul read; boss bar name not checked here +WitherBoss.canDestroy inline crates/horizon-client/src/mobs/wither.rs::custom_server_ai_step air and wither-immune checks in the block loop +WitherBoss.getHeadX port crates/horizon-client/src/mobs/wither.rs::head_pos +WitherBoss.getHeadY port crates/horizon-client/src/mobs/wither.rs::head_pos +WitherBoss.getHeadZ port crates/horizon-client/src/mobs/wither.rs::head_pos +WitherBoss.rotlerp port crates/horizon-client/src/mobs/control.rs::rotlerp result wrapped into 0 to 360, equal modulo 360 +WitherBoss.performRangedAttack port crates/horizon-client/src/mobs/wither.rs::shoot +WitherBoss.createAttributes skip:data attribute values come from data tables +WitherBoss.isPowered port crates/horizon-client/src/mobs/wither.rs::powered +ArmorStand.refreshDimensions skip:other no cached dimensions: bounds are computed from position, small and marker +ArmorStand.defineSynchedData port crates/horizon-client/src/game/lan_entities.rs::Game::lan_object_states flag and pose values sent to clients; defaults are the DEFAULT_POSE table +ArmorStand.canUseSlot inline crates/horizon-client/src/game/armor_stands.rs::Game::interact_armor_stand hand slots gated by show_arms; gap: minor: body and saddle are never offered and disabled bits are not read +ArmorStand.pushEntities missing minor: armour stands do not push nearby minecarts +ArmorStand.interact port crates/horizon-client/src/game/armor_stands.rs::Game::interact_armor_stand +ArmorStand.getClickedSlot inline crates/horizon-client/src/game/armor_stands.rs::Game::interact_armor_stand click height picks feet, chest, legs, head or offhand +ArmorStand.isDisabled inline crates/horizon-client/src/game/armor_stands.rs::Game::interact_armor_stand gap: minor: disabled-slot bits are not read; hands gated by show_arms only +ArmorStand.handleEntityEvent inline crates/horizon-client/src/game/armor_stands.rs::Game::attack_armor_stand event 32 becomes the wobble timer and the hit sound +ArmorStand.showBreakingParticles missing player: no plank particles when a stand breaks, by a creative hand or the second hit +ArmorStand.causeDamage port crates/horizon-client/src/entities.rs::ArmorStand::environment_tick gap: minor: fire damage only (4 per second burning, 0.15 per contact); hit and explosion paths are elsewhere +ArmorStand.brokenByPlayer inline crates/horizon-client/src/game/armor_stands.rs::Game::attack_armor_stand stand item dropped, then the equipment +ArmorStand.brokenByAnything inline crates/horizon-client/src/game/armor_stands.rs::Game::attack_armor_stand gap: minor: vanishing-curse items are not filtered on the hand-break path, only on the burn path +ArmorStand.playBrokenSound inline crates/horizon-client/src/game/armor_stands.rs::Game::attack_armor_stand break sound on both break paths +ArmorStand.setYBodyRot skip:other client rotation setter from packets; a stand turns only with its yaw field +ArmorStand.setYHeadRot skip:other client rotation setter from packets; a stand turns only with its yaw field +ArmorStand.kill inline crates/horizon-client/src/game/armor_stands.rs::Game::attack_armor_stand removes the stand; no game event +ArmorStand.setSmall port crates/horizon-client/src/entity_nbt.rs::armor_stand_from_nbt Small tag +ArmorStand.isSmall skip:other accessor; reads the small field +ArmorStand.setShowArms port crates/horizon-client/src/entity_nbt.rs::armor_stand_from_nbt ShowArms tag +ArmorStand.showArms skip:other accessor; reads the show_arms field +ArmorStand.setNoBasePlate port crates/horizon-client/src/entity_nbt.rs::armor_stand_from_nbt NoBasePlate tag +ArmorStand.showBasePlate skip:other accessor; reads the no_base_plate field +ArmorStand.setMarker port crates/horizon-client/src/entity_nbt.rs::armor_stand_from_nbt Marker tag +ArmorStand.isMarker skip:other accessor; reads the marker field +ArmorStand.setBit skip:other packed-flag helper; Horizon keeps separate bools and packs them when sent +ArmorStand.skipAttackInteraction missing minor: adventure-mode protection on stand hits is not checked +ArmorStand.onSyncedDataUpdated skip:other client refresh when flags sync; bounds are computed on demand +ArmorStand.getDimensionsMarker inline crates/horizon-client/src/game/armor_stands.rs::Game::armor_stand_hit marker stands are skipped, so they have no box +ArmorStand.getLightProbePosition skip:ui render light probe for markers; drawing only +ArmorStand.setArmorStandPose port crates/horizon-client/src/entity_nbt.rs::armor_stand_from_nbt Pose tag read into the six pose parts +ArmorStand.getArmorStandPose port crates/horizon-client/src/entity_nbt.rs::armor_stand_to_nbt Pose tag written from the six pose parts +BlockAttachedEntity.skipAttackInteraction inline crates/horizon-client/src/game/cushions.rs::Game::attack_cushion the mayInteract gate and the break; frames and paintings keep their own copies +BlockAttachedEntity.kill inline crates/horizon-client/src/game/cushions.rs::Game::break_cushion removal and the break; no game event +BlockAttachedEntity.ignoreExplosion missing player: explosions do not break paintings, item frames, cushions or armour stands; only players, mobs, items, TNT and minecarts are hurt +BlockAttachedEntity.move missing minor: a piston push does not break a cushion, painting or frame here +BlockAttachedEntity.push missing minor: a push from a blast or piston does not break a cushion, painting or frame +BlockAttachedEntity.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::cushion_from_nbt gap: minor: no 16-block check on block_pos; the stored block is used as is +Cushion.isBreakingDeniedFor inline crates/horizon-client/src/game/cushions.rs::Game::attack_cushion the may_build gate +Cushion.isBreakingDeniedAtPosFor inline crates/horizon-client/src/game/cushions.rs::Game::attack_cushion the mayInteract gate, folded into the one may_build check +Cushion.tickAtCheckInterval inline crates/horizon-client/src/game/cushions.rs::Game::tick_cushions gap: minor: fluid effects not applied; fire and survival checked every 100 ticks +Cushion.thunderHit missing player: a lightning strike does not break a cushion +Cushion.setPos skip:other position field write; the block position is kept apart in cushion.pos +Cushion.canBePlacedAt port crates/horizon-client/src/cushion.rs::can_place +Cushion.wouldSurviveAt port crates/horizon-client/src/cushion.rs::survives +Cushion.hasAnchorBelow inline crates/horizon-client/src/cushion.rs::survives anchor scan under the box +Cushion.isCoveredBySuffocatingBlocks inline crates/horizon-client/src/cushion.rs::survives per-block suffocating test through PlacementWorld::suffocating +Cushion.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::cushion_from_nbt color tag read +Cushion.get port crates/horizon-client/src/game/pick_entity.rs::Game::entity_pick_result the pick gives the cushion item in its colour +Cushion.applyImplicitComponent port crates/horizon-client/src/game/cushions.rs::Game::place_cushion colour from the stack's cushion colour data +HangingEntity.onSyncedDataUpdated skip:other client resync of the facing; Horizon keeps the facing as a field +HangingEntity.setDirection skip:other facing field write; bounds are derived on demand from the facing +HangingEntity.recalculateBoundingBox skip:other bounds are derived on demand from the facing +HangingEntity.calculateSupportBox inline crates/horizon-client/src/game/paintings.rs::Game::painting_survives support box moved half a block into the wall +HangingEntity.canCoexist port crates/horizon-client/src/game/paintings.rs::Game::painting_survives gap: minor: only other paintings are checked for overlap; item frames and the same-facing rule are not +HangingEntity.hasLevelCollision port crates/horizon-client/src/game/paintings.rs::Game::painting_survives block collision of the box diff --git a/tools/port_map/game2-002.tsv b/tools/port_map/game2-002.tsv new file mode 100644 index 0000000000..85fd0f47c5 --- /dev/null +++ b/tools/port_map/game2-002.tsv @@ -0,0 +1,139 @@ +ItemFrame.defineSynchedData skip:other entity data defaults; ItemFrame starts with item None and rotation 0 in place_item_frame +ItemFrame.setDirection inline crates/horizon-client/src/game/frames.rs::Game::frame_geometry facing sets the pose in frame_geometry; the box comes from ItemFrame::bounds +ItemFrame.recalculateBoundingBox inline crates/horizon-client/src/entities.rs::ItemFrame::bounds boxes are computed on demand; the position sync codec is network-only +ItemFrame.shouldDamageDropItem missing player: explosions never reach item frames here, so a blast does not break a frame and drop its item as in vanilla +ItemFrame.canHurtWhenFixed missing minor: creative players cannot break a fixed frame (vanilla lets them); attack_frame returns early for fixed frames +ItemFrame.hurtClient inline crates/horizon-client/src/game/frames.rs::Game::attack_frame gap: minor: no separate client-side fixed-frame gate; the creative exception is missing +ItemFrame.removeFramedMap inline crates/horizon-client/src/game/maps.rs::Game::lan_map_updates frame map markers come from item_frames each tick, so a removed frame needs no step +ItemFrame.getSlot missing minor: no slot 0 access for item frames in the item command path +ItemFrame.onSyncedDataUpdated skip:other network data-sync hook; the item is read from the frame field on demand +ItemFrame.setRotation inline crates/horizon-client/src/game/frames.rs::Game::interact_frame the rotate-by-one step; the neighbour update is in frame_output_changed +ItemFrame.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::item_frame_from_nbt gap: minor: Facing defaults to 2 where vanilla defaults to DOWN +ItemFrame.getFrameItemStackWithData missing minor: a broken frame never carries its custom name; drop_frame_items builds a plain item_frame stack +ItemFrame.getVisualRotationYInDegrees inline crates/horizon-client/src/game/frames.rs::Game::frame_geometry the 45 degree step per rotation step is drawn in frame_geometry +LeashFenceKnotEntity.recalculateBoundingBox port crates/horizon-client/src/mobs/leash.rs::knot_state position and box from the block position +LeashFenceKnotEntity.getOrCreateKnot inline crates/horizon-client/src/game/leads.rs::Game::interact_knot knots are not entities in game; the knot is used when a lead is tied +LeashFenceKnotEntity.getKnot inline crates/horizon-client/src/game/leads.rs::Game::interact_knot the existing knot is the leashed-holder check at the fence position +LeashFenceKnotEntity.createKnot inline crates/horizon-client/src/game/leads.rs::Game::interact_knot no in-game knot entity is created; LAN knot ids are assigned in lan_assign_ids +Painting.defineSynchedData skip:other entity data default variant; Horizon picks the variant in place_painting +Painting.get port crates/horizon-client/src/game/paintings.rs::PaintingVariants::get lookup of the variant by id; there is no component getter +Painting.applyImplicitComponent missing minor: a painting variant component on the item is ignored; placement always picks a random fitting variant +Painting.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::painting_from_nbt +Painting.dropItem port crates/horizon-client/src/game/paintings.rs::Game::break_painting gap: minor: the dropped painting item keeps no custom name +PaintingVariants.register skip:data registry bootstrap; variants load from the data files in PaintingVariants::load +FallingBlockEntity.callOnBrokenAfterFall inline crates/horizon-client/src/game/falling_damage.rs::Game::finish_falling_block anvil 1029 and brushable 2001 events are here; gap: minor: no dripstone landing sound on a broken landing +FallingBlockEntity.causeFallDamage port crates/horizon-client/src/game/falling_damage.rs::Game::finish_falling_block gap: minor: pointed dripstone uses the generic falling_block damage kind +FallingBlockEntity.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::falling_block_to_nbt gap: minor: TileEntityData of the carried block is not written +FallingBlockEntity.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::falling_block_from_nbt gap: minor: TileEntityData is not read; damage settings come from falling_block::Damage::from_nbt +FallingBlockEntity.fillCrashReportCategory skip:debug crash report detail +FallingBlockEntity.getTypeName missing player: death texts for falling blocks cannot name the block (Falling Anvil) because the hit has no source entity +FallingBlockEntity.teleport missing unsure: no portal travel for falling blocks found in nether_portal.rs and no end-arrival tick +ItemEntity.restoreFrom skip:other the ItemEntity value moves between levels with thrower kept; nothing to copy +ItemEntity.setFluidMovement inline crates/horizon-client/src/entities.rs::ItemEntity::tick water and lava drag and buoyancy are applied in tick +ItemEntity.fireImmune inline crates/horizon-client/src/entities.rs::ItemEntity::environment_tick fire immunity via resists_fire in environment_tick +ItemEntity.hurtClient skip:other client-side hurt prediction; items are not attackable, so there is no client-side hurt path +ItemEntity.hurtServer inline crates/horizon-client/src/game/explosion.rs::Game::explosion_hurt_entities gap: minor: explosions destroy items at damage 5 without subtracting health; fire and lava hurt are in environment_tick +ItemEntity.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::item_entity_to_nbt +ItemEntity.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::item_entity_from_nbt a missing Item returns None, which matches the discard +ItemEntity.getName missing minor: a dropped item's name (custom or item name) is not used anywhere in Horizon +ItemEntity.teleport inline crates/horizon-client/src/entities.rs::merge_with_neighbours the arrival merge is the normal per-tick merge, not a teleport step +ItemEntity.getVisualRotationYInDegrees inline crates/horizon-client/src/game/item_draw.rs::Game::dropped_item_geometry spin is applied in radians there; no degree conversion +ItemEntity.getSlot missing unsure: no slot 0 access for dropped items found in commands.rs +PrimedTnt.defineSynchedData skip:other fuse and block state are struct fields; spawn sets fuse 80; the block is always TNT +PrimedTnt.explode inline crates/horizon-client/src/game/tnt.rs::Game::tick_tnt gap: minor: power fixed at 4; no explosion_power and no used-portal damage calculator +PrimedTnt.addAdditionalSaveData missing minor: primed TNT is not written to saves, so a lit TNT is lost on save and reload +PrimedTnt.restoreFrom missing minor: PrimedTnt has no owner field, so the igniter is not kept through portal moves +PrimedTnt.teleport missing unsure: no used-portal flag on PrimedTnt; the portal damage calculator is not applied +Blaze.registerGoals port crates/horizon-client/src/mobs/nether.rs::register_blaze gap: minor: MoveTowardsRestriction goal (priority 5) is not registered +Blaze.createAttributes skip:data attribute defaults come from data tables +Blaze.isCharged inline crates/horizon-client/src/mobs/nether.rs::BlazeAttack::tick the charged state is m.extra.flag, set in the attack goal tick +Blaze.setCharged inline crates/horizon-client/src/mobs/nether.rs::BlazeAttack::tick same flag, set when a charge starts and ends +Creeper.registerGoals port crates/horizon-client/src/mobs/hostile.rs::register +Creeper.getMaxFallDistance missing minor: the creeper's own comfortable fall distance is not used; nav uses the generic mob value +Creeper.defineSynchedData skip:other swell, powered and ignited are Mob fields +Creeper.addAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt gap: minor: Fuse and ExplosionRadius are written as constants 30 and 3, not the creeper's values +Creeper.thunderHit port crates/horizon-client/src/game/lightning.rs::Game::thunder_hit +Creeper.spawnLingeringCloud missing player: a creeper that explodes with potion effects leaves no lingering area cloud +ElderGuardian.createAttributes skip:data attribute defaults come from data tables +Enderman.registerGoals port crates/horizon-client/src/mobs/enderman.rs::register gap: minor: no anger-reset target goal (priority 4) +Enderman.createAttributes skip:data attribute defaults come from data tables +Enderman.setTarget inline crates/horizon-client/src/mobs/enderman.rs::custom_server_ai_step the creepy flag is set per tick there; the attack speed boost is attacking_speed +Enderman.defineSynchedData skip:other carried block, creepy and stared-at state are Mob extra fields +Enderman.playStareSound inline crates/horizon-client/src/mobs/enderman.rs::LookForPlayer::tick gap: minor: the 400-tick throttle of the stare sound is not seen +Enderman.onSyncedDataUpdated skip:other client sync hook; the stare sound is played server-side from LookForPlayer +Enderman.addAdditionalSaveData missing minor: the carried block is not written to saves +Enderman.readAdditionalSaveData missing minor: the carried block is not read from saves +Enderman.canRandomlyTeleportTo port crates/horizon-client/src/mobs/enderman.rs::teleport_to the water check on the ground block +Enderman.repeatedlyTryToTeleport port crates/horizon-client/src/mobs/enderman.rs::ai_step pending teleports retry up to 64 times +Enderman.dropCustomDeathLoot missing player: an Enderman killed while holding a block drops no block +Enderman.hurtWithCleanWater missing player: a thrown splash water potion does nothing to an Enderman; vanilla hurts it +Enderman.EndermanLeaveBlockGoal.canPlaceBlock inline crates/horizon-client/src/mobs/enderman.rs::LeaveBlock::tick gap: minor: the place-on blacklist tag and the carried block canSurvive check are not in the tick +Endermite.registerGoals port crates/horizon-client/src/mobs/ai.rs::register Hostile family defaults; gap: unsure: the ClimbOnTopOfPowderSnow goal is not checked +Endermite.createAttributes skip:data attribute defaults come from data tables +Endermite.checkEndermiteSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules the Endermite and Silverfish arm +Ghast.registerGoals port crates/horizon-client/src/mobs/nether.rs::register_ghast +Ghast.isInvulnerableTo inline crates/horizon-client/src/mobs.rs::Mob::damage_with reflected fireball and fire immunity checks are in damage_with +Ghast.createAttributes skip:data attribute defaults come from data tables +Ghast.checkGhastSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules +Ghast.RandomFloatAroundGoal.getSuitableFlyToPosition port crates/horizon-client/src/mobs/happy_ghast.rs::FloatAround::start gap: minor: plain Ghast uses one random point in RandomFloatAround::start; the 64-try search runs for happy ghasts here +Ghast.RandomFloatAroundGoal.isGoodTarget port crates/horizon-client/src/mobs/happy_ghast.rs::FloatAround::good +Ghast.RandomFloatAroundGoal.chooseRandomPosition inline crates/horizon-client/src/mobs/happy_ghast.rs::FloatAround::start the random point pick is in start +Ghast.RandomFloatAroundGoal.chooseRandomPositionWithRestriction inline crates/horizon-client/src/mobs/happy_ghast.rs::FloatAround::start the home check is in start; plain ghasts have no home +Guardian.registerGoals port crates/horizon-client/src/mobs/guardian.rs::register gap: minor: no MoveTowardsRestriction goal (5) and no look-at-guardian goal (8) +Guardian.createAttributes skip:data attribute defaults come from data tables +Guardian.defineSynchedData skip:other moving flag and attack target are Mob extra state read by beam +Guardian.hasActiveAttackTarget inline crates/horizon-client/src/mobs/guardian.rs::beam the beam is shown while the flag and target are set +Guardian.getActiveAttackTarget inline crates/horizon-client/src/mobs/guardian.rs::beam the target is m.ai.target +Guardian.checkGuardianSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules the Guardian and ElderGuardian arm +Guardian.GuardianAttackGoal.canContinueToUse port crates/horizon-client/src/mobs/guardian.rs::GuardianAttack::can_continue +Guardian.GuardianAttackSelector.test inline crates/horizon-client/src/mobs/guardian.rs::register the selector is the player, squid and axolotl beyond 9 filters in register +Monster.updateNoActionTime missing minor: noActionTime is not kept, so the light-driven idle timer that feeds despawn is absent +Monster.getProjectile missing unsure: no held-ammo choice for mobs found in mobs/*.rs +PatrollingMonster.addAdditionalSaveData port crates/horizon-client/src/mobs/raider.rs::write_nbt +PatrollingMonster.readAdditionalSaveData port crates/horizon-client/src/mobs/raider.rs::read_nbt +PatrollingMonster.checkPatrollingMonsterSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules the Pillager arm: block light 8 or less, then mob rules +PatrollingMonster.LongDistancePatrolGoal.findPatrolCompanions port crates/horizon-client/src/mobs/raider.rs::companions +Phantom.registerGoals port crates/horizon-client/src/mobs/phantom.rs::register +Phantom.updatePhantomSizeInfo missing minor: vanilla sets size only from NBT, and Horizon has no size, so no scale or attack bonus +Phantom.finalizeSpawn inline crates/horizon-client/src/mobs/phantom.rs::register the anchor 5 blocks above is set at creation; size 0 +Phantom.readAdditionalSaveData missing unsure: anchor_pos and size are not read for phantoms +Phantom.addAdditionalSaveData missing unsure: anchor_pos and size are not written for phantoms +Phantom.getDefaultDimensions missing minor: no size scale on the phantom box; size is 0 unless NBT sets it +Phantom.PhantomBodyRotationControl.clientTick inline crates/horizon-client/src/mobs/phantom.rs::move_control the head yaw follows the body yaw there +Phantom.PhantomMoveTargetGoal.touchingTarget inline crates/horizon-client/src/mobs/phantom.rs::CircleAroundAnchor::tick the squared distance under 4 check near the move target +Phantom.PhantomSweepAttackGoal.canContinueToUse port crates/horizon-client/src/mobs/phantom.rs::SweepAttack::can_continue the cat hiss and scare check +Ravager.registerGoals port crates/horizon-client/src/mobs/ravager.rs::register +Ravager.updateControlFlags missing unsure: no rider control-flag switch for ravagers in ravager.rs +Ravager.createAttributes skip:data attribute defaults come from data tables +Ravager.addAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt AttackTick, StunTick and RoarTick are written there +Ravager.readAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_from_nbt the same three tick values are read there +Ravager.stunEffect port crates/horizon-client/src/mobs/ravager.rs::ai_step the stun particle is emitted from ai_step +Ravager.hasLineOfSight missing unsure: no stunned or roaring line-of-sight cut-off in ravager.rs +Ravager.applyRoarKnockbackClient inline crates/horizon-client/src/mobs/ravager.rs::roar the roar push is applied in roar +Ravager.strongKnockback inline crates/horizon-client/src/mobs/ravager.rs::roar the push of 4 along the direction is in roar +Ravager.addRoarParticleEffects inline crates/horizon-client/src/mobs/ravager.rs::roar the poof particles are emitted in roar +Shulker.registerGoals port crates/horizon-client/src/mobs/shulker.rs::register gap: minor: no defense target goal (priority 3) +Shulker.defineSynchedData skip:other attach face, peek and colour are Mob extra fields +Shulker.readAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_from_nbt AttachFace, Peek and Color are read there +Shulker.addAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt AttachFace, Peek and Color are written there +Shulker.findNewAttachment inline crates/horizon-client/src/mobs/shulker.rs::tick keeps its face or teleports in tick, through attachable and teleport +Shulker.makeBoundingBox port crates/horizon-client/src/mobs/shulker.rs::progress_aabb +Shulker.getDefaultDimensions inline crates/horizon-client/src/mobs/shulker.rs::progress_aabb gap: minor: the down-face height scale is not seen here +Shulker.startRiding missing unsure: no attach-face reset to DOWN when a shulker mounts a vehicle in shulker.rs +Shulker.stopRiding missing unsure: no body yaw reset after a shulker dismounts in shulker.rs +Shulker.move missing unsure: no shulker-box move type (teleport) found in the piston code +Shulker.findAttachableSurface port crates/horizon-client/src/mobs/shulker.rs::attachable +Shulker.isPositionBlocked inline crates/horizon-client/src/mobs/shulker.rs::can_stay_at the blocked-position check is part of can_stay_at +Shulker.onSyncedDataUpdated skip:other network sync hook; the box is computed from the face on demand +Shulker.getRenderPosition missing unsure: no render slide after a shulker teleport in shulker.rs +Shulker.setVariant inline crates/horizon-client/src/mobs/shulker.rs::register colour comes from spawn data; NO_COLOR when unset +Shulker.getColor inline crates/horizon-client/src/mobs/shulker.rs::texture colour is read from extra.color for the texture +Shulker.get skip:other component getter; Horizon reads extra.color directly +Shulker.applyImplicitComponent missing minor: a shulker spawned with a SHULKER_COLOR component ignores it +Shulker.ShulkerDefenseAttackGoal.getTargetSearchArea missing minor: shulkers do not defend nearby shulkers; there is no defense target goal +Shulker.ShulkerNearestAttackGoal.getTargetSearchArea inline crates/horizon-client/src/mobs/shulker.rs::register the 4-deep search box is the when filter in register +Shulker.ShulkerPeekGoal.canContinueToUse port crates/horizon-client/src/mobs/shulker.rs::Peek::can_continue +Silverfish.registerGoals port crates/horizon-client/src/mobs/hostile.rs::register +Silverfish.createAttributes skip:data attribute defaults come from data tables +Silverfish.getWalkTargetValue missing minor: no infested-host path value (10) for silverfish in ai.rs walk_target_value +Silverfish.checkSilverfishSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules the Endermite and Silverfish arm diff --git a/tools/port_map/game2-003.tsv b/tools/port_map/game2-003.tsv new file mode 100644 index 0000000000..6d774b2437 --- /dev/null +++ b/tools/port_map/game2-003.tsv @@ -0,0 +1,149 @@ +Strider.checkStriderSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules strider arm: climbs lava upward, then needs air above +Strider.onSyncedDataUpdated skip:other client copy of the boost sync; Horizon keeps one boost state on the server side (equine.rs boost) +Strider.defineSynchedData skip:java SynchedEntityData builder; the synced values are Mob fields (extra.flag, extra.counter2) +Strider.canUseSlot port crates/horizon-client/src/game/dispenser.rs::mob_can_use_slot saddle needs alive and adult +Strider.canDispenserEquipIntoSlot port crates/horizon-client/src/game/dispenser.rs::mob_can_dispenser_equip saddle or pick-up loot +Strider.getEquipSound port crates/horizon-client/src/game/dispenser.rs::equip_sound strider saddle sound +Strider.registerGoals port crates/horizon-client/src/mobs/strider.rs::register same goal priorities and speeds +Strider.setSuffocating inline crates/horizon-client/src/mobs/strider.rs::pre_tick stores the flag; the slowdown is applied on read (strider::movement_speed) +Strider.getPassengerAttachmentPoint port crates/horizon-client/src/mobs/riders.rs::passenger_attachment gap: minor: client-only walk bob of the rider is not applied (up to 0.06 block) +Strider.getDismountLocationForPassenger inline crates/horizon-client/src/game/mounts.rs::Game::dismount_mount gap: player: no lava check and no dismount floor or pose test; a rider can land in lava or in a blocked spot +Strider.tickRidden port crates/horizon-client/src/mobs/equine.rs::ridden_step strider branch: rider yaw, half pitch, boost factor, cold slowdown +Strider.playStepSound port crates/horizon-client/src/mobs/ai.rs::step_sound lava or normal step sound +Strider.isBeingTempted inline crates/horizon-client/src/mobs/ai.rs::play_ambient the Tempt goal running check is folded into the ambient sound guard +Strider.canAddPassenger missing minor: a player can still board a saddled strider whose head is in lava; vanilla refuses (isEyeInFluid LAVA) +Strider.getLeashOffset port crates/horizon-client/src/mobs/leash.rs::leash_offset gap: minor: the strider 0.6 x eye height override is not applied; the default eye-height offset is used +Strider.StriderPathNavigation.createPathFinder port crates/horizon-client/src/mobs/nav.rs::Navigation::evaluator ground nav kind gets the walk evaluator, as WalkNodeEvaluator +Strider.StriderPathNavigation.isStableDestination port crates/horizon-client/src/mobs/nav.rs::Mob::stable_destination lava counts as stable for lava walkers +Vex.registerGoals port crates/horizon-client/src/mobs/vex.rs::register float, charge, random move, look goals, owner and player targets +Vex.restoreFrom missing minor: a vex restored after a dimension trip keeps no owner link; Horizon has no mob restoreFrom +Vex.getVexFlag inline crates/horizon-client/src/mobs/vex.rs::Charge::can_continue the charging bit is the plain extra.flag; there is no flags byte +Vex.setVexFlag inline crates/horizon-client/src/mobs/vex.rs::Charge::start start sets extra.flag; stop and tick clear it +Vex.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize Vex arm: populate (sword) then enchant +Vex.populateDefaultEquipmentSlots port crates/horizon-client/src/mobs/finalize.rs::Ctx::populate iron sword in hand with drop chance 0 +Witch.registerGoals port crates/horizon-client/src/mobs/witch.rs::register gap: minor: raiders that hurt a witch are still targeted back (vanilla ignores them) +Witch.handleEntityEvent missing player: a witch shows purple magic puffs about once a minute while idle; not drawn here (the RNG draw is kept in ai_step) +Witch.getDamageAfterMagicAbsorb missing player: witches take full magic, indirect magic, sonic boom and thorns damage; vanilla cuts it to 15% and ignores its own hits +Zoglin.initCoreActivity inline crates/horizon-client/src/mobs/piglin.rs::register_zoglin gap: minor: look-at-target and move-to-target sinks are not separate; the goal set stands in for them +Zoglin.initIdleActivity inline crates/horizon-client/src/mobs/piglin.rs::register_zoglin gap: minor: idle is a stroll only; no look-target sometimes and no do-nothing pauses +Zoglin.initFightActivity inline crates/horizon-client/src/mobs/piglin.rs::register_zoglin gap: player: melee cooldown is 20 ticks; vanilla is 40 for adults and 15 for babies, so zoglins hit faster +Zoglin.findNearestValidAttackTarget inline crates/horizon-client/src/mobs/piglin.rs::register_zoglin target filter: not a creeper or a zoglin +Zoglin.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize Zoglin arm: 20 percent start as babies +Zoglin.createAttributes skip:data attribute defaults come from the data tables (mobs.rs attr lookup) +Zoglin.doHurtTarget port crates/horizon-client/src/mobs/piglin.rs::hoglin_hurt_target headbutt event, zoglin attack sound, hurt and throw +Zoglin.hurtServer inline crates/horizon-client/src/mobs/piglin.rs::register_zoglin gap: minor: HurtByTarget sets the attacker as target with no 4-block check against a much nearer current target +Zoglin.setAttackTarget inline crates/horizon-client/src/mobs/piglin.rs::register_zoglin gap: minor: target has no 200-tick expiry; Horizon sets the target directly +Zoglin.updateActivity missing player: a zoglin that starts a fight does not play its angry grunt (zoglin.angry) +Zoglin.customServerAiStep inline crates/horizon-client/src/mobs/piglin.rs::ai_step gap: minor: the brain tick is not run; the zoglin goal set runs in the general AI loop +Zoglin.setBaby port crates/horizon-client/src/mobs/finalize.rs::set_baby Zoglin arm: non-ageable baby age and 0.5 attack damage +Zoglin.handleEntityEvent port crates/horizon-client/src/mobs/piglin.rs::client_event event 4 starts the headbutt; the attack sound comes from hoglin_hurt_target +Zoglin.getAmbientSound missing player: a zoglin with a target grunts its ambient sound, not the angry one (zoglin.angry) +Breeze.createAttributes skip:data attribute defaults come from the data tables (mobs.rs attr lookup) +Breeze.makeBrain port crates/horizon-client/src/mobs/monster_brain.rs::init breezes get the fight default activity +Breeze.onSyncedDataUpdated port crates/horizon-client/src/mobs/breeze.rs::ai_step client pose change restarts the matching animation +Breeze.resetAnimations inline crates/horizon-client/src/mobs/breeze.rs::ai_step the stop calls of the pose-change branch +Breeze.emitJumpTrailParticles missing player: no block-dust trail behind a long jump (about 5 ticks of particles) +Breeze.emitGroundParticles missing player: no block-dust puffs at its feet while it stands, shoots, inhales or lands; Horizon only draws the random number +Breeze.playWhirlSound port crates/horizon-client/src/mobs/breeze.rs::tick whirl sound on a random clock, as a local sound +Breeze.deflection port crates/horizon-client/src/game/creatures.rs::Game::tick_projectiles breeze turns projectiles back, except wind charges +Breeze.getHurtBy port crates/horizon-client/src/mobs/monster_brain.rs::hurt_by_entity the living source of the last hurt +Breeze.registerDebugValues skip:debug debug subscription values +BreezeAi.initCoreActivity port crates/horizon-client/src/mobs/monster_brain.rs::breeze_packages core swim and look-at-target sink +BreezeAi.initIdleActivity port crates/horizon-client/src/mobs/monster_brain.rs::breeze_packages idle: attack nearest, attack hurt-by, slide to target, do nothing or stroll +BreezeAi.initFightActivity port crates/horizon-client/src/mobs/monster_brain.rs::breeze_packages fight: stop, shoot, long jump, shoot when stuck, slide; the entry gate is fight_requirements +LongJump.canStillUse inline crates/horizon-client/src/mobs/monster_brain.rs::B::tick_or_stop keep-running check: not standing and no jump cooldown +LongJump.start inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start inhaling memory and pose, charge sound, look at the jump target +LongJump.isFinishedInhaling inline crates/horizon-client/src/mobs/monster_brain.rs::long_jump_tick inhaling memory gone and pose INHALING +LongJump.isFinishedJumping inline crates/horizon-client/src/mobs/monster_brain.rs::long_jump_tick landed on the ground or in water +LongJump.outOfAggroRange inline crates/horizon-client/src/mobs/monster_brain.rs::long_jump_can_run follow-range check drops the target +LongJump.tooCloseForJump inline crates/horizon-client/src/mobs/monster_brain.rs::long_jump_can_run distance minus 4 at most zero +Shoot.checkExtraStartConditions inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start standing pose and target within range +Shoot.canStillUse inline crates/horizon-client/src/mobs/monster_brain.rs::B::tick_or_stop target and shoot memory still present +Shoot.start inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start shooting pose, 15 tick charge, inhale sound +Shoot.stop inline crates/horizon-client/src/mobs/monster_brain.rs::B::do_stop back to standing and 10 tick cooldown +Shoot.isTargetWithinRange inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start squared distance under 256 +Slide.checkExtraStartConditions inline crates/horizon-client/src/mobs/monster_brain.rs::B::try_start on ground, not in water, standing +Slide.randomPointInMiddleCircle port crates/horizon-client/src/mobs/monster_brain.rs::slide_start the middle-circle branch of the slide target +Creaking.defineSynchedData skip:java SynchedEntityData builder; the synced values are Mob fields (extra.ints, flags) +Creaking.createAttributes skip:data attribute defaults come from the data tables (mobs.rs attr lookup) +Creaking.setupAnimationStates port crates/horizon-client/src/mobs/creaking.rs::animate attack, invulnerability and tear-down animations +Creaking.creakingDeathEffects port crates/horizon-client/src/mobs/creaking_heart.rs::remove_protector gap: minor: no blameSourceForDamage or die(); health is set to 0 and the twitch sound plays +Creaking.canUsePortal missing minor: a heart-bound creaking can still go through a portal; game/nether_portal.rs mob_can_use_portal has no heart check +Creaking.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt home_pos is read back as the transient home +Creaking.checkEyeBlink inline crates/horizon-client/src/mobs/creaking.rs::animate eye flicker timing on the dying creaking +Creaking.knockback missing player: a creaking that a player looks at still gets shoved by hits; vanilla ignores knockback while it cannot move (Mob::knockback has no check) +Creaking.CreakingPathNavigation.createPathFinder missing minor: no home-distance node evaluator; paths do not block nodes more than 32 blocks from the heart +Creaking.HomeNodeEvaluator.getPathType missing minor: nodes more than 32 blocks from home are not marked blocked +CreakingAi.initCoreActivity port crates/horizon-client/src/mobs/monster_brain.rs::creaking_packages core swim, look-at-target and move sink +CreakingAi.initIdleActivity port crates/horizon-client/src/mobs/monster_brain.rs::creaking_packages idle: attack active creakings, look sometimes, stroll, look-walk or do nothing +CreakingAi.initFightActivity port crates/horizon-client/src/mobs/monster_brain.rs::creaking_packages fight: walk to target, melee on a 40 tick cooldown, stop when unreachable +AbstractCubeMob.registerGoals port crates/horizon-client/src/mobs/cube.rs::register float, random direction, keep jumping, attack goal and player and golem targets +AbstractCubeMob.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt Size stored minus one, and wasOnGround +AbstractCubeMob.refreshDimensions skip:other bounds are read from the size on each use, so there is no separate refresh; the position is never reset +AbstractCubeMob.onSyncedDataUpdated missing unsure: remote.rs applies no size sync to cubes; local play sets the size directly +AbstractCubeMob.setUpSplitCube port crates/horizon-client/src/game/creatures.rs::Game::mob_died gap: minor: children get half size and the same fixed offsets, but no random yaw +AbstractCubeMob.push missing player: a slime or magma cube that bumps into an iron golem does not hurt it +AbstractCubeMob.dealDamage inline crates/horizon-client/src/mobs/cube.rs::ai_step gap: player: only the player touch hits; a mob target such as an iron golem takes no damage +AbstractCubeMob.getPassengerAttachmentPoint port crates/horizon-client/src/mobs/riders.rs::passenger_attachment gap: minor: the rider sits at full height; vanilla lowers it by size/64 per block +AbstractCubeMob.finalizeSpawn port crates/horizon-client/src/mobs/cube.rs::finalize spawn size 1, 2 or 4, rolled by the slime and magma cube arm of spawning.rs +AbstractCubeMob.CubeMobAttackGoal.canContinueToUse port crates/horizon-client/src/mobs/cube.rs::CubeAttack::can_continue tired timer runs down while the target can still be attacked +MagmaCube.addTargetingGoals port crates/horizon-client/src/mobs/cube.rs::register player within 4 blocks in height, and iron golems +MagmaCube.finalizeSpawn port crates/horizon-client/src/mobs/cube.rs::finalize same spawn size roll as the other cubes +Slime.addTargetingGoals port crates/horizon-client/src/mobs/cube.rs::register same targets as magma cubes +Slime.finalizeSpawn port crates/horizon-client/src/mobs/cube.rs::finalize same spawn size roll as the other cubes +SulfurCube.defineSynchedData skip:java SynchedEntityData builder; the synced values are Mob fields (extra.counter2, flags) +SulfurCube.addBehaviourGoals port crates/horizon-client/src/mobs/sulfur_cube.rs::register temptation goal at 2 and search-for-items at 3 +SulfurCube.onSyncedDataUpdated skip:other client copy of the fuse sync; the fuse is mob state (sulfur_cube::fuse) +SulfurCube.saveToBucketTag inline crates/horizon-client/src/mobs/sulfur_cube.rs::to_nbt the bucket keeps the mob NBT, including Age and AgeLocked +SulfurCube.travelInFluid inline crates/horizon-client/src/mobs/sulfur_cube.rs::ai_step buoyancy bob runs in the tick, not in the fluid travel +SulfurCube.loadFromBucketTag inline crates/horizon-client/src/mobs/sulfur_cube.rs::from_nbt reads Age and AgeLocked back +SulfurCube.requiresCustomPersistence port crates/horizon-client/src/mobs/ai.rs::requires_custom_persistence holding a block or let out of a bucket +SulfurCube.hurtServer port crates/horizon-client/src/mobs/sulfur_cube.rs::accepts_damage fire or explosion primes; a held block shrugs off blows and only knocks back +SulfurCube.matchingArchetypes port crates/horizon-client/src/mobs/sulfur_cube.rs::archetype_of one archetype per item, not a list +SulfurCube.tickFuse inline crates/horizon-client/src/mobs/sulfur_cube.rs::ai_step gap: minor: fuse countdown and blast, but no leash drop or on-death effects at the blast +SulfurCube.primeWhenOnPoweredPosition missing player: an explosive cube sitting on a powered block does not prime itself; no signal check here +SulfurCube.customServerAiStep port crates/horizon-client/src/mobs/sulfur_cube.rs::ai_step timers, buoyancy, facing, growing up and player push +SulfurCube.collectEquipmentChanges inline crates/horizon-client/src/mobs/sulfur_cube.rs::ai_step goals are disabled while a block is held; archetype effects are read on demand, not applied as modifiers +SulfurCube.equipItem port crates/horizon-client/src/game/sulfur_cubes.rs::Game::interact_sulfur_cube swallows the item; the old block is ejected +SulfurCube.applyContactDamage inline crates/horizon-client/src/mobs/sulfur_cube.rs::player_push gap: minor: contact damage reaches only the player, not other entities +SulfurCube.isEquippableInSlot port crates/horizon-client/src/mobs/sulfur_cube.rs::swallowable swallowable items only +SulfurCube.canUseSlot port crates/horizon-client/src/game/dispenser.rs::mob_can_use_slot body slot needs alive and adult +SulfurCube.canHoldItem port crates/horizon-client/src/game/sulfur_cubes.rs::Game::sulfur_cube_pickups empty body, adult and not on a pickup timer +SulfurCube.pickUpItem port crates/horizon-client/src/game/sulfur_cubes.rs::Game::sulfur_cube_pickups takes one item from the ground into the body +SulfurCube.getBaseExperienceReward port crates/horizon-client/src/mobs/drops.rs::base_experience none for babies, 1 or 2 for adults +SulfurCube.setSpawnSize port crates/horizon-client/src/mobs/sulfur_cube.rs::finalize babies size 1, adults size 2 +SulfurCube.getBreedOffspring inline crates/horizon-client/src/mobs/sulfur_cube.rs::ai_step gap: minor: a child is shrunk to size 1 on its next tick, not at birth +SulfurCube.applyHorizontalHitAngleScale missing player: a hit with a block does not turn the launch by the attacker's aim; knockback takes no aim +SulfurCube.applyVerticalHitAnglePowerTransfer missing player: the vertical share of a hit is not moved by the attacker's aim, so launches from above or below differ +SulfurCube.applyVerticalPositionAnglePowerRotation port crates/horizon-client/src/mobs/sulfur_cube.rs::knockback feet-to-feet rotation at 0.8 with the power ratio clamp +SulfurCube.get skip:other data component view of the body item; Horizon reads the body item directly (sulfur_cube::body) +SulfurCube.applyImplicitComponent skip:other component copy when a cube is built from an item; Horizon sets the body directly +SulfurCube.getLeashOffset port crates/horizon-client/src/mobs/leash.rs::leash_offset gap: minor: cube rope point is half the box height; the eye-height default is used +SulfurCube.SulfurCubeTemptGoal.stopNavigation port crates/horizon-client/src/mobs/sulfur_cube.rs::SulfurTempt::stop stops the cube's wanted movement +SulfurCube.SulfurCubeTemptGoal.navigateTowards port crates/horizon-client/src/mobs/sulfur_cube.rs::SulfurTempt::tick looks at the player and sets the cube's direction +Hoglin.createAttributes skip:data attribute defaults come from the data tables (mobs.rs attr lookup) +Hoglin.hurtServer inline crates/horizon-client/src/mobs/piglin.rs::hoglin_repellent gap: player: a hit ends the pacified state and the attacker is targeted, but a hit hoglin never retreats from piglins (no AVOID activity) +Hoglin.ageBoundaryReached port crates/horizon-client/src/mobs/finalize.rs::age_boundary_reached baby or adult attack damage; the xp of 3 or 5 is in drops.rs::xp_reward +Hoglin.checkHoglinSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules not spawned on a nether wart block +Hoglin.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize 20 percent start as babies +Hoglin.mobInteract port crates/horizon-client/src/game/creatures.rs::Game::interact_mob gap: minor: a fed hoglin does not become persistent +Hoglin.finishConversion port crates/horizon-client/src/mobs/piglin.rs::ai_step zombification turns it into a zoglin with nausea +Hoglin.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt gap: minor: CannotBeHunted is not written +Hoglin.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt gap: minor: CannotBeHunted is not read +Hoglin.getBreedOffspring port crates/horizon-client/src/game/creatures.rs::Game::breed gap: minor: the hoglin child is not made persistent +HoglinAi.initCoreActivity inline crates/horizon-client/src/mobs/piglin.rs::register_hoglin gap: minor: look-at-target and move-to-target sinks are not separate; the goal set stands in for them +HoglinAi.initIdleActivity inline crates/horizon-client/src/mobs/piglin.rs::register_hoglin gap: player: hoglins do not walk away from adult piglins within 8 blocks; the rest of the idle is there +HoglinAi.initFightActivity inline crates/horizon-client/src/mobs/piglin.rs::register_hoglin gap: player: melee cooldown is 20 ticks; vanilla is 40 for adults and 15 for babies +HoglinAi.initRetreatActivity missing player: hoglins never run from piglins; the retreat activity (avoid target, 1.3 speed) does not exist here +HoglinAi.createIdleMovementBehaviors inline crates/horizon-client/src/mobs/piglin.rs::register_hoglin gap: minor: no look-target walk or do-nothing pauses; a plain stroll only +HoglinAi.updateActivity missing player: no grunt when a hoglin switches activity (angry or retreat); only the ambient roll plays +HoglinAi.onHitTarget inline crates/horizon-client/src/mobs/piglin.rs::hoglin_hurt_target gap: player: no retreat when piglins outnumber hoglins; the hit only attacks +HoglinAi.retreatFromNearestTarget missing player: no retreat from the nearest piglin or hoglin target (part of the missing retreat activity) +HoglinAi.setAvoidTarget missing player: no avoid target is set, so a hoglin never backs off after a piglin hit (retreat activity missing) +HoglinAi.isPosNearNearestRepellent inline crates/horizon-client/src/mobs/piglin.rs::HoglinAvoidRepellent::can_use the 8-block repellent distance check +HoglinAi.piglinsOutnumberHoglins missing player: the count of piglins against hoglins that starts a retreat is not kept +HoglinAi.maybeRetaliate port crates/horizon-client/src/mobs/piglin.rs::register_hoglin gap: minor: no check for hoglin attackers or the 4-block nearer-target rule; HurtByTarget alerts others +HoglinAi.setAttackTarget inline crates/horizon-client/src/mobs/piglin.rs::register_hoglin gap: minor: target has no 200-tick expiry and the breed target is not cleared +HoglinAi.setAttackTargetIfCloserThanCurrent inline crates/horizon-client/src/mobs/piglin.rs::register_hoglin NearestTarget keeps the closest target and skips pacified hoglins +HoglinAi.getSoundForActivity inline crates/horizon-client/src/mobs/ai.rs::play_ambient gap: player: the retreat grunt near repellents or while retreating is missing; the angry grunt while a target is set is ported +Evoker.EvokerAttackSpellGoal.createSpellEntity port crates/horizon-client/src/mobs/illager.rs::fang_floor gap: minor: the sturdy-face test is a full-collision test on the block below; the fang event comes from cast_fangs diff --git a/tools/port_map/game2-004.tsv b/tools/port_map/game2-004.tsv new file mode 100644 index 0000000000..a4b68e904d --- /dev/null +++ b/tools/port_map/game2-004.tsv @@ -0,0 +1,150 @@ +Evoker.EvokerWololoSpellGoal.canContinueToUse port crates/horizon-client/src/mobs/illager.rs::UseSpell::can_continue wololo branch: sheep target still set and warm-up left +Evoker.EvokerWololoSpellGoal.performSpellCasting port crates/horizon-client/src/game/creatures.rs::Game::handle_mob_events MobEvent::Wololo dyes the sheep red (color 14); the event is pushed from UseSpell::tick +Illusioner.registerGoals port crates/horizon-client/src/mobs/illusioner.rs::register same goal list and priorities +Illusioner.createAttributes skip:data attribute defaults (speed 0.5, follow 18, health 32) live in data tables +Illusioner.performRangedAttack port crates/horizon-client/src/mobs/undead.rs::perform_ranged_attack bow shot at speed 1.6 with the aim and uncertainty the skeleton uses +Illusioner.getArmPose port crates/horizon-client/src/mobs/illager.rs::arm_pose spellcasting, bow when aggressive, else crossed +Illusioner.IllusionerBlindnessSpellGoal.canUse port crates/horizon-client/src/mobs/illusioner.rs::Spell::can_use target id differs from last target, difficulty above normal +Illusioner.IllusionerBlindnessSpellGoal.start port crates/horizon-client/src/mobs/illusioner.rs::Spell::start remembers the target id for the next can_use +Illusioner.IllusionerBlindnessSpellGoal.performSpellCasting port crates/horizon-client/src/mobs/illusioner.rs::Spell::tick gap: minor: blindness reaches only a player target; vanilla also blinds villagers and iron golems +Illusioner.IllusionerMirrorSpellGoal.canUse port crates/horizon-client/src/mobs/illusioner.rs::Spell::can_use not already invisible +SpellcasterIllager.getArmPose port crates/horizon-client/src/mobs/illager.rs::arm_pose spellcasting pose while a spell is cast, else celebrating or crossed +SpellcasterIllager.isCastingSpell skip:other getter; Horizon reads extra.counter > 0 inline +SpellcasterIllager.setIsCastingSpell inline crates/horizon-client/src/mobs/illager.rs::UseSpell::start the spell id the client draws (extra.variant2) is set when the cast starts +SpellcasterIllager.getCurrentSpell skip:other getter; Horizon reads extra.variant2 inline in arm_pose and shows_item +SpellcasterIllager.SpellcasterUseSpellGoal.canContinueToUse port crates/horizon-client/src/mobs/illager.rs::UseSpell::can_continue target alive and warm-up left +Vindicator.registerGoals port crates/horizon-client/src/mobs/illager.rs::register_vindicator break door, open door, hold ground, melee, Johnny goal +Vindicator.createAttributes skip:data attribute defaults (speed 0.35, follow 12, health 24, attack 5) live in data tables +Vindicator.addAdditionalSaveData missing minor: the Johnny flag is not saved; Horizon's Johnny goal tests the custom name, so only a vindicator renamed after being named Johnny differs +Vindicator.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize raider spawn, then equipment and enchantments; the door flag it sets each tick comes from vindicator_server_ai_step +Vindicator.populateDefaultEquipmentSlots port crates/horizon-client/src/mobs/finalize.rs::Ctx::populate iron axe unless the mob is in a raid +Vindicator.setCustomName inline crates/horizon-client/src/mobs/illager.rs::register_vindicator gap: minor: the Johnny goal reads the current custom name each tick; vanilla latches the flag once a name is Johnny +Vindicator.VindicatorBreakDoorGoal.canContinueToUse port crates/horizon-client/src/mobs/doors.rs::BreakDoor::can_continue still in a raid, plus the base break checks +AbstractPiglin.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt writes IsImmuneToZombification and TimeInOverworld +AbstractPiglin.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt reads immunity, time in overworld and the pick-up flag +AbstractPiglin.customServerAiStep port crates/horizon-client/src/mobs/piglin.rs::ai_step gap: minor: zombification runs outside the Nether by the nether flag, not the PIGLINS_ZOMBIFY attribute +AbstractPiglin.finishConversion port crates/horizon-client/src/mobs/piglin.rs::ai_step spawns the zombified piglin with nausea and removes the old one +Piglin.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt gap: minor: CannotHunt is not read, so hunting ignores it; baby and inventory are read +Piglin.getSlot missing minor: item commands cannot reach a piglin's inventory slots (300 and up); 300 and up only address the open container +Piglin.defineSynchedData skip:java synched-data registration; Horizon keeps baby, crossbow and dancing state in Mob fields +Piglin.createAttributes skip:data attribute defaults (health 16, speed 0.35, attack 5) live in data tables +Piglin.checkPiglinSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules not spawning on a nether wart block +Piglin.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::piglin baby chance or spawn weapon, hunt wait, then equipment and enchantments +Piglin.populateDefaultEquipmentSlots port crates/horizon-client/src/mobs/finalize.rs::Ctx::populate golden armour at 10 percent per adult slot +Piglin.mobInteract port crates/horizon-client/src/game/piglins.rs::Game::give_piglin a gold ingot handed over starts admiring +Piglin.setBaby port crates/horizon-client/src/mobs/finalize.rs::set_baby gap: minor: sets the baby age only; the speed modifier is not set in this fn +Piglin.customServerAiStep port crates/horizon-client/src/mobs/piglin.rs::ai_step gap: minor: the brain tick is the registered goal set (register_piglin), not this fn +Piglin.finishConversion missing minor: a piglin turning into a zombified piglin in the Overworld drops nothing; vanilla drops its inventory and any gold it admires +Piglin.hurtServer inline crates/horizon-client/src/mobs/piglin.rs::celebrate gap: player: a hit by a player does not stop an admiring piglin or start its admiring-disabled wait; babies do not flee the attacker +Piglin.canUseNonMeleeWeapon port crates/horizon-client/src/game/piglins.rs::Game::piglin_wanted gap: minor: checks the preferred-weapons tag only, not the usable-crossbow tag or the kinetic weapon component +Piglin.holdInOffHand port crates/horizon-client/src/game/piglins.rs::Game::start_admiring gap: minor: does not drop an item already in the off hand and does not set persistence +Piglin.wantsToPickUp port crates/horizon-client/src/game/piglins.rs::Game::piglin_wants gap: minor: does not check the admiring-disabled wait after a hit by a player +Piglin.startRiding missing minor: baby piglins never mount a baby hoglin (see PiglinAi.babySometimesRideBabyHoglin) +Piglin.getTopPassenger missing minor: helper of baby piglin riding, which Horizon lacks +Piglin.getAmbientSound missing player: piglins always play their plain ambient grunt; the jealous, retreat, celebrate and admiring sounds of their activity never play +PiglinAi.initCoreActivity inline crates/horizon-client/src/mobs/piglin.rs::register_piglin gap: minor: no babyAvoidNemesis (babies do not flee a nemesis); avoidZombified and admiring stop are covered by goals +PiglinAi.initIdleActivity inline crates/horizon-client/src/mobs/piglin.rs::register_piglin gap: minor: no ride for baby piglins and no look at or interact with other piglins; hunting and repellent avoidance are present +PiglinAi.initFightActivity inline crates/horizon-client/src/mobs/piglin.rs::register_piglin gap: minor: no spear approach or attack, no back-off for crossbow piglins, no erase of the attack target near zombified piglins +PiglinAi.initCelebrateActivity inline crates/horizon-client/src/mobs/piglin.rs::Celebrate::walk gap: minor: walks to the spot only; no look at gold-holding players and no random strolls +PiglinAi.initAdmireItemActivity inline crates/horizon-client/src/mobs/piglin.rs::GoToWantedItem::can_use gap: minor: no give-up after 9 blocks or 200 ticks of trying to reach the item +PiglinAi.initRetreatActivity missing player: piglins never retreat from hoglins or other avoid targets; they keep fighting or idling instead +PiglinAi.initRideHoglinActivity missing player: baby piglins never ride baby hoglins +PiglinAi.createLookBehaviors inline crates/horizon-client/src/mobs/piglin.rs::register_piglin gap: minor: looks at players only; piglins and random look targets are not there +PiglinAi.createIdleLookBehaviors inline crates/horizon-client/src/mobs/piglin.rs::register_piglin gap: minor: looks at players only, no idle look at piglins or random points +PiglinAi.createIdleMovementBehaviors inline crates/horizon-client/src/mobs/piglin.rs::register_piglin gap: minor: strolls only; no walking up to other piglins or to a look target +PiglinAi.updateActivity inline crates/horizon-client/src/mobs/piglin.rs::register_piglin gap: minor: the activity choice is the goal priority order; the aggressive and dancing flags are set elsewhere +PiglinAi.isBabyRidingBaby missing player: baby piglins never ride baby hoglins, so no piglin dismounts for that reason +PiglinAi.pickUpItem port crates/horizon-client/src/game/piglins.rs::Game::piglin_pickups loved item admired, food eaten, else worn or stored +PiglinAi.holdInOffhand port crates/horizon-client/src/game/piglins.rs::Game::start_admiring gap: minor: does not drop an item already in the off hand and does not set persistence +PiglinAi.removeOneItemFromItemEntity port crates/horizon-client/src/game/piglins.rs::Game::piglin_pickups takes one item from the stack (nuggets whole) +PiglinAi.cancelAdmiring missing minor: a piglin that converts while admiring does not drop the gold ingot it holds +PiglinAi.throwItems port crates/horizon-client/src/game/piglins.rs::Game::finish_admiring gap: minor: only the barter path throws; the target choice is nearest player within 16, else a spot nearby +PiglinAi.throwItemsTowardPos port crates/horizon-client/src/game/piglins.rs::Game::piglin_throw gap: minor: no off-hand swing animation +PiglinAi.getBarterResponseItems port crates/horizon-client/src/game/piglins.rs::Game::finish_admiring rolls minecraft:gameplay/piglin_bartering for an adult; a baby gets the ingot back +PiglinAi.wantsToStopRiding missing player: baby piglins never ride baby hoglins, so no dismount rule runs +PiglinAi.isNearestValidAttackTarget inline crates/horizon-client/src/mobs/piglin.rs::ai_step gap: minor: only drops a mob target that is dead or unseen; vanilla also re-picks the nearest valid target +PiglinAi.isNearZombified inline crates/horizon-client/src/mobs/piglin.rs::register_piglin gap: minor: the 6-block avoid is an AvoidEntity goal; the attack target is not erased near a zombified piglin +PiglinAi.findNearestValidAttackTarget port crates/horizon-client/src/mobs/piglin.rs::hostile_to_player gap: minor: decides player targets only; angry-at, nemesis and universal anger are not chosen here +PiglinAi.canAdmire port crates/horizon-client/src/game/piglins.rs::Game::give_piglin adult piglin takes a gold ingot from the hand +PiglinAi.maybeRetaliate inline crates/horizon-client/src/mobs/goals.rs::HurtByTarget::start gap: minor: the hurt mob becomes the target; the 600-tick anger timer and the too-far check are not kept +PiglinAi.getSoundForActivity missing player: piglins never play their jealous, retreat, celebrate or admiring-item ambient sounds +PiglinAi.isNearAvoidTarget missing minor: only used by the retreat sound, which Horizon lacks +PiglinAi.babySometimesRideBabyHoglin missing player: baby piglins never ride baby hoglins +PiglinAi.broadcastAngerTarget inline crates/horizon-client/src/mobs/goals.rs::HurtByTarget::start gap: minor: alerts same-kind mobs nearby; no closer-target check and no hoglin rule +PiglinAi.broadcastUniversalAnger missing minor: the universal_anger game rule is stored but piglins ignore it +PiglinAi.setAngerTarget port crates/horizon-client/src/game/piglins.rs::Game::anger_piglins gap: minor: only the player path (600-tick anger); a hoglin target does not set the hunt wait +PiglinAi.setAngerTargetToNearestTargetablePlayerIfFound missing minor: only for universal anger, which Horizon does not apply +PiglinAi.setAngerTargetIfCloserThanCurrent inline crates/horizon-client/src/mobs/goals.rs::HurtByTarget::start gap: minor: alerts nearby piglins but never keeps a closer target +PiglinAi.getAvoidTarget missing player: no avoid target exists, so piglins do not back away from hoglins they are outnumbered by +PiglinAi.getNearestVisibleTargetablePlayer missing minor: only used by universal anger, which Horizon does not apply +PiglinAi.broadcastRetreat missing player: piglins do not warn others to retreat from a hoglin group +PiglinAi.retreatFromNearestTarget missing player: piglins do not retreat from a hoglin group +PiglinAi.wantsToStopFleeing missing player: no retreat state, so nothing ends one +PiglinAi.hoglinsOutnumberPiglins missing player: no retreat decision, so hoglins outnumbering piglins changes nothing +PiglinAi.setAvoidTargetAndDontHuntForAWhile missing player: piglins hit by a hoglin group do not stop attacking and back off +PiglinAi.dontKillAnyMoreHoglinsForAWhile port crates/horizon-client/src/mobs/piglin.rs::HuntHoglin::start gap: minor: sets only the hunting piglin's own wait; the wait from a hit on a hoglin is not set +PiglinAi.getRandomNearbyPos inline crates/horizon-client/src/game/piglins.rs::Game::finish_admiring gap: minor: a random spot within 4 blocks, not a land position +PiglinAi.isPlayerHoldingLovedItem missing player: piglins do not turn to look at a player holding gold, so that look never happens +PiglinAi.isNotHoldingLovedItemInOffHand inline crates/horizon-client/src/game/piglins.rs::Game::piglin_wants the off hand holds no loved item test +PiglinAi.isZombified inline crates/horizon-client/src/mobs/piglin.rs::register_piglin the zombified piglin and zoglin species list of the avoid goal +PiglinAi.findNearbyAdultPiglins inline crates/horizon-client/src/mobs/piglin.rs::HuntHoglin::start gap: minor: uses the follow range, not the sensor's nearest living entities list +AbstractSkeleton.registerGoals port crates/horizon-client/src/mobs/undead.rs::register restrict sun, flee sun, wolf avoid, stroll, look, targets +AbstractSkeleton.rideTick missing minor: a skeleton riding a spider does not turn its body with the mount +AbstractSkeleton.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::skeleton equipment, enchantments, pick-up chance, halloween pumpkin; the weapon goal is re-picked on the next tick +AbstractSkeleton.reassessWeaponGoal port crates/horizon-client/src/mobs/undead.rs::SkeletonWeapon::can_use bow goal with the hard or normal interval, else melee +AbstractSkeleton.onEquipItem inline crates/horizon-client/src/mobs/undead.rs::SkeletonWeapon::can_use gap: minor: the weapon is re-picked on the next goal check, not at the moment of equipping +AbstractSkeleton.wantsToPickUp missing minor: skeletons never pick up dropped items in Horizon (no generic mob pickup); vanilla refuses spears +Bogged.getArrow port crates/horizon-client/src/mobs/undead.rs::perform_ranged_attack Bogged arrows carry poison for 100 ticks +Bogged.shear port crates/horizon-client/src/game/creatures.rs::Game::interact_shear_mob shear sound, sheared flag, shearing loot, game event +Bogged.spawnShearedMushrooms port crates/horizon-client/src/game/creatures.rs::Game::interact_shear_mob drops the shearing/bogged loot table at the mob's height +WitherSkeleton.registerGoals port crates/horizon-client/src/mobs/undead.rs::register piglin and piglin brute target at priority 3, then the skeleton goals +WitherSkeleton.canHoldItem missing minor: no mob item pickup in Horizon, so the disliked-weapons tag never applies +WitherSkeleton.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::skeleton attack damage 4 for wither skeletons +WitherSkeleton.getArrow missing minor: arrows from a wither skeleton are not set on fire for 100 seconds +Spider.registerGoals port crates/horizon-client/src/mobs/hostile.rs::register spider goals: leap at target and the spider melee that gives up in bright light +Spider.isClimbing skip:other getter; Horizon reads the mob's climbing field +Spider.setClimbing inline crates/horizon-client/src/mobs/ai.rs::ai_step climbing set from the horizontal collision each tick +Spider.getVehicleAttachmentPoint port crates/horizon-client/src/mobs/riders.rs::passenger_attachment gap: minor: always the spider's fixed seat; vanilla uses a lower seat for riders narrower than the spider +Spider.SpiderEffectsGroupData.setRandomEffect port crates/horizon-client/src/mobs/finalize.rs::Ctx::spider hard-difficulty effect: speed, strength, regeneration or invisibility +AngerManagement.codec missing minor: warden anger is not saved; the anger tag is written empty, so a reloaded warden forgets its suspects +AngerManagement.createUuidAngerPairs missing minor: part of the anger save, which Horizon does not write +AngerManagement.convertFromUuids missing minor: part of the anger load, which Horizon does not read +AngerManagement.getTopSuspect port crates/horizon-client/src/mobs/warden.rs::AngerManagement::active_entity first suspect the filter accepts +AngerManagement.Sorter.compare port crates/horizon-client/src/mobs/warden.rs::AngerManagement::sort_and_update_highest angry first, then players, then the angriest +Warden. port crates/horizon-client/src/mobs/warden.rs::register pathfinding maluses and can-float; the anger and vibration state start empty +Warden.getAddEntityPacket missing unsure: the spawn packet's emerging flag (data 1) has no Horizon counterpart that I found +Warden.recreateFromPacket missing unsure: no client-side emerging pose from a spawn packet that I found +Warden.createAttributes skip:data attribute defaults (health 500, speed 0.3, damage 30) live in data tables +Warden.handleEntityEvent inline crates/horizon-client/src/mobs/warden.rs::animate gap: minor: could not confirm the attack animation (event 4) is started here; tendril and sonic boom are +Warden.onSyncedDataUpdated inline crates/horizon-client/src/mobs/warden.rs::set_pose starts the emerge, dig, roar or sniff animation on pose change +Warden.updateDynamicGameEventListener port crates/horizon-client/src/mobs/warden.rs::listen vibrations reach the warden through listen; there is no registration step +Warden.addAdditionalSaveData missing minor: warden anger is not saved (the anger tag is written empty) +Warden.readAdditionalSaveData missing minor: warden anger is not read back, so a reloaded warden is calm +Warden.playListeningSound inline crates/horizon-client/src/mobs/warden.rs::increase_anger plays the listening sound for the anger level, unless roaring +WardenAi.initCoreActivity inline crates/horizon-client/src/mobs/warden_brain.rs::activities swim, look at target, look sink, move sink +WardenAi.initDiggingActivity inline crates/horizon-client/src/mobs/warden_brain.rs::activities force unmount and digging +WardenAi.initIdleActivity inline crates/horizon-client/src/mobs/warden_brain.rs::activities roar target, sniff, stroll or idle +WardenAi.initInvestigateActivity inline crates/horizon-client/src/mobs/warden_brain.rs::activities roar target and walk to the disturbance +WardenAi.initSniffingActivity inline crates/horizon-client/src/mobs/warden_brain.rs::activities roar target and sniffing +WardenAi.isTarget port crates/horizon-client/src/mobs/warden.rs::brain_target gap: minor: returns the attack target; the look-at filter is the caller's check +WardenSpawnTracker.hasNearbyWarden inline crates/horizon-client/src/game/sculk.rs::Game::try_to_warn a warden within 24 blocks on each axis stops the warning +WardenSpawnTracker.getNearbyPlayers inline crates/horizon-client/src/game/sculk.rs::Game::try_to_warn gap: minor: only the player who set off the shrieker is counted, not other players within 16 blocks +WardenSpawnTracker.copyData inline crates/horizon-client/src/sculk.rs::WardenSpawnTracker::try_warn every tracker takes the highest one's level, cooldown and tick count +Drowned.checkDrownedSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules water below, spawner or deep enough, dark enough, frequency roll +Drowned.isDeepEnoughToSpawn inline crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules below sea level minus 5 +Drowned.populateDefaultEquipmentSlots port crates/horizon-client/src/mobs/finalize.rs::Ctx::populate 10 percent chance of a trident (10 in 16) or fishing rod +Drowned.canReplaceCurrentItem missing minor: no mob item pickup in Horizon, so the nautilus shell guard never applies +Drowned.rangedAttackUncertainty port crates/horizon-client/src/mobs/undead.rs::drowned_throw gap: minor: fixed uncertainty 6 (normal difficulty), not 14 minus 4 times the difficulty +Drowned.rideTick missing minor: a drowned riding a mount does not turn its body with the mount +Drowned.wantsToPickUp missing minor: no mob item pickup in Horizon, so the spear refusal never applies +Drowned.DrownedGoToBeachGoal.isValidTarget port crates/horizon-client/src/mobs/undead.rs::beach_target two empty blocks above and a standable block below +Drowned.DrownedGoToWaterGoal.getWaterPos port crates/horizon-client/src/mobs/undead.rs::DrownedGoToWater::can_use ten random spots 10 blocks around and up to 2 above, water only +Zombie.addBehaviourGoals port crates/horizon-client/src/mobs/undead.rs::register break door, turtle egg, melee, village, stroll, targets +Zombie.createAttributes skip:data attribute defaults (follow 35, speed 0.23, damage 3, armour 2) live in data tables +Zombie.defineSynchedData skip:java synched-data registration; Horizon keeps baby, special type and drowned state in Mob fields +Zombie.populateDefaultEquipmentSlots port crates/horizon-client/src/mobs/finalize.rs::Ctx::populate armour, then 1 percent (5 on hard) of an iron sword, spear or shovel +Zombie.canHoldItem missing minor: no mob item pickup in Horizon, so the baby-egg rule never applies +Zombie.wantsToPickUp missing minor: no mob item pickup in Horizon, so the glow ink sac refusal never applies +Zombie.onOffspringSpawnedFromEgg missing unsure: no egg-offspring pickup roll found for zombies in Horizon +Zombie.ZombieAttackTurtleEggGoal.playDestroyProgressSound inline crates/horizon-client/src/mobs/undead.rs::AttackTurtleEgg::tick zombie.destroy_egg sound while the egg is trampled +Zombie.ZombieAttackTurtleEggGoal.playBreakSound inline crates/horizon-client/src/mobs/undead.rs::AttackTurtleEgg::tick turtle egg break sound when the egg breaks diff --git a/tools/port_map/game2-005.tsv b/tools/port_map/game2-005.tsv new file mode 100644 index 0000000000..7bc79c10a4 --- /dev/null +++ b/tools/port_map/game2-005.tsv @@ -0,0 +1,142 @@ +ZombieVillager.defineSynchedData skip:java SynchedEntityData definitions; Horizon keeps the fields on Mob and sets them at spawn +ZombieVillager.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt zombie villager save arm; cure and gossip go through zombie_villager::write_nbt +ZombieVillager.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_from_nbt load arm; cure and gossip go through zombie_villager::read_nbt +ZombieVillager.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize ZombieVillager arm: villager type, then profession, then zombie spawn +ZombieVillager.initializeZombieVillagerData port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize profession drawn in the same arm, from a seeded fresh RNG rather than the mob random +ZombieVillager.handleEntityEvent port crates/horizon-client/src/mobs/zombie_villager.rs::start_converting cure sound (event 16) is queued when the cure starts +ZombieVillager.setVillagerData inline crates/horizon-client/src/game/zombie_villagers.rs::carry_over gap: minor: the cure path copies type and profession there; no setter for the implicit component path +ZombieVillager.get skip:other DataComponents getter for the villager variant; Horizon has no entity component map +ZombieVillager.applyImplicitComponent missing minor: a zombie villager spawned from an item carrying a villager variant component keeps a random type; Horizon applies implicit components only to buckets (load_bucket_data) +ZombifiedPiglin.createAttributes skip:data attribute defaults are entity-type data read in Mob::new (mobs.rs) +ZombifiedPiglin.maybePlayFirstAngerSound port crates/horizon-client/src/mobs/undead.rs::zombified_piglin_step first-anger counter and sound run in the step +ZombifiedPiglin.maybeAlertOthers port crates/horizon-client/src/mobs/undead.rs::zombified_piglin_step alert timer and line-of-sight check run in the step +ZombifiedPiglin.alertOthers port crates/horizon-client/src/mobs/undead.rs::zombified_piglin_step gap: minor: no isAlliedTo check; vertical reach approximated +ZombifiedPiglin.playAngerSound port crates/horizon-client/src/mobs/undead.rs::zombified_piglin_step angry sound queued in the step with volume 2.0 +ZombifiedPiglin.setTarget port crates/horizon-client/src/mobs/undead.rs::zombified_piglin_step anger and timers start on the first tick with a target, not inside setTarget +ZombifiedPiglin.checkZombifiedPiglinSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules ZombifiedPiglin arm: not peaceful and not on a nether wart block +ZombifiedPiglin.checkSpawnObstruction port crates/horizon-client/src/mobs/spawn_selection.rs::spawn_obstruction the Mob default two tests: no liquid, unobstructed +CatSpawner.spawnInVillage port crates/horizon-client/src/game/cat_spawner.rs::Game::spawn_cat_in_village village cats: more than four claimed beds within 48 blocks and fewer than five cats +CatSpawner.spawnCat port crates/horizon-client/src/game/cat_spawner.rs::Game::spawn_cat black variant inside cats_spawn_as_black structures; persistence flag from the caller +InventoryCarrier.pickUpItem port crates/horizon-client/src/game/villagers.rs::Game::villager_pickups gap: minor: villager arm only; piglins, sulfur cubes, pandas and others have their own pickup fns +AbstractVillager.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize villager arm: type, then the baby group +AbstractVillager.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt Offers in the villager arm; inventory via villager::write_nbt +AbstractVillager.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::read_villager Offers read here; inventory via villager::read_nbt +AbstractVillager.getSlot missing minor: commands cannot address a villager inventory slot (container.N); mob slot names cover weapon, armour and saddle only +AbstractVillager.addOffersFromItemListings port crates/horizon-client/src/merchant.rs::Trades::offers_from_set the allow_duplicates path: a picked trade stays in the pool +AbstractVillager.addOffersFromItemListingsWithoutDuplicates port crates/horizon-client/src/merchant.rs::Trades::offers_from_set the no-duplicates path: a picked trade leaves the pool +AbstractVillager.getRopeHoldPosition skip:ui lead rope drawing point only; the lead is drawn by leash_geometry in game.rs +AbstractVillager.stillValid missing player: the trade screen stays open after you walk away from the villager; vanilla closes it beyond 4 blocks, and no reach check exists for merchant screens +Villager.ageBoundaryReached missing player: a villager that grows up keeps the baby brain (the brain is built once with its baby flag), so it never goes to work +Villager.startTrading port crates/horizon-client/src/game/trading.rs::Game::interact_trader opens the trade screen for the player +Villager.resetSpecialPrices port crates/horizon-client/src/merchant.rs::update_special_prices recomputes special prices from scratch, which drops the old diff +Villager.needsToRestock inline crates/horizon-client/src/merchant.rs::Trader::should_restock any offer with uses left +Villager.allowedToRestock inline crates/horizon-client/src/merchant.rs::Trader::should_restock two restocks a day at most, 2400 ticks apart +Villager.catchUpDemand inline crates/horizon-client/src/merchant.rs::Trader::should_restock missed demand updates on a new day +Villager.defineSynchedData skip:java synched data definitions; Horizon keeps the fields on Mob +Villager.createDefaultVillagerData skip:data default type, profession and level; the spawn defaults of the Horizon mob fields +Villager.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::mob_to_nbt villager arm: data, xp, restocks, offers; food, gossip and decay via villager::write_nbt +Villager.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::read_villager data and offers here; food, gossip, decay and inventory via villager::read_nbt +Villager.getAmbientSound port crates/horizon-client/src/mobs/ai.rs::play_ambient gap: minor: the trade ambient sound is not played while trading +Villager.rewardTradeXp port crates/horizon-client/src/merchant.rs::Session::notify_trade gap: minor: no 10 second Regeneration on the villager at level-up +Villager.die port crates/horizon-client/src/game/villagers.rs::Game::villager_murdered gap: minor: releaseAllPois is not done on death +Villager.releaseAllPois missing unsure: releasePoi frees the bed, job site, potential job site and meeting point claims; Horizon has no claim store, only the block occupied property, so the effect is unclear +Villager.releasePoi missing unsure: same as releaseAllPois; the claim store is not clear in Horizon +Villager.eatUntilFull port crates/horizon-client/src/mobs/villager.rs::Ext::eat_and_digest +Villager.shouldIncreaseLevel inline crates/horizon-client/src/merchant.rs::Session::notify_trade level-up check: can level up and xp at the maximum +Villager.increaseMerchantCareer inline crates/horizon-client/src/merchant.rs::Session::notify_trade level plus the new level trade set +Villager.handleEntityEvent port crates/horizon-client/src/game/villagers.rs::Game::entity_event_particles villager event particles from the entity event table +Villager.finalizeSpawn port crates/horizon-client/src/mobs/finalize.rs::Ctx::finalize villager arm: breeding resets the profession, then type and the baby group +Villager.thunderHit port crates/horizon-client/src/game/lightning.rs::Game::thunder_hit villager struck by lightning becomes a witch; gap: minor: releaseAllPois is not done on conversion +Villager.wantsToPickUp port crates/horizon-client/src/game/villagers.rs::Game::villager_pickups the want test (food, pick-up tag, requested items) inside the pickup loop +Villager.countFoodPointsInInventory port crates/horizon-client/src/mobs/villager.rs::Ext::food_in_inventory +Villager.updateTrades port crates/horizon-client/src/game/trading.rs::Game::ensure_offers generates the trade set for profession and level; the level-up reroll is in merchant.rs notify_trade +Villager.maybeDecayGossip port crates/horizon-client/src/mobs/villager.rs::Ext::maybe_decay +Villager.golemSpawnConditionsMet port crates/horizon-client/src/mobs/villager.rs::Ext::wants_golem sleep memory check; the golem cooldown is an extra check here +Villager.get skip:other DataComponents getter for the villager variant; Horizon has no entity component map +Villager.applyImplicitComponent missing minor: a villager spawned from an item carrying a villager variant component keeps its biome type; Horizon applies implicit components only to buckets +VillagerData.withType skip:java immutable record copy; Horizon keeps type and profession as Mob fields +VillagerData.withProfession skip:java immutable record copy; the profession change is villager::set_profession +VillagerProfession.bootstrap skip:data registry bootstrap of professions; Horizon reads them from data tables +VillagerType.bootstrap skip:data registry bootstrap of villager types; Horizon reads them from data tables +WanderingTrader.registerGoals port crates/horizon-client/src/mobs/trader.rs::register the trader goal set; the shared register in mobs/ai.rs is not repeated here +WanderingTrader.updateTrades port crates/horizon-client/src/game/trading.rs::Game::ensure_offers the buying, uncommon and common sets of the wandering branch +WanderingTrader.addAdditionalSaveData missing minor: DespawnDelay and wander_target are not written; the trader save writes only its offers +WanderingTrader.readAdditionalSaveData missing minor: DespawnDelay and wander_target are not read back +WanderingTrader.rewardTradeXp port crates/horizon-client/src/merchant.rs::Session::notify_trade the orb for offers that reward experience; the trader gets no villager XP +WanderingTrader.WanderToPositionGoal.isTooFarAway port crates/horizon-client/src/mobs/trader.rs::too_far +WanderingTraderSpawner.getTraderData port crates/horizon-client/src/game/wandering_trader.rs::Game::load_trader_data +WanderingTraderSpawner.tryToSpawnLlamaFor port crates/horizon-client/src/game/wandering_trader.rs::Game::spawn_trader_llama +WanderingTraderSpawner.findSpawnPositionNear port crates/horizon-client/src/game/wandering_trader.rs::Game::find_trader_spawn_near +WanderingTraderSpawner.hasEnoughSpace port crates/horizon-client/src/game/wandering_trader.rs::Game::trader_has_space +Inventory.setSelectedSlot port crates/horizon-world/src/player_inventory.rs::PlayerInventory::set_selected invalid slot is an error here, not a thrown exception +Inventory.hasRemainingSpaceForItem port crates/horizon-world/src/inventory_pickup.rs::remaining +Inventory.getFreeSlot port crates/horizon-world/src/player_inventory.rs::PlayerInventory::free_slot +Inventory.addAndPickItem port crates/horizon-world/src/player_inventory.rs::PlayerInventory::add_and_pick +Inventory.pickSlot port crates/horizon-world/src/player_inventory.rs::PlayerInventory::pick_slot +Inventory.findSlotMatchingItem port crates/horizon-world/src/player_inventory.rs::PlayerInventory::find_matching +Inventory.findSlotMatchingCraftingIngredient port crates/horizon-world/src/player_inventory.rs::PlayerInventory::find_crafting +Inventory.getSuitableHotbarSlot port crates/horizon-world/src/player_inventory.rs::PlayerInventory::suitable_hotbar +Inventory.removeItem port crates/horizon-world/src/player_inventory.rs::PlayerInventory::remove slots past the main inventory address equipment +Inventory.removeItemNoUpdate port crates/horizon-world/src/player_inventory.rs::PlayerInventory::remove_no_update +Inventory.setItem port crates/horizon-world/src/player_inventory.rs::PlayerInventory::set slots past the main inventory address equipment +Inventory.save port crates/horizon-client/src/save.rs::Level::player_tag writes the non-empty slots of the Inventory list +Inventory.isEmpty port crates/horizon-world/src/player_inventory.rs::PlayerInventory::is_empty +Inventory.getItem port crates/horizon-world/src/player_inventory.rs::PlayerInventory::get slots past the main inventory address equipment +Inventory.replaceWith port crates/horizon-world/src/player_inventory.rs::PlayerInventory::replace_with +Inventory.removeFromSelected port crates/horizon-world/src/player_inventory.rs::PlayerInventory::remove_selected +Player.defineSynchedData skip:java synched data definitions; Horizon keeps the fields in game and player state +Player.turtleHelmetTick port crates/horizon-client/src/game/potions.rs::Game::turtle_helmet_tick +Player.isEquipped inline crates/horizon-client/src/game/potions.rs::Game::turtle_helmet_tick the head-slot check for the turtle helmet +Player.canPlayerFitWithinBlocksAndEntitiesWhen missing unsure: the pose fit check (no collision in the new pose box) was not found in Horizon +Player.increaseScore skip:other the score is experience.total in Horizon (death screen); there is no separate score counter +Player.getWeaponItem missing unsure: the riptide spin-attack item (autoSpinAttackItemStack) was not found; Horizon keeps spin_attack parts only +Player.destroyVanishingCursedItems port crates/horizon-client/src/game.rs::Game::drop_inventory vanishing-curse stacks are removed from the death drops +Player.hasCorrectToolForDrops port crates/horizon-client/src/game.rs::Game::tick_mining the correct-tool test for harvest drops +Player.readAdditionalSaveData port crates/horizon-client/src/save.rs::Level::read_player +Player.addAdditionalSaveData port crates/horizon-client/src/save.rs::Level::player_tag +Player.hurtHelmet inline crates/horizon-client/src/game.rs::Game::hurt_armor gap: minor: hurt_armor hits all four armour slots; the head-only hit from LivingEntity.hurt is not separated +Player.isAboveGround inline crates/horizon-world/src/edge_backoff.rs::back_off_from_edge the on-the-edge test before backing off +Player.canFallAtLeast inline crates/horizon-world/src/edge_backoff.rs::back_off_from_edge the no-collision drop test at the edge +Player.deflectProjectile port crates/horizon-client/src/game/deflect.rs::Game::attack_redirectable +Player.baseDamageScaleFactor port crates/horizon-client/src/game.rs::Game::sweep_base_damage +Player.isEffectiveAi skip:other client-side AI gate; Horizon has no effective-AI check in its loop +Player.hasContainerOpen missing unsure: the test for an open container menu (not the inventory menu) was not located +Player.startSleepInBed port crates/horizon-client/src/game.rs::Game::try_sleep +Player.isSleepingLongEnough missing unsure: the 100-tick sleep counter gate for the night skip was not found; the sleep timer only drives the fade +Player.updateSwimming port crates/horizon-client/src/motion.rs::MotionWorld::swimming +Player.getXpNeededForNextLevel port crates/horizon-client/src/entities.rs::Experience::needed +Player.hasEnoughFoodToDoExhaustiveManoeuvres missing unsure: the food-or-flying test for exhaustive moves was not found in motion.rs +Player.mayUseItemAt port crates/horizon-client/src/game.rs::Game::use_hand the may-build test before use-on in adventure mode +Player.isSwimming port crates/horizon-client/src/motion.rs::MotionWorld::swimming +Player.decorateDisplayNameComponent missing unsure: click and hover behaviour on player names in chat was not found +Player.getSlot port crates/horizon-client/src/game/commands/items.rs::Game::slots_named gap: minor: numeric slots 499 (carried item) and 500-503 (crafting grid) are not addressed +Player.extractParrotVariant missing unsure: the parrot variant read from a shoulder parrot NBT was not found in shoulders.rs +Player.convertParrotVariant skip:java Optional and OptionalInt conversion of the synched parrot variant; Horizon stores the id directly +Player.getItemSwapScale port crates/horizon-client/src/attack_strength.rs::AttackStrength::swap_scale +Player.debugInfo skip:debug +Player.printPlayerPermissions skip:debug +PlayerEquipment.set port crates/horizon-world/src/player_equipment.rs::set main hand goes to the selected slot +PlayerEquipment.get port crates/horizon-world/src/player_equipment.rs::get main hand reads the selected slot +ProfilePublicKey.Data.hasExpired port crates/horizon-protocol/src/secure_chat.rs::ProfileKey::has_expired gap: minor: the grace-period overload is not separate +ProfilePublicKey.Data.equals skip:java value equality of the key record +StackedContents.take inline crates/horizon-client/src/menu/place.rs::Picker::try_pick the count subtraction when a pick succeeds +StackedContents.getUniqueAvailableIngredientItems inline crates/horizon-client/src/menu/place.rs::Picker::new the stocked items that some ingredient accepts +StackedContents.anyIngredientMatches inline crates/horizon-client/src/menu/place.rs::Picker::new the accepts test used to filter items +StackedContents.RecipePicker.setInitialConnections port crates/horizon-client/src/menu/place.rs::Picker::new the connection matrix is built in the constructor +StackedContents.RecipePicker.findNewItemAssignmentPath port crates/horizon-client/src/menu/place.rs::Picker::find_path +StackedContents.RecipePicker.getSatisfiedIndex skip:java bitset index arithmetic; Horizon keeps satisfied as a plain bool vector +StackedContents.RecipePicker.getConnectionIndex port crates/horizon-client/src/menu/place.rs::Picker::at flat index of an item-ingredient pair +StackedContents.RecipePicker.assign inline crates/horizon-client/src/menu/place.rs::Picker::try_pick the assigned flag set along a successful path +StackedContents.RecipePicker.unassign inline crates/horizon-client/src/menu/place.rs::Picker::try_pick the assigned flag cleared when a path is undone +StackedContents.RecipePicker.getResidualIndex port crates/horizon-client/src/menu/place.rs::Picker::at flat index of the assignment, same layout as connections +StackedContents.RecipePicker.getVisitedIngredientIndex skip:java bitset index arithmetic; plain bool vectors in Horizon +StackedContents.RecipePicker.getVisitiedItemIndex skip:java bitset index arithmetic; plain bool vectors in Horizon +StackedContents.RecipePicker.clearAllVisited port crates/horizon-client/src/menu/place.rs::Picker::clear_visited +StackedItemContents.getBiggestCraftableStack port crates/horizon-client/src/menu/place.rs::Stacked::try_pick_all the largest craftable count, found by a bounded search +EvokerFangs.readAdditionalSaveData missing minor: fangs are not saved; a save in mid-bite drops them +EvokerFangs.addAdditionalSaveData missing minor: fangs are not saved; the warmup and owner are not written +EvokerFangs.getAnimationProgress port crates/horizon-client/src/game/fangs.rs::Game::fangs_geometry the bite progress that positions the fang model +EyeOfEnder.setItem skip:ui the item sprite a flying eye shows; Horizon draws the eye itself +EyeOfEnder.shouldRenderAtSqrDistance skip:ui render distance culling only +EyeOfEnder.spawnParticles port crates/horizon-client/src/game/end.rs::Game::eye_break_particles the portal crumbs drawn in toward the eye +EyeOfEnder.readAdditionalSaveData missing minor: flying eyes are not saved; a save mid-flight loses them diff --git a/tools/port_map/game2-006.tsv b/tools/port_map/game2-006.tsv new file mode 100644 index 0000000000..291d90935e --- /dev/null +++ b/tools/port_map/game2-006.tsv @@ -0,0 +1,140 @@ +FireworkRocketEntity. port crates/horizon-client/src/game/fireworks.rs::Game::new_rocket launch velocity triangle and lifetime 10*(1+flight)+randoms; the owner and attached target are set by the callers +FireworkRocketEntity.defineSynchedData skip:java SynchedEntityData definitions; Horizon keeps the rocket fields on the Rocket struct +FireworkRocketEntity.shouldRender port crates/horizon-client/src/game/fireworks.rs::Game::rocket_geometry attached rockets are not drawn; camera range is the caller's check +FireworkRocketEntity.onHitEntity missing player: a firework shot from a crossbow or dispenser flies through mobs and bursts only on a block or at its timer end; Horizon has no entity hit test for rockets +FireworkRocketEntity.onHitBlock inline crates/horizon-client/src/game/fireworks.rs::Game::tick_rockets a block hit ends the angled rocket early and explodes it; vanilla explodes only when it has explosions and skips the block entityInside effect +FireworkRocketEntity.dealExplosionDamage port crates/horizon-client/src/game/fireworks.rs::Game::explode_rocket damage 5+2 per explosion; attached target takes the full blast, others within 5 blocks with line of sight +FireworkRocketEntity.addAdditionalSaveData missing minor: rocket life, lifetime and item are not written to a save; Horizon does not save flying rockets +FireworkRocketEntity.readAdditionalSaveData missing minor: rocket life, lifetime and item are not read back; Horizon does not save flying rockets +FireworkRocketEntity.getExplosions port crates/horizon-client/src/game/fireworks.rs::fireworks_of explosion list from the item's fireworks component, empty without one +FireworkRocketEntity.calculateHorizontalHurtKnockbackDirection inline crates/horizon-client/src/game/fireworks.rs::Game::explode_rocket knockback direction is the blast point to the target, which the mob damage gets as its from point +FishingHook. port crates/horizon-client/src/game/fishing.rs::Game::use_fishing_rod the player cast: start point, velocity with the 0.0103365 triangle, luck and lure from the rod +FishingHook.defineSynchedData skip:java synced hooked id and biting flag; Horizon keeps them on the Hook struct and sends them in Hook::track +FishingHook.canHitEntity port crates/horizon-client/src/game/fishing.rs::Game::tick_fishing hook catches living mobs, other players (not the owner) and dropped items it touches +FishingHook.onHitEntity port crates/horizon-client/src/game/fishing.rs::Game::tick_fishing sets the Hooked state on the first target the bobber touches +FishingHook.onHitBlock port crates/horizon-client/src/game/fishing.rs::Game::tick_fishing the bobber stops where its movement hits a block (scene resolve) +FishingHook.setHookedEntity port crates/horizon-client/src/game/fishing.rs::Game::tick_fishing Hooked(id) state; the id goes out as meta 8 in Hook::track +FishingHook.getOpenWaterTypeForArea inline crates/horizon-client/src/game/fishing.rs::Game::calculate_open_water the area is walked layer by layer and folded into the layer check +FishingHook.getOpenWaterTypeForBlock port crates/horizon-client/src/game/fishing.rs::Game::open_water_type air or lily pad above water, still water source inside, anything else invalid +FishingHook.handleEntityEvent inline crates/horizon-client/src/game/fishing.rs::Game::retrieve_hook event 31 pull: retrieve_hook pulls the hooked mob, item or player when the rod is reeled in +FishingHook.getPlayerOwner skip:other owner is implicit: the host's hook is self.hook and a guest's is in that guest's play state +FishingHook.getAddEntityPacket port crates/horizon-client/src/game/fishing.rs::Hook::track add-entity payload: the owner entity id is the bobber's data field +LlamaSpit.onHitEntity port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit spit hit: 1.0 damage with the spit source to a player (mob_hits_player) or a mob +Projectile.applyImplicitComponent missing minor: a projectile given a can_break component by its item keeps no break predicate; Horizon applies implicit components only to buckets +Projectile.addAdditionalSaveData missing minor: owner, LeftOwner and HasBeenShot are not written; Horizon does not save flying projectiles +Projectile.ownedBy inline crates/horizon-client/src/projectiles.rs::Projectile::tick the owner is skipped in the target loop until the projectile has left it +Projectile.readAdditionalSaveData missing minor: owner, LeftOwner, HasBeenShot and can_break are not read back; Horizon does not save flying projectiles +Projectile.restoreFrom missing minor: the owner is not carried over a dimension change; Horizon has no projectile dimension change +Projectile.isOutsideOwnerCollisionRange inline crates/horizon-client/src/projectiles.rs::Projectile::tick gap: minor: left_owner tests the owner's box inflated 1.0 at the position only; vanilla sweeps along the motion and checks the owner's passengers too +Projectile.getMovementToShoot port crates/horizon-client/src/projectiles.rs::Projectile::shoot direction plus triangle spread 0.0172275 times uncertainty, scaled by power +Projectile.onAboveBubbleColumn port crates/horizon-client/src/arrows.rs::Ticker::bubble_column gap: minor: arrows only; thrown ender pearls get their push in creatures.rs tick_projectiles and other projectiles get none +Projectile.onInsideBubbleColumn port crates/horizon-client/src/arrows.rs::Ticker::bubble_column gap: minor: arrows only; thrown ender pearls get their push in creatures.rs tick_projectiles and other projectiles get none +Projectile.canBreakBlockInAdventureMode missing minor: the can_break predicate and adventure-mode rules for projectile block breaking are not modelled +Projectile.spawnProjectileFromRotation port crates/horizon-client/src/projectiles.rs::Projectile::shoot_from_rotation gap: minor: the aim and inherited velocity only; the add-to-world step is the caller's +Projectile.spawnProjectile inline crates/horizon-client/src/game/creatures.rs::Game::throw_item each shooter adds its own projectile (throw_item, shoot_crossbow, shoot_projectile_item, the bow in game.rs); throw_item is the player throw path +Projectile.applyOnProjectileSpawned port crates/horizon-client/src/game/enchant_effects.rs::Game::bow_projectile_ignite gap: minor: Flame ignite only; Piercing and Punch go through Projectile::set_bonus +Projectile.hitTargetOrDeflectSelf port crates/horizon-client/src/game/creatures.rs::Game::tick_projectiles gap: minor: deflection is checked for breezes only; the onHit dispatch is creatures.rs projectile_hit +Projectile.onRedirectProjectile port crates/horizon-client/src/game/deflect.rs::Game::deflect gap: minor: the player's attack turns the projectile; a projectile hitting another redirectable one is not modelled +Projectile.recreateFromPacket port crates/horizon-client/src/game/remote_projectiles.rs::Game::spawn_remote_projectile the owner id from the add-entity data is set on the remote projectile +Projectile.mayBreak missing player: a projectile hitting a decorated pot, chorus flower or pointed dripstone does not break it; no projectile break path found +Projectile.calculateHorizontalHurtKnockbackDirection inline crates/horizon-client/src/game/creatures.rs::Game::projectile_hit gap: minor: knockback goes from the shot's previous point, while vanilla uses the projectile's horizontal motion +ProjectileUtil.getHitResultOnMoveVector port crates/horizon-client/src/projectiles.rs::Projectile::tick sweep along the movement: block clip, then entity boxes; gap: minor: entity margin is a fixed 0.3, vanilla ramps it up over the first ticks +ProjectileUtil.getHitResultOnViewVector port crates/horizon-client/src/game/pick_entity.rs::pick_entity_hit gap: minor: the brush reach is the caller's; there is no separate view-vector trace +ProjectileUtil.getHitResult port crates/horizon-client/src/projectiles.rs::Projectile::tick block clip first, then the nearest entity box on the segment; same core as the move-vector sweep +ProjectileUtil.rotateTowardsMovement port crates/horizon-client/src/projectiles.rs::Projectile::update_rotation gap: minor: rotation is set at once, vanilla lerps it by rotationSpeed +ProjectileUtil.getMobArrow port crates/horizon-client/src/mobs/undead.rs::perform_ranged_attack gap: minor: builds the skeleton-family arrow with base damage from power; illager crossbow arrows are built in mobs/illager.rs::shoot_crossbow +ShulkerBullet.addAdditionalSaveData missing minor: the final target and move direction are not saved; Horizon does not save flying projectiles +ShulkerBullet.readAdditionalSaveData missing minor: the final target and move direction are not read back; Horizon does not save flying projectiles +ShulkerBullet.canHitEntity missing minor: no-physics entities are not skipped by the bullet's target search; Projectile::tick checks owner and boxes only +ShulkerBullet.onHitBlock port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit shulker bullet block hit: hit sound and two explosion particles at the bullet +ShulkerBullet.destroy inline crates/horizon-client/src/game/creatures.rs::Game::tick_projectiles the bullet is dropped from the flight list on any hit; the game event is not sent +ShulkerBullet.hurtServer missing player: a hit on a shulker bullet does not pop it (no hurt path for projectiles), so it keeps flying until it hits something else +ThrowableProjectile.applyInertia port crates/horizon-client/src/projectiles.rs::Projectile::tick default arm: gravity, then drag 0.8 in water and 0.99 in air; gap: minor: no bubble particles in water +ThrowableProjectile.handleFirstTickBubbleColumn missing minor: a thrown item that spawns inside a bubble column is not pushed on its first tick +AbstractArrow. port crates/horizon-client/src/arrows.rs::ArrowState::new arrow defaults: pickup, pickup item, shake, pierce; the owner and drawn ammo come from set_owner and set_item +AbstractArrow.defineSynchedData skip:java flags, pierce level and in-ground sync fields; Horizon keeps them on ArrowState and Projectile +AbstractArrow.lerpMotion skip:other client interpolation of a server motion packet; Horizon's remote arrows take the server position each update +AbstractArrow.onSyncedDataUpdated inline crates/horizon-client/src/arrows.rs::Ticker::on_hit_block the 7-tick shake starts when the arrow lands here; vanilla starts it when the client sees it stuck +AbstractArrow.hitTargetsOrDeflectSelf port crates/horizon-client/src/arrows.rs::Ticker::step_move_and_hit gap: minor: the nearest entity on the way is taken per tick; vanilla goes on through the rest within the same tick +AbstractArrow.addBubbleParticles port crates/horizon-client/src/projectiles.rs::Projectile::arrow_trail four bubble particles along a submerged arrow +AbstractArrow.move inline crates/horizon-client/src/arrows.rs::Ticker::tick should_fall then start_falling run from the tick for an arrow in the ground +AbstractArrow.resetPiercedEntities inline crates/horizon-client/src/arrows.rs::Ticker::on_hit_block the pierced list is cleared when the arrow lands +AbstractArrow.doKnockback port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit punch pushes along the flight, scaled by 0.6 and by knockback resistance +AbstractArrow.findHitEntity port crates/horizon-client/src/arrows.rs::Ticker::entity_hit box clip, then the margin test against the segment +AbstractArrow.findHitEntities port crates/horizon-client/src/arrows.rs::Ticker::step_move_and_hit gap: minor: only the nearest hit per tick is returned; a piercing arrow takes the rest on later ticks +AbstractArrow.canHitEntity port crates/horizon-client/src/arrows.rs::Ticker::step_move_and_hit gap: minor: skips pierced entities here; the player canHarmPlayer rule is in creatures.rs tick_projectiles +AbstractArrow.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::arrow_from_nbt shake, in ground, in block state, pickup and the arrow fields +AbstractArrow.tryPickup port crates/horizon-client/src/arrows.rs::ArrowState::player_touch disallowed, allowed (gives the pickup item) and creative only +AbstractArrow.setFlag port crates/horizon-client/src/projectiles.rs::Projectile::set_critical gap: minor: only the critical flag; the no-physics flag is a plain field +AbstractArrow.setPickupItemStack port crates/horizon-client/src/arrows.rs::ArrowState::set_item an empty stack falls back to the default pickup item +AbstractArrow.isNoPhysics inline crates/horizon-client/src/arrows.rs::Ticker::tick no_physics is a field read by the tick +AbstractArrow.getSlot missing minor: the arrow's pickup stack is not reachable as an entity slot, so item commands on it do nothing +AbstractArrow.Pickup.byOrdinal port crates/horizon-client/src/arrows.rs::Pickup::from_byte out of range is DISALLOWED +Arrow.updateColor missing player: a tipped arrow keeps no potion colour, so its flight and hit particles are not tinted +Arrow.makeParticle missing player: a tipped arrow in flight leaves no coloured entity-effect trail +Arrow.handleEntityEvent missing player: a tipped arrow that hits does not burst into its potion colour (event 0 has no port) +ThrownTrident.defineSynchedData skip:java loyalty and foil synced fields; Horizon keeps loyalty on the trident struct +ThrownTrident.isAcceptibleReturnOwner port crates/horizon-client/src/game/trident.rs::Game::tick_tridents the loyalty return needs the owner's eye position; gap: minor: no separate alive or spectator check +ThrownTrident.findHitEntities port crates/horizon-client/src/arrows.rs::Ticker::step_move_and_hit one entity hit per trident, none once it has dealt damage +ThrownTrident.hitBlockEnchantmentEffects port crates/horizon-client/src/game/trident.rs::Game::channel_rod_hit gap: minor: only Channeling's hit_block effect (lightning on a rod in a storm) is modelled +ThrownTrident.tryPickup port crates/horizon-client/src/arrows.rs::ArrowState::player_touch gap: minor: no owner check on a returned trident; any player in reach takes it when it is no-physics +ThrownTrident.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::arrow_from_nbt gap: minor: DealtDamage is not read back; the loyalty level comes from the release +ThrownTrident.getLoyaltyFromItem port crates/horizon-client/src/game/trident.rs::Game::release_trident loyalty level read from the thrown stack when the trident is released +ThrownTrident.tickDespawn inline crates/horizon-client/src/arrows.rs::Ticker::tick gap: minor: the 1200-tick life limit applies to every trident; the loyalty exemption is not checked +AbstractHurtingProjectile.shouldRenderAtSqrDistance skip:ui distance culling for the fireball model; Horizon culls with its own range check +AbstractHurtingProjectile.canHitEntity missing minor: no-physics entities are not skipped by the fireball's target search +WitherSkull.getBlockExplosionResistance missing minor: a dangerous skull's blast does not cap block resistance at 0.8; the blast call takes no dangerous flag +WitherSkull.onHit port crates/horizon-client/src/game/wither.rs::Game::wither_skull_hit skull hit damage, wither effect, then the blast at the impact point +AbstractWindCharge.makeBoundingBox port crates/horizon-client/src/projectiles.rs::ProjectileKind::size gap: minor: gives the width only; the box sits 0.15 below the position in vanilla +AbstractWindCharge.canHitEntity missing minor: no filter for end crystals or other wind charges found in the target list; unsure whether the caller excludes them +AbstractWindCharge.onHitBlock port crates/horizon-client/src/game/wind.rs::Game::wind_charge_hit a burst a quarter block out along the hit face +WindCharge.deflect port crates/horizon-client/src/game/wind.rs::reverse gap: minor: turns the charge at half speed; the noDeflectTicks delay is not modelled +WindCharge.shouldRenderAtSqrDistance skip:ui distance culling for the wind charge model; Horizon culls with its own range check +ThrownEgg.onHitEntity port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit egg hit on a mob: zero damage, then the egg break and chick roll run on any landing +ThrownEnderpearl.deregisterFromCurrentOwner skip:other chunk-ticket bookkeeping for a player's pearls; Horizon has no chunk tickets for flying pearls +ThrownEnderpearl.registerToCurrentOwner skip:other chunk-ticket bookkeeping for a player's pearls; Horizon has no chunk tickets for flying pearls +ThrownEnderpearl.getOwner skip:other owner is kept as a Who value and resolved by the caller +ThrownEnderpearl.findOwnerIncludingDeadPlayer skip:other owner lookup by UUID across dead players; Horizon keeps the owner id directly +ThrownEnderpearl.onHitEntity port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit a mob hit takes zero damage; the pearl still lands +ThrownEnderpearl.onHit port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit gap: minor: no 32 portal particles and no dimension change; the owner is teleported to where the pearl was, with 5 damage and a 5 percent endermite +ThrownEnderpearl.isAllowedToTeleportOwner missing minor: a pearl whose owner is dead or asleep when it lands still teleports the owner +ThrownEnderpearl.playSound port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit teleport sound at the landing point +ThrownEnderpearl.teleport port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit moves the player to the pearl's previous position and clears velocity and fall distance +ThrownEnderpearl.canTeleport missing minor: a pearl from the End to the Overworld needs the owner's seen-credits flag before it may teleport +ThrownEnderpearl.onInsideBlock missing player: a pearl that flies into an end gateway does not send its owner through it +Raid. port crates/horizon-client/src/raid.rs::Raid::new centre, difficulty-based group count, cooldown and active flag +Raid.getAllRaiders port crates/horizon-client/src/game/raids.rs::Game::raid_members the raiders of this raid, from the mobs carrying its id +Raid.getValidSpawnPos port crates/horizon-client/src/raid.rs::Raid::find_spawn_pos eight tries around the centre, as findRandomSpawnPos(level, 8) +Raid.getHealthOfLivingRaiders port crates/horizon-client/src/raid.rs::living_health sum of the living raiders' health +Raid.getBannerComponentPatch port crates/horizon-client/src/mobs/raider.rs::ominous_banner the ominous banner stack: its patterns, hidden tooltip, name and rarity +Raid.getDefaultNumSpawns port crates/horizon-client/src/raid.rs::Raid::wave_count gap: minor: the base count is in the same fn as the bonus and extra rolls +Raid.getPotentialBonusSpawns port crates/horizon-client/src/raid.rs::Raid::wave_count extra roll per type and difficulty: vindicators and pillagers, witches after wave 2, ravagers on the bonus wave +Raider.hasRaid port crates/horizon-client/src/mobs/raider.rs::in_raid gap: minor: checks only the raid id the raider holds; vanilla also looks for a raid at its position +Raider.readAdditionalSaveData missing minor: Wave, CanJoinRaid and RaidId are not read back from a save, so a loaded raider loses its wave and raid link +Raider.pickUpItem inline crates/horizon-client/src/game/raids.rs::Game::sync_raiders the banner pickup branch makes the raider the wave leader; gap: minor: the item already worn in the head slot is not dropped first +Raider.ObtainRaidLeaderBannerGoal.cannotPickUpBanner port crates/horizon-client/src/mobs/raider.rs::ObtainBanner::can_use gap: minor: checks only a known, reachable banner; the raid-over, can-be-leader and head-slot rules are not repeated here +Raider.RaiderMoveThroughVillageGoal.isValidRaid inline crates/horizon-client/src/mobs/raider.rs::MoveThroughVillage::can_use active raid and not over, checked first +Raider.RaiderMoveThroughVillageGoal.hasSuitablePoi inline crates/horizon-client/src/mobs/raider.rs::MoveThroughVillage::can_use gap: minor: picks from the known homes within 48 blocks; no POI search or occupancy check +Raider.RaiderMoveThroughVillageGoal.hasNotVisited inline crates/horizon-client/src/mobs/raider.rs::MoveThroughVillage::can_use homes already visited are filtered out of the candidates +Raider.RaiderMoveThroughVillageGoal.updateVisited inline crates/horizon-client/src/mobs/raider.rs::MoveThroughVillage::can_use the visited list is trimmed first in can_use, as vanilla canUse does +Raids.getId skip:other raids are looked up by their id, so no reverse lookup from a raid to its id is needed +Raids.canJoinRaid inline crates/horizon-client/src/game/raids.rs::Game::sync_raiders alive, the join flag and no action for 2400 ticks are checked in the raider sync +Raids.getOrCreateRaid port crates/horizon-client/src/raid.rs::Raids::create_or_extend the raid at the position is reused, otherwise a new one is made +Raids.getRaidCentersInChunk skip:debug the raid centres per chunk feed the debug raid markers only +PriorityProvider.Selector.codec skip:java record codec for a variant selector's condition and priority +VariantUtils.getDefaultOrAny port crates/horizon-client/src/mobs/finalize.rs::variant_index gap: minor: name to index with unknown names falling back to index 0; no registry holder lookup +VariantUtils.writeVariant inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt the variant key is written with the rest of the mob's save data +VariantUtils.readVariant port crates/horizon-client/src/entity_nbt.rs::read_variant reads the variant key from the save +ContainerEntity.addChestVehicleSaveData port crates/horizon-client/src/game/minecarts.rs::Minecart::to_nbt writes the cart's items and any loot table +ContainerEntity.readChestVehicleSaveData port crates/horizon-client/src/game/minecarts.rs::Minecart::from_nbt reads the cart's items from the save +ContainerEntity.chestVehicleDestroyed port crates/horizon-client/src/game/minecarts.rs::Game::hurt_cart a destroyed cart drops its items when entity drops are on +ContainerEntity.unpackChestVehicleLootTable missing unsure: Minecart.loot is only read and written with the save and is never set here, so a chest minecart's loot table may never roll into its slots +ContainerEntity.isChestVehicleEmpty inline crates/horizon-client/src/game/minecarts.rs::Minecart::container_signal the comparator signal reads the slots' emptiness; gap: minor: no separate empty test +ContainerEntity.removeChestVehicleItemNoUpdate skip:other slot edits happen on the chest block entity under the cart's key; store_cart_items and close_cart_container copy them back +ContainerEntity.setChestVehicleItem skip:other slot edits happen on the chest block entity under the cart's key; store_cart_items and close_cart_container copy them back +ContainerEntity.getChestVehicleSlot skip:other slot access happens on the chest block entity under the cart's key; store_cart_items and close_cart_container copy them back +DismountHelper.canDismountTo port crates/horizon-client/src/game/respawn_anchor.rs::Game::safe_dismount_location gap: minor: tests the floor and a 1.8 block box for the player, not the passenger's pose box +DismountHelper.findCeilingFrom inline crates/horizon-client/src/game/respawn_anchor.rs::Game::safe_dismount_location gap: minor: head room comes from the 1.8 box collision, not a ceiling scan +VehicleEntity.ignoreExplosion missing minor: with mobGriefing off a mob's blast still damages carts and boats; the explosion code has no mob-source rule for them +VehicleEntity.destroy port crates/horizon-client/src/game/boats.rs::Game::hurt_boat boats drop their item when entity drops are on; a minecart breaks through hurt_cart +VehicleEntity.defineSynchedData skip:java hurt time, hurt direction and damage fields; Horizon keeps them on the cart and boat structs diff --git a/tools/port_map/game2-007.tsv b/tools/port_map/game2-007.tsv new file mode 100644 index 0000000000..9250454527 --- /dev/null +++ b/tools/port_map/game2-007.tsv @@ -0,0 +1,147 @@ +AbstractBoat.setInitialPos port crates/horizon-client/src/game/boats.rs::Boat::new spawn sets the position and its previous copy; velocity and yaw start at zero +AbstractBoat.defineSynchedData skip:java SynchedEntityData definitions; Horizon keeps paddle, hurt and input state on the Boat struct +AbstractBoat.getPassengerAttachmentPoint port crates/horizon-client/src/game/boats.rs::Boat::seat seat height and the two-seat offsets, +0.2 for an animal; chest boat's 0.15 is included +AbstractBoat.onAboveBubbleColumn missing player: a boat over a bubble column is not lifted or pushed down and no splash plays on it; Horizon has no bubble column for boats +AbstractBoat.animateHurt port crates/horizon-client/src/game/boats.rs::Game::hurt_boat sets the hurt wobble and time; gap: minor: uses the server damage x10, no separate client x11 wobble value +AbstractBoat.tickBubbleColumn missing player: a boat in a bubble column does not rise or sink and ejects no passengers from a down column; Horizon has no bubble time on boats +AbstractBoat.handleEntityEvent missing player: the bubble column effect event (71 or 72) never reaches a boat, same gap as the bubble column lift +AbstractBoat.handleBubbleColumnEffect missing player: a boat in an up or down bubble column keeps its speed; Horizon never changes its vertical velocity from a column +AbstractBoat.setPaddleState inline crates/horizon-client/src/game/boats.rs::Game::tick_boat paddle flags are set from the rider's input inside the controlled branch +AbstractBoat.getLeashOffset missing player: a lead can be tied to a boat in vanilla (AbstractBoat is Leashable); Horizon leads only mobs +AbstractBoat.getStatus port crates/horizon-client/src/game/boats.rs::Game::boat_status in air, in water, under water or on land +AbstractBoat.getWaterLevelAbove port crates/horizon-client/src/game/boats.rs::Game::water_level_above +AbstractBoat.getGroundFriction inline crates/horizon-client/src/game/boats.rs::Game::boat_status land friction is the average of the full blocks under the hull +AbstractBoat.checkInWater inline crates/horizon-client/src/game/boats.rs::Game::boat_status sets the water level and reports the in-water status +AbstractBoat.isUnderwater inline crates/horizon-client/src/game/boats.rs::Game::boat_status the under-water and flowing-water check runs first in boat_status +AbstractBoat.floatBoat inline crates/horizon-client/src/game/boats.rs::Game::tick_boat buoyancy, friction and the first-water-touch snap are in tick_boat +AbstractBoat.controlBoat inline crates/horizon-client/src/game/boats.rs::Game::tick_boat steering, acceleration and paddle flags in the controlled branch of tick_boat +AbstractBoat.getDismountLocationForPassenger port crates/horizon-client/src/game/boats.rs::Game::boat_dismount_target gap: minor: no dismount pose check; the floor and water tests are there +AbstractBoat.clampRotation inline crates/horizon-client/src/game/boats.rs::Game::tick_riding the rider's yaw is clamped to 105 degrees off the boat; mobs get the head clamp in boat_passengers.rs seat_boat_mobs +AbstractBoat.calculatePassengerBodyYRot inline crates/horizon-client/src/game/boat_passengers.rs::Game::seat_boat_mobs animal turned 90 or 270 degrees when the boat is full +AbstractBoat.onPassengerTurned inline crates/horizon-client/src/game/boats.rs::Game::tick_riding the rider's turn is clamped each tick +AbstractBoat.remove inline crates/horizon-client/src/game/boats.rs::Game::hurt_boat destroyed boats eject mobs and drop the boat; gap: minor: no leash drop, since boats cannot be leashed here +AbstractBoat.checkFallDamage missing minor: boats keep no fall distance; boats take no fall damage either way +AbstractBoat.getPaddleState port crates/horizon-client/src/game/boats.rs::Boat::rowing_time paddle flag, set only while a rider drives +AbstractBoat.getControllingPassenger inline crates/horizon-client/src/game/boats.rs::Game::tick_riding the riding player is the controller; mobs never steer +AbstractBoat.setInput inline crates/horizon-client/src/game/boats.rs::Game::tick_riding input flags are copied from the keys each tick +AbstractBoat.isUnderWater port crates/horizon-client/src/game/boats.rs::Boat::under_water +AbstractBoat.modifyPassengerFluidInteractionBox missing minor: a rider's water check still uses the whole body; horizon-world boat_passenger_box exists but no game code calls it +AbstractChestBoat.remove inline crates/horizon-client/src/game/boats.rs::Game::hurt_boat a destroyed chest boat spills its 27 slots +AbstractChestBoat.openCustomInventoryScreen inline crates/horizon-client/src/game/boats.rs::Game::interact_boat sneaking opens the chest; gap: minor: no piglin anger +AbstractChestBoat.createMenu inline crates/horizon-client/src/game/boats.rs::Game::interact_boat the chest container is set up there; gap: minor: no spectator or loot table check +AbstractMinecart.setInitialPos port crates/horizon-client/src/game/minecarts.rs::Minecart::new spawn sets the position and its previous copy +AbstractMinecart.createMinecart port crates/horizon-client/src/game/minecarts.rs::Game::place_minecart placing on a rail: the rail offset for slopes and the item's kind; gap: minor: no rail snap, which only the experimental behaviour does +AbstractMinecart.defineSynchedData skip:java synched display block and offset; Horizon keeps the kind and its display offset on the Minecart struct +AbstractMinecart.getPassengerAttachmentPoint missing minor: a villager or wandering trader in a cart sits at a lowered seat in vanilla; Horizon seats only the player in a cart +AbstractMinecart.getDismountLocationForPassenger port crates/horizon-client/src/game/minecarts.rs::Game::dismount_cart beside the cart, else on top; gap: minor: the side comes from the yaw, and there is no pose or ceiling check +AbstractMinecart.getBlockSpeedFactor missing minor: a cart that has left the rails takes no block speed factor (soul sand, honey); on rails the factor is 1.0 in vanilla and Horizon has no such slowdown +AbstractMinecart.animateHurt port crates/horizon-client/src/game/minecarts.rs::Game::hurt_cart sets the hurt wobble and time; gap: minor: the damage is the server x10, with no separate client wobble value +AbstractMinecart.getCurrentBlockPosOrRailBelow port crates/horizon-client/src/game/minecarts.rs::Game::rail_block the rail one block below counts; the experimental y offset is not used +AbstractMinecart.comeOffTrack port crates/horizon-client/src/game/minecarts.rs::Game::come_off_track clamps speed, halves it on the ground, moves and drags in the air +AbstractMinecart.move port crates/horizon-client/src/game/minecarts.rs::Game::move_cart collision movement only; gap: minor: no push or pickup during the move, which push_carts and the cart tick do instead +AbstractMinecart.applyEffectsFromBlocks inline crates/horizon-client/src/game/minecarts.rs::Game::cart_lava_tick lava is applied there; gap: minor: other block effects on a cart (cobweb, berry bush, bubble column) are not applied +AbstractMinecart.getRedstoneDirection skip:other used only by the experimental NewMinecartBehavior; Horizon runs the old track behaviour +AbstractMinecart.applyNaturalSlowdown port crates/horizon-client/src/game/minecarts.rs::Game::natural_slowdown slowdown per ride state, the furnace push and water +AbstractMinecart.readAdditionalSaveData port crates/horizon-client/src/game/minecarts.rs::Minecart::from_nbt flipped and HasTicked read back; gap: minor: a custom display block and offset are not kept +AbstractMinecart.addAdditionalSaveData port crates/horizon-client/src/game/minecarts.rs::Minecart::to_nbt flipped and HasTicked written; gap: minor: a custom display block and offset are not saved +AbstractMinecart.pushOtherMinecart port crates/horizon-client/src/game/minecarts.rs::Game::push_from_cart cart against cart push with the furnace case; the old-movement facing check is used +Minecart.interact port crates/horizon-client/src/game/minecarts.rs::Game::interact_cart a plain rideable cart is boarded unless sneaking or already ridden +Minecart.activateMinecart port crates/horizon-client/src/game/minecarts.rs::Game::activate_cart a powered activator rail ejects the rider and hurts a rideable cart +Minecart.tick port crates/horizon-client/src/game/minecarts.rs::Game::tick_cart the per-tick cart update; gap: minor: client rotation smoothing for experimental movement is not modelled +Minecart.positionRider port crates/horizon-client/src/game/minecarts.rs::Game::tick_cart_riding the rider sits at the cart seat; the rider's turn with the cart is experimental-only +MinecartBehavior.setDeltaMovement skip:other behaviour forwarder; Horizon's Minecart struct holds the velocity directly +MinecartBehavior.setPos skip:other behaviour forwarder; Horizon's Minecart struct holds the position directly +MinecartHopper.activateMinecart port crates/horizon-client/src/game/minecarts.rs::Game::activate_cart an unpowered activator rail enables the hopper cart +MinecartHopper.tryConsumeItems inline crates/horizon-client/src/game/minecarts.rs::Game::tick_carts an enabled hopper cart sucks in once a tick +MinecartHopper.suckInItems port crates/horizon-client/src/game/machines.rs::Game::hopper_cart_suck container above first, then item entities at the hopper and the cart's box +MinecartTNT.explode port crates/horizon-client/src/game/minecarts.rs::Game::explode_cart power 4 plus speed, with the cart gone before the blast; gap: minor: the saved explosion power and speed factor are not applied +MinecartTNT.handleEntityEvent port crates/horizon-client/src/game/minecarts.rs::Game::prime_cart event 70 primes the fuse; Horizon primes locally with the same sound +MinecartTNT.primeFuse port crates/horizon-client/src/game/minecarts.rs::Game::prime_cart fuse 80 and the primed sound; gap: minor: no ignition source is recorded for who caused the blast +MinecartTNT.getBlockExplosionResistance missing minor: a primed TNT cart on rails gives rails zero blast resistance in vanilla; Horizon's blast uses the rail's normal resistance +MinecartTNT.shouldBlockExplode missing minor: a primed TNT cart on rails does not block the blast in vanilla; Horizon's blast ray is not changed by a cart on rails +MinecartTNT.readAdditionalSaveData port crates/horizon-client/src/game/minecarts.rs::Minecart::from_nbt fuse read back; gap: minor: explosion_power and explosion_speed_factor are not read +MinecartTNT.addAdditionalSaveData port crates/horizon-client/src/game/minecarts.rs::Minecart::to_nbt fuse written; gap: minor: explosion_power and explosion_speed_factor are not written +MinecartTNT.damageSourceIgnitesTnt port crates/horizon-client/src/game/minecarts.rs::Game::hurt_cart the fire flag from the caller primes the cart instead of breaking it +OldMinecartBehavior.tick port crates/horizon-client/src/game/minecarts.rs::Game::tick_cart gravity, track or off-track move, activator rail, yaw from motion and the flip +OldMinecartBehavior.getMotionDirection inline crates/horizon-client/src/game/minecarts.rs::Game::dismount_cart the dismount side is the cart's facing, taken from its yaw +OldMinecartBehavior.getKnownMovement skip:other client-side clamp of the known movement for networking; Horizon simulates carts itself +FoodData.add port crates/horizon-world/src/food.rs::FoodData::eat_properties food points and saturation are added and clamped to 0..20 and to the food level +FoodData.readAdditionalSaveData port crates/horizon-world/src/food_storage.rs::FoodData::read_nbt food level, tick timer, saturation and exhaustion read back +FoodData.addAdditionalSaveData port crates/horizon-world/src/food_storage.rs::FoodData::write_nbt food level, tick timer, saturation and exhaustion written +FoodProperties.Builder.build skip:java builder for the food component; the saturation rule (nutrition times modifier times 2) is in food.rs FoodData::eat +AbstractContainerMenu.addInventoryHotbarSlots skip:ui slot layout and positions only; Horizon indexes the player's slots in Menu::new and draws them in draw_inventory_slots +AbstractContainerMenu.addInventoryExtendedSlots skip:ui slot layout and positions only; Horizon indexes the player's slots in Menu::new and draws them in draw_inventory_slots +AbstractContainerMenu.addStandardInventorySlots skip:ui slot layout and positions only; Horizon indexes the player's slots in Menu::new and draws them in draw_inventory_slots +AbstractContainerMenu.stillValid port crates/horizon-client/src/lan/menu.rs::Slots::still_valid the container's block is still there and within 4 blocks of the player +AbstractContainerMenu.getType port crates/horizon-client/src/menu.rs::Menu::kind the menu's kind as its type +AbstractContainerMenu.checkContainerSize skip:other construction sanity check; Horizon's container sizes come from fixed tables +AbstractContainerMenu.checkContainerDataCount skip:other construction sanity check; Horizon's data counts come from fixed tables +AbstractContainerMenu.isValidSlotIndex inline crates/horizon-client/src/menu.rs::Menu::click an index inside the menu or -999 for a click outside the window +AbstractContainerMenu.addSlot inline crates/horizon-client/src/menu.rs::Menu::new slots are built per kind when the menu is made +AbstractContainerMenu.addDataSlot skip:java registers a DataSlot object; progress values are set directly by Menu::set_data +AbstractContainerMenu.addDataSlots skip:java registers DataSlot objects for a ContainerData; progress values are set directly by Menu::set_data +AbstractContainerMenu.addSlotListener skip:java listener list for local screens; a viewer reads the menu state directly +AbstractContainerMenu.setSynchronizer inline crates/horizon-client/src/lan/containers.rs::Hub::open_container the synchroniser is the guest's connection; opening sends the full content at once +AbstractContainerMenu.sendAllDataToRemote port crates/horizon-client/src/lan/containers.rs::Hub::send_menu_content all slots, the carried stack and the menu state in one packet +AbstractContainerMenu.getItems inline crates/horizon-client/src/lan/containers.rs::Hub::send_menu_content the item list for the content packet is built there +AbstractContainerMenu.broadcastFullState port crates/horizon-client/src/lan/containers.rs::Hub::send_menu_content a full resend of the contents; gap: minor: no separate local listener pass +AbstractContainerMenu.updateDataSlotListeners skip:java local listener dispatch for data changes; the wire side is ContainerSetData in sync_container_data +AbstractContainerMenu.triggerSlotListeners skip:java local listener dispatch for slot changes; the wire side is the slot diff in sync_containers +AbstractContainerMenu.synchronizeSlotToRemote port crates/horizon-client/src/lan/containers.rs::Hub::sync_containers sends each changed slot with a new state id +AbstractContainerMenu.synchronizeDataSlotToRemote port crates/horizon-client/src/lan/containers.rs::Hub::sync_container_data sends each changed furnace, brewing or crafter value +AbstractContainerMenu.synchronizeCarriedToRemote inline crates/horizon-client/src/lan/containers.rs::Hub::answer_click the cursor item is sent after a click when it differs; gap: minor: no sync on other carried changes +AbstractContainerMenu.setRemoteSlotUnsafe port crates/horizon-client/src/game/remote.rs::Game::set_menu_slot the server's slot update is applied; gap: minor: no hashed copy of the remote slot is kept +AbstractContainerMenu.getClickAction inline crates/horizon-client/src/menu.rs::Menu::click button 0 is the primary action, 1 the secondary +AbstractContainerMenu.tryItemClickBehaviourOverride port crates/horizon-client/src/menu/bundle.rs::Menu::bundle_click a bundle takes or puts items on a click on another stack; the NeoForge event is not modelled +AbstractContainerMenu.initializeContents port crates/horizon-client/src/game/remote.rs::Game::set_menu_slot the server's full content is applied to the open menu +AbstractContainerMenu.isValidQuickcraftType inline crates/horizon-client/src/menu.rs::Menu::click drag types 0 and 1 always, type 2 only in creative +AbstractContainerMenu.canItemQuickReplace port crates/horizon-client/src/menu.rs::Menu::quick_replace a slot takes the stack when empty or the same item with room +AbstractContainerMenu.transferState skip:other copies the remote-slot cache back to the player's inventory on close; Horizon's guest inventory is the one copy +AbstractContainerMenu.findSlot skip:other looks up a menu index for the held slot; Horizon's menus use fixed player slot indexes +AbstractContainerMenu.incrementStateId inline crates/horizon-client/src/lan/containers.rs::Hub::send_menu_content the state id goes up with each content packet; sync_containers does the same per slot change +AbstractFurnaceMenu.fillCraftSlotsStackedContents port crates/horizon-client/src/menu/place.rs::Menu::stacked_contents the recipe book's stacked contents are counted from the menu; gap: minor: the furnace's own slots are not counted +AbstractFurnaceMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for furnace slots, with the smeltable, fuel and inventory rules +AbstractFurnaceMenu.getBurnProgress skip:ui flame fraction for the screen; game.rs draw_inventory draws it from the furnace timers +AbstractFurnaceMenu.getLitProgress skip:ui arrow fraction for the screen; game.rs draw_inventory draws it from the furnace timers +AbstractFurnaceMenu.handlePlacement port crates/horizon-client/src/menu/place.rs::Menu::place_recipe recipe book placement into the furnace input and fuel +AnvilMenu.createInputSlotDefinitions skip:ui slot positions and the accept-all rule; positions come from station_slot_position and the accept rules from slot_rules +AnvilMenu.validateName port crates/horizon-client/src/menu.rs::Menu::rename name filtered of formatting codes and capped at 50 UTF-16 units +ArmorSlot.setByPlayer port crates/horizon-client/src/menu.rs::Menu::set_by_player_from an equipment change runs onEquipItem only when the item changes +ArmorSlot.mayPickup port crates/horizon-client/src/menu.rs::Menu::may_pickup a cursed armour piece cannot be taken out in survival; creative can +BeaconMenu.decodeEffect port crates/horizon-client/src/game/lan_stations.rs::Game::lan_station_beacon effect id from the registry, 0 for none; an unknown id fails the packet +BrewingStandMenu. inline crates/horizon-client/src/menu.rs::Menu::new the brewing stand's slots and data are built per kind when the menu is made +BrewingStandMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for the brewing stand's potion, ingredient and fuel slots +CartographyTableMenu.setupResultSlot port crates/horizon-client/src/menu.rs::Menu::carto_changed the map copy, scale or lock result from a paper, glass pane or map +CartographyTableMenu.canTakeItemForPickAll port crates/horizon-client/src/menu.rs::Menu::can_take_for_pick_all the result slot is never picked up in bulk +CartographyTableMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for the cartography slots +CartographyTableMenu.removed inline crates/horizon-client/src/game/stations.rs::Game::close_station the inputs go back when the table closes +CrafterMenu.addSlots inline crates/horizon-client/src/menu.rs::Menu::new the 3x3 grid, the result and the data are set up when the crafter menu is made +CrafterMenu.isSlotDisabled inline crates/horizon-client/src/game/machines/crafter.rs::Game::crafter_slot_clicked a disabled slot refuses a click; the disabled flags are read there +CrafterMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for the crafter's grid slots +CraftingMenu.slotsChanged port crates/horizon-client/src/menu.rs::Menu::inventory_slots_changed the crafting result is recomputed when the grid changes +CraftingMenu.finishPlacingRecipe inline crates/horizon-client/src/menu/place.rs::Menu::place_recipe the result is recomputed once the placed recipe is in the grid +CraftingMenu.removed port crates/horizon-client/src/game/lan_crafting.rs::Game::lan_crafting_closed the grid goes back when the table closes; the host side of a guest's table +CraftingMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for the crafting grid and result +CraftingMenu.canTakeItemForPickAll port crates/horizon-client/src/menu.rs::Menu::can_take_for_pick_all the result slot is never picked up in bulk +DispenserMenu.add3x3GridSlots inline crates/horizon-client/src/menu.rs::Menu::new the 3x3 dispenser grid is set up when the menu is made +DispenserMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for the dispenser grid +EnchantmentMenu. inline crates/horizon-client/src/game/stations.rs::Game::open_station slots and the offers are set up when the table opens +EnchantmentMenu.getGoldCount inline crates/horizon-client/src/menu.rs::Menu::enchant_click the lapis count is checked against the enchant cost there +EnchantmentMenu.removed inline crates/horizon-client/src/game/stations.rs::Game::close_station the inputs go back when the table closes +EnchantmentMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for the enchanting slots, with lapis and the item slot +FurnaceResultSlot.remove port crates/horizon-client/src/game.rs::Game::award_furnace_recipes the furnace output's experience is awarded from the recorded recipe uses; gap: minor: no per-slot taken count +GrindstoneMenu.mergeItems port crates/horizon-client/src/stations.rs::grindstone_result two repairable items merge into one with the summed durability +GrindstoneMenu.mergeEnchantsFrom inline crates/horizon-client/src/stations.rs::grindstone_result the second item's enchantments merge in, curses only when the target lacks them +GrindstoneMenu.removed inline crates/horizon-client/src/game/stations.rs::Game::close_station the repair inputs go back when the grindstone closes +GrindstoneMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for the grindstone inputs and result +HopperMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for the hopper's five slots +HorseInventoryMenu. port crates/horizon-client/src/game/lan_mount_menu.rs::Game::mount_engine_menu saddle, armour and the chest columns are set for the mount +InventoryMenu.isHotbarSlot inline crates/horizon-client/src/menu.rs::Menu::quick_move the hotbar range check is inline in the shift-click arm +InventoryMenu.slotsChanged port crates/horizon-client/src/menu.rs::Menu::inventory_slots_changed the crafting result is recomputed when the grid changes +InventoryMenu.removed port crates/horizon-client/src/game/lan_crafting.rs::Game::lan_crafting_closed the window-0 craft grid goes back when the inventory closes; the host side of a guest's inventory +InventoryMenu.quickMoveStack port crates/horizon-client/src/menu.rs::Menu::quick_move shift-click for the inventory, armour, grid and offhand +InventoryMenu.canTakeItemForPickAll port crates/horizon-client/src/menu.rs::Menu::can_take_for_pick_all the result slot is never picked up in bulk +ItemCombinerMenu.createInputSlots inline crates/horizon-client/src/menu.rs::Menu::new the anvil's input slots are set up when the menu is made; mayPlace is Menu::may_place +ItemCombinerMenu.createResultSlot inline crates/horizon-client/src/menu.rs::Menu::new the result slot is set up when the menu is made and takes no placed items +ItemCombinerMenu.removed inline crates/horizon-client/src/game/stations.rs::Game::close_station the input items go back when the station closes +ItemCombinerMenu.stillValid port crates/horizon-client/src/lan/menu.rs::Slots::still_valid the station's block is still there and within 4 blocks of the player diff --git a/tools/port_map/game2-008.tsv b/tools/port_map/game2-008.tsv new file mode 100644 index 0000000000..f929a90c07 --- /dev/null +++ b/tools/port_map/game2-008.tsv @@ -0,0 +1,149 @@ +LoomMenu. port crates/horizon-client/src/menu.rs::Menu::new the loom's banner, dye, pattern and result slots are laid out for Kind::Loom in the constructor +LoomMenu.clickMenuButton port crates/horizon-client/src/menu.rs::Menu::button pattern index check and selection; the select sound is played by game/loom.rs::Game::loom_button +LoomMenu.isValidPatternIndex inline crates/horizon-client/src/menu.rs::Menu::button the range check is inline in button +LoomMenu.quickMoveStack inline crates/horizon-client/src/menu.rs::Menu::quick_move the Kind::Loom arm +LoomMenu.removed port crates/horizon-client/src/game/stations.rs::Game::close_station input stacks are given back or dropped when the station closes +MenuType.register skip:data menu type registry bootstrap +MerchantContainer.isEmpty skip:java container emptiness helper; Horizon checks the stacks it needs directly +MerchantContainer.setItem inline crates/horizon-client/src/menu.rs::Menu::slot_changed the store is the generic put; the payment update is merchant_update, run from slot_changed for every merchant slot +MerchantMenu.quickMoveStack inline crates/horizon-client/src/menu.rs::Menu::quick_move the Kind::Merchant arm; a result shift-click also queues the trade sound event +MerchantMenu.playTradeSound missing player: a shift-click trade should play the villager yes sound; the menu queues Event::TradeSound and game/trading.rs drops it with an empty arm +MerchantResultSlot.remove port crates/horizon-client/src/menu.rs::Menu::remove gap: minor: the removeCount tally that feeds the crafted statistic is not kept +MerchantResultSlot.mayPickup port crates/horizon-client/src/menu.rs::Menu::may_pickup the Role::MerchantResult arm +ResultSlot.remove port crates/horizon-client/src/menu.rs::Menu::remove gap: minor: the removeCount tally for the crafted statistic is not kept +ResultSlot.safeClone skip:other onCraftedBy only matters for MAP_POST_PROCESSING, which only cartography results carry; map_crafted covers that take +ResultSlot.checkTakeAchievements port crates/horizon-client/src/game.rs::Game::award_crafted gap: minor: the recipe is awarded; the crafted statistic count from onCraftedBy is not added on this take +ResultSlot.copyAllInputItems inline crates/horizon-client/src/crafting.rs::Recipes::remainders gap: minor: vanilla copies every input when no recipe matches on the server; remainders returns none for an unmatched recipe +ResultSlot.getRemainingItems port crates/horizon-client/src/crafting.rs::Recipes::remainders container remainders and the special banner and book copies +Slot.safeTake port crates/horizon-client/src/menu.rs::Menu::safe_take +SlotRanges.create inline crates/horizon-command/src/args.rs::slot_ranges a range is a name and size pair built in slot_ranges +SlotRanges.addSlotRange inline crates/horizon-command/src/args.rs::slot_ranges the range closure adds the numbered names and the wildcard +SlotRanges.tryRead inline crates/horizon-command/src/args.rs::ArgType::parse the Slots and Slot arms read a name and check the range exists +SmithingMenu.createInputSlotDefinitions port crates/horizon-client/src/menu.rs::Menu::new the three input slots and the result slot; the input tests run in may_place (Role::SmithInput) +SmithingMenu.getRelevantItems inline crates/horizon-client/src/menu.rs::Menu::smith_result the three input stacks are read directly +SmithingMenu.createRecipeInput inline crates/horizon-client/src/menu.rs::Menu::smith_result the recipe input is passed to smithing_result +SmithingMenu.shrinkStackInSlot inline crates/horizon-client/src/menu.rs::Menu::on_take the SmithingMenu take arm removes one of each input +SmithingMenu.canTakeItemForPickAll port crates/horizon-client/src/menu.rs::Menu::can_take_for_pick_all the result slot is excluded +SmithingMenu.canMoveIntoInputSlots inline crates/horizon-client/src/menu.rs::Menu::quick_move the Anvil or Smithing arm checks for an empty input the stack fits +StonecutterMenu.hasInputItem inline crates/horizon-client/src/game/stations.rs::Station::stonecutter_changed the draw flag; the list scroll resets on the same input and recipe check +StonecutterMenu.isValidRecipeIndex inline crates/horizon-client/src/menu.rs::Menu::button the stonecutter arm checks the recipe index +StonecutterMenu.setupRecipeList port crates/horizon-client/src/menu.rs::Menu::stone_changed recipes for the input are selected and the selection and result reset +StonecutterMenu.canTakeItemForPickAll port crates/horizon-client/src/menu.rs::Menu::can_take_for_pick_all the result slot is excluded +StonecutterMenu.quickMoveStack inline crates/horizon-client/src/menu.rs::Menu::quick_move the Kind::Stonecutter arm +StonecutterMenu.removed port crates/horizon-client/src/game/stations.rs::Game::close_station input stacks are given back when the station closes; the result is derived and not kept +ArrowItem.asProjectile port crates/horizon-client/src/game/dispenser.rs::Game::shoot_projectile_item dispensed arrows are built in the arrow arm of the item shot +BlockItem.updateBlockEntityComponents port crates/horizon-client/src/block_entity_item.rs::place the placed block entity takes the item's components +BlockItem.canPlace port crates/horizon-world/src/level/place.rs::Sim::place the placement checks, including the survive check, run in Sim::place +BlockItem.onDestroyed missing unsure: a container item whose item entity is destroyed should spill its contents; no Horizon code does this +BoatItem.getBoat port crates/horizon-client/src/game/boats.rs::Game::place_boat boat placed at the hit point with the item's stack config +BucketItem.playEmptySound port crates/horizon-world/src/level/buckets.rs::Sim::empty_sound +BundleItem.getWeightSafe port crates/horizon-items/src/bundle.rs::Contents::weight_fraction float fraction; vanilla compares an exact Fraction +BundleItem.getBarWidth port crates/horizon-items/src/bundle.rs::Contents::bar +BundleItem.getBarColor port crates/horizon-items/src/bundle.rs::Contents::bar +BundleItem.removeOneItemFromBundle port crates/horizon-items/src/bundle.rs::Contents::remove_one +BundleItem.getTooltipImage port crates/horizon-items/src/bundle.rs::tooltip_layout gap: minor: the tooltip_display rule that hides the bundle contents is not checked here +BundleItem.onDestroyed missing unsure: a bundle whose item entity is destroyed should spill its contents; no Horizon code does this +BundleItem.playRemoveOneSound port crates/horizon-client/src/game.rs::Game::drop_bundle_content the menu queues the same sound in menu/bundle.rs +BundleItem.playInsertSound port crates/horizon-client/src/menu/bundle.rs::Menu::carried_bundle_click queues the insert or insert-fail sound +BundleItem.playDropContentsSound port crates/horizon-client/src/game.rs::Game::drop_bundle_content +CompassItem.isFoil port crates/horizon-assets/src/item_model.rs::Resolve::has_foil a compass with a lodestone_tracker component glints +CrossbowItem.tryLoadProjectiles port crates/horizon-client/src/game/crossbow.rs::Game::tick_crossbow the drawn ammo is stored on the crossbow once charged +CrossbowItem.shootProjectile port crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow +CrossbowItem.getProjectileShotVector inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow the multishot spread is rotated inline from the view and up vectors +CrossbowItem.createProjectile inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow gap: player: crossbow arrows keep the normal arrow hit sound; the crossbow hit sound (item.crossbow.hit) is never set +CrossbowItem.getShotPitch inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow the shot pitch per index is inline +CrossbowItem.getRandomShotPitch inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow the random pitch formula is inline +CrossbowItem.getPowerForTime inline crates/horizon-client/src/game/crossbow.rs::Game::tick_crossbow the charge fraction capped at one is the tick check against the charge time +DyeColor.getMixedColor missing player: a baby sheep, wolf or cat from parents of two dye colours gets a mixed colour; Horizon has no breeding dye mix (only sheep_color for spawns) +DyeColor.findColorMixInRecipes missing player: part of the same breeding dye mix; the crafting recipe lookup for two dyes is not done for offspring +FireworkRocketItem.createDispenseConfig port crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile rocket config: offset 0.5000099, uncertainty 1, power 0.5, event 1004 +FireworkRocketItem.getEntityJustOutsideOfBlockPos port crates/horizon-client/src/game/dispenser.rs::Game::dispense_position the position just outside the dispenser on the facing side +HangingEntityItem.mayPlace port crates/horizon-client/src/game/paintings.rs::Game::place_painting gap: player: the horizontal-face rule is checked for paintings only; place_item_frame has no vertical-face test +HangingEntityItem.appendHoverText missing player: hovering a painting item shows no title, author or size line in its tooltip +Item.getDestroySpeed port crates/horizon-items/src/items.rs::Items::mining_speed +Item.finishUsingItem port crates/horizon-client/src/game.rs::Game::consume_held the consumable effects are applied by apply_consume_effect +Item.getBarColor port crates/horizon-client/src/hud_overlays.rs::damage_bar width and hue from the damage fraction +Item.mineBlock port crates/horizon-client/src/game.rs::Game::mine_block_with_held +Item.isCorrectToolForDrops port crates/horizon-items/src/items.rs::Items::correct_for_drops +Item.getUseAnimation port crates/horizon-items/src/use_animation.rs::consumable_animation consumable animation; the block and spear poses are handled by the item pose code +Item.getPlayerPOVHitResult port crates/horizon-client/src/game.rs::Game::pick the view ray from the eyes; the fluid variant is Game::pick_fluid +Item.Properties.food skip:data registration builder; Horizon reads item data from tables +Item.Properties.usingConvertsTo skip:data registration builder +Item.Properties.durability skip:data registration builder +Item.Properties.craftRemainder skip:data registration builder +Item.Properties.fireResistant skip:data registration builder; resists_fire reads the data +Item.Properties.jukeboxPlayable skip:data registration builder +Item.Properties.repairable skip:data registration builder +Item.Properties.axe skip:data registration builder +Item.Properties.hoe skip:data registration builder +Item.Properties.shovel skip:data registration builder +Item.Properties.spawnEgg skip:data registration builder +Item.Properties.humanoidArmor skip:data registration builder +Item.Properties.wolfArmor skip:data registration builder +Item.Properties.horseArmor skip:data registration builder +Item.Properties.nautilusArmor skip:data registration builder +Item.Properties.requiredFeatures skip:data registration builder +Item.Properties.delayedHolderComponent skip:data registration builder +Item.Properties.loweredMobVisibility missing unsure: the mob_visibility component is not read by Horizon's mob targeting +Item.Properties.finalizeInitializer skip:data registration builder +Item.TooltipContext.of skip:java tooltip context factory; Horizon passes the level and player to the tooltip functions +ItemStack.validateStrict skip:java codec-side validation of components and stack size +ItemStack.hasNonDefault skip:other component presence query; Horizon reads stack fields directly +ItemStack.validateComponents skip:java codec-side validation +ItemStack.validateContainedItemSizes skip:java codec-side validation +ItemStack.getDamageValue skip:other field accessor; Horizon keeps damage on the stack +ItemStack.setDamageValue skip:other field write +ItemStack.isBroken inline crates/horizon-client/src/game.rs::Game::damage_held the break test (damage at or over max) is in damage_held +ItemStack.nextDamageWillBreak inline crates/horizon-client/src/game/trident.rs::Game::use_trident gap: minor: only the trident's check is ported here +ItemStack.processDurabilityChange port crates/horizon-client/src/game/enchant_effects.rs::Game::durability_loss the Unbreaking-adjusted loss +ItemStack.applyDamage port crates/horizon-client/src/game.rs::Game::damage_held gap: minor: no item durability advancement trigger fires +ItemStack.hurtAndConvertOnBreak missing unsure: a carrot or fungus on a stick that breaks should become a fishing rod; no conversion was found +ItemStack.hurtEnemy port crates/horizon-client/src/game/mace.rs::Game::weapon_wear the per-hit durability loss of a weapon +ItemStack.interactLivingEntity port crates/horizon-client/src/game.rs::Game::interact_entity equip-on-interact is in the mob paths (nautili.rs, ghasts.rs) +ItemStack.copy skip:java value copy +ItemStack.copyWithCount skip:java value copy with a count +ItemStack.transmuteCopyIgnoreEmpty port crates/horizon-items/src/item_data.rs::ItemStack::transmute keeps the components, changes the item +ItemStack.listMatches skip:java element-wise list equality for CraftingInput; Horizon compares recipe inputs directly +ItemStack.matchesIgnoringComponents port crates/horizon-items/src/item_data.rs::ItemStack::same_item_same_components gap: minor: no predicate for ignored component types +ItemStack.lenientOptionalFieldOf skip:java codec helper +ItemStack.hashItemAndComponents skip:java hash for collections +ItemStack.hashStackList skip:java hash for collections +ItemStack.onCraftedBy inline crates/horizon-client/src/game.rs::Game::click_inventory gap: minor: the crafted statistic is added only on the inventory grid craft path here +ItemStack.set skip:other component write; Horizon stores component data in typed fields +ItemStack.update skip:java functional update helper +ItemStack.applyComponentsAndValidate missing unsure: a validated component patch has no single Horizon counterpart; /item modify is a separate path +ItemStack.getStyledHoverName port crates/horizon-client/src/game/stations.rs::Game::stack_hover_name gap: minor: plain text; the rarity colour and italic style are not applied here +ItemStack.addToTooltip port crates/horizon-client/src/item_tooltip.rs::lines +ItemStack.addUnitComponentToTooltip inline crates/horizon-client/src/game/stations.rs::Game::stack_tooltip_lines unit lines such as unbreakable, gated by the tooltip display +ItemStack.canPlaceOnBlockInAdventureMode missing player: in adventure mode a block can be placed only when the held item's can_place_on list allows it; Horizon does not read that component +ItemStack.canBreakBlockInAdventureMode missing player: in adventure mode a block can be broken only when the held item's can_break list allows it; Horizon does not read that component +ItemStack.getDamageSource missing unsure: a stack's damage_type component is not read when a weapon hits; no Horizon reader found +ItemStackTemplate.fromNonEmptyStack port crates/horizon-items/src/item_data.rs::template_tag stack to template tag +ItemStackTemplate.withCount skip:java record copy with a new count +ItemStackTemplate.apply port crates/horizon-assets/src/item_model.rs::stack_from_template +ItemUtils.onContainerDestroyed missing unsure: destroyed container items do not spill their contents; see BlockItem.onDestroyed +Items.registerBlock skip:data item registry bootstrap +Items.registerItem skip:data item registry bootstrap +JukeboxSong.hasFinished port crates/horizon-items/src/items.rs::Song::finished +JukeboxSongPlayer.setSongWithoutPlaying missing unsure: no load path restores a jukebox's song start tick +JukeboxSongPlayer.play port crates/horizon-client/src/game/jukebox.rs::Game::start_song the song starts at the jukebox with its sound +JukeboxSongPlayer.shouldEmitJukeboxPlayingEvent inline crates/horizon-client/src/game/jukebox.rs::Game::tick_jukebox +JukeboxSongPlayer.spawnMusicParticles inline crates/horizon-client/src/game/jukebox.rs::Game::tick_jukebox the note particle is emitted in tick_jukebox +LeadItem.bindPlayerMobs port crates/horizon-client/src/game/leads.rs::Game::interact_knot +MaceItem.createAttributes skip:data attribute modifiers come from the item data +MaceItem.calculateImpactPosition inline crates/horizon-client/src/game/mace.rs::Game::mace_hurt_enemy the earlier impact below the player is kept +MaceItem.knockbackPredicate port crates/horizon-client/src/game/mace.rs::Game::smash_knockback the nearby entity filter for the smash +MapItem.create port crates/horizon-client/src/game/maps.rs::Game::register_exploration_maps +MapItem.createNewSavedData port crates/horizon-client/src/game/maps.rs::MapData::fresh +MapItem.getCorrectStateForFluidBlock inline crates/horizon-client/src/game/maps.rs::Game::update_map the map colour pass +MapItem.inventoryTick port crates/horizon-client/src/game/maps.rs::Game::tick_maps +MapItem.lockMap port crates/horizon-client/src/menu.rs::Menu::map_crafted the lock branch of the post processing +MapItem.useOn missing player: using a map on a banner block should toggle that banner's marker on the map; Horizon has no banner toggle +MobBucketItem.emptyContents port crates/horizon-world/src/level/buckets.rs::Sim::bucket_empty the MobWater and Nothing contents +PotionItem.getDefaultInstance skip:data default stack data +PotionItem.useOn missing player: a water bottle used on a block that turns to mud should make mud; no such conversion was found in potions.rs +PotionItem.getName port crates/horizon-client/src/game/stations.rs::Game::stack_hover_name potion name from the contents +ProjectileItem.DispenseConfig.Builder.build inline crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile per-item dispense config +ProjectileWeaponItem.getHeldProjectile port crates/horizon-client/src/game/crossbow.rs::Game::projectile_slot offhand first, then the held slot +ProjectileWeaponItem.createProjectile inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow the arrow from the ammo, with the weapon's piercing +ShieldItem.getName missing player: a shield with a banner base colour is named after that colour; Horizon names every shield the same diff --git a/tools/port_map/game2-009.tsv b/tools/port_map/game2-009.tsv new file mode 100644 index 0000000000..f458630af7 --- /dev/null +++ b/tools/port_map/game2-009.tsv @@ -0,0 +1,139 @@ +SmithingTemplateItem.createArmorTrimTemplate inline crates/horizon-client/src/game/stations.rs::Game::draw_station gap: player: the empty template slot cycles the armour icons in vanilla (SmithingScreen); Horizon draws one fixed netherite icon, and the additions slot gets no icon +SmithingTemplateItem.createNetheriteUpgradeTemplate inline crates/horizon-client/src/game/stations.rs::Game::draw_station gap: player: no cycling tool and armour icons and no ingot icon for the additions slot; Horizon draws one fixed netherite icon in the template slot +SmithingTemplateItem.createNetheriteUpgradeIconList missing player: an empty template slot in the smithing table does not cycle through the 11 tool and armour icons (SmithingScreen ticks baseIcon); Horizon shows one fixed netherite icon +SpawnEggItem.spawnMob port crates/horizon-client/src/game/creatures.rs::Game::use_spawn_egg gap: minor: no ENTITY_PLACE game event (sculk sensors do not hear the egg); consumes the egg and spawns the mob on the clicked face +SpawnEggItem.getType port crates/horizon-client/src/game/creatures.rs::Game::use_spawn_egg kind taken from the item id suffix (*_spawn_egg), not an ENTITY_DATA component +TridentItem.createAttributes skip:data attack damage 8 and attack speed -2.9 are item table data (horizon-items attack_speed modifier) +TridentItem.asProjectile skip:other ProjectileItem override that no code calls: no dispenser behaviour is registered for the trident in vanilla +WindChargeItem.asProjectile port crates/horizon-client/src/game/dispenser.rs::Game::shoot_projectile_item gap: minor: aims with the level random as vanilla does, then shoots with the dispenser uncertainty (6.67); vanilla's shoot is a no-op for wind charges, so the spread is wider in Horizon +WindChargeItem.createDispenseConfig port crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile position function, uncertainty 6.6666665, power 1.0 and event 1051 match +alchemy/Potion.hasInstantEffects inline crates/horizon-client/src/game/potions.rs::Game::splash the instant check is written inline for the splash level event and sound (2007/1054 vs 2002/1053) +PotionContents.createItemStack port crates/horizon-items/src/item_data.rs::ItemStack::with_potion item stack plus the potion in its data +PotionContents.is inline crates/horizon-client/src/cauldron.rs::interact gap: minor: only the water check is here; other callers compare the potion id themselves (dispenser.rs, Enderman) +PotionContents.isPotionWithoutCustomEffects inline crates/horizon-client/src/effects.rs::PlayerEffects::potion_effects Horizon has no custom effects on a potion stack, so this is always the potion alone +PotionContents.getAllEffects port crates/horizon-client/src/effects.rs::PlayerEffects::potion_effects gap: minor: the potion's own effect list only; no custom effects +PotionContents.withPotion port crates/horizon-items/src/item_data.rs::ItemStack::with_potion sets the potion and keeps the other data +PotionContents.withEffectAdded inline crates/horizon-client/src/game/dragon.rs::Game::tick_breath gap: minor: the dragon fireball cloud's instant damage (12) is hard-coded in the tick, not an added effect; the breath cloud's 6 is the same path +PotionContents.getColorOptional port crates/horizon-client-core/src/potions.rs::potion_color amplifier-weighted mix; base colour when there are no effects +PotionContents.hasEffects inline crates/horizon-client/src/game/potions.rs::Game::splash a lingering cloud is made only when the potion has effects +PotionContents.getPotionDescription port crates/horizon-client/src/brewing.rs::tooltip effect name with potency; joined with a space, not the potion.withAmplifier translation +PotionContents.onConsume port crates/horizon-client/src/game/potions.rs::Game::tick_drinking gap: minor: duration scale is always 1.0; POTION_DURATION_SCALE is not read +AttackRange.defaultFor inline crates/horizon-client/src/game/hand.rs::attack_range_contains gap: minor: fixed reach 0 to 3 with a 0.3 margin; no entity interaction range attribute +AttackRange.getClosesetHit port crates/horizon-client/src/game/hand.rs::GameHands::hit gap: minor: uses the crosshair's entity or block, not the closest hit along the attack range +AttackRange.effectiveMaxRange inline crates/horizon-client/src/game/hand.rs::attack_range_contains creative or survival reach; the mob factor is not used +BlockItemStateProperties.with port crates/horizon-client/src/game/block_items.rs::Game::picked_stack gap: minor: writes only honey_level for beehives and bee nests (BeehiveBlock.getCloneItemStack) +BlockItemStateProperties.get port crates/horizon-client/src/item_tooltip.rs::honey_line reads honey_level only +BlockItemStateProperties.apply port crates/horizon-client/src/game/block_items.rs::Game::apply_item_block_state sets the item's block state properties on the placed block +BlockItemStateProperties.updateState inline crates/horizon-client/src/game/block_items.rs::Game::apply_item_block_state the per-property set, only for properties the block has +BlocksAttacks.onBlocked port crates/horizon-client/src/game/shield.rs::Game::after_block gap: minor: block sound plays on this client only +BlocksAttacks.hurtBlockingItem port crates/horizon-client/src/game/shield.rs::Game::after_block item damage by the damage function, shield breaks at max damage; gap: minor: no ITEM_USED stat +BlocksAttacks.disableBlockingForTicks port crates/horizon-client/src/game/shield.rs::Game::blocked_by_attacker disable seconds times disable scale, rounded to ticks, sets the item cooldown +BlocksAttacks.resolveBlockedDamage port crates/horizon-world/src/blocking_rules.rs::BlockingRules::resolve damage reductions by angle and damage type +BundleContents.computeContentWeight port crates/horizon-items/src/bundle.rs::contents_weight sum of item weights times counts; nested bundles add 1/16 +BundleContents.getNumberOfItemsToShow port crates/horizon-items/src/bundle.rs::Contents::shown_count grid rule with 11 or 12 cells and the last-row gap +BundleContents.getSelectedItem port crates/horizon-items/src/bundle.rs::Contents::selected returns the selected index; the stack is items() at that index +BundleContents.copyWithContents skip:other no callers in the vanilla source +BundleContents.asMutable port crates/horizon-items/src/bundle.rs::Contents::of contents, weight and selection copied into the working state +BundleContents.equals skip:java value equality; Horizon compares the item data directly +BundleContents.Mutable.clearItems skip:other no callers in the vanilla source +BundleContents.Mutable.findStackIndexWithinRange inline crates/horizon-items/src/bundle.rs::Contents::try_insert the merge search over all stacks, only for stackable undamaged items +BundleContents.Mutable.getMaxAmountToAdd port crates/horizon-items/src/bundle.rs::Contents::room_for remaining weight over the item's weight, rounded down, at least 0 +BundleContents.Mutable.tryInsert port crates/horizon-items/src/bundle.rs::Contents::try_insert inserts what fits to the front, merged with its kind +BundleContents.Mutable.tryTransfer port crates/horizon-client/src/menu/bundle.rs::Menu::carried_bundle_click the slot's stack moves in as far as it fits; the slot's own take rules apply +BundleContents.Mutable.toggleSelectedItem port crates/horizon-items/src/bundle.rs::toggle_selected selects the index, or clears the selection when it is selected already or out of range +BundleContents.Mutable.indexIsOutsideAllowedBounds inline crates/horizon-items/src/bundle.rs::toggle_selected the bounds test is the inside check +BundleContents.Mutable.getStackedWeight port crates/horizon-items/src/bundle.rs::Weight::times weight times a count +BundleContents.Mutable.setItem missing minor: a slot write into a bundle (container setItem, reached by /item replace) skips the weight limit and merge; Horizon's /item command has no bundle handling +BundleContents.Mutable.addSlotWithItem missing minor: adding a new bundle slot (container path, /item) skips the weight check; Horizon's /item command has no bundle handling +BundleContents.Mutable.getWeightWithAddedItems inline crates/horizon-items/src/bundle.rs::Contents::try_insert the weight after adding must stay at or below one, computed from room_for +BundleContents.Mutable.mergeIdenticalStacks inline crates/horizon-items/src/bundle.rs::Contents::try_insert gap: minor: merges on insert only; no flatten pass for container writes +BundleContents.Mutable.toImmutable port crates/horizon-items/src/bundle.rs::Contents::store writes the contents and selection back into the component +ChargedProjectiles.ofNonEmpty port crates/horizon-client/src/game/crossbow.rs::Game::tick_crossbow loads the drawn ammo into the crossbow (Multishot draws three); empty stacks are not filtered here +ChargedProjectiles.contains port crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow the firework rocket check that sets the shot power +ChargedProjectiles.copyWithContents skip:other no callers in the vanilla source +ChargedProjectiles.asMutable skip:other no callers in the vanilla source +ChargedProjectiles.Mutable.addSlotWithItem skip:other no callers in the vanilla source +ChargedProjectiles.Mutable.toImmutable skip:other no callers in the vanilla source +Consumable.Builder.build port crates/horizon-items/src/items.rs::Items::load_with reads consume_seconds (1.6 default), the animation, sound and particles from the item JSON; effects are added separately +DyedItemColor.getOrDefault port crates/horizon-assets/src/item_model.rs::Resolve::tint the dyed_color component with the default tint (leather) when unset +FireworkExplosion.getColorName port crates/horizon-client/src/item_tooltip.rs::firework_lines dye name from the firework colour; unknown colours read as custom colour +FireworkExplosion.withFadeColors port crates/horizon-client/src/crafting.rs::Special::assemble sets the fade colours of the star-fade recipe result +ItemContainerContents.fromSlots port crates/horizon-items/src/item_data.rs::decode_components reads the container slot list from its NBT; only non-empty slots are kept as pairs +ItemContainerContents.fromItems port crates/horizon-client/src/block_entity_item.rs::collect writes the non-empty slots of the container into the item's components +ItemContainerContents.findLastNonEmptySlot skip:java trims trailing empty slots; Horizon keeps only the non-empty slot pairs +ItemContainerContents.asSlots port crates/horizon-items/src/item_data.rs::components_tag writes the non-empty slots as minecraft:container entries +ItemContainerContents.createStackFromSlot port crates/horizon-client/src/block_entity_item.rs::place reads a slot's stack into the placed container +ItemContainerContents.copyInto port crates/horizon-client/src/block_entity_item.rs::place copies the container items into the placed block entity +ItemContainerContents.itemCopies skip:other no callers in the vanilla source +ItemContainerContents.asMutable skip:other no callers in the vanilla source +ItemContainerContents.equals skip:java value equality; Horizon compares the item data directly +KineticWeapon.makeLocalHitSound port crates/horizon-client/src/game/spear.rs::Game::kinetic_hit_feedback the local hit sound, at most once per ten ticks per entity +KineticWeapon.computeDamageUseDuration missing unsure: the use duration (delay plus the damage window) is not computed in Horizon's use_spear or spear_tick; the windows are checked one by one +KineticWeapon.Condition.test port crates/horizon-client/src/spear.rs::Condition::test tick, attacker speed and relative speed against the scaled minimums +PiercingWeapon.makeHitSound port crates/horizon-client/src/game/spear.rs::Game::jab gap: minor: the hit sound plays at this player's feet only; vanilla plays it for everyone nearby +ResolvableProfile.createUnresolved port crates/horizon-client/src/skins.rs::parse_profile keeps a name or id as the unresolved key, resolved later +ResolvableProfile.createPartialProfile port crates/horizon-client/src/skins.rs::parse_profile id, else the name's offline UUID, else nil +ResolvableProfile.Dynamic.hashCode skip:java hash of the key and skin patch; Horizon compares them directly +ResolvableProfile.Static.name skip:other no callers in the vanilla source +ResolvableProfile.Static.equals skip:java value equality +ResolvableProfile.Static.hashCode skip:java hash for collections +Tool.Rule.minesAndDrops port crates/horizon-items/src/items.rs::Items::load_with rule read from the item JSON: speed and correct_for_drops set to true +TooltipDisplay.withHidden port crates/horizon-client/src/game/commands/items.rs::Game::apply_item_modifier gap: minor: only the /item modify toggle_tooltips path; the loot-table toggle function is not ported, and the raid banner's hidden pattern is read in item_tooltip shows() +WrittenBookContent.craftCopy port crates/horizon-client/src/crafting.rs::Special::assemble book copy: generation plus one, pages kept +WrittenBookContent.resolveForItem missing minor: selectors in a written book's text (@p and so on) are not resolved when the book reaches a player; the raw text shows +WrittenBookContent.resolve missing minor: no selector resolution for written book pages; the raw text shows +WrittenBookContent.markResolved missing minor: resolved flag is not kept, so a book is never marked as resolved +WrittenBookContent.resolvePage missing minor: no per-page selector resolution, and no drop of pages over 32767 characters +WrittenBookContent.isPageTooLarge missing minor: no check that a resolved page stays under the 32767 character limit +WrittenBookContent.getPages port crates/horizon-client/src/game/books.rs::view_pages gap: minor: returns the stored pages; the filtered-text choice (filterEnabled) is not modelled +WrittenBookContent.withReplacedPages missing minor: loot function set_written_book_pages is not in the loot modifiers, so a loot book gets no pages +BookCloningRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches BookCloning arm: one written book within the allowed generations, plus material +BookCloningRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble copies the book with generation plus one; material count sets the copies +BookCloningRecipe.getRemainingItems port crates/horizon-client/src/crafting.rs::Recipes::remainders the first written book is kept as one copy (BookCloning arm); the crafting remainder item is kept too +CraftingInput.getItem port crates/horizon-client/src/crafting.rs::Input::get reads the grid cell by x and y instead of a slot index +CraftingInput.equals skip:java value equality +CraftingInput.hashCode skip:java hash for collections +Ingredient.testOptionalIngredient port crates/horizon-client/src/crafting.rs::Ingredient::test the caller treats a missing ingredient as an empty slot +Ingredient.equals skip:java value equality +Ingredient.of port crates/horizon-client/src/crafting.rs::Ingredient::of ingredient from item ids +Ingredient.displayForSingleItem skip:other no callers in the vanilla source +Ingredient.getSingleItem missing unsure: returns the one item of a single-item ingredient; no Horizon function found in this pass +PlacementInfo.create port crates/horizon-client/src/crafting.rs::Placement::of placement for one ingredient, none when it is empty +PotionIngredient.test port crates/horizon-client/src/brewing.rs::PotionIngredient::test item id and the potion filter +PotionIngredient.of port crates/horizon-client/src/brewing.rs::PotionIngredient::parse built from the recipe JSON, with the potion filter +PotionIngredient.isPotionInput port crates/horizon-client/src/brewing.rs::Brewing::load bottle input set (Brewing.inputs) built at load +Recipe.BookInfo.mapCodec skip:java codec +RecipeManager.finalizeRecipeLoading port crates/horizon-client/src/crafting.rs::Recipes::load loads the recipes and their placements at load +RecipeManager.filterDisabled missing minor: recipes whose ingredients sit behind a disabled feature flag are not hidden; Horizon's recipe loading has no feature flag check +RecipeManager.isIngredientEnabled missing minor: no feature flag check for ingredients in recipe loading +RecipeManager.byKeyTyped port crates/horizon-client/src/crafting.rs::Recipes::recipe_matches gap: minor: lookup by id; no recipe type check +RecipeManager.listDisplaysForRecipe port crates/horizon-client/src/recipe_book.rs::Book::entry the entry holds the recipe's displays +RecipeManager.IngredientCollector.accept skip:other collects ingredients for the synced property sets; Horizon's client reads its own recipe and brewing tables +RepairItemRecipe.canCombine port crates/horizon-client/src/crafting.rs::Special::repair_pair two items of the same kind that can combine +RepairItemRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble repair arm: combined durability and the bonus +ShapedRecipePattern.of port crates/horizon-client/src/crafting.rs::Recipes::load pattern read from the recipe JSON at load +ShapedRecipePattern.unpack port crates/horizon-client/src/crafting.rs::Recipes::load pattern cells read at load, empty where the key is blank +ShapedRecipePattern.firstNonEmpty port crates/horizon-client/src/crafting.rs::shrink trims empty rows and columns at both ends +ShapedRecipePattern.lastNonEmpty port crates/horizon-client/src/crafting.rs::shrink trims empty rows and columns at both ends +ShapedRecipePattern.matches port crates/horizon-client/src/crafting.rs::Recipes::assemble_if_matches shaped arm: exact size, cell tests, mirrored match +SmithingTransformRecipe.display missing unsure: the recipe book slot displays for a smithing transform are not built in Horizon's recipe book; the smithing table uses StationRecipes::smithing_result +SmithingTrimRecipe.display missing unsure: the recipe book slot displays for a smithing trim are not built in Horizon's recipe book; the smithing table uses StationRecipes::smithing_result +TransmuteRecipe.computeResultSize port crates/horizon-client/src/crafting.rs::Special::assemble result count plus the material count when it is added +TransmuteRecipe.matches port crates/horizon-client/src/crafting.rs::Special::matches Transmute arm: one input and the material list +TransmuteRecipe.assemble port crates/horizon-client/src/crafting.rs::Special::assemble Transmute arm: result with the input's components +TransmuteRecipe.resultDisplay port crates/horizon-client/src/recipe_book.rs::RecipeIndex::result the recipe's result id +TransmuteRecipe.createPlacementInfo port crates/horizon-client/src/crafting.rs::Recipes::placement the placement of the input and the materials +RecipeDisplay.isEnabled missing minor: no feature flag check, so a display for a disabled feature still shows +SlotDisplay.applyDemoTransformation port crates/horizon-client/src/stations.rs::StationRecipes::trim_demo gap: minor: only the trim demo draws (up to 256 tries); the dyed demo is not modelled +SlotDisplay.AnyFuel.resolve missing unsure: no any-fuel display list found in Horizon's recipe book +SlotDisplay.Composite.resolve missing unsure: no composite display list found in Horizon's recipe book +SlotDisplay.DyedSlotDemo.resolve missing unsure: no dyed demo display found in Horizon's recipe book +SlotDisplay.ItemSlotDisplay.resolve port crates/horizon-client/src/recipe_book.rs::Book::items_of the item of a single-item slot +SlotDisplay.ItemStackSlotDisplay.resolve port crates/horizon-client/src/recipe_book.rs::Book::items_of gap: minor: keeps item ids only; the stack's components are not carried into the display +SlotDisplay.OnlyWithComponent.resolve missing unsure: no display that filters by an item component found in Horizon's recipe book +SlotDisplay.SmithingTrimDemoSlotDisplay.resolve port crates/horizon-client/src/stations.rs::StationRecipes::trim_demo a random base trimmed by a random material +SlotDisplay.WithAnyPotion.resolve missing unsure: no any-potion display list found in Horizon's recipe book +SlotDisplay.WithRemainder.resolve missing unsure: no remainder display found in Horizon's recipe book; remainders exist in Recipes::remainders for play +SlotDisplay.WithRemainder.isEnabled missing minor: no feature flag check for the remainder display +BlockPlaceContext.at port crates/horizon-world/src/level/place.rs::Sim::placement_at the placement position and face from the clicked block +UseOnContext.isSecondaryUseActive port crates/horizon-client/src/game.rs::Game::use_hand the shift key sets the context's sneaking flag diff --git a/tools/port_map/game2-010.tsv b/tools/port_map/game2-010.tsv new file mode 100644 index 0000000000..154759437c --- /dev/null +++ b/tools/port_map/game2-010.tsv @@ -0,0 +1,140 @@ +Enchantment.getSlotItems inline crates/horizon-world/src/loot.rs::LootData::enchantment_level slot filter and item lookup folded into the best-level loop +Enchantment.matchingSlot inline crates/horizon-world/src/loot.rs::LootData::enchantment_level slot_in_group test; location and attribute code test slots inline +Enchantment.isImmuneToDamage port crates/horizon-client/src/game/enchant_effects.rs::Game::wears_burn_from_stepping_immunity gap: minor: ignores the effect's requirements and damage source; frost_walker is the only damage_immunity enchantment +Enchantment.modifyDamageProtection port crates/horizon-items/src/enchant.rs::Enchantments::modify_value_with armor protection is summed per damage type in apply_effect_status (potions.rs) +Enchantment.doPostPiercingAttack port crates/horizon-client/src/game/enchant_effects.rs::Game::post_piercing_attack gap: minor: the effect's own requirements are not read; Lunge's checks are hard-coded +Enchantment.tick port crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed gap: minor: only Soul Speed's tick effects; it is the only tick effect in the data +Enchantment.onProjectileSpawned port crates/horizon-client/src/game/enchant_effects.rs::Game::bow_projectile_ignite Flame's ignite; the only projectile_spawned effect in the data +Enchantment.onHitBlock port crates/horizon-client/src/game/trident.rs::Game::channel_rod_hit Channeling's lightning rod hit; the only hit_block effect in the data +Enchantment.modifyItemFilteredCount port crates/horizon-items/src/enchant.rs::Enchantments::modify_value_with one enchantment's effects for one component, item context +Enchantment.modifyEntityFilteredValue port crates/horizon-items/src/enchant.rs::Enchantments::modify_value_with one enchantment's effects for one component, entity context +Enchantment.modifyDamageFilteredValue port crates/horizon-items/src/enchant.rs::Enchantments::modify_value_with one enchantment's effects for one component, damage context +Enchantment.locationContext skip:java LootParams builder; Horizon passes plain values to location_requirements +Enchantment.entityContext skip:java LootParams builder; Horizon passes an EffectContext struct +Enchantment.blockHitContext skip:java LootParams builder; Horizon passes an EffectContext struct +Enchantment.applyEffects inline crates/horizon-items/src/enchant.rs::Enchantments::modify_value_with condition check and apply loop folded into modify_value_with +Enchantment.stopLocationBasedEffects inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed gap: minor: only Soul Speed; the boots-changed branch removes its modifiers +Enchantment.FloatAction.asGeneric skip:java lambda adapter for float effects +Enchantment.Builder skip:data datagen builder; Horizon reads enchantment JSON +LevelBasedValue.Exponent.calculate port crates/horizon-items/src/enchant.rs::level_value minecraft:exponent: base to the power +LevelBasedValue.Fraction.calculate port crates/horizon-items/src/enchant.rs::level_value minecraft:fraction; zero denominator gives 0 +LevelBasedValue.Linear.calculate port crates/horizon-items/src/enchant.rs::level_value minecraft:linear +LevelBasedValue.Lookup.calculate port crates/horizon-items/src/enchant.rs::level_value minecraft:lookup with fallback +ChangeItemDamage.apply port crates/horizon-client/src/game.rs::Game::damage_held gap: minor: the held item only; Soul Speed's boots use wear_boots and Thorns its own path +EnchantmentAttributeEffect.getModifier port crates/horizon-items/src/enchant.rs::Enchantments::attributes slot suffix on the id is added in apply_enchantment_attributes +EnchantmentAttributeEffect.onChangedBlock inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed modifiers switch on there (Soul Speed only) +EnchantmentAttributeEffect.onDeactivated inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed modifiers switch off there (Soul Speed only) +EnchantmentAttributeEffect.makeAttributeMap skip:java multimap builder for one modifier +EnchantmentHelper.updateEnchantments port crates/horizon-items/src/item_data.rs::ItemStack::enchant gap: minor: sets one enchantment; no bulk consumer form +EnchantmentHelper.processAmmoUse port crates/horizon-client/src/game/enchant_effects.rs::Game::bow_enchantments Infinity as a flag; the ammo count change is decided by the caller +EnchantmentHelper.processBlockExperience inline crates/horizon-client/src/game.rs::Game::break_block_as_player Silk Touch zeroes the XP at the call site; spawners keep it +EnchantmentHelper.processMobExperience skip:other no enchantment in the data uses mob_experience, so the amount never changes +EnchantmentHelper.createBook missing player: the creative menu has no enchanted book for each enchantment (CreativeModeTabs); creative.rs lists none +EnchantmentHelper.runIterationOnItem port crates/horizon-items/src/item_data.rs::ItemStack::enchantments the (enchantment, level) iteration; callers loop over it +EnchantmentHelper.runIterationOnEquipment inline crates/horizon-client/src/game/enchant_effects.rs::Game::apply_enchantment_attributes gap: minor: worn armor only; hand slots are not read +EnchantmentHelper.isImmuneToDamage port crates/horizon-client/src/game/enchant_effects.rs::Game::wears_burn_from_stepping_immunity gap: minor: worn armor only; no requirements or damage source check +EnchantmentHelper.modifyKnockback port crates/horizon-client/src/game/enchant_effects.rs::Game::extra_knockback gap: minor: the player's held weapon; mob weapons are read in mobs/gear.rs +EnchantmentHelper.doPostAttackEffects port crates/horizon-client/src/game/enchant_effects.rs::Game::post_attack gap: minor: wind burst, thorns and Channeling's victim effect are separate fns +EnchantmentHelper.doPostAttackEffectsWithItemSourceOnBreak port crates/horizon-client/src/game/enchant_effects.rs::Game::post_attack gap: minor: melee hits with the held item only; projectile and attacker-less hits are not here +EnchantmentHelper.stopLocationBasedEffects inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed gap: minor: only Soul Speed; the boots-changed branch stops it +EnchantmentHelper.tickEffects inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed gap: minor: only Soul Speed's tick effect; it is the only one in the data +EnchantmentHelper.processProjectileCount port crates/horizon-client/src/game/crossbow.rs::Game::tick_crossbow gap: minor: Multishot's three shots are hard-coded, not read from projectile_count +EnchantmentHelper.processProjectileSpread port crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow gap: minor: 10 degrees hard-coded, not read from projectile_spread (same value) +EnchantmentHelper.getPiercingCount inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow projectile_piercing read for the arrow's pierce level there +EnchantmentHelper.onHitBlock port crates/horizon-client/src/game/trident.rs::Game::channel_rod_hit Channeling's lightning rod hit; the only hit_block effect in the data +EnchantmentHelper.modifyDurabilityToRepairFromXp port crates/horizon-client/src/game/enchant_effects.rs::Game::mending Mending's repair with XP +EnchantmentHelper.getFishingLuckBonus port crates/horizon-client/src/game/fishing.rs::Game::use_fishing_rod Luck of the Sea read when the rod is cast +EnchantmentHelper.getFishingTimeReduction port crates/horizon-client/src/game/fishing.rs::Game::use_fishing_rod Lure ticks read at cast; used by catching_fish +EnchantmentHelper.getTridentReturnToOwnerAcceleration port crates/horizon-client/src/game/trident.rs::ThrownTrident::loyalty_pull Loyalty level pull and acceleration +EnchantmentHelper.modifyCrossbowChargingTime port crates/horizon-client/src/game/crossbow.rs::Game::charge_ticks Quick Charge: 1.25 s minus 0.25 s per level +EnchantmentHelper.getTridentSpinAttackStrength inline crates/horizon-client/src/game/trident.rs::Game::release_trident Riptide's strength 1.5 + 0.75 per level above the first, folded into the release +EnchantmentHelper.hasTag port crates/horizon-items/src/enchant.rs::Enchantments::in_tag per enchantment id; callers loop over the stack +EnchantmentHelper.has port crates/horizon-items/src/enchant.rs::Enchantments::has_component any enchantment on the stack with the component +EnchantmentHelper.pickHighestLevel port crates/horizon-client/src/game/crossbow.rs::Game::charging_sounds gap: minor: Quick Charge sounds only; the trident sound is hard-coded in release_trident (riptide_N) +EnchantmentHelper.getHighestLevel inline crates/horizon-client/src/game/crossbow.rs::Game::charging_sounds reads the quick_charge level directly +EnchantmentHelper.filterCompatibleEnchantments inline crates/horizon-items/src/enchant.rs::Enchantments::select exclusivity filter folded into the selection loop +EnchantmentHelper.isEnchantmentCompatible port crates/horizon-items/src/enchant.rs::Enchantments::compatible pair check; the /enchant command loops over the existing ones +EnchantmentHelper.enchantItemFromProvider port crates/horizon-client/src/game/mob_gear.rs::Game::enchant_spawn_equipment gap: minor: mob spawn gear only; pillager, vindicator, skeleton trap and enderman providers are not read here +Equippable.llamaSwag skip:data datagen factory for the carpet equippable data +Equippable.saddle skip:data datagen factory for the saddle equippable data +Equippable.harness skip:data datagen factory for the harness equippable data +Equippable.equipOnTarget port crates/horizon-client/src/game/ghasts.rs::Game::interact_happy_ghast gap: minor: the harness on a happy ghast only; saddles are put on in mounts.rs and nautili.rs +Equippable.canBeEquippedBy port crates/horizon-client/src/game/mount_menu.rs::shows_saddle gap: minor: the saddle slot's entity check only; the armour swap has no entity test +Equippable.Builder skip:data datagen builder for equippable data +ItemCost.withComponents skip:data datagen helper; no runtime caller in 26.3 +ItemCost.test port crates/horizon-client/src/merchant.rs::ItemCost::test item match, then the stack data when the cost has data +MerchantOffer. skip:java constructor; Horizon builds the offer in merchant.rs +MerchantOffer.writeToStream skip:java wire encoding (stream codec) +MerchantOffer.createFromStream skip:java wire decoding (stream codec) +MerchantOffers.copy skip:java list copy +VillagerTrade.builder skip:data datagen factory for trade data +VillagerTrade.Builder skip:data datagen builder for trade data +BaseCommandBlock.createSource inline crates/horizon-client/src/game/command_blocks.rs::Game::command_block_success the track_output gate that decides whether the source exists +BaseCommandBlock.CloseableCommandBlockSource.acceptsSuccess port crates/horizon-client/src/game/command_blocks.rs::Game::command_block_success track_output and send_command_feedback; a removed block gets no output +BaseCommandBlock.CloseableCommandBlockSource.shouldInformAdmins port crates/horizon-client/src/game/command_blocks.rs::Game::command_block_success command_block_output rule, with feedback on +BaseSpawner.setEntityData port crates/horizon-client/src/mobs/spawner.rs::Spawner::set_entity_id +BaseSpawner.getOrCreateDisplayEntity port crates/horizon-client/src/game/spawner.rs::Game::update_spawner_display display mob made per spawner from its next spawn data +BaseSpawner.onEventTriggered port crates/horizon-client/src/mobs/spawner.rs::Spawner::client_event block event 1 resets the client's delay +BaseSpawner.getOrCreateNextSpawnData port crates/horizon-client/src/mobs/spawner.rs::Spawner::next_or_create next spawn data picked once and kept +Level.isInWorldBounds port crates/horizon-world/src/level/update.rs::Sim::is_in_valid_bounds height and horizontal range +Level.isInValidBounds port crates/horizon-world/src/level/update.rs::Sim::is_in_valid_bounds chunk-level bounds folded into the same range +Level.isInWorldBoundsHorizontal inline crates/horizon-world/src/level/update.rs::Sim::is_in_valid_bounds the -30000000..30000000 test +Level.isInValidBoundsHorizontal inline crates/horizon-world/src/level/update.rs::Sim::is_in_valid_bounds chunk validity folded into the block range +Level.getChunk missing unsure: no getter for a full chunk object found; block reads go through World::block +Level.getFluidState port crates/horizon-client/src/game.rs::Column::state gap: minor: the isInValidBounds check is not repeated here +Level.isDarkOutside port crates/horizon-client/src/game.rs::GameLevel::sky_darken gap: minor: the sky darkness value; the fixed-time dimension check is not here +Level.playSound port crates/horizon-world/src/level/update.rs::Sim::sound +Level.playSeededSound missing unsure: no seeded sound path found; Sim::sound takes no seed +Level.addBlockEntityTicker port crates/horizon-client/src/game.rs::GameLevel::load_block_entity the ticker comes with the block entity load +Level.guardEntityTick skip:debug crash-report wrapper around an entity tick +Level.getBlockEntity skip:other bare block_entities map lookup at each caller; no port of its own +Level.setBlockEntity port crates/horizon-client/src/game.rs::GameLevel::load_block_entity +Level.loadedAndEntityCanStandOnFace missing unsure: no face-sturdy test for placed hanging entities found +Level.getWorldBorderAdjustedRespawnData missing unsure: respawn clamp to the world border not found +Level.hasEntities missing unsure: no entity-in-box test found for this use +Level.getClockTimeTicks port crates/horizon-client/src/game.rs::GameLevel::day_time day time; no separate clock time +Level.getThunderLevel port crates/horizon-client/src/world.rs::World::levels thunder scaled by rain +Level.setThunderLevel port crates/horizon-client/src/game/remote.rs::Game::handle_packet weather game event sets the thunder level +Level.setRainLevel port crates/horizon-client/src/game/remote.rs::Game::handle_packet weather game event sets the rain level +Level.fillReportDetails skip:debug crash-report details +BlockGetter.getBlockEntity skip:other bare block_entities map lookup at each caller +BlockGetter.isBlockInLine skip:other no caller in 26.3 code +BlockGetter.clipWithInteractionOverride missing unsure: no interaction-shape override found for the pick ray; clip_ray not checked in depth +BlockGetter.traverseBlocks port crates/horizon-core/src/geometry/traversal.rs::traverse_blocks +BlockGetter.forEachBlockIntersectedBetween port crates/horizon-core/src/geometry/sweep.rs::sweep_intersected_blocks +BlockGetter.addCollisionsAlongTravel inline crates/horizon-client/src/collisions.rs::SceneCollisions::resolve_with_context gap: minor: the shape list along the path is built inside the resolver +BlockGetter.getFurthestCorner port crates/horizon-core/src/geometry/sweep.rs::furthest_corner +ChunkPos.maxFromRegion missing unsure: region scan bound in IOWorker; not checked against save.rs +ChunkPos.isValid inline crates/horizon-world/src/level/update.rs::Sim::is_in_valid_bounds horizontal chunk range folded into the block range +ChunkPos.hash skip:java hash code for ColumnPos keys +ChunkPos.getRegionX port crates/horizon-core/src/position.rs::ChunkPos::region +ChunkPos.getRegionZ port crates/horizon-core/src/position.rs::ChunkPos::region +ChunkPos.getBlockAt skip:other no caller in 26.3 code +ChunkPos.contains port crates/horizon-core/src/position.rs::BlockPos::chunk a block is in a chunk when its chunk() is that chunk +ChunkPos.getChessboardDistance inline crates/horizon-client/src/ticket_levels.rs::compute Chebyshev distance folded into the ticket level loop +ChunkPos.distanceSquared missing unsure: caller not identified in 26.3 sources; no port located +CollisionGetter.isUnobstructed port crates/horizon-client/src/game.rs::GameLevel::is_unobstructed the state-based overload +CollisionGetter.noBorderCollision port crates/horizon-client/src/collisions.rs::EntityCollisions::movement_border border shape when inside close to the border +CollisionGetter.getPreMoveCollisions inline crates/horizon-client/src/collisions.rs::SceneCollisions::resolve_with_context gap: minor: entity and block shapes are not returned as a list; the resolver takes them together +CollisionGetter.getBlockAndLiquidCollisions port crates/horizon-client/src/collisions.rs::no_collision_or_liquid gap: minor: a yes or no test, not the shape list +CollisionGetter.getBlockCollisionsFromContext port crates/horizon-client/src/collisions.rs::SceneCollisions::collision_block gap: minor: per-block shape; the box walk is not here +CollisionGetter.clipIncludingBorder missing unsure: no world-border clamp of a ray found in clip_ray +CollisionGetter.collidesWithSuffocatingBlock port crates/horizon-client/src/collisions.rs::SceneCollisions::is_suffocating +EntityGetter.getEntitiesOfClass skip:other class filter repeated at each caller over the entity lists; no shared lookup +EntityGetter.getPlayerByUUID skip:other uuid find repeated at each caller (e.g. allays.rs) +Explosion.getIndirectSourceEntity inline crates/horizon-client/src/game/explosion.rs::Game::explosion_hurt_entities the projectile owner rule is folded into the hurt loop +GameType.byName port crates/horizon-command/src/args.rs::GameMode::from_name name lookup; the default is the caller's +GameType.isValidId missing minor: no id check; set_mode_id falls back to survival for unknown ids +LevelAccessor.createTick port crates/horizon-world/src/level/update.rs::Sim::schedule_tick the scheduled tick; no tick object is returned +LevelHeightAccessor.isOutsideBuildHeight port crates/horizon-world/src/level/query.rs::Sim::is_inside_build_height negated +LevelReader.getHeight port crates/horizon-client/src/world.rs::World::world_surface gap: minor: WORLD_SURFACE only; other heightmap types not read here +LevelReader.getBlockStatesIfLoaded port crates/horizon-client/src/world.rs::World::loaded gap: minor: the loaded check only; the states are read with World::block +LevelReader.getNoiseBiome skip:other no caller in 26.3 code +LevelReader.getHeightmapPos port crates/horizon-client/src/world.rs::World::world_surface gap: minor: the height only; the BlockPos is built by the caller +LevelReader.getChunk missing unsure: same as Level.getChunk +LevelReader.findBlocksInBoxByManhattanDistance missing unsure: no block search by Manhattan distance found in Horizon +LevelWriter.destroyBlock port crates/horizon-world/src/level/update.rs::Sim::destroy_block +LocalMobCapCalculator.getPlayersNear port crates/horizon-client/src/mobs/spawn_selection.rs::SpawnState::players_near +LocalMobCapCalculator.addMob port crates/horizon-client/src/mobs/spawn_selection.rs::SpawnState::add_local +LocalMobCapCalculator.MobCounts.add port crates/horizon-client/src/mobs/spawn_selection.rs::SpawnState::add_local per-player count +LocalMobCapCalculator.MobCounts.canSpawn port crates/horizon-client/src/mobs/spawn_selection.rs::SpawnState::can_spawn_local diff --git a/tools/port_map/game2-011.tsv b/tools/port_map/game2-011.tsv new file mode 100644 index 0000000000..01bb8b6491 --- /dev/null +++ b/tools/port_map/game2-011.tsv @@ -0,0 +1,142 @@ +NaturalSpawner.getFilteredSpawningCategories port crates/horizon-client/src/mobs/spawn_selection.rs::SpawnState::filtered_categories categories in spawn order, filtered by enemies, persistent and the global cap +NaturalSpawner.isRightDistanceToPlayerAndSpawnPoint port crates/horizon-client/src/mobs/spawn_selection.rs::right_distance 24 block player distance, respawn point distance and chunk checks +NaturalSpawner.getMobForSpawn inline crates/horizon-client/src/mobs/spawn_selection.rs::spawn_category_for_position gap: minor: no warning log when a type cannot be created; the type is skipped there +NaturalSpawner.isValidPositionForMob inline crates/horizon-client/src/mobs/spawn_selection.rs::spawn_category_for_position the far-away removal check; gap: minor: NeoForge spawn position event hook is not modelled +NaturalSpawner.getRandomSpawnMobAt inline crates/horizon-client/src/mobs/spawn_selection.rs::spawn_category_for_position the water ambient reduce roll, then the weighted pick from mobs_at +NaturalSpawner.isInNetherFortressBounds inline crates/horizon-client/src/mobs/spawn_selection.rs::mobs_at the monster fortress check on nether bricks comes first in mobs_at +NaturalSpawner.getRandomPosWithin port crates/horizon-client/src/mobs/spawn_selection.rs::spawn_category_for_chunk random column in the chunk, height up to one above the world surface +NaturalSpawner.SpawnState.canSpawnForCategoryGlobal port crates/horizon-client/src/mobs/spawn_selection.rs::SpawnState::can_spawn_global per chunk cap from the spawnable chunk count +NaturalSpawner.SpawnState.canSpawnForCategoryLocal port crates/horizon-client/src/mobs/spawn_selection.rs::SpawnState::can_spawn_local local cap per player near the chunk; the debug flag is not kept +NoiseColumn.getBlock port crates/horizon-world/src/worldgen/structure/start.rs::NoiseColumn::get air outside the column height +NoiseColumn.setBlock skip:other no callers in vanilla; Horizon builds the column once and never writes it +PotentialCalculator.PointCharge.getPotentialChange inline crates/horizon-client/src/mobs/spawn_selection.rs::SpawnState::energy_change one charge's term charge over distance, infinity at zero distance, summed in energy_change +ServerExplosion.interactWithBlocks inline crates/horizon-client/src/game/explosion.rs::Game::server_explode_as shuffles the targets, runs each block's explosion hit, then pops the merged drops +ServerExplosion.createFire inline crates/horizon-client/src/game/explosion.rs::Game::server_explode_as one in three chance on air above a solid block, with the block's fire state +ServerExplosion.canTriggerBlocks inline crates/horizon-client/src/game/wind.rs::Game::wind_burst breeze charges need mob griefing before the trigger pass; the trigger-only check is implicit because only wind bursts reach it +ServerExplosion.StackCollector.tryMerge port crates/horizon-client/src/game/explosion.rs::add_or_append_stack merges up to 16 of the same item and damage +SignalGetter.getBestOwnOrNeighbourSignal missing player: an explosive sulfur cube on or next to redstone power does not light its fuse every tick; Horizon's sulfur cube only primes on fire or explosion damage and best_neighbor_signal has no own-signal part +SpawnData.CustomSpawnRules.checkLightBoundaries skip:java codec validation of the light limit range; Horizon decodes the limits in spawner.rs +StructureManager.forWorldGenRegion skip:other wraps the level as a worldgen region view; Horizon passes the level into worldgen fns, no manager object +StructureManager.startsForStructure inline crates/horizon-client/src/game/structures.rs::Game::structures_reaching_where gap: minor: returns starts whose box holds the position, not every start the section references +StructureManager.fillStartsForStructure inline crates/horizon-client/src/game/structures.rs::Game::structures_reaching_where the reference loop and the valid start check +StructureManager.getStructureAt port crates/horizon-client/src/game/structures.rs::Game::structures_reaching returns every start whose box holds the position; the caller takes the one it needs +StructureManager.getStructureWithPieceAt port crates/horizon-client/src/game/structures.rs::Game::structures_at gap: minor: returns names of structures with a piece at the position; no predicate filter here +StructureManager.matching inline crates/horizon-client/src/game/structures.rs::Game::structures_reaching_where the predicate becomes the wanted closure +StructureManager.structureHasPieceAt inline crates/horizon-client/src/game/structures.rs::Game::structures_reaching_where the piece box test behind the piece flag +StructureManager.getAllStructuresAt inline crates/horizon-client/src/game/structures.rs::Game::structures_reaching_where gap: minor: the chunk's full reference map is not returned; only starts whose box holds the position +Biome.getTemperature port crates/horizon-world/src/worldgen/biome_temperature.rs::BiomeTemperature::temperature height adjusted temperature with the 1024 entry cache +Biome.warmEnoughToRain port crates/horizon-world/src/worldgen/biome_temperature.rs::BiomeTemperature::warm_enough_to_rain temperature at least 0.15 +Biome.shouldMeltFrozenOceanIcebergSlightly port crates/horizon-world/src/worldgen/biome_temperature.rs::BiomeTemperature::should_melt_frozen_ocean_iceberg_slightly temperature above 0.1 +Biome.getGrassColor port crates/horizon-client/src/biome.rs::Biomes::grass the grass modifier on the base colour +Biome.getBaseGrassColor inline crates/horizon-client/src/biome.rs::Biomes::load the grass_color override, else the colormap sample, resolved at load +Biome.getGrassColorFromTexture port crates/horizon-client/src/biome.rs::sample grass colormap by clamped temperature and downfall +Biome.getFoliageColorFromTexture port crates/horizon-client/src/biome.rs::sample foliage colormap by clamped temperature and downfall +Biome.getDryFoliageColorFromTexture port crates/horizon-client/src/biome.rs::sample dry foliage colormap by clamped temperature and downfall +Biome.BiomeBuilder.putAttributes skip:java builder for the biome bootstrap; Horizon reads biome data +Biome.BiomeBuilder.build skip:java builder for the biome bootstrap; Horizon reads biome data +Biome.BiomeBuilder.toString skip:debug debug text only +BiomeGenerationSettings.Builder.addFeature skip:java builder for the biome bootstrap; feature lists come from data +BiomeGenerationSettings.PlainBuilder.addFeature skip:java builder for the biome bootstrap +BiomeGenerationSettings.PlainBuilder.addFeatureStepsUpTo skip:java builder for the biome bootstrap +BiomeGenerationSettings.PlainBuilder.build skip:java builder for the biome bootstrap +BiomeManager.getFiddledDistance inline crates/horizon-core/src/biome.rs::BiomeZoom::quart_at_block the eight corner distances with fiddled offsets; the nearest one is picked there +BiomeManager.getFiddle port crates/horizon-core/src/biome.rs::fiddle uniform offset of plus or minus 0.45 +BiomeResolver.getBiomesWithin inline crates/horizon-world/src/worldgen/structure/start.rs::try_generate the monument's surrounding check walks the same quart box of radius 29 around the sea level +Climate.parameters skip:data builds parameter points from float literals in the biome bootstrap; Horizon reads quantized points from data +Climate.Parameter.span port crates/horizon-world/src/worldgen/climate/index.rs::Parameter::span union of two parameter ranges +Climate.Parameter.toString skip:debug debug text only +Climate.Parameter.distance port crates/horizon-world/src/worldgen/climate/index.rs::Parameter::distance distance from a value to the range +Climate.ParameterList.codec skip:java codec +Climate.ParameterList.findValueBruteForce port crates/horizon-world/src/worldgen/climate/index.rs::ClimateIndex::brute_force lowest fitness, ties keep the first entry +Climate.ParameterList.findValueIndex port crates/horizon-world/src/worldgen/climate/index.rs::ClimateSearch::find tree search with the previous leaf as the candidate +Climate.ParameterPoint.fitness port crates/horizon-world/src/worldgen/climate/index.rs::ParameterPoint::fitness sum of squared distances over the seven axes +Climate.RTree.create port crates/horizon-world/src/worldgen/climate/index.rs::ClimateIndex::with_branching builds the tree with the branching factor +Climate.RTree.build port crates/horizon-world/src/worldgen/climate/index.rs::build recursive split into buckets by the cheapest axis +Climate.RTree.sort inline crates/horizon-world/src/worldgen/climate/index.rs::build the axis order sort runs inside build +Climate.RTree.comparator port crates/horizon-world/src/worldgen/climate/index.rs::compare_space compares centres on one axis, optionally by absolute value +Climate.RTree.bucketize inline crates/horizon-world/src/worldgen/climate/index.rs::build buckets sliced to the bucket size inside build +Climate.RTree.cost inline crates/horizon-world/src/worldgen/climate/index.rs::build the summed span of each axis picks the best split inside build +Climate.RTree.buildParameterSpace port crates/horizon-world/src/worldgen/climate/index.rs::space union of the children's ranges per axis +Climate.RTree.search port crates/horizon-world/src/worldgen/climate/index.rs::ClimateSearch::find search from the root with the previous result kept +Climate.RTree.Node.distance port crates/horizon-world/src/worldgen/climate/index.rs::distance squared distance from the target to the node's box +Climate.RTree.SubTree.search port crates/horizon-world/src/worldgen/climate/index.rs::ClimateIndex::search recursive search over the children +Climate.Sampler.sample port crates/horizon-world/src/worldgen/climate.rs::ClimateSampler::sample samples the six climate fields at a quart +FixedBiomeSource.findBiomeHorizontal missing unsure: flat worlds use one fixed biome, and this override picks a random point in the box; Horizon's ring search is the generic one and draws random values in a different order +FixedBiomeSource.findClosestBiome3d port crates/horizon-world/src/worldgen/generator.rs::Pipeline::find_closest_biome generic 3D search; for one biome its first sample is the origin +MobSpawnSettings.SpawnerData.toString skip:debug debug text only +MultiNoiseBiomeSource.stable skip:other preset check used only by world creation dimension validation +MultiNoiseBiomeSource.createResolver port crates/horizon-world/src/worldgen/biome.rs::MultiNoiseBiomeSource::resolver resolver over the sampler +MultiNoiseBiomeSource.createResolverForChunk port crates/horizon-world/src/worldgen/biome.rs::BiomeResolver::prepare_volume samples the climate volume for the chunk; sample_prepared then resolves each quart +MultiNoiseBiomeSource.addDebugInfo skip:debug debug screen line with the climate values +AbstractBedBlock.getSleepHeight inline crates/horizon-client/src/game.rs::Game::tick_sleep the sleep height is the fixed bed top 0.5625 above the head cell; the shape is not read +AbstractBedBlock.onStopSleeping missing player: waking up on a straw bed in the Overworld should break it (straw_bed_rule destroy_on_leave); Horizon's wake_up never breaks the bed +AbstractBedBlock.kickVillagerOutOfBed missing player: right-clicking a bed a villager sleeps in neither wakes the villager nor shows the occupied message; Horizon's bed use never reads the occupied state +AbstractBedBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class the other half removed takes this half with it, and occupied comes from the other half +AbstractBedBlock.playerWillDestroy port crates/horizon-world/src/level/hooks.rs::Sim::player_destroy creative breaking of the foot removes the head +AbstractBedBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class the cell in front must be replaceable; gap: minor: no world border check on that cell +AbstractBedBlock.getConnectedDirection port crates/horizon-world/src/level/shapes.rs::Sim::bed_neighbour_direction the way to the other half: facing from the foot, opposite from the head +AbstractBedBlock.getBlockType skip:other chooses the block entity half for the combiner; Horizon keeps one bed block state with no combined block entity +AbstractBedBlock.findBunkBedStandUpPosition inline crates/horizon-client/src/game/respawn_anchor.rs::Game::bed_stand_up bunk bed branch: around the bed, a block lower, above, then the unsafe fallbacks +AbstractBedBlock.findStandUpPositionAtOffset inline crates/horizon-client/src/game/respawn_anchor.rs::Game::bed_stand_up the offset loop; each cell goes through safe_dismount_location +AbstractBedBlock.createBlockStateDefinition skip:data state definition comes from the block data tables +AbstractBedBlock.setPlacedBy port crates/horizon-world/src/level/place.rs::Sim::set_placed_by places the head in the cell the bed faces +AbstractBedBlock.getSeed skip:other model seed of a random block variant; beds have one model, nothing reads it +AbstractBedBlock.bedSurroundStandUpOffsets inline crates/horizon-client/src/game/respawn_anchor.rs::Game::bed_stand_up the ten cells around the bed, built inside bed_stand_up +AbstractBedBlock.bedAboveStandUpOffsets inline crates/horizon-client/src/game/respawn_anchor.rs::Game::bed_stand_up the two cells on the bed, built inside bed_stand_up +AbstractCandleBlock.onProjectileHit port crates/horizon-client/src/arrows.rs::Ticker::block_reaction a fire arrow lights an unlit, dry candle or candle cake +AbstractCandleBlock.extinguish inline crates/horizon-world/src/level/fluids.rs::Sim::place_liquid gap: minor: water flow turns a lit candle off with its sound; the smoke particles and game event are not here (the candle cake click is CandleCakeBlock's) +AbstractCandleBlock.setLit inline crates/horizon-client/src/arrows.rs::Ticker::block_reaction the lit=true write for a fire arrow; the lit=false write is in place_liquid +AbstractCandleBlock.onExplosionHit port crates/horizon-client/src/game/wind.rs::Game::wind_trigger_block a wind burst puts out a lit candle; only trigger blasts do this, as in vanilla +AbstractCauldronBlock.useItemOn port crates/horizon-client/src/game.rs::Game::use_on_cauldron held item interaction with a cauldron +AbstractSkullBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class powered from the neighbour signal at placement +AbstractSkullBlock.neighborChanged port crates/horizon-world/src/level/redstone.rs::Sim::skull_neighbor_changed powered state follows the neighbour signal +AnvilBlock.useWithoutItem inline crates/horizon-world/src/level/interact.rs::Sim::use_without_item anvil screen opened from the use result, in the station list +AnvilBlock.getMenuProvider inline crates/horizon-world/src/level/interact.rs::Sim::use_without_item the menu itself is built by the client station code; gap: minor: only the open is here +AnvilBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate facing rotated through the state transform table +BambooStalkBlock.createBlockStateDefinition skip:data state definition comes from the block data tables +BambooStalkBlock.getShape skip:data the stalk box comes from the block data tables by leaves size +BambooStalkBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class no placement in fluid, age and stage from the bamboo below +BambooStalkBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block breaks the stalk when it cannot survive on the scheduled tick +BambooStalkBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class schedules the survive check and cycles age when the bamboo above grows +BambooStalkBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid height and stage limits for growth +BambooStalkBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal grows the stalk up to the limit +BambooStalkBlock.getHeightAboveUpToMax port crates/horizon-world/src/level/tick.rs::Sim::bamboo_height_above counts bamboo above up to the max +BambooStalkBlock.getHeightBelowUpToMax port crates/horizon-world/src/level/tick.rs::Sim::bamboo_height_below counts bamboo below up to the max +BannerBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class standing banner placement +BannerBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class a standing banner breaks when its support goes +BannerBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate rotation through the state transform table +BannerBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror mirror through the state transform table +BaseCoralPlantTypeBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class waterlogged when the clicked cell is full water +BaseCoralPlantTypeBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class goes to air when it cannot survive below +BaseCoralPlantTypeBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class needs a sturdy top face below +BaseCoralPlantTypeBlock.getFluidState skip:other waterlogged water comes from the block state in the generic fluid code +BaseFireBlock.inPortalDimension inline crates/horizon-world/src/level/portal.rs::Sim::fire_can_be_placed_at the Overworld or Nether check is inside fire_can_be_placed_at +BaseFireBlock.playerWillDestroy port crates/horizon-world/src/level/hooks.rs::Sim::player_destroy fire broken by a player emits level event 1009 +BaseFireBlock.isPortal inline crates/horizon-world/src/level/portal.rs::Sim::fire_can_be_placed_at the portal frame check and axis pick are inside fire_can_be_placed_at +BasePressurePlateBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class goes to air when it cannot survive below +BasePressurePlateBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class rigid block or centre support below +BasePressurePlateBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block re-checks the pressed signal on the scheduled tick +BasePressurePlateBlock.entityInside port crates/horizon-world/src/level/hooks.rs::Sim::entity_inside an entity pressing an unpowered plate runs the pressed check +BasePressurePlateBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal a powered plate removed updates its neighbours +BasePressurePlateBlock.getEntityCount inline crates/horizon-world/src/level/redstone.rs::Sim::plate_signal_strength counts entities in the detection area +BaseRailBlock.isRail port crates/horizon-world/src/level/rails.rs::Sim::is_rail rail test on a block state +BaseRailBlock.getShape skip:data the rail box comes from the block data tables; rail_shape only reads the shape property +BaseRailBlock.onPlace port crates/horizon-world/src/level/rails.rs::Sim::rail_place the placed rail takes its shape and connections +BaseRailBlock.neighborChanged port crates/horizon-world/src/level/rails.rs::Sim::rail_neighbor_changed neighbour update of a rail +BaseRailBlock.shouldBeRemoved inline crates/horizon-world/src/level/rails.rs::Sim::rail_neighbor_changed the rigid support check below and beside ascending rails is in the neighbour update +BaseRailBlock.updateDir port crates/horizon-world/src/level/rails.rs::Sim::rail_update_dir shape update for the rail's direction +BaseRailBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal rail arm updates neighbours after removal +BaseRailBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class BaseRail arm of placement +BaseRailBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate rotation through the state transform table +BaseRailBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror mirror through the state transform table +BaseRailBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class BaseRail arm of the shape update +BaseRailBlock.getFluidState skip:other waterlogged water comes from the block state in the generic fluid code +BeaconBlock.useWithoutItem inline crates/horizon-world/src/level/interact.rs::Sim::use_without_item beacon screen opened from the use result, in the station list +BeehiveBlock.hiveContainsBees inline crates/horizon-client/src/game/beehives.rs::Game::use_beehive the bees-in-hive check before angering bees in the honey harvest path +BeehiveBlock.releaseBeesAndResetHoneyLevel port crates/horizon-client/src/game/beehives.rs::Game::empty_hive gap: minor: the honey reset is not in empty_hive; set_honey_level does it +BeehiveBlock.resetHoneyLevel port crates/horizon-client/src/game/beehives.rs::GameHive::set_honey_level honey level written back to the hive +BeehiveBlock.spawnParticle inline crates/horizon-client/src/animate_tick.rs::beehive_drip the drip point from the hive shape; the honey drip particle is added there +BeehiveBlock.spawnFluidParticle inline crates/horizon-client/src/animate_tick.rs::beehive_drip the dripping honey particle position +BeehiveBlock.createBlockStateDefinition skip:data state definition comes from the block data tables +BeehiveBlock.playerWillDestroy missing player: in survival, breaking a hive with bees or honey should give the hive item back with its bees and honey level; Horizon's break path only angers the bees +BeehiveBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate rotation through the state transform table diff --git a/tools/port_map/game2-012.tsv b/tools/port_map/game2-012.tsv new file mode 100644 index 0000000000..dd5298e7b5 --- /dev/null +++ b/tools/port_map/game2-012.tsv @@ -0,0 +1,148 @@ +BellBlock.neighborChanged port crates/horizon-world/src/level/redstone.rs::Sim::bell_neighbor_changed Rings on a new signal and stores POWERED. +BellBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item Class::Bell arm calls bell_hit. +BellBlock.isProperHit inline crates/horizon-world/src/level/redstone.rs::Sim::bell_hit Proper-hit test is folded into bell_hit. +BellBlock.getVoxelShape skip:data Shape precomputed from the extracted block shapes (level/data.rs). +BellBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Class::Bell arm picks attachment and facing. +BellBlock.onExplosionHit port crates/horizon-client/src/game/wind.rs::Game::wind_trigger_block gap: player: a wind charge burst that hits a bell should ring it; Horizon sends an Up-face use that bell_hit rejects, so it stays silent. TNT does not reach this (canTriggerBlocks is false). +BellBlock.getConnectedDirection port crates/horizon-world/src/level/survive.rs::Sim::bell_connected_direction Same floor/ceiling/wall mapping. +BellBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +BellBlock.rotate skip:data Rotation is a precomputed table from extracted data (worldgen/structure.rs rotate). +BigDripleafBlock.makeShapes skip:data Shape precomputed from the extracted block shapes (level/data.rs). +BigDripleafBlock.canReplace inline crates/horizon-world/src/level/bonemeal.rs::Sim::dripleaf_can_grow_into Only used by canGrowInto; folded into dripleaf_can_grow_into. +BigDripleafBlock.place port crates/horizon-world/src/level/bonemeal.rs::Sim::dripleaf_place Sets big dripleaf with waterlogged and facing. +BigDripleafBlock.getFluidState port crates/horizon-world/src/level/mod.rs::Behaviours::fluid Waterlogged fluid is read from per-state facts. +BigDripleafBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal Kind::BigDripleaf arm places the stem and the leaf above. +BigDripleafBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::big_dripleaf_tick Signal resets tilt; UNSTABLE to PARTIAL (10 ticks) to FULL (100 ticks) with sound. +BigDripleafBlock.playTiltSound inline crates/horizon-world/src/level/tick.rs::Sim::big_dripleaf_tick Random pitch sound is inside the tick. +BigDripleafBlock.setTiltAndScheduleTick inline crates/horizon-world/src/level/tick.rs::Sim::big_dripleaf_tick Tick path schedules the next tilt; the UNSTABLE entity path is big_dripleaf_entity_inside in hooks.rs. +BigDripleafBlock.setTilt inline crates/horizon-world/src/level/tick.rs::Sim::big_dripleaf_tick Tilt write plus block_change game event folded into the tick. +BigDripleafBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Class::BigDripleaf arm takes facing from the part below. +BigDripleafBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +Block.getId skip:data Block state ids are the extracted registry indices. +Block.stateById skip:data State lookup by id over the registry built from extracted data. +Block.byItem skip:data Item to block link comes from the item table (info.block in horizon-items/src/items.rs). +Block.box skip:data Shape builder; Horizon reads precomputed shapes from extracted data. +Block.boxes skip:data Shape array builder; shapes come from extracted data. +Block.cube skip:data Shape builder; shapes come from extracted data. +Block.boxZ skip:data Shape builder; shapes come from extracted data. +Block.dropFromLootTable inline crates/horizon-client/src/game/spill.rs::GameLevel::drop_interact_loot gap: minor: returns nothing, where vanilla returns whether anything dropped; folded into the interact-loot drop. +Block.popExperience port crates/horizon-client/src/game.rs::Game::spawn_orbs gap: minor: popExperience checks the block_drops rule, and the ore XP in break_block_as_player does not. +Block.fallOn port crates/horizon-world/src/block_landing.rs::BlockLandingRegistry::fall_on Default and hay rules apply fall damage with the block's reduction. +Block.getShapeForEachState skip:java Shape lookup cache over the state table; Horizon precomputes shapes per state. +Block.tryDropExperience inline crates/horizon-client/src/game.rs::Game::break_block_as_player Sampled block XP and pop folded into the break path; silk touch zeroes it as in vanilla (hard-coded check). +Block.ShapePairKey.equals skip:java Hash key for the shape cache. +Blocks.ocelotOrParrot port crates/horizon-client/src/mobs/spawn_rules.rs::is_valid_spawn Leaves spawn rule for ocelots and parrots (VALID_SPAWN_LEAVES). +Blocks.logProperties skip:data Block.Properties builder; the values come from extracted block data. +Blocks.netherStemProperties skip:data Block.Properties builder; the values come from extracted block data. +Blocks.leavesProperties skip:data Block.Properties builder; the values come from extracted block data. +Blocks.shulkerBoxProperties skip:data Block.Properties builder; the values come from extracted block data. +Blocks.pistonProperties skip:data Block.Properties builder; the values come from extracted block data. +Blocks.candleProperties skip:data Block.Properties builder; the values come from extracted block data. +Blocks.registerLegacyStair skip:data Registry bootstrap; Horizon reads block definitions from extracted data. +Blocks.registerStair skip:data Registry bootstrap; Horizon reads block definitions from extracted data. +Blocks.registerSlab skip:data Registry bootstrap; Horizon reads block definitions from extracted data. +Blocks.register skip:data Registry bootstrap; Horizon reads block definitions from extracted data. +BrushableBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Class::Brushable arm schedules the 2-tick update. +BubbleColumnBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick Bubble and current particles plus whirlpool and upwards ambient sounds. +BubbleColumnBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Class::BubbleColumn arm schedules the water tick and the 5-tick recheck. +BubbleColumnBlock.pickupBlock missing player: an empty bucket on a bubble column should scoop a water bucket and remove the column; Horizon's bucket_pickup takes only liquids and waterlogged blocks, so nothing happens. +ButtonBlock.makeShapes skip:data Shape precomputed from the extracted block shapes (level/data.rs). +ButtonBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item Class::Button arm presses it, or consumes when already pressed. +ButtonBlock.onExplosionHit port crates/horizon-client/src/game/wind.rs::Game::wind_trigger_block Wind charge bursts use the button like a player use; TNT does not reach it. +ButtonBlock.playSound port crates/horizon-world/src/level/redstone.rs::Sim::button_press Press sound; the release sound is in button_check_pressed. +ButtonBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal Button and lever arm updates neighbours when powered and not moved. +ButtonBlock.getDirectSignal port crates/horizon-world/src/level/redstone.rs::Sim::state_direct_signal Strong power read from the per-state facts. +ButtonBlock.updateNeighbours port crates/horizon-world/src/level/redstone.rs::Sim::attached_update_neighbours Updates the button and the block it is attached to. +ButtonBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +CactusBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::cactus_random_tick Growth, cactus flower chance and reset at age 15. +CactusBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Class::Cactus arm schedules a 1-tick survival check. +CactusBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class Solid or lava side neighbours block it; needs cactus or support tag below and no liquid above. +CampfireBlock.entityInside port crates/horizon-client/src/motion.rs::MotionWorld::check_inside_blocks Lit campfire hurts living entities through environment_hurt. +CampfireBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Waterlogged, lit, signal fire from hay below, facing from the player. +CampfireBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Waterlogged tick and signal fire from the block below. +CampfireBlock.placeLiquid port crates/horizon-world/src/level/fluids.rs::Sim::place_liquid Water puts it out with the extinguish sound and sets waterlogged. +CampfireBlock.onProjectileHit missing player: a burning arrow (Flame bow) that hits an unlit campfire should light it; Horizon's projectile_hit only dispatches bells and targets. +CampfireBlock.getFluidState port crates/horizon-world/src/level/mod.rs::Behaviours::fluid Waterlogged fluid is read from per-state facts. +CampfireBlock.rotate skip:data Rotation is a precomputed table from extracted data. +CampfireBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +CampfireBlock.getTicker port crates/horizon-client/src/game/campfire.rs::Game::tick_campfire Cooking tick when lit, cooldown tick when unlit. +CandleBlock.useItemOn missing player: an empty hand on a lit candle should snuff it out with the extinguish sound; Horizon has no snuff path (candles only light from arrows). +CandleBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced Adding a candle to a candle with fewer than four is allowed. +CandleBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Class::Candle arm cycles the candle count when placed on a candle. +CandleBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Waterlogged water tick. +CandleBlock.getFluidState port crates/horizon-world/src/level/mod.rs::Behaviours::fluid Waterlogged fluid is read from per-state facts. +CandleBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +CandleBlock.placeLiquid port crates/horizon-world/src/level/fluids.rs::Sim::place_liquid Water waterlogs the candle and puts out a lit one. +CandleCakeBlock.useItemOn missing player: an empty hand on a lit candle cake should snuff the candle; Horizon has no snuff path. +CandleCakeBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item CandleCake arm eats the cake and drops the candle. +CandleCakeBlock.candleHit missing player: the hit-on-candle check for the missing candle-cake snuff. +CandleCakeBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Candle cake drops to air when unsupported below. +CarvedPumpkinBlock.getWeatherStateFromPattern inline crates/horizon-client/src/game/golems.rs::Game::try_spawn_copper_golem Weathering comes from the copper block below; waxed blocks count as unwaxed. +CarvedPumpkinBlock.getOrCreateSnowGolemBase inline crates/horizon-client/src/game/golems.rs::Game::find_golem Snow column under the head; the lazy pattern cache is not needed. +CarvedPumpkinBlock.getOrCreateSnowGolemFull inline crates/horizon-client/src/game/golems.rs::Game::find_golem Snow golem with a pumpkin head on the snow column. +CarvedPumpkinBlock.getOrCreateIronGolemBase inline crates/horizon-client/src/game/golems.rs::Game::find_golem Iron arms and body under the head. +CarvedPumpkinBlock.getOrCreateIronGolemFull inline crates/horizon-client/src/game/golems.rs::Game::find_golem Iron golem pattern with air cells. +CarvedPumpkinBlock.getOrCreateCopperGolemBase port crates/horizon-client/src/game/golems.rs::Game::copper_golem_base Copper block under the head. +CarvedPumpkinBlock.getOrCreateCopperGolemFull inline crates/horizon-client/src/game/golems.rs::Game::try_spawn_copper_golem Copper golem spawn with the pumpkin head; the head check is in is_golem_head. +CarvedPumpkinBlock.replaceCopperBlockWithChest inline crates/horizon-client/src/game/golems.rs::Game::try_spawn_copper_golem Copper block becomes its chest with flag 2 through copper_chest_from_block. +CauldronBlock.shouldHandlePrecipitation inline crates/horizon-world/src/level/tick.rs::Sim::precipitation_tick The 5 percent rain and 10 percent snow chances are inside the precipitation tick. +CauldronBlock.handlePrecipitation port crates/horizon-world/src/level/tick.rs::Sim::precipitation_tick Rain fills to water cauldron, snow to powder snow cauldron. +CauldronBlock.receiveStalactiteDrip port crates/horizon-world/src/level/speleothem.rs::Sim::cauldron_tick Water and lava drips fill the cauldron with level events 1047 and 1046. +ChainBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Chain takes the default placement, waterlogged from the fluid. +ChainBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Waterlogged water tick, then the default update. +ChainBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +ChainBlock.getFluidState port crates/horizon-world/src/level/mod.rs::Behaviours::fluid Waterlogged fluid is read from per-state facts. +ChangeOverTimeBlock.getNextState port crates/horizon-world/src/level/tick.rs::Sim::copper_change_over_time Counts weathering neighbours within 4 and rolls the chance, 0.75 for unaffected. +ChangeOverTimeBlock.isSameAgeingType inline crates/horizon-world/src/level/tick.rs::Sim::copper_change_over_time Same weathering family is the WeatheringCopper check in the neighbour loop. +ChiseledBookShelfBlock.useItemOn port crates/horizon-client/src/game/chiseled_bookshelf.rs::Game::use_chiseled_bookshelf Holding a book inserts it into the hit slot. +ChiseledBookShelfBlock.useWithoutItem port crates/horizon-client/src/game/chiseled_bookshelf.rs::Game::use_chiseled_bookshelf Empty hand takes the book out of the hit slot. +ChiseledBookShelfBlock.addBook inline crates/horizon-client/src/game/chiseled_bookshelf.rs::Game::use_chiseled_bookshelf Used stat, insert sound (enchanted variant), slot update folded in. +ChiseledBookShelfBlock.removeBook inline crates/horizon-client/src/game/chiseled_bookshelf.rs::Game::use_chiseled_bookshelf Pickup sound, book given or dropped, block_change event folded in. +ChiseledBookShelfBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +ChiseledBookShelfBlock.rotate skip:data Rotation is a precomputed table from extracted data. +ChorusFlowerBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block Destroys the flower when it cannot survive. +ChorusFlowerBlock.isRandomlyTicking skip:data Random tick flag per state comes from extracted data. +ChorusFlowerBlock.placeGrownFlower inline crates/horizon-world/src/level/tick.rs::Sim::chorus_flower_random_tick Sets the age and level event 1033 inside the random tick. +ChorusFlowerBlock.placeDeadFlower inline crates/horizon-world/src/level/tick.rs::Sim::chorus_flower_random_tick Sets age 5 and level event 1034 inside the random tick. +ChorusFlowerBlock.allNeighborsEmpty inline crates/horizon-world/src/level/tick.rs::Sim::chorus_flower_random_tick The all_empty check of the random tick; the worldgen copy is ChorusPlantFeature::neighbors_empty. +ChorusFlowerBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Class::ChorusFlower arm schedules a survival check when the side or below changes. +ChorusFlowerBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class Needs a chorus plant or support tag below, or a single plant neighbour in the air. +ChorusFlowerBlock.generatePlant port crates/horizon-world/src/worldgen/chorus_plant.rs::ChorusPlantFeature::place Connects the base plant and grows the tree. +ChorusFlowerBlock.growTreeRecursive port crates/horizon-world/src/worldgen/chorus_plant.rs::ChorusPlantFeature::grow Random height, stems and flowers at the tips. +ChorusFlowerBlock.onProjectileHit missing player: an arrow or other breakable projectile hitting a chorus flower should break it and drop it; Horizon's projectile_hit only dispatches bells and targets. +ChorusPlantBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Class::ChorusPlant arm updates the side connection and schedules the survival check. +ChorusPlantBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block Destroys the plant when it cannot survive. +ChorusPlantBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class Plant or support tag below, or a plant neighbour with support under it. +ChorusPlantBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +CommandBlock.neighborChanged port crates/horizon-client/src/game/command_blocks.rs::set_powered Stores the redstone power and marks the condition when it turns on. +CommandBlock.getAnalogOutputSignal port crates/horizon-client/src/game/command_blocks.rs::analog_signal Success count of the command block. +CommandBlock.rotate skip:data Rotation is a precomputed table from extracted data. +CommandBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +ComparatorBlock.getOutputSignal port crates/horizon-world/src/level/redstone.rs::Sim::comparator_output Stored output signal of the comparator. +ComparatorBlock.calculateOutputSignal port crates/horizon-world/src/level/redstone.rs::Sim::comparator_calculate_output Compare and subtract modes against the side input. +ComparatorBlock.shouldTurnOn port crates/horizon-world/src/level/redstone.rs::Sim::diode_should_turn_on Input against side input in compare or subtract mode. +ComparatorBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item Class::Comparator arm cycles the mode with the click sound and refreshes output. +ComparatorBlock.checkTickOnNeighbor port crates/horizon-world/src/level/redstone.rs::Sim::diode_check_tick_on_neighbor Schedules the 2-tick refresh when output or power would change. +ComparatorBlock.refreshOutputState port crates/horizon-world/src/level/redstone.rs::Sim::comparator_refresh Updates the output, powered state and neighbours in front. +ComparatorBlock.triggerEvent skip:other No effect: vanilla BlockEntity.triggerEvent returns false and the comparator entity does not override it. +ComparatorBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. +ComposterBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place A full composter schedules its ready tick in 20 ticks. +ComposterBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block Full composter becomes ready (level 8) with the ready sound. +ComposterBlock.getContainer port crates/horizon-client/src/game/machines.rs::Containers::view Composter level picks the input, output or empty view. +ComposterBlock.InputContainer.getSlotsForFace port crates/horizon-client/src/game/machines.rs::View::slots_for_face Up face, slot 0 while the level is under 7. +ComposterBlock.InputContainer.canPlaceItemThroughFace port crates/horizon-client/src/game/composter.rs::Game::composter_container_insert Only from above, below level 7, and compostable. +ComposterBlock.InputContainer.setChanged port crates/horizon-client/src/game/composter.rs::Game::composter_container_insert Composts the item at once through addLayer. +ComposterBlock.OutputContainer.getSlotsForFace port crates/horizon-client/src/game/machines.rs::View::slots_for_face Down face, slot 0 at level 8. +ComposterBlock.OutputContainer.setChanged port crates/horizon-world/src/level/interact.rs::Behaviours::composter_empty Empties the composter to level 0. +ChestBlock.getBlockType port crates/horizon-client/src/game/storage.rs::Game::chest_partner Partner half and right-half flag give the single, first or second combine type. +ChestBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Class::Chest arm joins or splits a double chest with the neighbour. +ChestBlock.getShape skip:data Outline shape per state comes from extracted block shapes. +ChestBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Class::Chest arm sets facing, double type and waterlogged. +ChestBlock.getChestType inline crates/horizon-world/src/level/place.rs::Sim::placement_class Left or right partner check folded into the Chest placement arm. +ChestBlock.getFluidState port crates/horizon-world/src/level/mod.rs::Behaviours::fluid Waterlogged fluid is read from per-state facts. +ChestBlock.candidatePartnerFacing port crates/horizon-world/src/level/place.rs::Sim::chest_candidate_partner_facing Facing of a single chest on the side, when it can connect. +ChestBlock.useWithoutItem port crates/horizon-client/src/game.rs::Game::open_menu Opens the chest menu, with the open stat and piglin anger. +ChestBlock.getTicker port crates/horizon-client/src/block_entity_anim.rs::ticker Client-side lid animation ticker. +ChestBlock.isCatSittingOnChest port crates/horizon-client/src/game/storage.rs::Game::chest_blocked_at A sitting cat above blocks the chest. +ChestBlock.rotate skip:data Rotation is a precomputed table from extracted data. +ChestBlock.createBlockStateDefinition skip:data Block state properties come from extracted data. diff --git a/tools/port_map/game2-013.tsv b/tools/port_map/game2-013.tsv new file mode 100644 index 0000000000..2917bd1afe --- /dev/null +++ b/tools/port_map/game2-013.tsv @@ -0,0 +1,148 @@ +ConcretePowderBlock.onLand port crates/horizon-world/src/level/falling.rs::Behaviours::falling_block_landed landing branch turns the powder to concrete when the spot is water or touches liquid +ConcretePowderBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class ConcretePowder arm: concrete when the clicked spot is water or touches liquid +ConcretePowderBlock.shouldSolidify inline crates/horizon-world/src/level/falling.rs::Behaviours::falling_block_landed gap: minor: both overloads folded into the landing branch; only the water fluid test, canBeHydrated is not read +ConcretePowderBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class concrete arm: turns to concrete when touching liquid, else default shape +CopperBulbBlock.onPlace inline crates/horizon-world/src/level/hooks.rs::Sim::on_place copper_bulb_check runs on placement when the old block differs +CopperBulbBlock.neighborChanged inline crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed arm calls copper_bulb_check (checkAndFlip) on neighbour change +CopperBulbBlock.createBlockStateDefinition skip:data lit and powered properties come from the block state tables +CopperBulbBlock.getAnalogOutputSignal port crates/horizon-world/src/level/redstone.rs::Sim::analog_output CopperBulb arm: 15 when lit, else 0 +CopperChestBlock.getHingeSound inline crates/horizon-client/src/mobs/transport_items.rs::open_or_close weathered, oxidized and plain open and close sound ids are chosen inside open_or_close +CopperGolemStatueBlock.createBlockStateDefinition skip:data facing, pose and waterlogged properties come from the block state tables +CopperGolemStatueBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class facing opposite the player and waterlogged when the spot is water +CopperGolemStatueBlock.rotate skip:data rotation results come from the exported BlockState rotate table (worldgen/structure.rs) +CopperGolemStatueBlock.updatePose inline crates/horizon-world/src/level/interact.rs::Behaviours::turn_statue become-statue sound, pose cycle and block_change game event are folded into turn_statue +CopperGolemStatueBlock.isPathfindable skip:data path types come from the per-state path data (mobs/path.rs) +CopperGolemStatueBlock.getCloneItemStack port crates/horizon-client/src/game/block_items.rs::Game::picked_stack gap: player: the picked statue does not get its pose in block_state, so a middle-click pick gives it standing +CopperGolemStatueBlock.getFluidState skip:data waterlogged water source comes from the per-state fluid facts table (Level::fluid) +CopperGolemStatueBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class statue arm runs waterlogged_tick then the default shape update +CrafterBlock.getAnalogOutputSignal port crates/horizon-client/src/game/machines.rs::Containers::signal crafter signal is the filled or disabled slot count from the block entity +CrafterBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Crafter arm sets orientation from look direction and triggered from neighbour signal +CrafterBlock.setPlacedBy port crates/horizon-world/src/level/place.rs::Sim::set_placed_by schedules the 4-tick crafter tick when triggered +CrafterBlock.rotate skip:data rotation results come from the exported BlockState rotate table (worldgen/structure.rs) +CrafterBlock.mirror skip:data mirror results come from the exported BlockState mirror table (worldgen/structure.rs) +CrafterBlock.createBlockStateDefinition skip:data orientation, triggered and crafting properties come from the block state tables +CraftingTableBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item CraftingTable arm opens the menu; the interact_with_crafting_table stat is awarded in Game::open_station +CreakingHeartBlock.getTicker port crates/horizon-world/src/level/creaking.rs::Sim::heart_tick scheduled heart tick; the uprooted check is done inside heart_update_state +CreakingHeartBlock.animateTick missing player: the creak idle sound near a heart with its logs at night is never played (no creaking_heart.idle sound in Horizon) +CreakingHeartBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class CreakingHeart arm schedules the 1-tick heart update then the default shape +CreakingHeartBlock.isSurroundedByLogs missing player: the six-side pale oak log check only gates the idle creak sound in animateTick +CreakingHeartBlock.createBlockStateDefinition skip:data axis, creaking_heart_state and natural properties come from the block state tables +CreakingHeartBlock.tryAwardExperience inline crates/horizon-client/src/game/creaking.rs::Game::creaking_heart_broken natural heart drops 20-24 xp orbs when broken by a player outside creative and spectator +CreakingHeartBlock.getAnalogOutputSignal port crates/horizon-client/src/mobs/creaking_heart.rs::output_signal gap: minor: distance-based signal, no explicit uprooted check; a heart with no creaking or protector gives 0 +CropBlock.growCrops inline crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal crop arm adds the bonemeal age increase capped at max age and sets the state +CropBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class Crop arm: raw light of at least 8 and the supports_crops ground check +CropBlock.entityInside missing player: ravagers no longer trample crops they walk into; Horizon only breaks leaves for ravagers +DaylightDetectorBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item DaylightDetector arm checks mayBuild, cycles inverted, sends block change and updates the signal +DaylightDetectorBlock.getTicker port crates/horizon-world/src/level/mod.rs::Behaviours::tick_daylight_detector gap: minor: every 20 game ticks; only the Nether is skipped here, the End has no sky light but is not excluded +DaylightDetectorBlock.createBlockStateDefinition skip:data power and inverted properties come from the block state tables +DecoratedPotBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class DecoratedPot arm: waterlogged tick then the default shape update +DecoratedPotBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class DecoratedPot arm: facing from the player, waterlogged from the spot, cracked false +DecoratedPotBlock.useWithoutItem port crates/horizon-client/src/game/decorated_pot.rs::Game::use_decorated_pot empty-hand path plays insert_fail, negative wobble and block change +DecoratedPotBlock.createBlockStateDefinition skip:data facing, waterlogged and cracked properties come from the block state tables +DecoratedPotBlock.getFluidState skip:data waterlogged water source comes from the per-state fluid facts table (Level::fluid) +DecoratedPotBlock.onProjectileHit missing player: a projectile hitting a pot does not crack it, and the pot is not broken by the hit +DecoratedPotBlock.getCloneItemStack missing player: middle-click pick of a pot gives an empty pot, not one with its sherd decorations +DecoratedPotBlock.rotate skip:data rotation results come from the exported BlockState rotate table (worldgen/structure.rs) +DetectorRailBlock.entityInside port crates/horizon-world/src/level/hooks.rs::Sim::entity_inside when not powered, checks for minecarts inside the search box +DetectorRailBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block when powered, rechecks the minecart presence +DetectorRailBlock.getDirectSignal skip:data the power to the block above comes from the per-state strong signal facts (direct_signal default arm) +DetectorRailBlock.updatePowerToConnected inline crates/horizon-world/src/level/rails.rs::Sim::detector_rail_check_pressed neighbour notify on the connected rails is folded into check_pressed +DetectorRailBlock.onPlace inline crates/horizon-world/src/level/hooks.rs::Sim::on_place rail state update and check_pressed on a new placement +DetectorRailBlock.getSearchBB inline crates/horizon-world/src/level/rails.rs::Sim::detector_rail_check_pressed the 0.2-inset search box is built inline +DetectorRailBlock.rotate skip:data rail shape rotation comes from the exported BlockState rotate table (worldgen/structure.rs) +DetectorRailBlock.mirror skip:data rail shape mirror comes from the exported BlockState mirror table (worldgen/structure.rs) +DetectorRailBlock.createBlockStateDefinition skip:data shape, powered and waterlogged properties come from the block state tables +DiodeBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class Diode arm: the block below must be a rigid sturdy face +DiodeBlock.getInputSignal port crates/horizon-world/src/level/redstone.rs::Sim::diode_input_signal input from the facing target, wire power, comparator analog and conductor cases +DiodeBlock.getAlternateSignal port crates/horizon-world/src/level/redstone.rs::Sim::diode_alternate_signal maximum of the two side control inputs +DiodeBlock.ownSignal skip:data the output while powered comes from the per-state weak signal facts (state_signal default arm) +DiodeBlock.getSignal skip:data the facing check and output per side come from the per-state weak signal facts (state_signal default arm) +DiodeBlock.setPlacedBy port crates/horizon-world/src/level/place.rs::Sim::set_placed_by schedules the 1-tick update when the diode should turn on +DiodeBlock.updateNeighborsInFront port crates/horizon-world/src/level/redstone.rs::Sim::diode_update_neighbors_in_front neighbour notify on the block behind the output +DiodeBlock.shouldPrioritize port crates/horizon-world/src/level/redstone.rs::Sim::diode_should_prioritize checks for a diode facing back at it +DispenserBlock.registerProjectileBehavior inline crates/horizon-client/src/game/dispenser.rs::Game::dispense_item egg, snowball and wind charge dispense as projectiles in dispense_item +DispenserBlock.neighborChanged port crates/horizon-world/src/level/redstone.rs::Sim::dispenser_neighbor_changed a signal at the block or above starts a 4-tick dispense and sets triggered +DispenserBlock.rotate skip:data rotation results come from the exported BlockState rotate table (worldgen/structure.rs) +DispenserBlock.createBlockStateDefinition skip:data facing and triggered properties come from the block state tables +DoorBlock.getShape skip:data outline shapes come from the per-state shape table (facts shape index) +DoorBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Door arm: the other half follows, and the lower half breaks when unsupported +DoorBlock.onExplosionHit missing player: a wind charge blast does not swing a door open; canOpenByWindCharge is read from data but no code uses it +DoorBlock.playerWillDestroy port crates/horizon-world/src/level/hooks.rs::Sim::player_destroy creative break of the upper half removes the lower half without drops +DoorBlock.isPathfindable skip:data path types come from the per-state path data (mobs/path.rs) +DoorBlock.setPlacedBy port crates/horizon-world/src/level/place.rs::Sim::set_placed_by Door branch places the upper half +DoorBlock.neighborChanged port crates/horizon-world/src/level/redstone.rs::Sim::door_neighbor_changed redstone power toggles the door open and plays the sound +DoorBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class Door arm: lower half needs a sturdy face below, upper half needs the same door below +DoorBlock.playSound port crates/horizon-world/src/level/redstone.rs::Sim::door_sound open or close sound from the block set type with a random pitch +DoorBlock.rotate skip:data rotation results come from the exported BlockState rotate table (worldgen/structure.rs) +DoorBlock.mirror skip:data mirror results come from the exported BlockState mirror table (worldgen/structure.rs) +DoorBlock.getSeed skip:other other: only seeds a random model offset, and doors have no offset type +DoorBlock.createBlockStateDefinition skip:data half, facing, open, hinge and powered properties come from the block state tables +DoublePlantBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class DoublePlant arm: the partner half follows, and the lower half breaks when unsupported +DoublePlantBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class DoublePlant arm: needs room below the build limit and a replaceable block above +DoublePlantBlock.setPlacedBy port crates/horizon-world/src/level/place.rs::Sim::set_placed_by DoublePlant branch places the upper half, waterlogged from its own spot +DoublePlantBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class DoublePlant arm: upper half needs the lower half of the same block below +DoublePlantBlock.copyWaterloggedFrom inline crates/horizon-world/src/level/place.rs::Sim::set_placed_by upper half waterlogged from the water at its spot, inline in set_placed_by +DoublePlantBlock.playerWillDestroy port crates/horizon-world/src/level/hooks.rs::Sim::player_destroy gap: minor: only the creative bottom-half removal is read here, the drop branch is not +DoublePlantBlock.preventDropFromBottomPart inline crates/horizon-world/src/level/hooks.rs::Sim::player_destroy bottom half removed without drops on a creative break of the upper half +DoublePlantBlock.getSeed port crates/horizon-client/src/mesher.rs::offset gap: minor: the jitter seed uses y 0 for every block, vanilla uses the lower half's y, so tall grass and flower offsets differ +DriedGhastBlock.createBlockStateDefinition skip:data facing, hydration_level and waterlogged properties come from the block state tables +DriedGhastBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class DriedGhast arm: waterlogged tick, then the default shape update +DriedGhastBlock.tickWaterlogged inline crates/horizon-world/src/level/timeline.rs::Sim::dried_ghast_tick waterlogged branch: transition sound and hydration plus one, or the ghastling spawn event at full hydration +DriedGhastBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class DriedGhast arm: waterlogged from the spot and facing opposite the player +DriedGhastBlock.getFluidState skip:data waterlogged water source comes from the per-state fluid facts table (Level::fluid) +DriedGhastBlock.placeLiquid port crates/horizon-world/src/level/fluids.rs::Sim::place_liquid gap: player: waterlogs the block like the generic path, but plays no dried ghast place-in-water sound +DriedGhastBlock.setPlacedBy missing player: no dried ghast placement sound is played on placing it (no dried_ghast place or place-in-water sound id is referenced) +EndGatewayBlock.getPortalDestination port crates/horizon-client/src/game/end.rs::Game::gateway_job gap: minor: the thrown ender pearl branch (no relative flags) is not read, only the player travels through gateways here +EndPortalFrameBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class EndPortalFrame arm: facing opposite the player and eye set to false +EndPortalFrameBlock.rotate skip:data rotation results come from the exported BlockState rotate table (worldgen/structure.rs) +EndPortalFrameBlock.createBlockStateDefinition skip:data facing and eye properties come from the block state tables +EntityBlock.getListener inline crates/horizon-client/src/game/sculk.rs::Game::post_game_event gap: minor: listeners within reach are found in post_game_event, which reads only the sculk listener tables +EyeblossomBlock.randomTick port crates/horizon-world/src/level/timeline.rs::Sim::eyeblossom_tick random tick runs tryChangingState and picks the long switch sound +FaceAttachedHorizontalDirectionalBlock.canAttach inline crates/horizon-world/src/level/survive.rs::Sim::can_survive_class the sturdy-face test on the attached side is inline in the FaceAttached arm +FaceAttachedHorizontalDirectionalBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class FaceAttached arm: tries the nearest looking directions and keeps the first that survives +FaceAttachedHorizontalDirectionalBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class FaceAttached arm: breaks when the attached side is no longer sturdy +FallingBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class FallingBlock arm schedules the falling tick (2, or 5 for dragon egg) then the default shape +FarmlandBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Farmland arm schedules a 1-tick check when the block above changes and farmland cannot survive +FarmlandBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class Farmland arm: survives when the block above is not solid or maintains farmland +FarmlandBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Farmland arm: the base block when farmland cannot survive at the spot +FarmlandBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block Farmland arm turns the block to its base when it cannot survive +FarmlandBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::farmland_random_tick moisture falls or rises with water in reach and rain above, else turns to base +FarmlandBlock.turnToBaseBlock port crates/horizon-world/src/level/tick.rs::Sim::turn_to_base_block gap: player: entities standing on it are not pushed up when it turns to dirt (pushEntitiesUp is not read) +FarmlandBlock.isNearWater inline crates/horizon-world/src/level/tick.rs::Sim::farmland_random_tick gap: minor: the 9x2x9 water search is inline and tests the water fluid only, not canBeHydrated +FenceBlock.isSameFence inline crates/horizon-world/src/level/shapes.rs::Sim::fence_connects_to the same-fence test (fence tag and wooden match) is inline in fence_connects_to +FenceBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Fence arm: each horizontal side connects per neighbour and waterlogged from the spot +FenceBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Fence arm: waterlogged tick and the horizontal side connection update +FenceBlock.createBlockStateDefinition skip:data north, east, south, west and waterlogged properties come from the block state tables +FireBlock.makeShapes skip:data the per-age and per-side outline shapes come from the per-state shape table +FireBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Fire arm: keeps the fire with its age when it can survive, else air +FireBlock.getStateForPlacement port crates/horizon-world/src/level/shapes.rs::Sim::fire_state_for fire state from the sides that can catch fire, or the plain fire when the spot below is sturdy +FireBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class Fire arm: a sturdy block below or a valid fire location +FireBlock.isNearRain port crates/horizon-world/src/level/tick.rs::Sim::fire_near_rain rain at the spot or on one of its four sides +FireBlock.getBurnOdds port crates/horizon-world/src/level/survive.rs::Sim::burn_odds waterlogged blocks never burn, else the burn odds table +FireBlock.checkBurnOut port crates/horizon-world/src/level/tick.rs::Sim::fire_check_burn_out burn-out roll that either ages the fire or removes the block +FireBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place Fire arm: base fire placement, then the fire tick scheduled at 30 plus a random 0 to 9 +FireBlock.createBlockStateDefinition skip:data age and the per-side properties come from the block state tables +FireBlock.setFlammable skip:data flammability odds are data (burn odds table read by survive.rs), set by the registry bootstrap +FireBlock.bootStrap skip:data registry bootstrap of the flammable blocks; Horizon reads the odds from data tables +FlowerPotBlock.useItemOn missing player: right-clicking an empty flower pot with a flower or sapling does nothing, so no potted plant can be made +FlowerPotBlock.useWithoutItem missing player: right-clicking a potted plant with an empty hand does not take the plant out +FlowerPotBlock.getCloneItemStack missing player: middle-click pick of a potted plant gives an empty flower pot, not the plant +FlowerPotBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class FlowerPot arm: breaks when the block under it stops supporting it +GrassBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid grass arm: air above and inside build height +GrassBlock.stopBonemealSpread inline crates/horizon-world/src/level/bonemeal.rs::Sim::grass_bonemeal the per-step stop test (grass below, full collision at the spot) is inline in the attempt loop +GrowingPlantBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class GrowingPlant arm: attached to a supporting face, a body or a head on the growth side +GrowingPlantBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block GrowingPlant is in the tick list that breaks the plant when it cannot survive +GrowingPlantBodyBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class GrowingPlantBody arm: 1-tick survival check, and a random-age head replaces the body when the growth side opens +GrowingPlantBodyBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid PlantBody arm: finds the head and checks its growth spot +GrowingPlantBodyBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal PlantBody arm forwards to the head's bonemeal growth +GrowingPlantBodyBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced GrowingPlantBody arm: not replaceable by a held head item +GrowingPlantHeadBlock.isRandomlyTicking skip:data the random-ticking flag per state comes from the exported facts (Level::is_randomly_ticking) +GrowingPlantHeadBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block GrowingPlantHead arm: below age 25 and the growth roll grows it into the next spot +GrowingPlantHeadBlock.isMaxAge skip:other other: an AGE == 25 predicate that no pinned game code calls for plant heads (only crop, loot and villager code use isMaxAge) +GrowingPlantHeadBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class GrowingPlantHead arm: 1-tick survival check, and turns to body when the head or body sits on the growth side +GrowingPlantHeadBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid PlantHead arm: the growth spot is free and inside build height +HangingMossBlock.canStayAtPosition port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class HangingMoss arm: attaches above to a multiface or to another moss +HangingMossBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class HangingMoss arm: 1-tick check when unattached, and the tip follows the block below +HangingMossBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block HangingMoss arm breaks the moss when it cannot stay +HangingMossBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid HangingMoss arm: air below the tip inside build height +HangingMossBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal HangingMoss arm sets the tip on the air below the tip diff --git a/tools/port_map/game2-014.tsv b/tools/port_map/game2-014.tsv new file mode 100644 index 0000000000..a6dc36bb27 --- /dev/null +++ b/tools/port_map/game2-014.tsv @@ -0,0 +1,144 @@ +HoneyBlock.doesEntityDoHoneyBlockSlideEffects inline crates/horizon-client/src/motion.rs::MotionWorld::honey_block_inside gap: minor: only the player slides here; boats, minecarts, TNT and mobs get no slide sound or event +HoneyBlock.fallOn port crates/horizon-world/src/block_landing.rs::BlockLandingRegistry::fall_on honey rule: slide sound, entity event 54, fall damage 0.2, fall sound +HoneyBlock.isSlidingDown inline crates/horizon-client/src/motion.rs::MotionWorld::honey_block_inside gap: player: mobs and other entities do not slide down honey, only the player does +HoneyBlock.maybeDoSlideAchievement port crates/horizon-client/src/game/advancements.rs::Game::trigger_honey_slide gap: minor: the every-20-ticks check sits in the caller, Game::tick +HoneyBlock.doSlideMovement inline crates/horizon-client/src/motion.rs::MotionWorld::honey_block_inside velocity damping and fall distance reset +HoneyBlock.showParticles port crates/horizon-client/src/game.rs::Game::honey_slide_particles five honey block particles at the entity +IceBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block melts through Sim::ice_melt when block light is above 11 minus dampening +InfestedBlock.spawnInfestation inline crates/horizon-client/src/game.rs::Game::break_block_as_player silverfish at the broken block's bottom centre when blocks drop and there is no silk touch; poof via mob_poof +InfestedBlock.infestedStateByHost inline crates/horizon-client/src/game/creatures.rs::Game::handle_mob_events the name swap minecraft:X to minecraft:infested_X in MobEvent::Infest; the host list is checked by the silverfish goals +InfestedBlock.getNewStateWithProperties inline crates/horizon-client/src/game/creatures.rs::Game::handle_mob_events property copy is the same name swap; the map cache is java +IronBarsBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class arms per horizontal side and waterlogged from the replaced fluid +IronBarsBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class waterlogged tick and side attachment +IronBarsBlock.skipRendering port crates/horizon-world/src/block_render.rs::FaceVisibility::skips bars face culling for the mesher +IronBarsBlock.createBlockStateDefinition skip:data - state properties come from the data tables +JigsawBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate orientation property through the rotate table +JigsawBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror orientation property through the mirror table +JigsawBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class orientation from clicked face and horizontal facing +JigsawBlock.useWithoutItem missing - player: a game master right-clicking a jigsaw block gets no jigsaw editor; the client has no jigsaw screen +JukeboxBlock.setPlacedBy missing - minor: a jukebox item carrying a disc (creative Ctrl+pick) fills the block entity in Game::apply_placed_item but HAS_RECORD stays false, so no disc model shows +JukeboxBlock.useWithoutItem port crates/horizon-client/src/game/jukebox.rs::Game::use_jukebox takes the disc out through eject_record when one is in +JukeboxBlock.ownSignal port crates/horizon-client/src/game/jukebox.rs::jukebox_signals 15 while a song plays +JukeboxBlock.getAnalogOutputSignal port crates/horizon-client/src/game/jukebox.rs::jukebox_signals comparator output of the song +JukeboxBlock.getTicker port crates/horizon-client/src/game/jukebox.rs::Game::tick_jukebox ticks only while a song plays +LayeredCauldronBlock.isFull inline crates/horizon-client/src/cauldron.rs::interact the level 3 test that gates bucket filling +LayeredCauldronBlock.canReceiveStalactiteDrip port crates/horizon-world/src/level/speleothem.rs::Sim::cauldron_can_receive the water cauldron is the rain type here +LayeredCauldronBlock.handlePrecipitation port crates/horizon-world/src/level/tick.rs::Sim::precipitation_tick rain or snow chance, cycles the level below 3 +LayeredCauldronBlock.receiveStalactiteDrip port crates/horizon-world/src/level/speleothem.rs::Sim::cauldron_tick adds one level below full with level event 1047 +LeavesBlock.isRandomlyTicking inline crates/horizon-world/src/level/tick.rs::Sim::random_tick_block the same distance 7 and not persistent test gates the leaf arm +LeavesBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block drops resources and removes decaying leaves +LeavesBlock.decaying inline crates/horizon-world/src/level/tick.rs::Sim::random_tick_block not persistent and distance 7 test +LeavesBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class waterlogged tick and distance schedule +LeavesBlock.updateDistance port crates/horizon-world/src/level/tick.rs::Sim::leaves_update_distance +LeavesBlock.getFluidState port crates/horizon-world/src/level/update.rs::Sim::fluid_at waterlogged fluid from block data +LeavesBlock.createBlockStateDefinition skip:data - state properties come from the data tables +LeavesBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class persistent and waterlogged, then distance +LecternBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class gap: minor: a Book in the placed item's data does not set HAS_BOOK here; apply_placed_item fills the block entity only +LecternBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate facing through the rotate table +LecternBlock.createBlockStateDefinition skip:data - state properties come from the data tables +LecternBlock.tryPlaceBook port crates/horizon-client/src/game/books.rs::Game::use_lectern a book goes onto an empty lectern +LecternBlock.placeBook inline crates/horizon-client/src/game/books.rs::Game::use_lectern consumes the held book, sets has_book, plays the put sound +LecternBlock.resetBookState port crates/horizon-client/src/game/books.rs::Game::lectern_menu_done book removal clears powered and has_book +LecternBlock.changePowered port crates/horizon-client/src/game/books.rs::Game::set_lectern_powered +LecternBlock.updateBelow inline crates/horizon-client/src/game/books.rs::Game::set_lectern_powered neighbour update below the lectern +LecternBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal powered lectern updates the block below on removal +LecternBlock.getDirectSignal port crates/horizon-world/src/level/redstone.rs::Sim::state_direct_signal gap: minor: no Lectern arm; the power of a powered lectern comes from the data table's strong value +LecternBlock.getAnalogOutputSignal port crates/horizon-client/src/game/books.rs::lectern_signal page-based comparator signal, 0 without a book +LecternBlock.useWithoutItem port crates/horizon-client/src/game/books.rs::Game::open_lectern opens the book view when a book is on the lectern +LecternBlock.getMenuProvider port crates/horizon-client/src/game/books.rs::Game::lectern_menu the lectern menu over the block entity's book and page +LeverBlock.makeShapes skip:data - collision and outline shapes are precomputed from the data +LeverBlock.useWithoutItem port crates/horizon-world/src/level/redstone.rs::Sim::lever_pull gap: player: powering a lever on shows no redstone dust puff at the lever (makeParticle) +LeverBlock.onExplosionHit missing - player: an explosion that can trigger blocks does not toggle a lever; explosion.rs has no lever case +LeverBlock.playSound inline crates/horizon-world/src/level/redstone.rs::Sim::lever_pull click sound at volume 0.3 and pitch 0.6 on or 0.5 off +LeverBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal powered lever updates neighbours on removal unless moved by a piston +LeverBlock.getDirectSignal port crates/horizon-world/src/level/redstone.rs::Sim::state_direct_signal powered lever's strong power from the data table +LeverBlock.createBlockStateDefinition skip:data - state properties come from the data tables +LightBlock.createBlockStateDefinition skip:data - state properties come from the data tables +LightBlock.useWithoutItem port crates/horizon-world/src/level/interact.rs::Sim::use_without_item game masters cycle the light level +LightBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class waterlogged tick +LightBlock.getFluidState port crates/horizon-world/src/level/update.rs::Sim::fluid_at waterlogged fluid from block data +LightBlock.getCloneItemStack missing - player: creative pick-block on a light block gives a light at level 0; Game::picked_stack writes no block state for light +LightBlock.setLightOnStack missing - player: the light level is never put on a picked light item, so pick-block loses the level +LightningRodBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class facing from clicked face, waterlogged from the replaced fluid +LightningRodBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class waterlogged tick +LightningRodBlock.getFluidState port crates/horizon-world/src/level/update.rs::Sim::fluid_at waterlogged fluid from block data +LightningRodBlock.getDirectSignal port crates/horizon-world/src/level/redstone.rs::Sim::state_direct_signal powered rod's strong power on its facing side +LightningRodBlock.updateNeighbours inline crates/horizon-world/src/level/tick.rs::Sim::tick_block the unpowering tick updates the block in front; the removal path does the same in affect_neighbors_after_removal +LightningRodBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block unpowers after 8 ticks and updates the block in front +LightningRodBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal powered rod updates the block in front on removal +LightningRodBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place schedules the 8-tick unpower when placed powered +LightningRodBlock.createBlockStateDefinition skip:data - state properties come from the data tables +LiquidBlock.getCollisionShape port crates/horizon-world/src/liquid_collision.rs::LiquidCollision::shape still source blocks are solid for living entities standing on them +LiquidBlock.ifMobIsColliding inline crates/horizon-world/src/liquid_collision.rs::LiquidCollision::shape the living entity check +LiquidBlock.getFluidState port crates/horizon-world/src/level/update.rs::Sim::fluid_at fluid from block data (level to fluid state) +LiquidBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class bubble column schedule below +LiquidBlock.neighborChanged port crates/horizon-world/src/level/fluids.rs::Sim::liquid_block_changed gap: minor: spread and bubble-column tick only; the Neo fluid interaction check is not ported here +LiquidBlock.pickupBlock port crates/horizon-world/src/level/fluids.rs::Sim::pickup_block source blocks only, leaves air and yields the bucket +LoomBlock.useWithoutItem port crates/horizon-client/src/game/stations.rs::Game::open_station opens the loom station +LoomBlock.getMenuProvider port crates/horizon-client/src/game/stations.rs::Game::open_station the loom menu over the block position +Mirror.mirror missing - minor: unsure: integer rotation mirror for 16-step rotations (banners, heads) not found; template transform works on positions only +Mirror.getRotation missing - minor: unsure: the 180 degree turn that a mirror implies is not found; template transform applies mirror to positions only +MossyCarpetBlock.makeShapes skip:data - collision and outline shapes are precomputed from the data +MossyCarpetBlock.getCollisionShape skip:data - collision shape per state comes from the data table +MossyCarpetBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class base needs a non-air block below; a topper needs a moss carpet below +MossyCarpetBlock.hasFaces port crates/horizon-world/src/level/shapes.rs::Sim::mossy_carpet_has_faces +MossyCarpetBlock.placeAt port crates/horizon-world/src/block_placement.rs::BlockPlacement::topper carpet and topper placement for worldgen; the topper rule is shared +MossyCarpetBlock.setPlacedBy port crates/horizon-world/src/level/place.rs::Sim::set_placed_by gap: minor: the topper is placed above a new carpet; the placed-carpet update path is the same +MossyCarpetBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +MossyCarpetBlock.createBlockStateDefinition skip:data - state properties come from the data tables +MossyCarpetBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate side properties through the rotate table +MossyCarpetBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror side properties through the mirror table +MossyCarpetBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid +MossyCarpetBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal +MultifaceBlock.makeShapes skip:data - collision and outline shapes are precomputed from the data +MultifaceBlock.createBlockStateDefinition skip:data - state properties come from the data tables +MultifaceBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class removes a face that lost its neighbour, air when none left +MultifaceBlock.getFluidState port crates/horizon-world/src/level/update.rs::Sim::fluid_at waterlogged fluid from block data +MultifaceBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class every face needs an attachable neighbour +MultifaceBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced replaceable by the same item only with a vacant face +MultifaceBlock.isValidStateForPlacement port crates/horizon-world/src/level/sculk.rs::Sim::sculk_valid_for_placement gap: minor: sculk vein form only; the supported-face check is not repeated +MultifaceBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate face properties through the rotate table +MultifaceBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror face properties through the mirror table +MultifaceBlock.mapDirections inline crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate face property permutation is folded into the rotate table +MultifaceBlock.hasFace port crates/horizon-world/src/level/survive.rs::Sim::multiface_has_face +MultifaceBlock.removeFace port crates/horizon-world/src/level/sculk.rs::Sim::vein_with_face gap: minor: clears the face only; the air result when no face is left is not here +MultifaceBlock.getDefaultMultifaceState skip:data - default state comes from the data tables +MultifaceBlock.hasAnyFace port crates/horizon-world/src/level/sculk.rs::Sim::available_faces the caller tests the list for emptiness +MultifaceBlock.hasAnyVacantFace inline crates/horizon-world/src/level/sculk.rs::Sim::available_faces vacancy is the list length below six at the caller +MultifaceSpreader.canSpreadInAnyDirection port crates/horizon-world/src/level/bonemeal.rs::Sim::lichen_can_spread_in_any_direction +MultifaceSpreader.spreadFromFaceTowardRandomDirection port crates/horizon-world/src/level/bonemeal.rs::Sim::lichen_spread_random gap: minor: loops every starting face too; no fixed-face entry point +MultifaceSpreader.spreadFromFaceTowardAllDirections port crates/horizon-world/src/level/sculk.rs::Sim::sculk_spread_all gap: minor: sculk veins only; the lichen form is in lichen_spread_random +MultifaceSpreader.spreadFromFaceTowardDirection inline crates/horizon-world/src/level/bonemeal.rs::Sim::lichen_spread_random target search and placement folded into the lichen loop +MultifaceSpreader.spreadToFace inline crates/horizon-world/src/level/bonemeal.rs::Sim::lichen_spread_random placement by getStateForPlacement and setBlock folded into the lichen loop +MultifaceSpreader.DefaultSpreaderConfig.stateCanBeReplaced inline crates/horizon-world/src/level/bonemeal.rs::Sim::lichen_can_spread_into air, same block or water source test folded into the lichen target check +MultifaceSpreader.SpreadConfig.canSpreadFrom inline crates/horizon-world/src/level/bonemeal.rs::Sim::lichen_spread_target from-face test folded into the lichen target search; the sculk form is worldgen/sculk.rs::SculkPatchFeature::can_spread_from +MultifaceSpreader.SpreadConfig.placeBlock inline crates/horizon-world/src/level/bonemeal.rs::Sim::lichen_spread_random placement folded into the lichen loop; the post-process mark is not kept +MushroomBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::mushroom_random_tick +MushroomBlock.canSpread inline crates/horizon-world/src/level/tick.rs::Sim::mushroom_random_tick at most four of the same mushroom in the 9x3x9 box, counted in the tick +MushroomBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid mushroom arm +NetherPortalBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class portal breaks when the frame is incomplete +NetherPortalBlock.getPortalTransitionTime port crates/horizon-client/src/game/nether_portal.rs::Game::tick_nether_portal gap: minor: creative and default delay rules by invulnerability; the delay counter is in the same fn +NetherPortalBlock.getExitPortal port crates/horizon-client/src/game/nether_portal.rs::Game::nether_exit_portal gap: minor: the Nether exit only; the End variant is Game::end_portal_exit +NetherPortalBlock.getDimensionTransitionFromExit port crates/horizon-client/src/game/nether_portal.rs::Game::portal_plan +NetherPortalBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick portal ambient sound and particles +NetherPortalBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate axis swap through the rotate table +NoteBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class instrument follows the block below +NoteBlock.neighborChanged port crates/horizon-world/src/level/redstone.rs::Sim::note_neighbor_changed +NoteBlock.getCustomSoundId missing - player: a player head on top of a note block does not change its sound; the head's sound is stored but the note play never reads it +NoteBlock.createBlockStateDefinition skip:data - state properties come from the data tables +PathBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class pushes entities up when the path would not survive +PathBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class schedules a tick when the block above changes +PathBlock.turnToBaseBlock port crates/horizon-world/src/level/tick.rs::Sim::turn_to_base_block +PathBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class solid block above turns the path back to dirt +PitcherCropBlock.makeShapes skip:data - collision and outline shapes are precomputed from the data +PitcherCropBlock.getCollisionShape skip:data - collision shape per state comes from the data table +PitcherCropBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +PitcherCropBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class +PitcherCropBlock.createBlockStateDefinition skip:data - state properties come from the data tables +PitcherCropBlock.entityInside missing - player: a ravager walking through a pitcher crop does not break it when mob griefing is on +PitcherCropBlock.isRandomlyTicking inline crates/horizon-world/src/level/tick.rs::Sim::random_tick_block the lower half and not max age test gates the pitcher arm +PitcherCropBlock.canGrowInto port crates/horizon-world/src/level/bonemeal.rs::Sim::pitcher_can_grow +PitcherCropBlock.isLower inline crates/horizon-world/src/level/survive.rs::Sim::can_survive_class lower-half test used by the survival check +PitcherCropBlock.isMaxAge port crates/horizon-world/src/level/bonemeal.rs::Sim::crop_max_age +PitcherCropBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid pitcher arm +PitcherCropBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal pitcher growth diff --git a/tools/port_map/game2-015.tsv b/tools/port_map/game2-015.tsv new file mode 100644 index 0000000000..cca7c36364 --- /dev/null +++ b/tools/port_map/game2-015.tsv @@ -0,0 +1,141 @@ +PointedDripstoneBlock.fallOn port crates/horizon-world/src/block_landing.rs::BlockLandingRegistry::fall_on stalagmite tip landing damage (fall + 2.5, multiplier 2.0) in the DripstoneTip rule +PointedDripstoneBlock.randomTick port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_random_tick fluid transfer and stalagmite growth both run from the random tick +PointedDripstoneBlock.findRootBlock inline crates/horizon-world/src/level/speleothem.rs::Sim::dripstone_fluid_above root search is the find_block_vertical call inside the fluid lookup +PointedDripstoneBlock.findFillableCauldronBelowStalactiteTip port crates/horizon-world/src/level/speleothem.rs::Sim::dripstone_find_cauldron +PointedDripstoneBlock.findStalactiteTipAboveCauldron inline crates/horizon-world/src/level/speleothem.rs::Sim::cauldron_tick tip search upward from the cauldron folded into the cauldron tick +PointedDripstoneBlock.getCauldronFillFluidType inline crates/horizon-world/src/level/speleothem.rs::Sim::cauldron_tick water or lava filter on the fluid above folded into the cauldron tick +PointedDripstoneBlock.canGrow inline crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_grow gap: minor: checks the fluid two blocks above is water, not that it is a source block +PointedDripstoneBlock.getDripParticle port crates/horizon-client/src/game/level_particles.rs::Game::dripstone_drip_particle +PotentSulfurBlock.isSourceIfFluid inline crates/horizon-world/src/level/shapes.rs::Sim::potent_sulfur_state source_if_fluid check inside the state rule +PotentSulfurBlock.onPlace missing player: placing a continuous sulfur geyser block gives no geyser start sound and no block-activate game event; no erupting state ever starts in Horizon +PotentSulfurBlock.spawnBubbleParticlesAt inline crates/horizon-client/src/animate_tick.rs::block_animate_tick two sulfur bubbles in the potent sulfur arm; add_visible is the always-visible add +PotentSulfurBlock.triggerEvent missing minor: only stores the eruption tick that the geyser plume timing reads; geysers never erupt in Horizon (see getTicker) +PotentSulfurBlock.getTicker missing player: wet or dormant sulfur never makes nearby players nauseous from noxious gas; periodic geysers never erupt and continuous ones do not launch entities or play the plume +PowderSnowBlock.skipRendering port crates/horizon-world/src/block_render.rs::FaceVisibility::skips same_block skip kind from the block data +PowderSnowBlock.fallOn port crates/horizon-world/src/block_landing.rs::BlockLandingRegistry::fall_on powder snow landing sound from 4 blocks, small or big by 7 +PowderSnowBlock.getEntityInsideCollisionShape inline crates/horizon-client/src/motion.rs::MotionWorld::check_inside_blocks gap: minor: each powder snow cell is swept as a full box; vanilla uses the entity-context shape, which for falling entities over 2.5 blocks is a 0.9 tall box +RailBlock.updateState port crates/horizon-world/src/level/rails.rs::Sim::rail_update_state +RailBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate rail shape rotation from the generated state table, as PoweredRailBlock.rotate +RailBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror rail shape mirror from the generated state table, as PoweredRailBlock.rotate +RailBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +RailState.updateConnections port crates/horizon-world/src/level/rails.rs::Sim::rail_connections +RailState.hasRail inline crates/horizon-world/src/level/rails.rs::Sim::rail_count_potential_connections rail test at the position, above and below folded into the count +RailState.getRail port crates/horizon-world/src/level/rails.rs::Sim::rail_get +RailState.hasConnection port crates/horizon-world/src/level/rails.rs::Sim::rail_has_connection +RailState.countPotentialConnections port crates/horizon-world/src/level/rails.rs::Sim::rail_count_potential_connections +RailState.connectTo port crates/horizon-world/src/level/rails.rs::Sim::rail_connect_to +ScaffoldingBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +ScaffoldingBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class distance, bottom and waterlogged for the Scaffolding arm +ScaffoldingBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class waterlogged water tick and the one-tick schedule +ScaffoldingBlock.tick missing unsure: Sim::tick_block has no Scaffolding arm, so the distance and bottom are not recomputed each tick and the fall or break at distance 7 may be missing (player would see scaffolding fall or break when its support goes) +ScaffoldingBlock.getCollisionShape port crates/horizon-world/src/block_collision.rs::ScaffoldingCollision::shape +ScaffoldingBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged state from block data +RedStoneOreBlock.interact port crates/horizon-world/src/level/interact.rs::Behaviours::attack_block gap: player: no red dust burst when the ore lights; the lit set is in attack_block and game/sculk.rs step_on_redstone_ore +RedStoneOreBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block +RedStoneOreBlock.spawnAfterBreak inline crates/horizon-world/src/level/removal.rs::break_experience ore experience (1 to 5) for the break is in break_experience +RedStoneOreBlock.spawnParticles missing player: no red dust particles burst around a redstone ore when it lights from mining, stepping on it, or a right-click with an item +RedstoneWireBlock.makeShapes skip:data wire shapes are per-state data from the block tables +RedstoneWireBlock.getMissingConnections inline crates/horizon-world/src/level/redstone.rs::Sim::wire_connection_state the missing-side loop of the connection state +RedstoneWireBlock.updateShape port crates/horizon-world/src/level/redstone.rs::Sim::redstone_wire_update_shape +RedstoneWireBlock.isCross port crates/horizon-world/src/level/redstone.rs::Sim::wire_is_cross +RedstoneWireBlock.isDot port crates/horizon-world/src/level/redstone.rs::Sim::wire_is_dot +RedstoneWireBlock.getConnectingSide port crates/horizon-world/src/level/redstone.rs::Sim::wire_connecting_side +RedstoneWireBlock.canSurvive port crates/horizon-world/src/level/redstone.rs::Sim::wire_can_survive_on checks the block below through the same sturdy-face test +RedstoneWireBlock.canSurviveOn port crates/horizon-world/src/level/redstone.rs::Sim::wire_can_survive_on +RedstoneWireBlock.updatePowerStrength port crates/horizon-world/src/level/redstone.rs::Sim::wire_update_power gap: minor: only the default evaluator; the experimental evaluator behind the redstone experiments flag is not modelled +RedstoneWireBlock.getBlockSignal port crates/horizon-world/src/level/redstone.rs::Sim::wire_block_signal +RedstoneWireBlock.checkCornerChangeAt port crates/horizon-world/src/level/redstone.rs::Sim::wire_check_corner +RedstoneWireBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place wire arm of the placed-block hook +RedstoneWireBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal wire arm of the removal hook +RedstoneWireBlock.updateNeighborsOfNeighboringWires port crates/horizon-world/src/level/redstone.rs::Sim::wire_update_neighbors_of_neighboring_wires +RedstoneWireBlock.neighborChanged port crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed gap: minor: no block-is-this check for the experimental evaluator +RedstoneWireBlock.getDirectSignal port crates/horizon-world/src/level/redstone.rs::Sim::state_direct_signal zero while the wire signal is off +RedstoneWireBlock.getSignal port crates/horizon-world/src/level/redstone.rs::Sim::wire_signal +RedstoneWireBlock.shouldConnectTo port crates/horizon-world/src/level/redstone.rs::Sim::should_wire_connect_to +RedstoneWireBlock.spawnParticlesAlongLine inline crates/horizon-client/src/animate_tick.rs::block_animate_tick the dust line closure in the redstone wire arm +RedstoneWireBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick redstone wire arm: dust per connected side by power colour +RedstoneWireBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate rotation from the generated state table, as PoweredRailBlock.rotate +RedstoneWireBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror mirror from the generated state table +RedstoneWireBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +RedstoneWireBlock.updatesOnShapeChange port crates/horizon-world/src/level/interact.rs::Sim::wire_use the cross or dot toggle on right-click calls the shape-change update +RespawnAnchorBlock.canBeCharged inline crates/horizon-client/src/game/respawn_anchor.rs::Game::use_respawn_anchor the charge below four check before a glowstone charge +RespawnAnchorBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick +RespawnAnchorBlock.getScaledChargeLevel inline crates/horizon-world/src/level/redstone.rs::Sim::analog_output the charges over four times 15 comparator value +SaplingBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid sapling arm: tree can grow and the minimum height fits +SaplingBlock.isBonemealSuccess port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_success +SculkBlock.getDecayPenalty inline crates/horizon-world/src/level/sculk.rs::Sim::sculk_attempt_use_charge the decay penalty is computed inside the sculk charge branch +SculkCatalystBlock.spawnAfterBreak inline crates/horizon-world/src/level/removal.rs::break_experience catalyst experience of 5 in break_experience +SculkSensorBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class waterlogged from the clicked fluid +SculkSensorBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged state from block data +SculkSensorBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place sensor arm: clears a stored power on placement +SculkSensorBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class waterlogged water tick +SculkSensorBlock.updateNeighbours port crates/horizon-world/src/level/sculk.rs::Sim::sensor_update_neighbours +SculkSensorBlock.getTicker port crates/horizon-client/src/game/sculk.rs::Game::tick_sculk_block the vibration tick: pending vibrations start and are received here; the cooldown is the block tick in horizon-world tick.rs +SculkSensorBlock.getDirectSignal port crates/horizon-world/src/level/redstone.rs::Sim::state_direct_signal the upward signal comes from the generated strong facts +SculkSensorBlock.tryResonateVibration port crates/horizon-client/src/game/sculk.rs::Game::activate_sensor resonator game event and amethyst sound; the sensor activation is in the same fn +SculkSensorBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +SculkSensorBlock.spawnAfterBreak inline crates/horizon-world/src/level/removal.rs::break_experience sensor experience of 5 in break_experience +SculkShriekerBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +SculkShriekerBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class waterlogged water tick +SculkShriekerBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class waterlogged from the clicked fluid +SculkShriekerBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged state from block data +SculkShriekerBlock.spawnAfterBreak inline crates/horizon-world/src/level/removal.rs::break_experience shrieker experience of 5 in break_experience +SculkShriekerBlock.getTicker port crates/horizon-client/src/game/sculk.rs::Game::tick_sculk_block the vibration tick for shriekers +SculkSpreader.save port crates/horizon-client/src/sculk.rs::cursors_to_nbt the cursors tag of a catalyst: pos, charge, decay and update delays, facings +SculkSpreader.ChargeCursor.shouldUpdate port crates/horizon-world/src/level/sculk.rs::Sim::sculk_cursor_update gap: minor: the world generation case is in worldgen/sculk.rs; this is the live tick gate +SculkSpreader.ChargeCursor.mergeWith inline crates/horizon-world/src/level/sculk.rs::Sim::sculk_update_cursors the merge of cursors on the same position in the update loop +SculkSpreader.ChargeCursor.getBlockBehaviour port crates/horizon-world/src/level/sculk.rs::Sim::sculk_behaviour +SculkSpreader.ChargeCursor.canMoveToPos port crates/horizon-world/src/worldgen/sculk.rs::SculkPatchFeature::valid_movement the 12 block horizontal limit during world generation +SculkSpreader.ChargeCursor.isMovementUnobstructed port crates/horizon-world/src/level/sculk.rs::Sim::sculk_unobstructed_move +SculkSpreader.ChargeCursor.isUnobstructed inline crates/horizon-world/src/level/sculk.rs::Sim::sculk_unobstructed_move the single face test inside the movement check +SculkVeinBlock.attemptUseCharge port crates/horizon-world/src/level/sculk.rs::Sim::sculk_attempt_use_charge vein behaviour arm +SculkVeinBlock.SculkVeinSpreaderConfig.stateCanBeReplaced port crates/horizon-world/src/level/sculk.rs::Sim::sculk_can_spread_into +SelectableSlotContainer.getRelativeHitCoordinatesForBlockFace inline crates/horizon-world/src/level/interact.rs::hit_slot face coordinates of the click, folded into hit_slot +SelectableSlotContainer.getSection inline crates/horizon-world/src/level/interact.rs::hit_slot section clamp folded into hit_slot +SeaPickleBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class adds a pickle to a same block; waterlogged from the clicked fluid +SeaPickleBlock.mayPlaceOn port crates/horizon-world/src/level/survive.rs::Sim::may_place_on +SeaPickleBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class sea pickle arm, checks the block below +SeaPickleBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +SeaPickleBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced a pickle is replaceable when stacking one more +SeaPickleBlock.getShape skip:data per-state shape table from the vanilla data +SeaPickleBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged state from block data +SeaPickleBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +SeaPickleBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid sea pickle arm: waterlogged and coral block below +ShelfBlock.isPathfindable skip:data path types come from the per-state path data (mobs/path.rs) +ShelfBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +ShelfBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class shelf arm of the placement rules +ShelfBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate facing rotation from the generated state table +ShelfBlock.useItemOn missing player: right-clicking a shelf with an item does nothing: the item is not put on a slot, a filled slot is not taken out, and a powered shelf does not swap with the hotbar; interact.rs has no shelf item use +ShelfBlock.swapSingleItem missing player: the held item is not exchanged with the item in the clicked shelf slot +ShelfBlock.swapHotbar missing player: a powered shelf chain does not swap its items with the hotbar +ShelfBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged state from block data +ShelfBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class +ShulkerBoxBlock.useWithoutItem port crates/horizon-client/src/game.rs::Game::open_menu opens the box and awards the open stat; canOpen via shulker_can_open +ShulkerBoxBlock.playerWillDestroy port crates/horizon-world/src/level/removal.rs::pre_remove gap: minor: only classifies the box as keeping its contents; the break itself is not in this fn +ShulkerBoxBlock.getDrops port crates/horizon-world/src/level/removal.rs::pre_remove gap: minor: the contents kept on the dropped box come from the same kind; the loot drop is not traced here +ShulkerBoxBlock.getBlockSupportShape missing unsure: shulker_is_closed returns None in the level and the client, so an open box keeps the full cube as its support shape +ShulkerBoxBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate facing rotation from the generated state table +SideChainPartBlock.addBlocksConnectingTowards inline crates/horizon-world/src/level/shelf.rs::Sim::shelf_chain_size the walk that counts connected shelves +SideChainPartBlock.canConnect inline crates/horizon-world/src/level/shelf.rs::Sim::shelf_on_powering_up chain length test before connecting +SideChainPartBlock.isBeingUpdatedByNeighbor inline crates/horizon-world/src/level/shelf.rs::Sim::shelf_on_powering_up checks for a connected neighbour before the power-up join +SideChainPartBlock.getNeighbors skip:java neighbour cache object; Horizon builds its Neighbors struct at each caller +SideChainPartBlock.Neighbors.isConnectableToThisBlock inline crates/horizon-world/src/level/shelf.rs::Sim::chain_neighbor connectable and same facing test +SideChainPartBlock.Neighbors.createNewNeighbor port crates/horizon-world/src/level/shelf.rs::Sim::chain_neighbor +SideChainPartBlock.Neighbors.getOrCreateNeighbor port crates/horizon-world/src/level/shelf.rs::Sim::chain_neighbor cached lookup per position +SideChainPartBlock.Neighbors.left inline crates/horizon-world/src/level/shelf.rs::Sim::chain_neighbor left direction and steps +SideChainPartBlock.Neighbors.right inline crates/horizon-world/src/level/shelf.rs::Sim::chain_neighbor right direction and steps +SideChainPartBlock.SideChainNeighbor.connectToTheRight port crates/horizon-world/src/level/shelf.rs::Part::connected_to_the_right +SideChainPartBlock.SideChainNeighbor.connectToTheLeft port crates/horizon-world/src/level/shelf.rs::Part::connected_to_the_left +SideChainPartBlock.SideChainNeighbor.disconnectFromRight port crates/horizon-world/src/level/shelf.rs::Part::disconnected_from_the_right +SideChainPartBlock.SideChainNeighbor.disconnectFromLeft port crates/horizon-world/src/level/shelf.rs::Part::disconnected_from_the_left +SignBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class waterlogged water tick +SignBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged state from block data +SignBlock.otherPlayerIsEditingSign missing unsure: minor: no check that another player is editing the sign; sign_edit.rs tracks sign_editing per connection only +SignBlock.setPlacedBy port crates/horizon-client/src/game.rs::Game::use_hand gap: minor: the sign editor opens after a sign is placed; no allowed-editor player is stored +SnifferEggBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place sniffer egg arm: moss boost, place event and the hatch timer +SnowLayerBlock.isPathfindable skip:data path types come from the per-state path data (mobs/path.rs) +SnowLayerBlock.getShadeBrightness missing unsure: minor: no shade brightness lookup for snow layers found; vanilla returns 0.2 for a full eight layer block +SnowLayerBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class snow layer arm: air when it cannot survive +SnowLayerBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block +SnowLayerBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced +SnowLayerBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class diff --git a/tools/port_map/game2-016.tsv b/tools/port_map/game2-016.tsv new file mode 100644 index 0000000000..2d12a8f050 --- /dev/null +++ b/tools/port_map/game2-016.tsv @@ -0,0 +1,150 @@ +SpeleothemBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +SpeleothemBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Speleothem arm: waterlogged tick, then tip direction and thickness. +SpeleothemBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Speleothem arm picks tip direction and thickness. +SpeleothemBlock.calculateTipDirection inline crates/horizon-world/src/level/place.rs::Sim::placement_class Tip direction choice is inside the placement arm. +SpeleothemBlock.isSpeleothemWithDirection port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_with +SpeleothemBlock.onProjectileHit missing player: a thrown trident faster than 0.6 that hits pointed dripstone should break it. Horizon's trident only sticks in the block (game/trident.rs), so the dripstone stays. +SpeleothemBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block Speleothem arm: a stalagmite that cannot survive breaks, else a falling stalactite starts. +SpeleothemBlock.isTip port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_is_tip +SpeleothemBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged fluid from block data +SpeleothemBlock.getShape port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape shape per state from the table +SpeleothemBlock.isStalactiteStartPos inline crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_random_tick The start test is the `start` check inside the random tick. +SpeleothemBlock.growStalactiteOrStalagmiteIfPossible port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_grow +SpeleothemBlock.findTip port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_find_tip +SpeleothemBlock.canTipGrow port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_can_tip_grow +SpeleothemBlock.isUnmergedTipWithDirection port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_unmerged_tip +SpeleothemBlock.grow port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_grow_to +SpeleothemBlock.createSpeleothem port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_create +SpeleothemBlock.createMergedTips inline crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_grow_to The merged tip pair is the unmerged-tip branch of grow_to. +SpeleothemBlock.growStalagmiteBelow port crates/horizon-world/src/level/speleothem.rs::Sim::speleothem_grow_stalagmite_below +SpeleothemBlock.isFreeHangingStalactite port crates/horizon-world/src/level/speleothem.rs::Sim::is_free_hanging_stalactite +SpongeBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place Sponge arm absorbs water when the old state differs. +SpongeBlock.neighborChanged port crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed Sponge arm calls sponge_try_absorb. +SpongeBlock.removeWaterBreadthFirstSearch port crates/horizon-world/src/level/hooks.rs::Sim::sponge_remove_water +StairBlock.getShape port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape shape per state from the table +StairBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Stair arm of the placement rules. +StairBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Stair arm of the update rules (straight, inner, outer). +StairBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +StairBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +StairBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +StairBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged fluid from block data +StrawBedBlock.getShape port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape head and foot shapes per state from the table +StrawBedBlock.getSleepHeight missing minor: sleep height is not computed per bed. tick_sleep (game.rs) puts every sleeper at head y + 0.5625; vanilla uses the bed shape top plus 0.125. +StructureBlock.useWithoutItem missing player: in creative, clicking a structure block should open its editor. Horizon has no structure block behaviour, so nothing opens. +StructureBlock.setPlacedBy missing minor: the placer is not stored as the structure block's author. +StructureBlock.neighborChanged missing player: a redstone pulse should save, load or clear the structure. Horizon never triggers a structure block. +StructureBlock.trigger missing player: a structure block in Load mode that gets a pulse should place its structure. Horizon does nothing. +SweetBerryBushBlock.getShape port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape shape per age from the table +SweetBerryBushBlock.isRandomlyTicking port crates/horizon-world/src/level/mod.rs::Behaviours::is_randomly_ticking RANDOM_TICKS flag from block data +SweetBerryBushBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block SweetBerry arm: 1 in 5 growth at light 9 or more. +SweetBerryBushBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid SweetBerry kind: age below 3. +SweetBerryBushBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal SweetBerry kind: age plus one, capped at 3. +TallDryGrassBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal TallDryGrass kind spreads short dry grass to a spreadable neighbour. +TallGrassBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid TallGrass kind: grown block fits and the space above is empty. +TargetBlock.updateRedstoneOutput port crates/horizon-world/src/level/redstone.rs::Sim::target_projectile_hit Strength from the hit point, then power set and reset scheduled. +TargetBlock.getRedstoneStrength inline crates/horizon-world/src/level/redstone.rs::Sim::target_projectile_hit Distance and strength math is inside target_projectile_hit. +TargetBlock.setOutputPower inline crates/horizon-world/src/level/redstone.rs::Sim::target_projectile_hit Sets power and schedules the reset inside target_projectile_hit. +TargetBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block Target arm: power goes back to 0. +TargetBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place Target arm: power reset on a new placement. +TntBlock.onPlace port crates/horizon-world/src/level/redstone.rs::Sim::tnt_check Tnt arm of on_place: primes and removes TNT when powered. +TntBlock.neighborChanged port crates/horizon-world/src/level/redstone.rs::Sim::tnt_check Tnt arm of neighbor_changed. +TntBlock.onProjectileHit port crates/horizon-client/src/game/creatures.rs::Game::arrow_event gap: minor: only the burning-arrow case reaches TNT here (BlockReaction::PrimeTnt in arrows.rs). +TrialSpawnerBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +TrialSpawnerBlock.getTicker port crates/horizon-client/src/game/trial_spawner.rs::Game::tick_trial_spawner the per-tick trial spawner update +TripWireBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class TripWire arm connects to neighbours. +TripWireBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class TripWire arm: horizontal connections. +TripWireBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place TripWire arm: updates the source hooks. +TripWireBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal TripWire arm: source update with the wire powered. +TripWireBlock.playerWillDestroy missing player: cutting a tripwire with shears should disarm it, so its hooks stay quiet. Horizon's break path never sets DISARMED (rails.rs reads it), so the hooks still fire. +TripWireBlock.entityInside port crates/horizon-world/src/level/hooks.rs::Sim::entity_inside gap: minor: counts every entity in the box; vanilla skips entities that ignore block triggers. +TripWireBlock.tick port crates/horizon-world/src/level/tick.rs::Sim::tick_block TripWire arm: tripwire_tick re-checks while powered. +TripWireBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +TripWireBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +TripWireBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +TripWireHookBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class TripWireHook arm: sturdy face behind the hook. +TripWireHookBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Hook arm: breaks off when it cannot survive. +TripWireHookBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Hook arm picks a facing that can survive. +TripWireHookBlock.notifyNeighbors port crates/horizon-world/src/level/rails.rs::Sim::tripwire_hook_notify +TripWireHookBlock.getDirectSignal port crates/horizon-world/src/level/redstone.rs::Sim::state_direct_signal strong power from per-state data (powered and facing) +TripWireHookBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +TripWireHookBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +TurtleEggBlock.stepOn missing player: walking onto turtle eggs without sneaking should crack them (1 in 100 per step). Horizon's motion step_event does nothing, so eggs never break underfoot. +TurtleEggBlock.destroyEgg port crates/horizon-world/src/block_landing.rs::BlockLandingRegistry::fall_on gap: player: only the fall roll (1 in 3) is here; the 1 in 100 step roll from stepOn is missing. +TurtleEggBlock.decreaseEggs inline crates/horizon-world/src/block_landing.rs::BlockLandingRegistry::fall_on One egg fewer, or the block removed, inside the TurtleEgg rule. +TurtleEggBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place TurtleEgg arm: crack level event on sand. +TurtleEggBlock.shouldUpdateHatchLevel inline crates/horizon-world/src/level/timeline.rs::Sim::turtle_egg_tick The hatch chance check opens turtle_egg_tick. +TurtleEggBlock.playerDestroy missing player: breaking a stack of 2 to 4 turtle eggs in survival should remove one egg and leave the rest. Horizon removes the whole stack (hooks.rs player_destroy). +TurtleEggBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced TurtleEgg arm: adds an egg to a stack below 4. +TurtleEggBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class TurtleEgg arm adds an egg to a stack. +TurtleEggBlock.getShape port crates/horizon-world/src/interaction_shapes.rs::InteractionShapes::shape shape per egg count from the table +TurtleEggBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +TurtleEggBlock.canDestroyEgg inline crates/horizon-world/src/block_landing.rs::BlockLandingRegistry::fall_on Entity check (turtle, bat, player protection, griefing) is in the TurtleEgg rule. +VaultBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +VaultBlock.getTicker port crates/horizon-client/src/game/vault.rs::Game::tick_vault the per-tick vault update +VaultBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +VegetationBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Vegetation arm: breaks when it cannot survive. +VegetationBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class Vegetation arm: checks the block below. +VegetationBlock.isPathfindable skip:data per-state path type is exported state data (StateData.path) +VineBlock.makeShapes skip:java builds the VoxelShape arrays; shapes are data +VineBlock.countFaces port crates/horizon-world/src/level/survive.rs::Sim::vine_count_faces +VineBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Vine arm: horizontal faces from the neighbour. +VineBlock.copyRandomFaces inline crates/horizon-world/src/level/tick.rs::Sim::vine_random_tick Random face copy is the spread step of vine_random_tick. +VineBlock.hasHorizontalConnection inline crates/horizon-world/src/level/tick.rs::Sim::vine_random_tick Check after the copy, inside vine_random_tick. +VineBlock.canSpread port crates/horizon-world/src/level/tick.rs::Sim::vine_can_spread +VineBlock.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced Vine arm: a clicked vine with a free face. +VineBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Vine arm picks a supported face. +VineBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +VineBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +VineBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +WallBlock.makeShapes skip:java builds the VoxelShape arrays; shapes are data +WallBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class Wall arm connects to neighbours and sets the post. +WallBlock.topUpdate port crates/horizon-world/src/level/shapes.rs::Sim::wall_shape Up-neighbour case; wall_shape is the shared core of the update. +WallBlock.sideUpdate port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class Side-neighbour branch of the Wall arm. +WallBlock.shouldRaisePost inline crates/horizon-world/src/level/shapes.rs::Sim::wall_shape Post rule is the last step of wall_shape. +WallBlock.updateSides inline crates/horizon-world/src/level/shapes.rs::Sim::wall_shape Side values are set inside wall_shape. +WallBlock.makeWallState inline crates/horizon-world/src/level/shapes.rs::Sim::wall_shape none, low or tall per side inside wall_shape. +WallBlock.getFluidState port crates/horizon-world/src/registry_fluid.rs::RegistryFluidWorld::fluid_state waterlogged fluid from block data +WallBlock.createBlockStateDefinition skip:data block state definition is data (blocks table in horizon-items) +WallBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate +WallBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror +WeatheringCopper.getFirst port crates/horizon-world/src/level/tick.rs::Sim::copper_stepped all_the_way walk back to the first oxidation stage. +WeatheringCopper.WeatherState.forEach skip:java enum iteration helper; Rust walks the state list directly +WitherSkullBlock.canSpawnMob inline crates/horizon-client/src/game/dispenser.rs::Game::dispense_wither_skull Height, difficulty and soul base checks are in dispense_wither_skull. +WitherSkullBlock.getOrCreateWitherFull inline crates/horizon-client/src/game/wither.rs::Game::check_wither_spawn Full pattern is matched inline in check_wither_spawn. +AbstractFurnaceBlockEntity.consumeFuel inline crates/horizon-client/src/furnace.rs::Furnace::tick Fuel count drops by one when a burn starts; bucket remainder in the same branch. +AbstractFurnaceBlockEntity.canBurn inline crates/horizon-client/src/furnace.rs::Furnace::tick Output slot check (same item, room left) inside tick. +AbstractFurnaceBlockEntity.getTotalCookTime port crates/horizon-client/src/furnace.rs::total_time +AbstractFurnaceBlockEntity.getSlotsForFace port crates/horizon-client/src/game/machines.rs::View::slots_for_face Furnace faces: down 2 and 1, up 0, sides 1. +AbstractFurnaceBlockEntity.canTakeItemThroughFace port crates/horizon-client/src/game/machines.rs::Containers::can_take +AbstractFurnaceBlockEntity.canPlaceItem port crates/horizon-client/src/game/machines.rs::Containers::can_place Furnace arm: output refused, fuel slot takes fuel or a bucket. +AbstractFurnaceBlockEntity.setRecipeUsed inline crates/horizon-client/src/furnace.rs::Furnace::tick The recipe count goes up at each finished smelt. +AbstractFurnaceBlockEntity.awardUsedRecipesAndPopExperience port crates/horizon-client/src/game.rs::Game::award_furnace_recipes gap: minor: pays the experience and clears the used recipes; the recipe unlock and recipe-crafted trigger are not in this fn. +AbstractFurnaceBlockEntity.getRecipesToAwardAndPopExperience port crates/horizon-client/src/furnace.rs::Furnace::recipe_experience gap: minor: returns counts and experience, not the recipe list. +AbstractFurnaceBlockEntity.fillStackedContents port crates/horizon-client/src/menu/place.rs::Menu::stacked_contents Furnace arm counts all three slots. +BannerBlockEntity.saveAdditional port crates/horizon-client-core/src/block_entity_nbt.rs::canonical banner arm of the saved NBT form +BannerBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place banner patterns and name from item components +BannerBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect banner patterns and name to item components +BaseContainerBlockEntity.loadAdditional port crates/horizon-client-core/src/block_entity_nbt.rs::items container slots from the saved list +BaseContainerBlockEntity.saveAdditional port crates/horizon-client-core/src/block_entity_nbt.rs::canonical container NBT in its saved form +BaseContainerBlockEntity.sendChestLockedNotifications missing minor: a chest with a Lock tag should refuse to open with a message. Horizon has no Lock check; chest_menu_blocked (game/storage.rs) only looks at the block above. +BaseContainerBlockEntity.isEmpty port crates/horizon-client/src/game/machines.rs::Containers::is_empty +BaseContainerBlockEntity.removeItem port crates/horizon-client/src/game/machines.rs::Game::take_one gap: minor: takes one item, not a chosen count. +BaseContainerBlockEntity.createMenu port crates/horizon-client/src/game.rs::Game::open_menu opens the container's screen +BaseContainerBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place container items, name and lock from item components +BaseContainerBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect container items, name and lock to item components +BaseContainerBlockEntity.getLootContext port crates/horizon-client/src/structure_blocks.rs::roll_container position seed and biome feed the loot table roll. +BeaconBlockEntity.updateBase port crates/horizon-client/src/game/beacon.rs::Game::beacon_base counts the pyramid levels (steps 1 to 4) +BeaconBlockEntity.storeEffect port crates/horizon-client/src/save.rs::encode_block_entity primary and secondary written to the beacon tag +BeaconBlockEntity.loadEffect port crates/horizon-client/src/save.rs::decode_block_entity primary and secondary read from the beacon tag +BeaconBlockEntity.createMenu port crates/horizon-client/src/game/beacon.rs::Game::beacon_opened +BeaconBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place beacon name and lock from item components +BeaconBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect beacon name and lock to item components +BeehiveBlockEntity.setChanged port crates/horizon-client/src/game/beehives.rs::Game::tick_beehive fire check runs every tick here instead of on each setChanged; releases with EMERGENCY. +BeehiveBlockEntity.releaseAllOccupants port crates/horizon-client/src/beehive.rs::Beehive::release_all +BeehiveBlockEntity.loadAdditional port crates/horizon-client/src/beehive.rs::Beehive::from_nbt +BeehiveBlockEntity.saveAdditional port crates/horizon-client/src/beehive.rs::Beehive::to_nbt +BeehiveBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place bees from item components +BeehiveBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect bees to item components +BeehiveBlockEntity.BeeData.toOccupant skip:java BeeData is a wrapper; Horizon stores the Occupant itself in the hive. +BeehiveBlockEntity.Occupant.setBeeReleaseData inline crates/horizon-client/src/beehive.rs::release Ticks inside the hive count towards age and love, inside release. +BeehiveBlockEntity.Occupant.updateBeeAge inline crates/horizon-client/src/beehive.rs::release Age moves towards zero inside release. diff --git a/tools/port_map/game2-017.tsv b/tools/port_map/game2-017.tsv new file mode 100644 index 0000000000..59478ea0ab --- /dev/null +++ b/tools/port_map/game2-017.tsv @@ -0,0 +1,147 @@ +BellBlockEntity.areRaidersNearby inline crates/horizon-client/src/block_entity_anim.rs::Bell::tick resonation start check (raider within 32 blocks) folded into Bell::tick +BellBlockEntity.makeRaidersGlow port crates/horizon-client/src/game/block_entity_anims.rs::Game::tick_block_entity_anims GLOWING 60 applied to the raiders Bell::tick returns +BellBlockEntity.showBellParticles port crates/horizon-client/src/block_entity_anim.rs::Bell::tick particle positions and colours computed here; emitted in tick_block_entity_anims +BellBlockEntity.isRaiderWithinRange inline crates/horizon-client/src/block_entity_anim.rs::Bell::tick raider_in closure (48 blocks) +BlockEntity.validateBlockState skip:other guard that throws on a wrong block state; no player behaviour +BlockEntity.getPosFromTag missing minor: a block entity saved in the wrong chunk keeps its saved x/z; no chunk fix-up in decode_block_entity +BlockEntity.saveCustomOnly port crates/horizon-client/src/block_entity_item.rs::custom_only saved NBT without id, position and components +BlockEntity.loadStatic port crates/horizon-client/src/save.rs::decode_block_entity creates the block entity from its saved tag +BlockEntity.fillCrashReportCategory skip:debug crash report detail +BlockEntity.applyComponents inline crates/horizon-client/src/block_entity_item.rs::place implicit components applied as part of placing the item's data +BlockEntity.collectComponents port crates/horizon-client/src/block_entity_item.rs::pick component patch built when a block entity is picked +BrewingStandBlockEntity.getPotionBits inline crates/horizon-client/src/brewing.rs::BrewingStand::tick bottle occupancy sent as out.bottles +BrewingStandBlockEntity.isBrewable port crates/horizon-client/src/brewing.rs::BrewingStand::brewable +BrewingStandBlockEntity.doBrew port crates/horizon-client/src/brewing.rs::BrewingStand::brew +BrewingStandBlockEntity.getSlotsForFace port crates/horizon-client/src/game/machines.rs::View::slots_for_face +BrushableBlockEntity.dropContent port crates/horizon-client/src/game/brush.rs::Game::brushing_completed item drops from the hit face; loot is unpacked in unpack_brush_loot +BrushableBlockEntity.tryLoadLootTable port crates/horizon-client/src/game/brush.rs::Game::unpack_brush_loot reads LootTable and seed from the saved tag +BrushableBlockEntity.trySaveLootTable skip:other no own code: the raw saved tag (spill::saved_entity) keeps LootTable +BrushableBlockEntity.getUpdateTag missing minor: unsure: no client update tag carrying hit direction and item; no Horizon equivalent found +BrushableBlockEntity.loadAdditional skip:other no own code: the raw saved tag (spill::saved_entity) keeps item and LootTable +BrushableBlockEntity.saveAdditional skip:other no own code: the raw saved tag (spill::saved_entity) keeps item and LootTable +CalibratedSculkSensorBlockEntity.VibrationUser.canReceiveVibration port crates/horizon-client/src/game/sculk.rs::Game::sculk_listen calibrated back-signal frequency filter +CampfireBlockEntity.markUpdated port crates/horizon-client/src/game/campfire.rs::Game::tick_campfire gap: minor: placing food (use_on_campfire) sends no block update +CampfireBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place container components applied when the item's data is placed +CampfireBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect container component collected from Items +ChestBlockEntity.loadAdditional port crates/horizon-client/src/save.rs::decode_block_entity Items (or loot table, via the chunk) +ChestBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity Items +ChestBlockEntity.stopOpen port crates/horizon-client/src/game/block_entity_anims.rs::Game::container_opener_event opener count down (close side), spectators excluded +ChestBlockEntity.swapContents missing minor: unsure: double-chest halves are not swapped during an old-world upgrade; no such step found in save.rs +ChestBlockEntity.recheckOpen missing minor: unsure: player openers are not recounted when the block next to a chest changes; only golem openers are rechecked (recheck_golem_openers) +ChestLidController.tickLid port crates/horizon-client/src/block_entity_anim.rs::ChestLid::tick lid openness step of 0.1 per tick +ChiseledBookShelfBlockEntity.removeItem port crates/horizon-client/src/game/machines.rs::Game::take_one removes one item from the slot +ChiseledBookShelfBlockEntity.setItem port crates/horizon-client/src/game/chiseled_bookshelf.rs::Game::use_chiseled_bookshelf player insert path; slot update via chiseled_bookshelf_update_state +ChiseledBookShelfBlockEntity.canTakeItem port crates/horizon-client/src/game/machines.rs::Containers::can_take gap: minor: the target-has-room check is left to take_one and put_back +ChiseledBookShelfBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place Items as container component +ChiseledBookShelfBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect Items as container component +CommandBlockEntity.onModeSwitch port crates/horizon-client/src/game/command_blocks.rs::Game::apply_command_block mode switch; the AUTO schedule follows the setAutomatic step +CommandBlockEntity.isConditional port crates/horizon-client/src/game/command_blocks.rs::conditional +CommandBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place custom name component +CommandBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect custom name component +CommandBlockEntity.removeComponentsFromTag port crates/horizon-client/src/block_entity_item.rs::pick CustomName, conditionMet and powered left out of the item data +ConduitBlockEntity.updateDestroyTarget port crates/horizon-client/src/game/block_entity_anims.rs::Game::conduit_attack kept target while alive and within 8 blocks, else a new one +ContainerOpenersCounter.decrementOpeners port crates/horizon-client/src/game/block_entity_anims.rs::Game::count_opener decrement side of the counter +ContainerOpenersCounter.hasContainerOpen port crates/horizon-client/src/mobs/transport_items.rs::has_open golems only; players are counted in lid_openers +CrafterBlockEntity.isSlotDisabled port crates/horizon-client/src/containers.rs::Crafter::can_place disabled check (disabled[slot]) inside can_place +CrafterBlockEntity.canPlaceItem port crates/horizon-client/src/game/machines.rs::Containers::can_place calls Crafter::can_place +CrafterBlockEntity.smallerStackExist port crates/horizon-client/src/containers.rs::Crafter::can_place the smaller-stack check later in the slots +CrafterBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity crafter arm: items, disabled slots, triggered, ticks +CrafterBlockEntity.isEmpty port crates/horizon-client/src/game/machines.rs::Containers::is_empty +CrafterBlockEntity.fillStackedContents missing minor: unsure: recipe-book fill of a crafter is not handled; recipe_book.rs has no crafter case +CrafterBlockEntity.addDisabledSlots port crates/horizon-client/src/save.rs::encode_block_entity disabled_slots in the crafter arm +CrafterBlockEntity.slotCanBeDisabled port crates/horizon-client/src/containers.rs::Crafter::slot_can_be_disabled +CreakingHeartBlockEntity.updateCreakingState inline crates/horizon-client/src/mobs/creaking_heart.rs::server_tick creaking_heart_state set in server_tick +CreakingHeartBlockEntity.setCreakingInfo inline crates/horizon-client/src/mobs/creaking_heart.rs::protector uuid reset and ticks reset inside protector +CreakingHeartBlockEntity.isProtector port crates/horizon-client/src/mobs/creaking_heart.rs::protector +CreakingHeartBlockEntity.loadAdditional port crates/horizon-client/src/save.rs::decode_block_entity creaking uuid +CreakingHeartBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity creaking uuid +DecoratedPotBlockEntity.saveAdditional skip:other no own code: the raw saved tag keeps sherds and item +DecoratedPotBlockEntity.loadAdditional skip:other no own code: the raw saved tag keeps sherds and item +DecoratedPotBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect sherds and item as components +DecoratedPotBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place sherds and item as components +DecoratedPotBlockEntity.splitTheItem missing minor: unsure: no split-count step for the pot's item found in decorated_pot.rs +DecoratedPotBlockEntity.wobble port crates/horizon-client/src/game/decorated_pot.rs::Game::use_decorated_pot wobble event sent on a hit +DispenserBlockEntity.insertItem port crates/horizon-client/src/game/machines.rs::Game::add_item slot-by-slot insert, as hoppers use it +DispenserBlockEntity.loadAdditional port crates/horizon-client/src/save.rs::decode_block_entity Items (9 slots) +DispenserBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity Items (9 slots) +EnderChestBlockEntity.startOpen port crates/horizon-client/src/game/block_entity_anims.rs::Game::container_opener_event opener count up (open side) +EnderChestBlockEntity.stopOpen port crates/horizon-client/src/game/block_entity_anims.rs::Game::container_opener_event opener count down (close side) +EnderChestBlockEntity.recheckOpen missing minor: unsure: player openers are not recounted when the block next to an ender chest changes +HopperBlockEntity.loadAdditional port crates/horizon-client/src/save.rs::decode_block_entity gap: minor: TransferCooldown is not saved (transient state in machines.rs) +HopperBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity gap: minor: TransferCooldown is not saved (transient state in machines.rs) +HopperBlockEntity.setItem port crates/horizon-client/src/game/machines.rs::Game::try_move_in_item gap: minor: unpackLootTable(null) on set is not done here +HopperBlockEntity.inventoryFull inline crates/horizon-client/src/game/machines.rs::Game::hopper_try_move_items all-slots-at-max check before sucking +HopperBlockEntity.getSlots port crates/horizon-client/src/game/machines.rs::View::slots_for_face +HopperBlockEntity.createFlatSlots skip:java cached slot index array +HopperBlockEntity.canPlaceItemInContainer port crates/horizon-client/src/game/machines.rs::Containers::can_place +HopperBlockEntity.canTakeItemFromContainer port crates/horizon-client/src/game/machines.rs::Containers::can_take +HopperBlockEntity.getSourceContainer port crates/horizon-client/src/game/machines.rs::Game::suck_from_container container above the hopper +HopperBlockEntity.getItemsAtAndAbove port crates/horizon-client/src/game/machines.rs::Game::suck_item_entities item entities in the suck box +HopperBlockEntity.entityInside inline crates/horizon-client/src/game/machines.rs::Game::suck_item_entities item entering the hopper is polled each tick instead +JigsawBlockEntity.generate missing player: creative: the Jigsaw block's Generate button and structure growth do not exist; no jigsaw screen or block-driven generation found (/place only places templates) +JukeboxBlockEntity.notifyItemChangedInJukebox port crates/horizon-client/src/game/jukebox.rs::Game::set_has_record gap: minor: no BLOCK_CHANGE game event +JukeboxBlockEntity.getComparatorOutput port crates/horizon-client/src/game/jukebox.rs::jukebox_signals comparator value of the song +JukeboxBlockEntity.loadAdditional port crates/horizon-client/src/save.rs::decode_block_entity RecordItem and ticks +JukeboxBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity RecordItem and ticks +JukeboxBlockEntity.setTheItem inline crates/horizon-client/src/game/jukebox.rs::Game::use_jukebox gap: player: hoppers and other containers cannot insert a disc +JukeboxBlockEntity.canPlaceItem port crates/horizon-client/src/game/jukebox.rs::Game::use_jukebox gap: player: hopper insertion of discs is missing +JukeboxBlockEntity.setSongItemWithoutPlaying skip:other @VisibleForTesting setter; no player behaviour +JukeboxBlockEntity.tryForcePlaySong skip:other @VisibleForTesting hook; no player behaviour +LecternBlockEntity.hasBook inline crates/horizon-client/src/game/books.rs::Game::use_lectern book-present check before a book is placed +LecternBlockEntity.onBookItemRemove port crates/horizon-client/src/game/books.rs::Game::lectern_menu_done book gone: has_book off, container changed +LecternBlockEntity.resolveBook missing minor: unsure: written-book components (selectors, scores) are not resolved when a book goes on a lectern; books.rs has no resolve step +LecternBlockEntity.createCommandSourceStack missing minor: unsure: only used by resolveBook (see there) +LecternBlockEntity.loadAdditional port crates/horizon-client/src/save.rs::decode_block_entity Book and Page +LecternBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity Book and Page +SculkCatalystBlockEntity.CatalystListener.tryAwardItSpreadsAdvancement missing player: no 'It Spreads' advancement for a mob killed next to a sculk catalyst; Horizon never fires kill_mob_near_sculk_catalyst +SculkShriekerBlockEntity.loadAdditional port crates/horizon-client/src/save.rs::decode_block_entity shrieker state from the sculk arm +SculkShriekerBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity shrieker state from the sculk arm +SculkShriekerBlockEntity.tryGetPlayer inline crates/horizon-client/src/game/sculk.rs::Game::sculk_listen ctx.player flag decides whether a player set it off +SculkShriekerBlockEntity.tryToWarn port crates/horizon-client/src/game/sculk.rs::Game::try_to_warn +SculkShriekerBlockEntity.tryRespond port crates/horizon-client/src/game/sculk.rs::Game::shrieker_respond +SculkShriekerBlockEntity.playWardenReplySound port crates/horizon-client/src/game/sculk.rs::Game::warden_reply_sound +SculkShriekerBlockEntity.trySummonWarden port crates/horizon-client/src/game/sculk.rs::Game::try_summon_warden +SculkShriekerBlockEntity.VibrationUser.canReceiveVibration port crates/horizon-client/src/game/sculk.rs::Game::sculk_listen not shrieking and a player is the source +SculkShriekerBlockEntity.VibrationUser.onReceiveVibration port crates/horizon-client/src/game/sculk.rs::Game::try_shriek +ShelfBlockEntity.loadAdditional skip:other no own code: the raw saved tag keeps Items (shelf_signal reads it) +ShelfBlockEntity.saveAdditional skip:other no own code: the raw saved tag keeps Items (shelf_signal reads it) +ShelfBlockEntity.getUpdateTag missing minor: unsure: no client update tag for shelves +ShelfBlockEntity.setChanged missing player: shelves cannot be used: no item insert or take on a shelf; no shelf interaction code found +ShelfBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place Items as container component +ShelfBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect Items as container component +ShulkerBoxBlockEntity.updateAnimation port crates/horizon-client/src/block_entity_anim.rs::Shulker::tick lid progress +ShulkerBoxBlockEntity.moveCollidedEntities missing player: unsure: a player in the swept lid box is not pushed out when the box opens; the code comment says the level does it and no push code was found +ShulkerBoxBlockEntity.doNeighborUpdates missing minor: unsure: neighbour updates on lid open and close are left to the level; no code found +ShulkerBoxBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity shulker box uses the chest arm (27 slots) +ShulkerBoxBlockEntity.loadFromTag port crates/horizon-client/src/block_entity_item.rs::place contents from the item's data +SignBlockEntity.getText skip:other accessor; SignData keeps front and back sides as fields +SignBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity sign arm: sign.encode() +SignBlockEntity.loadAdditional port crates/horizon-client/src/signs.rs::SignData::decode +SignBlockEntity.resolveLines missing minor: unsure: sign text selectors are not resolved; SignText keeps plain strings (component_text) +SignBlockEntity.resolveLine missing minor: unsure: sign text selectors are not resolved; SignText keeps plain strings (component_text) +SignBlockEntity.updateText port crates/horizon-client/src/game/sign_edit.rs::Game::lan_sign_update guest's sign write +SignBlockEntity.updateMessages missing minor: unsure: sign text selectors are not resolved; SignText keeps plain strings (component_text) +SignBlockEntity.setText port crates/horizon-client/src/game/sign_edit.rs::Game::sign_write +SignBlockEntity.canExecuteClickCommands missing player: sign text click events are not kept; Horizon flattens sign text to plain strings, so clicking a sign runs nothing +SignBlockEntity.executeClickCommandsIfPresent missing player: sign text click events are not kept; clicking a sign runs nothing +SignBlockEntity.createCommandSourceStack missing minor: unsure: only used by click commands (see executeClickCommandsIfPresent) +SignBlockEntity.markUpdated port crates/horizon-client/src/game/trial_spawner.rs::Game::mark_block_entity_updated +SignBlockEntity.setWaxed port crates/horizon-client/src/game/sign_edit.rs::Game::use_sign_applicator honeycomb wax +SignBlockEntity.playerIsTooFarAwayToEdit port crates/horizon-client/src/game/sign_edit.rs::Game::lan_sign_update reach check when a guest writes +SignBlockEntity.tick missing minor: unsure: no per-tick clearing of the edit permission; the reach check only happens when a guest writes +SignBlockEntity.removeComponentsFromTag port crates/horizon-client/src/block_entity_item.rs::pick front, back and waxed left out of the item data +SignText.load port crates/horizon-client/src/signs.rs::SignData::decode +SignText.validateLineCounts skip:java codec check of four lines; Horizon fixes the array size +SignText.withGlowingText port crates/horizon-client/src/game/sign_edit.rs::Game::use_sign_applicator glow ink sac +SignText.withColor port crates/horizon-client/src/game/sign_edit.rs::Game::use_sign_applicator dye +SignText.hasMessage port crates/horizon-client/src/signs.rs::SignText::is_empty inverse of is_empty +SignText.getRenderMessages port crates/horizon-client/src/block_entity_render.rs::sign_text_calls lines drawn on the sign +SignText.filteredMessagesForSerialization skip:service chat-filtered sign text for clients; Horizon has no text filter +SignText.hasAnyClickCommands missing player: sign text click events are not kept; no click command exists on any sign +SignText.hasEditableText skip:other Horizon sign text has no click events, so every line is editable +SignText.equals skip:java value equality +SignText.hashCode skip:java value hashing +SignText.Mutable.asImmutable skip:java copy into the immutable record +SignText.Mutable.setLine port crates/horizon-client/src/game/sign_edit.rs::Game::sign_write writes the edited lines to the side +SignText.Mutable.modifyLine missing minor: unsure: component resolution helper; see SignBlockEntity.resolveLines +SignText.Mutable.modifyLines missing minor: unsure: component resolution helper; see SignBlockEntity.resolveLines diff --git a/tools/port_map/game2-018.tsv b/tools/port_map/game2-018.tsv new file mode 100644 index 0000000000..7b6016c464 --- /dev/null +++ b/tools/port_map/game2-018.tsv @@ -0,0 +1,150 @@ +SkullBlockEntity.saveAdditional port crates/horizon-client/src/save.rs::encode_block_entity skull arm writes profile, note_block_sound and custom_name +SkullBlockEntity.loadAdditional port crates/horizon-client/src/save.rs::decode_block_entity skull arm reads the same three keys +SkullBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place profile, note sound and custom name from the item components are applied to the saved tag +SkullBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect per-part component read; the skull's three parts come from the parts table +SkullBlockEntity.removeComponentsFromTag inline crates/horizon-client/src/block_entity_item.rs::pick the three keys are removed from the saved NBT inside pick's part loop +SpawnerBlockEntity.triggerEvent port crates/horizon-client/src/mobs/spawner.rs::Spawner::client_event event 1 resets the client spawn delay to the minimum; other events return false +SpawnerBlockEntity.setEntityData missing minor: an op's spawn egg with an ENTITY_DATA component only sets the mob type on a spawner here (use_spawn_egg_on_spawner); the rest of the egg's entity data is dropped +StructureBlockEntity.updateBlockState missing player: the structure block's mode is never written to its block state, so the block does not switch between SAVE, LOAD, CORNER and DATA +StructureBlockEntity.usedBy missing player: right-clicking a structure block opens no edit screen; Horizon has no structure block screen +StructureBlockEntity.setStructureName missing player: the structure name box does nothing; the structure block edit packet (SET_STRUCTURE_BLOCK, id 60) has no handler +StructureBlockEntity.setMode missing player: changing the mode in the structure block screen does nothing +StructureBlockEntity.detectSize missing player: the Detect Size button does nothing, so a corner-marked area is never sized +StructureBlockEntity.getEnclosingBoundingBox missing player: no area is found between corner blocks, so Detect Size and saving have nothing to use +StructureBlockEntity.saveStructure missing player: the Save button and redstone save do nothing, so a built structure cannot be saved from the game +StructureBlockEntity.placeStructureIfSameSize missing player: the Load button does nothing when the template size matches +StructureBlockEntity.loadStructureInfo missing player: a structure block never reads a template's size and author +StructureBlockEntity.placeStructure port crates/horizon-world/src/worldgen/structure/processor.rs::place_in_world gap: player: the block's own settings (mirror, rotation, integrity, seed, strict, ignore entities) and its redstone trigger are not read; only the placement runs +StructureBlockEntity.getStructureTemplate port crates/horizon-world/src/worldgen/structure.rs::StructureResources::template name lookup only; nothing calls it for a structure block +StructureBlockEntity.unloadStructure missing minor: no structure block to unload a template from; templates in StructureResources are never dropped +StructureBlockEntity.isStructureLoadable missing player: the Load button never reports whether the template exists +StructureBlockEntity.renderMode missing player: a structure block in creative shows no outline box for its SAVE or LOAD mode +StructureBlockEntity.getRenderableBox missing player: no outline box is drawn around the structure's area +TestInstanceBlockEntity.Data.withSize skip:java record copy-with builder; no behaviour of its own +TestInstanceBlockEntity.Data.withStatus missing minor: test instance status never changes; Horizon has no game test runner (/test only reports no instances) +TestInstanceBlockEntity.Data.withError missing minor: no game test runner, so no error message is ever set on a test instance block +TheEndGatewayBlockEntity.beamAnimationTick port crates/horizon-client/src/block_entity_anim.rs::EndGateway::tick client ticker: age goes up and the teleport cooldown goes down +TheEndGatewayBlockEntity.getSpawnPercent inline crates/horizon-client/src/block_entity_render.rs::gateway_beam spawn percent is (age + partial) / 200, clamped, inside the beam function +TheEndGatewayBlockEntity.getCooldownPercent inline crates/horizon-client/src/block_entity_render.rs::gateway_beam cooldown percent is one minus the clamped cooldown over 40, inside the beam function +TheEndGatewayBlockEntity.findExitPosition inline crates/horizon-client/src/game/end.rs::Game::resolve_gateway the known-exit branch's find_exit closure: tallest block from two up and five out, then one above it +TheEndGatewayBlockEntity.findOrCreateValidTeleportPos port crates/horizon-client/src/game/end.rs::Game::resolve_gateway the find branch: tentative chunk, valid end stone, an island when none, then the tallest block +TheEndGatewayBlockEntity.findExitPortalXZPosTentative inline crates/horizon-client/src/game/end.rs::Game::resolve_gateway the find branch walks back over non-empty and forward over empty chunks; gap: minor: an unloaded chunk stops the walk instead of loading it +TheEndGatewayBlockEntity.getChunk port crates/horizon-client/src/game/end.rs::Game::chunk_empty gap: minor: returns whether the chunk is empty, not the chunk; an unloaded chunk reads as None +TheEndGatewayBlockEntity.findValidSpawnInChunk inline crates/horizon-client/src/game/end.rs::Game::resolve_gateway end stone with two open blocks above it, closest to the origin +TheEndGatewayBlockEntity.getParticleAmount port crates/horizon-client/src/block_entity_render.rs::BlockEntityRenderer::gateway_faces the count is the number of gateway faces that render; render_tick passes its length to animate_tick, which draws one particle per face +TrialSpawnerBlockEntity.createDefaultSpawner port crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::default default cooldown 36000 and range 14; the no-creative player filter is in TrialSpawner::detect +TrialSpawnerBlockEntity.setEntityId missing player: a spawn egg used on a trial spawner does not change the mob it spawns; only spawners take eggs (use_spawn_egg_on_spawner) +TrialSpawnerBlockEntity.setEntityData missing player: an op's spawn egg with entity data does nothing to a trial spawner +TrialSpawnerBlockEntity.getState port crates/horizon-client/src/game/trial_spawner.rs::Game::trial_spawner_block reads the state and ominous properties of the block +TrialSpawnerBlockEntity.setState port crates/horizon-client/src/game/trial_spawner.rs::Game::set_trial_spawner_state writes the state property and the ominous flag together +TrialSpawnerBlockEntity.markUpdated port crates/horizon-client/src/game/trial_spawner.rs::Game::mark_block_entity_updated +TrialSpawner.removeOminous inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::tick_and_get_next the cooldown branch clears the ominous flag and emits OminousSet(false); the block write goes through set_trial_spawner_state +TrialSpawner.shouldMobBeUntracked port crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::server_tick gap: minor: drops mobs that are gone or beyond 47 blocks; no separate alive or dimension check +TrialSpawner.addBecomeOminousParticles inline crates/horizon-client/src/game/trial_visuals.rs::Game::trial_level_event the 3020 arm draws the 20 trial omen and soul fire flame particles +TrialSpawner.overrideEntityToSpawn missing player: a spawn egg on a trial spawner cannot change its mob; the egg only reaches spawners +TrialSpawner.FullConfig.overrideEntity missing player: same as overrideEntityToSpawn; the trial spawner's mob type cannot be set from an egg +TrialSpawner.FullConfig.overrideEntityData missing player: same as overrideEntityToSpawn; egg entity data never reaches a trial spawner +TrialSpawnerState.calculatePositionToSpawnSpawner port crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::item_spawner_position +TrialSpawnerState.calculatePositionAbove inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::item_spawner_position the clip two to five blocks above the chosen entity and the drop point under it +TrialSpawnerState.selectEntityToSpawnItemAbove inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::item_spawner_position picks a mob or a player at random, as vanilla does +TrialSpawnerState.ParticleEmission.addParticle inline crates/horizon-client/src/game/trial_visuals.rs::Game::client_tick_trial_spawner each state's particle is emitted by this fn; the Game::particle helper sends it +TrialSpawnerStateData.pack skip:java immutable snapshot copy of the data for save and sync; no behaviour of its own +TrialSpawnerStateData.isReadyToSpawnNextMob inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::tick_and_get_next the Active spawn branch checks the spawn time and the simultaneous-mob target +TrialSpawnerStateData.countAdditionalPlayers inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::tick_and_get_next additional players are detected players minus one, floored at zero +TrialSpawnerStateData.findPlayerWithOminousEffect inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::try_detect_players Trial Omen wins, else the last Bad Omen player; folded into the detect step +TrialSpawnerStateData.resetAfterBecomingOminous port crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::apply_ominous gap: minor: mobs are discarded without Mob.dropPreservedEquipment +TrialSpawnerStateData.transformBadOmenIntoTrialOmen port crates/horizon-client/src/game/trial_spawner.rs::Game::bad_omen_to_trial_omen Bad Omen level plus one becomes Trial Omen for 18000 ticks per level +TrialSpawnerStateData.isReadyToOpenShutter inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::tick_and_get_next WaitingForRewardEjection opens the shutter after the delay and plays its sound +TrialSpawnerStateData.isReadyToEjectItems inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::tick_and_get_next EjectingReward ejects on the ejection interval +TrialSpawnerStateData.getOrCreateDisplayEntity port crates/horizon-client/src/game/spawner.rs::Game::update_spawner_display the shown mob is made again when its type changes; a trial spawner shows one only with a spinning mob +TrialSpawnerStateData.getDispensingItems inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::spawn_ominous_item_spawner rolls the loot once and keeps a non-empty roll +TrialSpawnerStateData.lowResolutionPosition inline crates/horizon-client/src/mobs/trial_spawner.rs::TrialSpawner::spawn_ominous_item_spawner the 30 by 20 by 30 cell of the position plus the level seed +VaultBlockEntity.saveAdditional port crates/horizon-client/src/vault.rs::Vault::save writes config, shared data and server data +VaultBlockEntity.loadAdditional port crates/horizon-client/src/vault.rs::Vault::load reads server data, config and shared data back +VaultBlockEntity.getServerData skip:other accessor that is null on the client; Horizon keeps one Vault struct holding both halves, so there is no server object to return +VaultBlockEntity.Client.emitDeactivationParticles inline crates/horizon-client/src/game/trial_visuals.rs::Game::trial_level_event the 3016 arm draws 20 flame particles in the cage and plays the deactivate sound +VaultBlockEntity.Client.emitIdleParticles inline crates/horizon-client/src/game/trial_visuals.rs::Game::client_tick_vault half the ticks: a smoke and a flame particle inside the cage +VaultBlockEntity.Client.emitConnectionParticlesForPlayer inline crates/horizon-client/src/game/trial_visuals.rs::Game::vault_connection_particles two to five connection particles aimed from the keyhole at the player +VaultBlockEntity.Client.emitConnectionParticlesForNearbyPlayers port crates/horizon-client/src/game/trial_visuals.rs::Game::vault_connection_particles gap: player: only the local player gets connection particles; other connected players get none +VaultBlockEntity.Client.isWithinConnectionRange inline crates/horizon-client/src/game/trial_visuals.rs::Game::vault_connection_particles range check against the connected particles range +VaultBlockEntity.Client.playIdleSounds inline crates/horizon-client/src/game/trial_visuals.rs::Game::client_tick_vault ambient sound at a two percent chance while a display item shows +VaultBlockEntity.Client.randomPosCenterOfCage inline crates/horizon-client/src/game/trial_visuals.rs::Game::trial_level_event the 3016 arm draws each axis from 0.4 to 0.6 +VaultBlockEntity.Client.randomPosInsideCage port crates/horizon-client/src/game/trial_visuals.rs::cage_point +VaultBlockEntity.Client.keyholePos inline crates/horizon-client/src/game/trial_visuals.rs::Game::vault_connection_particles keyhole point: bottom centre, 1.75 up, half a block out of the facing side +VaultBlockEntity.Server.setVaultState port crates/horizon-client/src/game/vault.rs::Game::set_vault_state writes the vault_state block property; the exit and entry effects run in Vault::on_transition +VaultBlockEntity.Server.cycleDisplayItemFromLootTable port crates/horizon-client/src/vault.rs::Vault::cycle_display_item +VaultBlockEntity.Server.getRandomDisplayItemFromLootTable inline crates/horizon-client/src/vault.rs::Vault::cycle_display_item rolls the display table and picks one item at random +VaultBlockEntity.Server.unlock inline crates/horizon-client/src/vault.rs::Vault::try_insert_key the unlock step sets the ejection list and display item and starts the 14 tick delay +VaultBlockEntity.Server.resolveItemsToEject inline crates/horizon-client/src/vault.rs::Vault::try_insert_key rolls the vault loot table when a key is inserted +VaultBlockEntity.Server.isValidToInsert inline crates/horizon-client/src/vault.rs::Vault::try_insert_key the stack must match the key item's components and hold at least its count +VaultBlockEntity.Server.shouldCycleDisplayItem inline crates/horizon-client/src/vault.rs::Vault::server_tick cycles the display item every 20 ticks while Active +VaultConfig.validate port crates/horizon-client/src/vault.rs::VaultConfig::decode an activation range above the deactivation range fails the whole config +VaultServerData.addToRewardedPlayers inline crates/horizon-client/src/vault.rs::Vault::try_insert_key adds the player and keeps at most 128 rewarded players +VaultServerData.setItemsToEject inline crates/horizon-client/src/vault.rs::Vault::try_insert_key the unlock stores the rolled items as the ejection list +VaultServerData.getNextItemToEject inline crates/horizon-client/src/vault.rs::Vault::try_insert_key the display item becomes the last item to eject, also after each ejection in tick_and_get_next +VaultServerData.popNextItemToEject inline crates/horizon-client/src/vault.rs::Vault::tick_and_get_next the ejecting state pops the last item +VaultSharedData.set inline crates/horizon-client/src/vault.rs::Vault::load the display item, connected players and particles range are read straight into the vault on load +VaultState.ejectResultItem inline crates/horizon-client/src/vault.rs::Vault::tick_and_get_next the ejecting branch spawns the item upward, sends level event 3017 and plays the eject sound +TreeGrower.getConfiguredFeature inline crates/horizon-world/src/level/growth.rs::Sim::grow_tree the flower list is used when a flower is near and the list is not empty, then a weighted pick +TreeGrower.getSurroundingBlockStates inline crates/horizon-world/src/level/growth.rs::Sim::two_by_two reads the four blocks of the two by two square +TreeGrower.removeSaplings inline crates/horizon-world/src/level/growth.rs::Sim::grow_tree the mega branch clears the four sapling blocks before placing +TreeGrower.resetSaplings inline crates/horizon-world/src/level/growth.rs::Sim::grow_tree restores the four blocks after a failed placement +TreeGrower.isTwoByTwoSapling inline crates/horizon-world/src/level/growth.rs::Sim::two_by_two all four blocks must be the sapling's own block +MovingPistonBlock.destroy missing minor: breaking a moving piston does not remove the extended base piston next to it +MovingPistonBlock.useWithoutItem missing minor: a right click on a moving piston with no block entity does not remove it +MovingPistonBlock.getDrops missing minor: a moving piston drops nothing; vanilla drops the drops of the block it carries +MovingPistonBlock.getBlockEntity port crates/horizon-world/src/level/pistons.rs::Sim::moving_piston +MovingPistonBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate rotation goes through the table built from block data +MovingPistonBlock.createBlockStateDefinition skip:data state properties come from the block data tables +PistonBaseBlock.getShape skip:data collision and outline shapes come from the block data tables; the moving piston shape is in piston_shape.rs +PistonBaseBlock.onPlace port crates/horizon-world/src/level/hooks.rs::Sim::on_place the piston arm re-checks extension when the block changes and no moving piston is there +PistonBaseBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class facing opposite the nearest look direction, not extended +PistonBaseBlock.getNeighborSignal port crates/horizon-world/src/level/pistons.rs::Sim::piston_neighbor_signal +PistonBaseBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate rotation goes through the table built from block data +PistonBaseBlock.createBlockStateDefinition skip:data state properties come from the block data tables +PistonHeadBlock.getShape skip:data collision and outline shapes come from the block data tables; the head shape is in piston_shape.rs +PistonHeadBlock.isFittingBase port crates/horizon-world/src/level/pistons.rs::Sim::piston_head_fitting_base +PistonHeadBlock.playerWillDestroy inline crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal the base goes on removal whichever way the head leaves; creative only skips the drop, which creative players never get +PistonHeadBlock.affectNeighborsAfterRemoval port crates/horizon-world/src/level/hooks.rs::Sim::affect_neighbors_after_removal removes the fitting extended base +PistonHeadBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class the head becomes air when it cannot survive from its facing side +PistonHeadBlock.canSurvive port crates/horizon-world/src/level/pistons.rs::Sim::piston_head_can_survive +PistonHeadBlock.neighborChanged port crates/horizon-world/src/level/hooks.rs::Sim::neighbor_changed the head passes the neighbour change to its base while it can survive +PistonHeadBlock.getCloneItemStack missing player: unsure: middle-click on a piston head probably gives no item; the head has no item form and block_items.rs has no piston case +PistonHeadBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate rotation goes through the table built from block data +PistonHeadBlock.createBlockStateDefinition skip:data state properties come from the block data tables +PistonMovingBlockEntity.getProgress port crates/horizon-world/src/piston_shape.rs::MovingPistonState::interpolated_progress +PistonMovingBlockEntity.getYOff port crates/horizon-world/src/piston_shape.rs::MovingPistonState::interpolated_offset the y part of the offset +PistonMovingBlockEntity.getZOff port crates/horizon-world/src/piston_shape.rs::MovingPistonState::interpolated_offset the z part of the offset +PistonMovingBlockEntity.moveEntityByPiston port crates/horizon-client/src/game/piston_push.rs::Game::move_pushed moves a body along the push with the piston no-clip rule +PistonMovingBlockEntity.moveStuckEntities port crates/horizon-client/src/game/piston_push.rs::Game::push_for_piston the honey branch selects sticky passengers after the collided ones +PistonMovingBlockEntity.loadAdditional missing minor: moving piston data is not read from the save; moving pistons are not persisted at all +PistonMovingBlockEntity.saveAdditional missing minor: moving pistons are not written to the save, so a save taken mid-move leaves no entry for the moving_piston block +PistonMovingBlockEntity.setLevel skip:data on load an unknown moved block becomes air; Horizon resolves moved states from the block registry +PistonStructureResolver.isSticky port crates/horizon-world/src/level/pistons.rs::Sim::is_sticky_piston +PistonStructureResolver.canStickToEachOther port crates/horizon-world/src/level/pistons.rs::Sim::can_stick +AmbientDesertBlockSoundsPlayer.columnContainsTriggeringBlock inline crates/horizon-client/src/animate_tick.rs::sand_sound_nearby the column check runs for each of the four sides at eight blocks out +AmbientLeavesBlockSoundPlayer.of inline crates/horizon-client/src/animate_tick.rs::leaves_ambient_sound the 1 in 300 chance, the poplar log count and the three leaf count are in leaves_ambient_sound +BlockBehaviour.isPathfindable port crates/horizon-client/src/game/natural_spawning.rs::GameLevel::pathfindable_land gap: minor: only the LAND type; WATER is read as a fluid state in dolphin.rs and AIR is not used +BlockBehaviour.showAsInteractableInSpectatorMode port crates/horizon-client/src/game/hand.rs::Game::spectator_crosshair the block branch lists the menu blocks and portals; a chest with a redstone block on top is not interactable +BlockBehaviour.canBeReplaced port crates/horizon-world/src/level/place.rs::Sim::can_be_replaced default case: replaceable, and not the same item as the one in hand +BlockBehaviour.getDrops port crates/horizon-items/src/items.rs::Items::block_drops the block's loot table for a player break +BlockBehaviour.getLightDampening skip:data the dampening value is read from the block data table (level/data.rs) +BlockBehaviour.propagatesSkylightDown skip:data the skylight_down flag is read from the block data table (level/data.rs) +BlockBehaviour.BlockStateBase. skip:java precomputed state cache; Horizon keeps these values in its block tables +BlockBehaviour.BlockStateBase.hasLargeCollisionShape skip:data flag read from the block collision data table (block_collision_scan.rs) +BlockBehaviour.BlockStateBase.Cache. skip:java state cache built at load; Horizon's level data cache holds these fields +BlockBehaviour.Properties. skip:java block definition builder; Horizon reads block values from the data tables +BlockBehaviour.Properties.ofFullCopy skip:java builder copy; values come from block data +BlockBehaviour.Properties.ofLegacyCopy skip:java builder copy; values come from block data +BlockBehaviour.Properties.mapColor skip:java builder setter; values come from block data +BlockBehaviour.Properties.strength skip:java builder setter; values come from block data +BlockBehaviour.Properties.offsetType skip:java builder setter; values come from block data +StateHolder.findNextInCollection missing minor: cycling a block property to its next value (the debug stick's cycle) is not in Horizon +StateHolder.toString skip:debug string form of a state for logs and debug output +StateHolder.valueIndex skip:java index lookup over a fixed array; Horizon keeps properties in a name-keyed map +StateHolder.getNullableValue port crates/horizon-world/src/worldgen/structure/legacy.rs::PieceContext::property returns None for a missing property +StateHolder.getValue port crates/horizon-world/src/worldgen/structure/legacy.rs::PieceContext::property gap: minor: a missing property yields None instead of an exception +StateHolder.setValueInternal port crates/horizon-world/src/registry.rs::BlockRegistry::resolve properties set by name are resolved to a state id; a value not allowed fails +StateHolder.initializeNeighbors skip:java neighbour table built once in Java; Horizon resolves states by key (registry.rs state_key) +StateHolder.getValues skip:java stream over the state's property values; Horizon iterates the name-keyed map +StateHolder.codec port crates/horizon-client/src/game/persist.rs::Game::state_from_nbt gap: minor: decodes Name and Properties; the write side is in the NBT writers diff --git a/tools/port_map/game2-019.tsv b/tools/port_map/game2-019.tsv new file mode 100644 index 0000000000..d241357afb --- /dev/null +++ b/tools/port_map/game2-019.tsv @@ -0,0 +1,146 @@ +state/predicate/BlockPredicate.test inline crates/horizon-client/src/game/end.rs::Game::podium_level bedrock check only; the exit-portal 7x5 pattern is approximated by a bedrock count in podium_level +Property.value skip:java - wraps a value in a Property.Value record; Horizon keeps property names and value strings instead (state lookups are StateProperties::value) +Property.toString skip:debug - debug string for error text only +Property.hashCode skip:java - cached Java hash of class and name +Property.parseValue inline crates/horizon-client/src/game/persist.rs::Game::state_from_nbt gap: minor: whole-state lookup from Name and Properties; an invalid property falls back to the default state instead of keeping the valid ones +RotationSegment.convertToDirection inline crates/horizon-world/src/level/place.rs::Sim::placement_class ceiling hanging sign placement maps the above sign's segment 0/4/8/12 to a direction inline +SideChainPart.whenDisconnectedFromTheRight port crates/horizon-world/src/level/shelf.rs::Part::disconnected_from_the_right same table +SideChainPart.whenDisconnectedFromTheLeft port crates/horizon-world/src/level/shelf.rs::Part::disconnected_from_the_left same table +BlockScanUtils.sectionBasedScan inline crates/horizon-world/src/level/timeline.rs::box_scan_order gap: minor: only the visiting order; no section skip, no abort, no virtual AIR outside the world bounds +BlockScanUtils.findBlocksInSection inline crates/horizon-client/src/light.rs::light_chunk gap: minor: section loop only for light emitters; no generic predicate and consumer +BlockScanUtils.findBlocksWithCache inline crates/horizon-client/src/mobs/world.rs::MobWorld::nearest_block gap: minor: first match in Manhattan box order, used by mob goals; explicit position lists are not generalised +WorldBorder.getDistanceToBorder port crates/horizon-core/src/geometry/border.rs::BorderGeometry::distance_to_border - +WorldBorder.lerpSizeBetween port crates/horizon-client/src/worldborder.rs::WorldBorder::lerp_between - +WorldBorder.setSafeZone port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_worldborder plain field write in the /worldborder damage buffer arm; no listener notification +WorldBorder.setDamagePerBlock port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_worldborder plain field write in the /worldborder damage amount arm; no listener notification +WorldBorder.setWarningTime port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_worldborder plain field write in the /worldborder warning time arm; no listener notification +WorldBorder.setWarningBlocks port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_worldborder plain field write in the /worldborder warning distance arm; no listener notification +WorldBorder.applyInitialSettings inline crates/horizon-client/src/worldborder.rs::WorldBorder::unpack saved settings restored on every load; no initialized flag +WorldBorder.MovingBorderExtent.getMinX port crates/horizon-core/src/geometry/border.rs::BorderGeometry::bounds bounds returns all four edges +WorldBorder.MovingBorderExtent.getMinZ port crates/horizon-core/src/geometry/border.rs::BorderGeometry::bounds bounds returns all four edges +WorldBorder.MovingBorderExtent.getMaxX port crates/horizon-core/src/geometry/border.rs::BorderGeometry::bounds bounds returns all four edges +WorldBorder.MovingBorderExtent.getMaxZ port crates/horizon-core/src/geometry/border.rs::BorderGeometry::bounds bounds returns all four edges +WorldBorder.MovingBorderExtent.calculateSize port crates/horizon-core/src/geometry/border.rs::BorderGeometry::moving_size - +WorldBorder.MovingBorderExtent.getLerpSpeed port crates/horizon-core/src/geometry/border.rs::BorderGeometry::lerp_speed - +WorldBorder.MovingBorderExtent.update port crates/horizon-core/src/geometry/border.rs::BorderGeometry::tick - +WorldBorder.MovingBorderExtent.getCollisionShape port crates/horizon-core/src/geometry/border.rs::BorderGeometry::collision_shape - +WorldBorder.StaticBorderExtent.updateBox port crates/horizon-core/src/geometry/border.rs::BorderGeometry::stationary computes the static bounds and collision shape +CarvingMask.getIndex inline crates/horizon-core/src/carving.rs::CarvingMask::carve index formula is computed inside carve +CarvingMask.visit port crates/horizon-core/src/carving.rs::CarvingMask::try_visit_mut - +CarvingMask.visitSegment port crates/horizon-core/src/carving.rs::Cursor::next splits runs into per-column bottom and top +ChunkAccess.getHighestSectionPosition port crates/horizon-world/src/worldgen/terrain.rs::TerrainChunk::highest_section_y - +ChunkAccess.replaceMissingSections skip:java - sections are allocated up front, so there are no null slots to fill +ChunkAccess.getBlockEntitiesPos inline crates/horizon-client/src/save.rs::chunks_with_block_entities gap: minor: block entity positions are read from the chunk's list when saving; no separate position set +ChunkAccess.getOrCreateHeightmapUnprimed port crates/horizon-world/src/worldgen/terrain.rs::TerrainChunk::ensure_heightmap - +ChunkAccess.getHeight port crates/horizon-world/src/heightmap.rs::Heightmap::highest_taken - +ChunkAccess.addReferenceForStructure inline crates/horizon-world/src/worldgen/generator_structures.rs::StructureState::references references are derived from the starts on each call; nothing is stored per chunk +ChunkAccess.isYSpaceEmpty skip:java - only a shortcut for PathNavigationRegion's all-air check; the block reads themselves are unchanged +ChunkAccess.setBlockEntityNbt port crates/horizon-world/src/worldgen/generator_decoration.rs::World::set_block_entity gap: minor: replaces any entry at the position; vanilla keeps a live block entity and stores NBT only as pending +ChunkAccess.findBlockLightSources port crates/horizon-client/src/light.rs::light_chunk light emitters are seeded from the palette in one pass over the region +ChunkAccess.findBlocks inline crates/horizon-client/src/light.rs::light_chunk gap: minor: only the light-emitter predicate; no generic predicate and consumer +ChunkAccess.collectBiomesInPalette port crates/horizon-world/src/worldgen/biome_chunk.rs::BiomeRegion::present_in_chunk one chunk at a time, sorted; the caller gathers the region +ChunkAccess.carverBiome port crates/horizon-world/src/worldgen/generator.rs::Pipeline::carver_biome - +ChunkAccess.fillBiomesFromNoise port crates/horizon-world/src/worldgen/biome_chunk.rs::BiomeChunk::fill - +ChunkAccess.getOrCreateOffsetList inline crates/horizon-world/src/worldgen/terrain.rs::TerrainChunk::mark_postprocessing lists exist for every section up front +ChunkGenerator.createBiomes skip:java - async wrapper around doCreateBiomes (CompletableFuture) +ChunkGenerator.doCreateBiomes port crates/horizon-world/src/worldgen/generator.rs::Pipeline::biome_chunk biomes come from the cached per-chunk biome build +ChunkGenerator.getStructureGeneratingAt port crates/horizon-client/src/vanillagen.rs::VanillaTerrain::locate nearest start search for /locate, via the generator worker +ChunkGenerator.applyBiomeDecoration port crates/horizon-world/src/worldgen/generator_decoration.rs::Decoration::decorate biome feature placement; structure starts are placed in structure/start.rs +ChunkGenerator.tryGenerateStructure port crates/horizon-world/src/worldgen/structure/start.rs::try_generate - +ChunkGenerator.fetchReferences missing - minor: unsure: new starts begin at 0 references (start.rs); a previous start's reference count is not read +ChunkGeneratorStructureState.createForFlat missing - unsure: no superflat or flat-preset structure path found in worldgen +ChunkGeneratorStructureState.createForNormal port crates/horizon-world/src/worldgen/generator_structures.rs::StructureState::new - +ChunkGeneratorStructureState.hasBiomesForStructureSet port crates/horizon-world/src/worldgen/generator_structures.rs::StructureState::new keeps the sets with a structure in a possible biome +ChunkGeneratorStructureState.getRingPositionsFor port crates/horizon-world/src/worldgen/generator_structures.rs::StructureState::ring_positions - +DataLayer.set port crates/horizon-client/src/light.rs::set_level writes one nibble of the packed light byte +DataLayer.packFilled inline crates/horizon-client/src/light.rs::pack_lighting uniform layers are stored as one packed byte (0xF0 or 0) +DataLayer.getData port crates/horizon-protocol/src/chunk.rs::LightData::layers - +DataLayer.copy skip:java - clone of the layer data +DataLayer.toString skip:debug - debug dump of the layer +DataLayer.layerToString skip:debug - debug dump of the layer +DataLayer.isDefinitelyFilledWith skip:java - homogeneity shortcut; Horizon keeps uniform light as one byte +LevelChunk. port crates/horizon-client/src/chunk.rs::Chunk::empty gap: minor: builds an empty chunk only; loading saved data is in save.rs +LevelChunk.setUnsavedListener skip:java - save-listener hook; Horizon tracks dirty sections instead +LevelChunk.markUnsaved skip:other - save bookkeeping only; Horizon tracks dirty sections +LevelChunk.getTicksForSerialization port crates/horizon-client/src/save.rs::tick_list serializes the pending ticks +LevelChunk.getListenerRegistry skip:java - per-section game-event registry lookup; Horizon dispatches game events by position (update.rs game_event) +LevelChunk.createBlockEntity port crates/horizon-client/src/containers.rs::BlockEntity::for_block - +LevelChunk.getBlockEntity skip:other - World.block_entities is read directly by the game (game.rs:764, 4000); no lookup function +LevelChunk.addAndRegisterBlockEntity skip:other - World.block_entities is written directly (game.rs:1134); no registration step +LevelChunk.isInLevel port crates/horizon-world/src/level/update.rs::Sim::is_in_valid_bounds - +LevelChunk.getBlockEntityNbtForSaving port crates/horizon-client/src/save.rs::encode_block_entity - +LevelChunk.removeGameEventListener skip:java - listener registry bookkeeping; game events dispatch by position +LevelChunk.removeBlockEntityTicker skip:java - tickers are rebuilt from block state (block_entities.rs tickers) +LevelChunk.runPostLoad missing - minor: unsure: no post-load step for block entities found +LevelChunk.replaceBiomes port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_fillbiome the /fillbiome command replaces the biomes of a region +LevelChunk.postProcessGeneration missing - player: unsure: post-generation fixups (fluids, block shapes) may not run when a chunk goes live; not confirmed +LevelChunk.promotePendingBlockEntity skip:java - pending NBT slot; Horizon decodes block entities when they load (save.rs decode_block_entity) +LevelChunk.unregisterTickContainerFromLevel skip:java - tick container bookkeeping; scheduled ticks live in level/update.rs +LevelChunk.registerDebugValues skip:debug - debug values only +LevelChunk.clearAllBlockEntities missing - minor: unsure: no unload step that clears a chunk's block entities was confirmed +LevelChunk.registerAllBlockEntitiesAfterLevelLoad skip:java - ticker registration after load; Horizon ticks the block entities it holds +LevelChunk.addGameEventListener skip:java - listener registry; game events dispatch by position +LevelChunk.updateBlockEntityTicker skip:java - ticker bookkeeping; tickers are rebuilt from block state (block_entities.rs tickers) +LevelChunk.BoundTickingBlockEntity.tick port crates/horizon-client/src/game/block_entity_ticks.rs::Game::tick_block_entities ticks every loaded block entity in one pass +LevelChunkSection.recalcBlockCounts missing - minor: unsure: no recount of block counts found; non-air counts are kept while writing +LevelChunkSection.read port crates/horizon-protocol/src/chunk.rs::Section::read reads block states and biomes together +LevelChunkSection.readBiomes inline crates/horizon-protocol/src/chunk.rs::Section::read - +LevelChunkSection.fillBiomesFromNoise port crates/horizon-world/src/worldgen/biome_chunk.rs::BiomeChunk::fill - +PalettedContainer.codecRO skip:java - codec +PalettedContainer.codec skip:java - codec +PalettedContainer.createOrReuseData inline crates/horizon-core/src/paletted.rs::PalettedContainer::resize storage and palette are built inside resize +PalettedContainer.getAndSet port crates/horizon-core/src/paletted.rs::PalettedContainer::set set returns the previous value +PalettedContainer.set port crates/horizon-core/src/paletted.rs::PalettedContainer::set - +PalettedContainer.get port crates/horizon-core/src/paletted.rs::PalettedContainer::get - +PalettedContainer.getAll missing - minor: no Horizon caller needs the distinct values; has() answers predicate queries +PalettedContainer.reencodeContents inline crates/horizon-core/src/paletted.rs::PalettedContainer::resize palette ids are remapped inside resize +PalettedContainer.forEachInPalette inline crates/horizon-world/src/worldgen/biome_chunk.rs::BiomeRegion::present_in_chunk gap: minor: no palette walk; collects distinct stored values across the sections +PalettedContainer.recreate port crates/horizon-core/src/paletted.rs::PalettedContainer::new new container with the same strategy holding one value +PalettedContainer.count missing - minor: unsure: per-value counts for recalcBlockCounts not found +PalettedContainer.Data.copyFrom inline crates/horizon-core/src/paletted.rs::PalettedContainer::resize copies the old values into the new storage +PalettedContainer.Data.getSerializedSize skip:java - byte count for the Java packet buffer; Horizon writes the wire form via to_wire +PalettedContainer.Data.copy skip:java - clone of the storage and palette +ProtoChunk.getTicksForSerialization port crates/horizon-world/src/worldgen/terrain.rs::TerrainChunk::block_ticks gap: minor: returns the tick lists; no packing for serialization +ProtoChunk.setBlockEntity port crates/horizon-world/src/worldgen/generator_decoration.rs::World::set_block_entity - +ProtoChunk.addEntity port crates/horizon-world/src/worldgen/generator_decoration.rs::World::add_entity - +ProtoChunk.setStartForStructure missing - minor: unsure: no below-zero retrogen bounds check on structure starts found +ProtoChunk.setPersistedStatus missing - minor: unsure: no setter for a proto chunk's persisted status found +ProtoChunk.packOffsetCoordinates inline crates/horizon-world/src/worldgen/terrain.rs::TerrainChunk::mark_postprocessing the packed x|y|z offset is built inline +ProtoChunk.unpackOffsetCoordinates missing - minor: unsure: no replay of stored post-process offsets found +ProtoChunk.markPosForPostProcessing port crates/horizon-world/src/worldgen/terrain.rs::TerrainChunk::mark_postprocessing - +ProtoChunk.getBlockEntityNbtForSaving missing - minor: unsure: no proto chunk block entity NBT save step found +ProtoChunk.removeBlockEntity inline crates/horizon-world/src/worldgen/generator_decoration.rs::World::set_block entry is dropped when the block at the position changes +Strategy.getIndex port crates/horizon-client/src/chunk.rs::index block form (y<<8 | z<<4 | x); the biome form is biome_chunk.rs::index +ChunkStatus.register skip:data - status registry entry +ChunkStatus.getStatusList skip:data - status list is the ALL constant in chunk_status.rs +EntityStorage.storeEntities port crates/horizon-client/src/save.rs::SaveDir::save_entities - +EntityStorage.reportSaveFailureIfPresent skip:other - error reporting plumbing; failures come back as Result from save_entities +EntityStorage.reportLoadFailureIfPresent skip:other - error reporting plumbing; failures come back as Result from load_entities +IOWorker.isOldChunkAround missing - minor: unsure: no old-chunk scan around a position found +IOWorker.getOrCreateOldDataForRegion skip:java - cache of old-chunk data per region +IOWorker.createOldDataForRegion skip:java - cache of old-chunk data per region +IOWorker.isOldChunk missing - minor: unsure: same old-chunk check as isOldChunkAround; not found +IOWorker.store port crates/horizon-client/src/save.rs::write_region_records - +IOWorker.loadAsync port crates/horizon-client/src/streaming.rs::ChunkGenerator::load gap: minor: the saved-chunk hook only; the disk read is in save.rs +IOWorker.synchronize skip:java - waits for queued async writes; Horizon's save takes a wait flag (streaming.rs Streaming::save) +IOWorker.scanChunk missing - minor: unsure: no chunk scan step found +IOWorker.submitThrowingTask skip:java - Java future wrapper around submitTask +IOWorker.submitTask port crates/horizon-client/src/streaming.rs::Streaming::submit - +IOWorker.storePendingChunk skip:java - pending-write map of the Java worker +IOWorker.tellStorePending skip:java - pending-write counter of the Java worker +IOWorker.runStore skip:java - Java writer thread loop +IOWorker.close skip:java - closes the Java worker thread and file handles +IOWorker.waitForShutdown skip:java - Java thread join +RegionFile. port crates/horizon-world/src/region.rs::RegionFile::from_file - +RegionFile.getExternalChunkPath port crates/horizon-world/src/region.rs::external_path - +RegionFile.getChunkDataInputStream port crates/horizon-world/src/region.rs::RegionFile::read - +RegionFile.createChunkInputStream inline crates/horizon-world/src/region.rs::RegionFile::read - +RegionFile.createExternalChunkInputStream inline crates/horizon-world/src/region.rs::RegionFile::read - +RegionFile.doesChunkExist port crates/horizon-world/src/region.rs::RegionFile::read existence is a read that returns None when the chunk is absent +RegionFile.getChunkDataOutputStream skip:java - Java stream factory; RegionFile::write takes the record directly +RegionFile.createExternalStub inline crates/horizon-world/src/region.rs::RegionFile::write - +RegionFile.writeToExternalFile inline crates/horizon-world/src/region.rs::RegionFile::write - +RegionFile.writeHeader port crates/horizon-world/src/region.rs::RegionFile::write_header_entry - +RegionFile.close skip:java - file handle is released when the RegionFile is dropped +RegionFile.padToFullSector inline crates/horizon-world/src/region.rs::RegionFile::write last sector is padded with zeros +RegionFile.ChunkBuffer.close skip:java - Java stream close diff --git a/tools/port_map/game2-020.tsv b/tools/port_map/game2-020.tsv new file mode 100644 index 0000000000..a408711594 --- /dev/null +++ b/tools/port_map/game2-020.tsv @@ -0,0 +1,150 @@ +SectionStorage.unpackPendingLoads missing minor: POI region files are written but never read back; POI come from blocks (poi_sections.rs), so saved POI claims are not restored +SectionStorage.flushAll inline crates/horizon-client/src/game.rs::Game::save_level saves modified chunks with their POI and flushes the writer +SectionStorage.getOrLoad port crates/horizon-client/src/poi_sections.rs::PoiSections::get gap: minor: the section's POI list is derived from its blocks, not loaded from the POI file +SectionStorage.getOrCreate skip:other no POI record to create; PoiSections::get derives each section's list from its blocks +SectionStorage.prefetch skip:java async prefetch of POI files; nothing is loaded from disk, so there is nothing to prefetch +SectionStorage.unpackChunk missing minor: POI region files are never read back; POI come from blocks (poi_sections.rs), so saved POI claims are not restored +SectionStorage.tryRead missing minor: POI region files are never read back; POI come from blocks (poi_sections.rs), so saved POI claims are not restored +SectionStorage.writeChunk port crates/horizon-client/src/save.rs::SaveDir::save_poi queues the chunk's POI records to the region writer; claims kept by resolve_poi +SectionStorage.setDirty inline crates/horizon-client/src/game.rs::Game::update_world the chunk modified list marks chunks to save together with their POI +SectionStorage.PackedChunk.parse missing minor: POI region files are never read back, so POI ticket claims are not restored on load +SerializableChunkData.parse port crates/horizon-client/src/save.rs::decode_column gap: minor: entity tags are read by Persistent::load (load_entities), not here +SerializableChunkData.logErrors skip:debug logs load problems; Horizon warns with log::warn where a problem is found +SerializableChunkData.copyOf skip:java snapshot copy for the async writer; Horizon shares the chunk as Arc in Record::Chunk +SerializableChunkData.saveTicks port crates/horizon-client/src/save.rs::tick_list builds the block_ticks and fluid_ticks tags +SerializableChunkData.getChunkStatusFromTag inline crates/horizon-client/src/save.rs::decode_with gap: minor: decode_with only checks that Status is full and does not return the status; partial statuses go to decode_proto +SerializableChunkData.postLoadChunk inline crates/horizon-client/src/save.rs::decode_column gap: minor: decodes the block entities here; entity tags are attached by Persistent::load +SerializableChunkData.packStructureData inline crates/horizon-client/src/save.rs::encode_with writes structures.starts and structures.References +SerializableChunkData.unpackStructureStart skip:other starts are written but not read back; worldgen regenerates them from the seed (vanillagen.rs) +SerializableChunkData.unpackStructureReferences inline crates/horizon-client/src/save.rs::decode_column gap: minor: reads the references but does not drop ones more than 8 chunks away +SerializableChunkData.packOffsets inline crates/horizon-client/src/save.rs::encode_proto packs the PostProcessing marks per section +SimpleRegionStorage.write port crates/horizon-client/src/save.rs::SaveDir::queue queues the record for the region writer thread +DimensionType. skip:data validates a dimension type's numbers when datapack data loads; Horizon uses fixed dimension data +DimensionType.createDirectCodec skip:java codec for the dimension type JSON +EnderDragonFight.createDefault inline crates/horizon-client/src/game/dragon.rs::Game::load_fight the defaults load_fight uses when no saved fight exists (scan on, no gateways yet) +EnderDragonFight. inline crates/horizon-client/src/game/dragon.rs::Game::load_fight reads the saved fight fields +EnderDragonFight.init inline crates/horizon-client/src/game/dragon.rs::Game::load_fight gap: minor: the gateway shuffle is here; the boss bar object and player predicate are fight_has_players and dragon_boss_progress +EnderDragonFight.scanState inline crates/horizon-client/src/game/dragon.rs::Game::tick_fight gap: minor: sets previously_killed from end_portal_active; does not spawn the inactive portal or discard a stray dragon +EnderDragonFight.findOrCreateDragon inline crates/horizon-client/src/game/dragon.rs::Game::tick_fight gap: minor: no lookup for an existing dragon; Horizon holds one fight dragon +EnderDragonFight.setRespawnStage port crates/horizon-client/src/game/dragon.rs::Game::set_respawn gap: minor: the END stage (new dragon, summoned-entity trigger) is not set here; finish_respawn ends the respawn and the trigger is not fired +EnderDragonFight.hasActiveExitPortal port crates/horizon-client/src/game/dragon.rs::Game::end_portal_active gap: minor: checks one column at the origin for bedrock and portal blocks, not portal block entities over 17x17 chunks +EnderDragonFight.findExitPortal port crates/horizon-client/src/game/dragon.rs::Game::exit_portal gap: minor: returns the remembered portal origin; there is no pattern search of the chunks +EnderDragonFight.isArenaLoaded port crates/horizon-client/src/game/dragon.rs::Game::arena_loaded gap: minor: checks a 4x4 block of lit chunks, not full status plus loaded entities +EnderDragonFight.updatePlayers port crates/horizon-client/src/game/dragon.rs::Game::fight_has_players gap: minor: the boss bar's player set is not kept; dragon_boss_progress checks each player's distance +EnderDragonFight.updateCrystalCount port crates/horizon-client/src/game/dragon.rs::Game::count_crystals counts crystals on the spike tops +EnderDragonFight.spawnNewGateway inline crates/horizon-client/src/game/dragon.rs::Game::dragon_killed gap: minor: pops the next gateway and its position; placing it is place_gateway_at (end.rs) +EnderDragonFight.spawnExitPortal port crates/horizon-client/src/game/end.rs::Game::activate_exit_portal gap: minor: places the active podium only; try_respawn places the inactive one +EnderDragonFight.createNewDragon inline crates/horizon-client/src/game/dragon.rs::Game::tick_fight creates the dragon over the podium with a random yaw; Dragon::new builds it +EnderDragonFight.updateDragon port crates/horizon-client/src/game/dragon.rs::Game::dragon_boss_progress gap: minor: returns the health fraction for the bar; the dragon's custom name is not used +EnderDragonFight.abortRespawnSequence port crates/horizon-client/src/game/dragon.rs::Game::abort_respawn clears the respawn, resets the spike crystals and reactivates the portal +EnderDragonFight.respawnDragon port crates/horizon-client/src/game/dragon.rs::Game::try_respawn turns the portal bedrock back to end stone, takes the crystals and starts the respawn +EnderDragonFight.resetSpikeCrystals port crates/horizon-client/src/game/dragon.rs::Game::reset_spike_crystals makes spike crystals vulnerable again and clears their beams +Visibility.fromFullChunkStatus missing unsure: Horizon has entity_ticking checks in ticket_levels.rs but no TICKING/TRACKED/HIDDEN split; not clear whether entities in loaded, non-ticking chunks stay visible +GameEvent.register skip:data registers a game event with its default radius; Horizon reads events from fixed tables (notification_radius) +GameEventDispatcher.post port crates/horizon-client/src/game/sculk.rs::Game::post_game_event delivers the event to sensors, shriekers and wardens within the radius +GameEventDispatcher.handleGameEventMessagesInQueue inline crates/horizon-client/src/game/sculk.rs::Game::post_game_event gap: minor: no sort by distance before delivery; the selector keeps the nearest candidate, which only differs on ties +VibrationSystem.Listener.forceScheduleVibration port crates/horizon-client/src/game/sculk.rs::Game::sculk_stand_on a step on a sensor schedules the step event after the sensor's own checks +VibrationSystem.Listener.scheduleVibration port crates/horizon-client/src/sculk.rs::Data::schedule adds the candidate to the selector; the distance is computed by sculk_listen +VibrationSystem.Ticker.trySelectAndScheduleVibration port crates/horizon-client/src/sculk.rs::Data::tick gap: minor: picks the chosen candidate and travel time; the particle is sent by tick_sculk_block +VibrationSystem.Ticker.tryReloadVibrationParticle missing minor: the travelling vibration particle is sent once when the trip starts; vanilla resends it until a player receives it, which matters only for players who arrive mid-trip +VibrationSystem.Ticker.receiveVibration port crates/horizon-client/src/game/sculk.rs::Game::sculk_receive gap: minor: the adjacent-chunk hold-back is in Data::tick, not here +VibrationSystem.Ticker.areAdjacentChunksTicking inline crates/horizon-client/src/game/sculk.rs::Game::tick_sculk_blocks computes the adjacent flag per sensor from the 3x3 block-ticking chunks +GameRules.codec skip:java codec for the saved game rules; to_nbt and from_nbt do the read and write +GameRules.get port crates/horizon-client-core/src/gamerules.rs::GameRules::get returns the rule's value, or None for an unknown rule +GameRules.set port crates/horizon-client-core/src/gamerules.rs::GameRules::set gap: minor: the change is not sent to connected clients; GameRuleValues goes out only on a client's request +GameRules.setAll skip:debug only the gametest framework copies whole rule sets +GameRules.visitGameRuleTypes skip:other visits rules to build brigadier argument types; horizon-command builds its own command tree +GameRules.registerBoolean skip:data registry registration of a boolean rule; Horizon's rules are a fixed table +GameRules.registerInteger skip:data registry registration of an integer rule; Horizon's rules are a fixed table +GameRules.register skip:data registry registration; Horizon's rules are a fixed table +blockpredicates/BlockPredicate.allOf skip:java builder for a predicate; Horizon decodes predicate JSON into its enum (decode_with_blocks) +blockpredicates/BlockPredicate.anyOf skip:java builder for a predicate; Horizon decodes predicate JSON into its enum (decode_with_blocks) +blockpredicates/BlockPredicate.matchesBlocks skip:java builder for a predicate; Horizon decodes predicate JSON into its enum (decode_with_blocks) +blockpredicates/BlockPredicate.matchesTag skip:java builder for a predicate; Horizon decodes predicate JSON into its enum (decode_with_blocks) +blockpredicates/BlockPredicate.matchesFluids skip:java builder for a predicate; Horizon decodes predicate JSON into its enum (decode_with_blocks) +blockpredicates/BlockPredicate.hasSturdyFace skip:java builder for a predicate; Horizon decodes predicate JSON into its enum (decode_with_blocks) +blockpredicates/BlockPredicate.solid skip:java builder for a predicate; Horizon decodes predicate JSON into its enum (decode_with_blocks) +WorldCarver.canReach port crates/horizon-core/src/carving/ellipsoid.rs::can_reach the same reach test for a carver step +DensityVolume.indexUnchecked port crates/horizon-core/src/noise/volume.rs::DensityVolume::index same index formula; Horizon's version is bounds-checked +DensityVolume.maxBlockX port crates/horizon-core/src/noise/volume.rs::DensityVolume::maximum the x component of maximum +DensityVolume.maxBlockY port crates/horizon-core/src/noise/volume.rs::DensityVolume::maximum the y component of maximum +DensityVolume.maxBlockZ port crates/horizon-core/src/noise/volume.rs::DensityVolume::maximum the z component of maximum +DensityVolume.intersects port crates/horizon-core/src/density/beardifier.rs::StructureBounds::intersects the box test Beardifier uses against a volume +DensityVolume.containsBlockRelative inline crates/horizon-core/src/noise/volume.rs::DensityVolume::index_of_block the range and step checks +DistanceMetric.compute port crates/horizon-core/src/density/terrain_noise.rs::DistanceMetric::compute3 the 3D overload; compute2 is the 2D one +SamplerContext.getCacheCell port crates/horizon-core/src/density/cache.rs::SamplerContext::prepare_cache checks and grows the cache slots for a cache id +BlockColumnFeature.layer skip:java builder for a column layer; decode reads the layers from JSON +BlockColumnFeature.simple skip:java builder for a one-layer column; decode reads the feature from JSON +BlockPileFeature.mayPlaceOn inline crates/horizon-world/src/worldgen/block_pile.rs::BlockPileFeature::try_place the support test: a path below is a coin flip, otherwise a sturdy top face +EndIslandFeature.place port crates/horizon-client/src/game/end.rs::Game::place_end_island the end stone blob with a seed from the position +EndPodiumFeature.dropPreviousAndSetBlock port crates/horizon-world/src/worldgen/end_podium.rs::EndPodiumFeature::replace breaks the old block and sets the new one when they differ +EndSpikeFeature.EndSpike.isCenterWithinChunk inline crates/horizon-world/src/worldgen/end_spike.rs::EndSpikeFeature::place the chunk check of each spike's centre +EndSpikeFeature.SpikeCacheLoader.load port crates/horizon-world/src/worldgen/end_spike.rs::spikes_for_seed the spike layout for a seed +FallenTreeFeature.placeFallenTree port crates/horizon-world/src/worldgen/tree/fallen.rs::FallenTreeFeature::place stump, log and decorators +FallenTreeFeature.setGroundHeightForFallenLogStartPos inline crates/horizon-world/src/worldgen/tree/fallen.rs::FallenTreeFeature::place the search down for ground under the log start +FallenTreeFeature.placeFallenLog port crates/horizon-world/src/worldgen/tree/fallen.rs::FallenTreeFeature::place_log gap: minor: places one log per call; the loop over the length and the log decorators are in place +FallenTreeFeature.Builder.build skip:java builder for the feature +FossilFeature.countEmptyCorners inline crates/horizon-world/src/worldgen/structure/features.rs::FossilFeature::place counts empty corners against max_empty_corners_allowed +HugeFungusFeature.isReplaceable port crates/horizon-world/src/worldgen/fungus.rs::HugeFungusFeature::replaceable replaceable blocks, plus the plant test when asked +HugeFungusFeature.placeHatDropBlock port crates/horizon-world/src/worldgen/fungus.rs::HugeFungusFeature::hat_drop the hat drop roll and the optional weeping vines +IcebergFeature.removeFloatingSnowLayer inline crates/horizon-world/src/worldgen/iceberg.rs::IcebergFeature::carve removes the snow above a carved iceberg block +IcebergFeature.setIcebergBlock port crates/horizon-world/src/worldgen/iceberg.rs::IcebergFeature::generate_block sets ice, snow or the water block for one position +IcebergFeature.getEllipseC inline crates/horizon-world/src/worldgen/iceberg.rs::IcebergFeature::generate_block the ellipse c adjustment near the top +IcebergFeature.signedDistanceCircle inline crates/horizon-world/src/worldgen/iceberg.rs::IcebergFeature::generate_block the circle branch of the signed distance +IcebergFeature.signedDistanceEllipse port crates/horizon-world/src/worldgen/iceberg.rs::signed_ellipse the rotated ellipse distance +IcebergFeature.heightDependentRadiusEllipse inline crates/horizon-world/src/worldgen/iceberg.rs::IcebergFeature::place the ellipse radius per height, in the ellipse branch of place +IcebergFeature.isIcebergState port crates/horizon-world/src/worldgen/iceberg.rs::IcebergFeature::iceberg_state packed ice, snow block or blue ice +LargeDripstoneFeature.makeDripstone inline crates/horizon-world/src/worldgen/dripstone.rs::LargeDripstoneFeature::place samples bluntness and height scale and builds the column +LargeDripstoneFeature.placeDebugMarkers skip:debug debug markers (diamond, gold and creeper head blocks) for the wind offset +LargeDripstoneFeature.LargeDripstone.isSuitableForWind inline crates/horizon-world/src/worldgen/dripstone.rs::LargeDripstoneFeature::place the radius and bluntness test that enables wind +MultifaceGrowthFeature.validateBlock skip:java codec check that the block is a multiface block +MultifaceGrowthFeature.validDirections port crates/horizon-world/src/worldgen/vegetation.rs::MultifaceGrowthFeature::decode the directions from the ceiling, floor and horizontal flags, built once at decode +MultifaceGrowthFeature.getShuffledDirectionsExcept inline crates/horizon-world/src/worldgen/vegetation.rs::MultifaceGrowthFeature::place the shuffled list without the excluded direction +OverlayFeature.getSubFeatures skip:data lists sub-features for datagen and inspection; placement does not use it +OverlayFeature.place inline crates/horizon-world/src/worldgen/feature_graph.rs::FeatureDefinition::evaluate places each overlaid feature in turn and reports whether any placed +ReplaceBlobsFeature.findTarget inline crates/horizon-world/src/worldgen/replace_blobs.rs::ReplaceBlobsFeature::place the walk down from the origin to the first target block +RootSystemFeature.isAllowedTreeSpace port crates/horizon-world/src/worldgen/root_system.rs::RootSystemFeature::space_for_tree air, or water low enough, above the origin for the tree +RootSystemFeature.placeDirt inline crates/horizon-world/src/worldgen/root_system.rs::RootSystemFeature::dirt_and_tree the rooted dirt column up to the target height +RootSystemFeature.placeRoots port crates/horizon-world/src/worldgen/root_system.rs::RootSystemFeature::hanging_roots the hanging roots below the canopy +ScatteredOreFeature.offsetTargetPos inline crates/horizon-world/src/worldgen/ore_feature.rs::ScatteredOreFeature::place the random offset per axis, rounded as Math.round +SpeleothemClusterFeature.canBeAdjacentToWater port crates/horizon-world/src/worldgen/dripstone.rs::SpeleothemClusterFeature::adjacent_ok base stone or water next to the cluster +SpeleothemClusterFeature.replaceBlocksWithBaseBlocks port crates/horizon-world/src/worldgen/dripstone.rs::SpeleothemClusterFeature::replace_with_base turns a run of replaceable blocks into the base block +SpeleothemClusterFeature.getChanceOfStalagmiteOrStalactite inline crates/horizon-world/src/worldgen/dripstone.rs::SpeleothemClusterFeature::place the chance from the distance to the cluster edge +SpeleothemFeature.getTipDirection inline crates/horizon-world/src/worldgen/speleothem.rs::SpeleothemFeature::place picks up or down from the base blocks above and below +SpeleothemFeature.createPatchOfBaseBlocks inline crates/horizon-world/src/worldgen/speleothem.rs::SpeleothemFeature::place the base block patch with its directional spread +SpeleothemUtils.isCircleMostlyEmbeddedInStone port crates/horizon-world/src/worldgen/dripstone.rs::LargeDripstoneFeature::circle_embedded the ring of the circle that must be inside stone +SpeleothemUtils.buildBaseToTipColumn port crates/horizon-world/src/worldgen/dripstone.rs::Speleothem::grow builds the base, middle, frustum and tip blocks in order +SpeleothemUtils.placeBaseBlockIfPossible port crates/horizon-world/src/worldgen/dripstone.rs::Speleothem::place_base_if_possible sets the base block where a replaceable block is +SpeleothemUtils.createPointedBlock port crates/horizon-world/src/worldgen/dripstone.rs::Speleothem::pointed_state the pointed block for a direction and thickness +SpeleothemUtils.isNeitherEmptyNorWater port crates/horizon-world/src/worldgen/dripstone.rs::Speleothem::empty_or_water the negation of empty_or_water +TemplateFeature.simple skip:java builder for a single-template feature; decode reads it from JSON +TemplateFeature.getRotatedOffset inline crates/horizon-world/src/worldgen/structure/features.rs::TemplateFeature::place the rotated half-size offset of the template +TreeFeature.isAirOrLeaves inline crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::dark_oak the air-or-leaves test before a dark oak log +UnderwaterMagmaFeature.getFloorY port crates/horizon-world/src/worldgen/underwater_magma.rs::UnderwaterMagmaFeature::scan scans down through water to the floor +UnderwaterMagmaFeature.isValidPlacement port crates/horizon-world/src/worldgen/underwater_magma.rs::UnderwaterMagmaFeature::valid not in water or air, covered below and hidden from the sides +UnderwaterMagmaFeature.isWaterOrAir inline crates/horizon-world/src/worldgen/underwater_magma.rs::UnderwaterMagmaFeature::valid the water-or-air rejection at the start +UnderwaterMagmaFeature.isVisibleFromOutside port crates/horizon-world/src/worldgen/underwater_magma.rs::UnderwaterMagmaFeature::occluded the face occlusion test +VegetationPatchFeature.distributeVegetation inline crates/horizon-world/src/worldgen/vegetation.rs::VegetationPatchFeature::place the chance loop over the surface positions +WaterloggedVegetationPatchFeature.placeGroundPatch inline crates/horizon-world/src/worldgen/vegetation.rs::VegetationPatchFeature::place the ground patch (ground_patch) and then the water conversion of the surface +WaterloggedVegetationPatchFeature.isExposed inline crates/horizon-world/src/worldgen/vegetation.rs::VegetationPatchFeature::place the exposed test over the four sides and below +WaterloggedVegetationPatchFeature.isExposedDirection inline crates/horizon-world/src/worldgen/vegetation.rs::VegetationPatchFeature::place the exposed test for one face +FeatureSize.minClippedHeightCodec skip:java codec for min_clipped_height +FoliagePlacer.foliagePlacerParts skip:java codec fields for radius and offset +FoliagePlacer.shouldSkipLocationSigned port crates/horizon-world/src/worldgen/tree/foliage.rs::FoliagePlacer::skip_signed the skip test for a leaf position, with the double trunk case +RootPlacer.getPotentiallyWaterloggedState port crates/horizon-world/src/worldgen/tree.rs::RootPlacer::waterlogged sets waterlogged from the water at the position +BlockStateProvider.of skip:java wraps one state as a provider; decode builds providers from JSON +NoiseProvider.noiseProviderCodec skip:java codec fields for seed, noise, scale and states +NoiseProvider.getRandomState port crates/horizon-world/src/worldgen/state_provider/noise.rs::index maps the noise value to an index in the states +TreeDecorator.Context.placeVine port crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::place_vine places a vine on the given face +TreeDecorator.Context.isWaterOrWaterNearby inline crates/horizon-world/src/worldgen/tree/decorator.rs::Decorate::mushroom_replaceable the water test over the centre and four sides +TrunkPlacer.placeBelowTrunkBlock port crates/horizon-world/src/worldgen/tree/trunk.rs::TrunkPlacer::place_below_trunk places the block below the trunk when the provider gives one +FlatLevelGeneratorSettings.validateHeight skip:java codec check that layers total at most 4064; flat.rs clamps each layer to that instead +FlatLevelGeneratorSettings.withBiomeAndLayers port crates/horizon-client/src/title/flat.rs::FlatSettings::parse gap: minor: builds the layers and biome from the preset text; structure overrides and decorations come from the base +FlatLevelGeneratorSettings.adjustGenerationSettings missing player: superflat layers and decorations are saved with the world but no code applies them, so the generated world does not get the flat layers or its decorations +FlatLevelGeneratorSettings.updateLayers missing player: nothing expands the flat layers into the world's block layers (see adjustGenerationSettings) +MaterialRuleContext.getBiome port crates/horizon-world/src/worldgen/material_condition.rs::Context::biome the biome at the position, cached per column +MaterialRuleContext.getMinSurfaceLevel port crates/horizon-world/src/worldgen/material_condition.rs::Context::minimum_surface_level the preliminary surface level minus the depth, cached per column +MaterialRuleContext.getNoiseSampler port crates/horizon-world/src/worldgen/material_condition.rs::Context::bind_noise binds a noise by name and dimension, once +MaterialRuleContext.createNoiseSampler2d port crates/horizon-world/src/worldgen/material_condition.rs::Context::noise the 2D noise sample, cached per column +MaterialRuleContext.createNoiseSampler3d port crates/horizon-world/src/worldgen/material_condition.rs::Context::noise the 3D noise sample, cached per block +MaterialRuleContext.getDensitiesInChunk skip:java prefill buffer for density sampling; Horizon samples the density per block diff --git a/tools/port_map/game2-021.tsv b/tools/port_map/game2-021.tsv new file mode 100644 index 0000000000..079883f8db --- /dev/null +++ b/tools/port_map/game2-021.tsv @@ -0,0 +1,146 @@ +MaterialRuleContext.LazyXZCondition.test inline crates/horizon-world/src/worldgen/material_condition.rs::Compiled::test result cached per column revision (Hole, Steep); the compute is the Compiled kind +MaterialRuleContext.LazyYCondition.test inline crates/horizon-world/src/worldgen/material_condition.rs::Compiled::test result cached per block revision (stone depth, water, gradient, biome, y); the compute is the Compiled kind +MaterialSystem.buildSurface port crates/horizon-world/src/worldgen/material_stage.rs::build_surface vanilla X/Z/descending-Y surface pass with the eroded-badlands and frozen-ocean hooks +MaterialSystem.getSurfaceGradientX inline crates/horizon-world/src/worldgen/material_stage.rs::build_columns gx from first_available neighbours (clamped to 0..15) computed per column before update_xz +MaterialSystem.getSurfaceGradientZ inline crates/horizon-world/src/worldgen/material_stage.rs::build_columns gz from first_available neighbours (clamped to 0..15) computed per column before update_xz +MaterialSystem.isStone inline crates/horizon-world/src/worldgen/material_stage.rs::build_columns the not-air and no-fluid test is written inline in the stone-ceiling lookahead +MaterialSystem.erodedBadlandsExtension port crates/horizon-world/src/worldgen/material_extensions.rs::MaterialExtensions::eroded_badlands pillar buffer and roof noise, fills air up to the extension top +MaterialSystem.getBand port crates/horizon-world/src/worldgen/material_surface.rs::MaterialSurface::band clay band lookup with the offset noise +BoundingBox.intersectingChunks skip:other gametest and test-instance block chunk forcing only, not normal play +BoundingBox.encapsulatingPositions inline crates/horizon-world/src/worldgen/tree.rs::TreeFeature::place bounds fold over roots, trunks, foliage and decorations written inline before update_leaves +BoundingBox.encapsulatingBoxes port crates/horizon-world/src/worldgen/structure/temple.rs::union slice in, box out; vanilla returns Optional.empty for no boxes, union indexes boxes[0] +BoundingBox.encapsulating port crates/horizon-world/src/worldgen/structure.rs::BoundingBox::encapsulate mutates self instead of returning a new box +BoundingBox.move port crates/horizon-world/src/worldgen/structure.rs::BoundingBox::moved returns a new box; vanilla mutates in place +BoundingBox.inflatedBy port crates/horizon-world/src/worldgen/structure.rs::BoundingBox::inflated takes xz and y amounts; vanilla callers all pass equal x and z +BoundingBox.getLength inline crates/horizon-client/src/game/commands.rs::Game::cmd_clone size vector computed inline for clone; no execute-if-blocks port +BoundingBox.getCenter port crates/horizon-world/src/worldgen/structure.rs::BoundingBox::center +BoundingBox.forAllCorners port crates/horizon-world/src/worldgen/structure.rs::BoundingBox::corners corner order differs; the empty-corner count lives in FossilFeature::place +BoundingBox.toString skip:debug debug string +BoundingBox.equals skip:java derived PartialEq +BoundingBox.hashCode skip:java derived Hash +PoolElementStructurePiece.move port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolPiece::moved gap: minor: only the vertical move; dx and dz are dropped (the one vanilla caller moves in y only) +PoolElementStructurePiece.toString skip:debug debug string +ScatteredFeaturePiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/simple.rs::Scattered::saved width, height, depth and height position in the tag +ScatteredFeaturePiece.updateHeightPositionToLowestGroundHeight port crates/horizon-world/src/worldgen/structure/simple.rs::Scattered::lowest_ground lowest motion-blocking height over the box, then moves the box +Structure.simpleCodec skip:java codec +Structure.onTopOfChunkCenterWithoutBiomeCheck port crates/horizon-world/src/worldgen/structure/start.rs::top_of_chunk_center the biome test is added in front of the same height read; no separate no-check path +Structure.getMeanFirstOccupiedHeight inline crates/horizon-world/src/worldgen/structure/start.rs::try_generate corner heights summed and divided by 4 in the shipwreck branch +Structure.getLowestY inline crates/horizon-world/src/worldgen/structure/start.rs::try_generate min of the corner heights taken per structure branch +Structure.getLowestYIn5by5Box inline crates/horizon-world/src/worldgen/structure/start.rs::try_generate 5x5 rotation offsets and corner min in the mansion and end city branches +Structure.GenerationContext.makeRandom inline crates/horizon-world/src/worldgen/structure/start.rs::try_generate random seeded with set_large_feature_seed for the chunk +Structure.GenerationContext.isValidBiome inline crates/horizon-world/src/worldgen/structure/start.rs::try_generate start position quart checked against the structure's biomes +Structure.GenerationContext.couldStructureExistInColumn port crates/horizon-world/src/worldgen/generator.rs::StructureQueries::column_has_valid_biome scans quart y from min to max for a valid biome +Structure.GenerationContext.couldValidBiomeExistInTerrainColumn port crates/horizon-world/src/worldgen/generator.rs::StructureQueries::column_has_valid_biome column from min_y minus 1 to max_y +Structure.GenerationStub.getPiecesBuilder skip:java builder over a plain Vec of pieces +Structure.StructureSettings.Builder.build skip:java settings builder +StructurePiece.getBlock port crates/horizon-world/src/worldgen/structure/legacy.rs::PieceFrame::get air outside the chunk box, else the world block +StructurePiece.isReplaceableByStructures inline crates/horizon-world/src/worldgen/structure/legacy.rs::PieceFrame::fill_column_down air, liquid, glow lichen and seagrass test written inline in the fill loop +StructurePiece.createBoundingBox port crates/horizon-world/src/worldgen/structure/start.rs::StructureStart::new box of all pieces +StructurePiece.findCollisionPiece inline crates/horizon-world/src/worldgen/structure/end_city.rs::Assembly::children first piece whose box intersects, with the depth test inline; the fortress builder keeps its bool check in Builder::collides +StructurePiece.setOrientation port crates/horizon-world/src/worldgen/structure/legacy.rs::PieceFrame::new mirror and rotation from the orientation; set at construction +StructureSet.entry skip:java record constructor for a weighted selection entry +StructureStart.loadStaticStart missing minor: starts are written on save (save.rs) but never read back, so saved piece state is not restored on load +StructureStart.getBoundingBox port crates/horizon-world/src/worldgen/structure/start.rs::StructureStart::new the box is a field set here, inflated for terrain-adapting structures +TemplateStructurePiece.move inline crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolPiece::moved template position moved with the box in the same call +RandomSpreadStructurePlacement.validate skip:data codec check that spacing exceeds separation +RandomSpreadStructurePlacement.isPlacementChunk port crates/horizon-world/src/worldgen/structure/start.rs::StructureRegistry::structure_chunk candidate chunk equals the source chunk; exclusion zones are a separate flag +StructurePlacement.getLocatePos port crates/horizon-world/src/worldgen/generator_structures.rs::StructureState::locate reports the chunk's minimum block x and z at y 0; locate offset is zero +JigsawPlacement.isStartTooCloseToWorldHeightLimits inline crates/horizon-world/src/worldgen/structure/jigsaw.rs::assemble_drawing padding check on the centre piece box, same test +JigsawPlacement.getRandomNamedJigsaw inline crates/horizon-world/src/worldgen/structure/jigsaw.rs::assemble_drawing first shuffled jigsaw with the start jigsaw name anchors the centre piece +JigsawPlacement.Placer.tryPlacingChildren port crates/horizon-world/src/worldgen/structure/jigsaw.rs::Placer::children per jigsaw: target pool and fallback, candidates, rotations, expansion hack and bounds +SinglePoolElement.encodeTemplate skip:java codec encode of the template id +SinglePoolElement.getTemplate port crates/horizon-world/src/worldgen/structure.rs::StructureResources::template cached template lookup by id +SinglePoolElement.getDataMarkers skip:other data-mode markers go to StructurePoolElement.handleDataMarker, which is empty for pool elements +SinglePoolElement.getShuffledJigsawBlocks port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::shuffled_jigsaws shuffle then stable sort by selection priority +SinglePoolElement.getBoundingBox port crates/horizon-world/src/worldgen/structure/jigsaw.rs::PoolElement::bounding_box +BuriedTreasurePieces.BuriedTreasurePiece.isLiquid port crates/horizon-world/src/worldgen/structure/legacy.rs::PieceContext::is_liquid water and lava test +DesertPyramidPiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/temple.rs::DesertPyramidPiece::save hasPlacedChest0 to 3 tags +DesertPyramidPiece.addCellar inline crates/horizon-world/src/worldgen/structure/temple.rs::DesertPyramidPiece::post_process stairs then room, in the cellar block of post_process +DesertPyramidPiece.addCellarStairs inline crates/horizon-world/src/worldgen/structure/temple.rs::DesertPyramidPiece::post_process sandstone stairs and sand steps, variant draw from the level random +DesertPyramidPiece.addCellarRoom inline crates/horizon-world/src/worldgen/structure/temple.rs::DesertPyramidPiece::post_process cellar room blocks inside post_process +DesertPyramidPiece.placeSand inline crates/horizon-world/src/worldgen/structure/temple.rs::DesertPyramidPiece::sand_positions each suspicious sand position is added in the list here +DesertPyramidPiece.placeSandBox inline crates/horizon-world/src/worldgen/structure/temple.rs::DesertPyramidPiece::sand_positions box fill of candidate sand positions +DesertPyramidPiece.placeCollapsedRoofPiece inline crates/horizon-world/src/worldgen/structure/temple.rs::DesertPyramidPiece::post_process sandstone with chance 0.33, else sand +EndCityPieces.EndCityPiece.makeSettings inline crates/horizon-world/src/worldgen/structure/end_city.rs::pieces ignore list chosen by the overwrite flag +EndCityPieces.EndCityPiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/end_city.rs::EndCityPiece::save rotation and OW flag, plus GD +EndCityPieces.EndCityPiece.handleDataMarker port crates/horizon-world/src/worldgen/structure/end_city.rs::EndCityPiece::post_process gap: minor: no Level.isInSpawnableBounds check before sentry and elytra spawns; sentry, elytra and chest markers are done +IglooPieces.IglooPiece.makeSettings inline crates/horizon-world/src/worldgen/structure/simple.rs::igloo_pieces rotation, pivot and ignore processor set when the piece is built +IglooPieces.IglooPiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/simple.rs::TemplatePiece::saved Rot tag for the igloo handler +IglooPieces.IglooPiece.handleDataMarker port crates/horizon-world/src/worldgen/structure/simple.rs::TemplatePiece::post_process chest marker clears the block and sets the igloo chest loot below +JungleTemplePiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/temple.rs::JungleTemplePiece::save four placed flags +JungleTemplePiece.MossStoneSelector.next inline crates/horizon-world/src/worldgen/structure/temple.rs::JungleTemplePiece::post_process cobblestone at 0.4, else mossy cobblestone, in the stone box fill +MineshaftPieces.MineShaftCorridor.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::save +MineshaftPieces.MineShaftCorridor.findCorridorSize inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::Builder::create corridor length drawn as 2 to 4, then shortened until the box is free +MineshaftPieces.MineShaftCorridor.addChildren port crates/horizon-world/src/worldgen/structure/mineshaft.rs::Builder::add_children +MineshaftPieces.MineShaftCorridor.postProcess port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::corridor +MineshaftPieces.MineShaftCorridor.placeDoubleLowerOrUpperSupport port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::double_support +MineshaftPieces.MineShaftCorridor.fillColumnDown skip:other override never called: no mineshaft piece calls fillColumnDown in vanilla +MineshaftPieces.MineShaftCorridor.fillColumnBetween inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::pillar_or_chain the fill loops written inline +MineshaftPieces.MineShaftCorridor.canHangChainBelow inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::pillar_or_chain centre sturdy below and not falling test, inline +MineshaftPieces.MineShaftCorridor.maybePlaceCobWeb inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::corridor cobweb chance and sturdy-neighbour test written in the corridor body +MineshaftPieces.MineShaftCorridor.hasSturdyNeighbours port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::sturdy_neighbours +MineshaftPieces.MineShaftCrossing.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::save +MineshaftPieces.MineShaftCrossing.findCrossing inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::Builder::create crossing arm of the random shaft piece +MineshaftPieces.MineShaftCrossing.addChildren port crates/horizon-world/src/worldgen/structure/mineshaft.rs::Builder::add_children +MineshaftPieces.MineShaftCrossing.postProcess port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::crossing +MineshaftPieces.MineShaftCrossing.placeSupportPillar inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::crossing corner pillars where air is above, written in the crossing body +MineshaftPieces.MineShaftPiece.canBeReplaced inline crates/horizon-world/src/worldgen/structure/legacy.rs::PieceFrame::place keep list (planks, log, fence, chain) checked in place +MineshaftPieces.MineShaftPiece.isSupportingBox inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::support air-above test for x 0 to 2, in the support body +MineshaftPieces.MineShaftPiece.isInInvalidLocation port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::invalid blocking biome at the box centre and liquid test +MineshaftPieces.MineShaftRoom.addChildren port crates/horizon-world/src/worldgen/structure/mineshaft.rs::Builder::add_children +MineshaftPieces.MineShaftRoom.postProcess port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::room +MineshaftPieces.MineShaftRoom.move inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::generate the vertical move of the room and its entrance boxes happens in generate +MineshaftPieces.MineShaftRoom.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::save +MineshaftPieces.MineShaftStairs.findStairs inline crates/horizon-world/src/worldgen/structure/mineshaft.rs::Builder::create stairs arm of the random shaft piece +MineshaftPieces.MineShaftStairs.addChildren port crates/horizon-world/src/worldgen/structure/mineshaft.rs::Builder::add_children +MineshaftPieces.MineShaftStairs.postProcess port crates/horizon-world/src/worldgen/structure/mineshaft.rs::MineshaftPiece::stairs +MineshaftStructure.generatePiecesAndAdjust port crates/horizon-world/src/worldgen/structure/mineshaft.rs::generate +NetherFortressPieces.BridgeCrossing.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.BridgeCrossing.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create box check and collision test, then the piece +NetherFortressPieces.BridgeStraight.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.BridgeStraight.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.CastleCorridorStairsPiece.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.CastleCorridorStairsPiece.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.CastleCorridorTBalconyPiece.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.CastleCorridorTBalconyPiece.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.CastleEntrance.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.CastleEntrance.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.CastleSmallCorridorCrossingPiece.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.CastleSmallCorridorCrossingPiece.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.CastleSmallCorridorLeftTurnPiece.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.CastleSmallCorridorLeftTurnPiece.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.CastleSmallCorridorPiece.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.CastleSmallCorridorPiece.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.CastleSmallCorridorRightTurnPiece.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.CastleSmallCorridorRightTurnPiece.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.CastleStalkRoom.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.CastleStalkRoom.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.MonsterThrone.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.NetherBridgePiece.updatePieceWeight inline crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::generate_piece weight total, or -1 when no piece can still be placed, computed at the top of generate_piece +NetherFortressPieces.NetherBridgePiece.generateChildForward port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::forward foot from the bounding box and orientation, then add +NetherFortressPieces.NetherBridgePiece.generateChildLeft port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::side left side: west for north and south facing +NetherFortressPieces.NetherBridgePiece.generateChildRight port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::side right side: east for north and south facing +NetherFortressPieces.RoomCrossing.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.RoomCrossing.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressPieces.StairsRoom.addChildren port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::add_children +NetherFortressPieces.StairsRoom.createPiece port crates/horizon-world/src/worldgen/structure/fortress.rs::Builder::create +NetherFortressStructure.findGenerationPoint port crates/horizon-world/src/worldgen/structure/fortress.rs::generate builds the start and pieces for the chunk; the stub position is set by the start code +NetherFossilPieces.NetherFossilPiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/nether_fossil.rs::NetherFossilPiece::save +OceanMonumentPieces.MonumentBuilding. port crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new frame, room graph and wing and penthouse children +OceanMonumentPieces.MonumentBuilding.generateRoomGraph inline crates/horizon-world/src/worldgen/structure/monument.rs::MonumentBuilding::new room grid, connections and source search built in the constructor +OceanMonumentPieces.MonumentBuilding.generateWing port crates/horizon-world/src/worldgen/structure/monument.rs::wing +OceanMonumentPieces.MonumentBuilding.generateEntranceArchs inline crates/horizon-world/src/worldgen/structure/monument.rs::building entrance arches block in building +OceanMonumentPieces.MonumentBuilding.generateEntranceWall inline crates/horizon-world/src/worldgen/structure/monument.rs::building entrance wall block in building +OceanMonumentPieces.MonumentBuilding.generateRoofPiece inline crates/horizon-world/src/worldgen/structure/monument.rs::building roof block in building +OceanMonumentPieces.MonumentBuilding.generateLowerWall inline crates/horizon-world/src/worldgen/structure/monument.rs::building lower wall block in building +OceanMonumentPieces.MonumentBuilding.generateMiddleWall inline crates/horizon-world/src/worldgen/structure/monument.rs::building middle wall block in building +OceanMonumentPieces.MonumentBuilding.generateUpperWall inline crates/horizon-world/src/worldgen/structure/monument.rs::building upper wall block in building +OceanMonumentPieces.OceanMonumentPiece.makeBoundingBox port crates/horizon-world/src/worldgen/structure/monument.rs::room_box room box keyed by room index and direction +OceanMonumentPieces.OceanMonumentPiece.generateWaterBox port crates/horizon-world/src/worldgen/structure/monument.rs::W::water ice and water kept, air above sea level +OceanMonumentPieces.OceanMonumentPiece.generateDefaultFloor port crates/horizon-world/src/worldgen/structure/monument.rs::W::floor +OceanMonumentPieces.OceanMonumentPiece.generateBoxOnFillOnly port crates/horizon-world/src/worldgen/structure/monument.rs::W::fill_only +OceanMonumentPieces.OceanMonumentPiece.chunkIntersects port crates/horizon-world/src/worldgen/structure/monument.rs::W::hits +OceanMonumentStructure.findGenerationPoint inline crates/horizon-world/src/worldgen/structure/start.rs::try_generate surrounding biome check around the chunk centre, then the top-of-chunk stub +OceanMonumentStructure.createTopPiece inline crates/horizon-world/src/worldgen/structure/start.rs::try_generate MonumentBuilding::new at the chunk corner minus 29 with a random direction +OceanRuinPieces.archyRuleProcessor inline crates/horizon-world/src/worldgen/structure/ocean_ruin.rs::processors sand to suspicious sand with loot and a cap of 5 in the warm branch, cold bricks in the other +OceanRuinPieces.allPositions inline crates/horizon-world/src/worldgen/structure/ocean_ruin.rs::pieces eight cluster offsets drawn inline before the ruins count diff --git a/tools/port_map/game2-022.tsv b/tools/port_map/game2-022.tsv new file mode 100644 index 0000000000..6679766c07 --- /dev/null +++ b/tools/port_map/game2-022.tsv @@ -0,0 +1,147 @@ +OceanRuinPieces.OceanRuinPiece.makeSettings port crates/horizon-world/src/worldgen/structure/ocean_ruin.rs::processors block_rot, block_ignore and the suspicious sand/gravel rule; rotation set by TemplatePiece +OceanRuinPieces.OceanRuinPiece.create missing minor: no reader for saved ocean ruin pieces; Horizon writes piece NBT but never reads it back +OceanRuinPieces.OceanRuinPiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/simple.rs::TemplatePiece::saved Rot, Integrity, BiomeType, IsLarge +OceanRuinPieces.OceanRuinPiece.handleDataMarker inline crates/horizon-world/src/worldgen/structure/ocean_ruin.rs::post_process chest and drowned markers handled in post_process +OceanRuinPieces.OceanRuinPiece.getHeight inline crates/horizon-world/src/worldgen/structure/ocean_ruin.rs::post_process footprint height search done inline +RuinedPortalPiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/ruined_portal.rs::PortalProps::save VerticalPlacement and Properties; Rotation and Mirror written by TemplatePiece::saved +RuinedPortalPiece.makeSettings inline crates/horizon-world/src/worldgen/structure/ruined_portal.rs::start processor list built inside start +RuinedPortalPiece.getLavaProcessorRule inline crates/horizon-world/src/worldgen/structure/ruined_portal.rs::start lava rule chosen inside start +RuinedPortalPiece.addNetherrackDripColumnsBelowPortal inline crates/horizon-world/src/worldgen/structure/ruined_portal.rs::post_process loop inline in post_process +RuinedPortalPiece.addNetherrackDripColumn port crates/horizon-world/src/worldgen/structure/ruined_portal.rs::drip_column +RuinedPortalPiece.spreadNetherrack inline crates/horizon-world/src/worldgen/structure/ruined_portal.rs::post_process netherrack spread loop inline in post_process +RuinedPortalPiece.canBlockBeReplacedByNetherrackOrMagma port crates/horizon-world/src/worldgen/structure/ruined_portal.rs::replaceable +RuinedPortalPiece.getBlockReplaceRule inline crates/horizon-world/src/worldgen/structure/ruined_portal.rs::start rule JSON built inline in start +RuinedPortalStructure.sample inline crates/horizon-world/src/worldgen/structure/ruined_portal.rs::start air pocket roll inline in start +RuinedPortalStructure.findSuitableY inline crates/horizon-world/src/worldgen/structure/ruined_portal.rs::start height search inline in start +ShipwreckPieces.ShipwreckPiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/simple.rs::TemplatePiece::saved isBeached, Rot, height_adjusted +ShipwreckPieces.ShipwreckPiece.makeSettings inline crates/horizon-world/src/worldgen/structure/simple.rs::shipwreck_piece rotation, pivot and ignore list set in shipwreck_piece +ShipwreckPieces.ShipwreckPiece.handleDataMarker inline crates/horizon-world/src/worldgen/structure/simple.rs::TemplatePiece::post_process loot markers placed in post_process +ShipwreckPieces.ShipwreckPiece.isTooBigToFitInWorldGenRegion port crates/horizon-world/src/worldgen/structure/simple.rs::shipwreck_too_big +ShipwreckPieces.ShipwreckPiece.calculateBeachedPosition inline crates/horizon-world/src/worldgen/structure/simple.rs::TemplatePiece::post_process beached height inline in post_process +ShipwreckPieces.ShipwreckPiece.adjustPositionHeight inline crates/horizon-world/src/worldgen/structure/simple.rs::TemplatePiece::post_process height stored in state inside post_process +StrongholdPieces.resetPieces inline crates/horizon-world/src/worldgen/structure/stronghold.rs::generate fresh piece weights per attempt inside generate +StrongholdPieces.updatePieceWeight inline crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::piece_from_small_door weight total and availability check inline +StrongholdPieces.ChestCorridor.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.FillerCorridor.findPieceBox port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::piece_from_small_door filler length search +StrongholdPieces.FiveCrossing.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/stronghold.rs::StrongholdPiece::save leftLow, leftHigh, rightLow, rightHigh +StrongholdPieces.FiveCrossing.addChildren port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::add_children +StrongholdPieces.FiveCrossing.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.LeftTurn.addChildren port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::add_children +StrongholdPieces.LeftTurn.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.Library.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create tall flag from box height +StrongholdPieces.PieceWeight.doPlace port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::piece_from_small_door placeCount below maxPlaceCount check +StrongholdPieces.PieceWeight.isValid port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::piece_from_small_door same availability check +StrongholdPieces.PortalRoom.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.PrisonHall.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.RightTurn.addChildren port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::add_children +StrongholdPieces.RightTurn.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.RoomCrossing.addChildren port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::add_children +StrongholdPieces.RoomCrossing.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.SmoothStoneSelector.next port crates/horizon-world/src/worldgen/structure/stronghold.rs::smooth edge stone bricks: cracked, mossy, infested, plain +StrongholdPieces.StairsDown.addChildren port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::add_children +StrongholdPieces.StairsDown.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.Straight.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/stronghold.rs::StrongholdPiece::save Left and Right flags +StrongholdPieces.Straight.addChildren port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::add_children +StrongholdPieces.Straight.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.StraightStairsDown.createPiece port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::create +StrongholdPieces.StrongholdPiece.generateSmallDoor port crates/horizon-world/src/worldgen/structure/stronghold.rs::StrongholdPiece::small_door opening, wood, grates and iron door blocks +StrongholdPieces.StrongholdPiece.randomSmallDoor port crates/horizon-world/src/worldgen/structure/stronghold.rs::random_door +StrongholdPieces.StrongholdPiece.generateSmallDoorChildForward port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::forward +StrongholdPieces.StrongholdPiece.generateSmallDoorChildLeft port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::side right=false +StrongholdPieces.StrongholdPiece.generateSmallDoorChildRight port crates/horizon-world/src/worldgen/structure/stronghold.rs::Builder::side right=true +StrongholdStructure.findGenerationPoint port crates/horizon-world/src/worldgen/structure/start.rs::try_generate Stronghold arm: chunk origin, pieces from generate +SwampHutPiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/simple.rs::SwampHutPiece::save Witch and Cat written from one flag +SwampHutPiece.spawnCat inline crates/horizon-world/src/worldgen/structure/simple.rs::SwampHutPiece::post_process cat spawned with the witch under one flag +WoodlandMansionPieces.MansionGrid. port crates/horizon-world/src/worldgen/structure/mansion.rs::MansionGrid::new +WoodlandMansionPieces.MansionPiecePlacer.traverseOuterWalls port crates/horizon-world/src/worldgen/structure/mansion.rs::Placer::outer_walls +WoodlandMansionPieces.MansionPiecePlacer.entrance inline crates/horizon-world/src/worldgen/structure/mansion.rs::Placer::create entrance piece added inside create +WoodlandMansionPieces.MansionPiecePlacer.traverseWallPiece port crates/horizon-world/src/worldgen/structure/mansion.rs::Placer::wall_piece +WoodlandMansionPieces.MansionPiecePlacer.traverseTurn inline crates/horizon-world/src/worldgen/structure/mansion.rs::Placer::outer_walls corner and turn inside outer_walls +WoodlandMansionPieces.MansionPiecePlacer.traverseInnerTurn inline crates/horizon-world/src/worldgen/structure/mansion.rs::Placer::outer_walls inner turn inside outer_walls +WoodlandMansionPieces.MansionPiecePlacer.addRoom1x1 port crates/horizon-world/src/worldgen/structure/mansion.rs::Placer::room_1x1 +WoodlandMansionPieces.MansionPiecePlacer.addRoom1x2 port crates/horizon-world/src/worldgen/structure/mansion.rs::Placer::room_1x2 +WoodlandMansionPieces.MansionPiecePlacer.addRoom2x2 port crates/horizon-world/src/worldgen/structure/mansion.rs::Placer::room_2x2 +WoodlandMansionPieces.MansionPiecePlacer.addRoom2x2Secret inline crates/horizon-world/src/worldgen/structure/mansion.rs::Placer::create secret 2x2 added inside create +WoodlandMansionPieces.SecondFloorRoomCollection.get1x2SideEntrance port crates/horizon-world/src/worldgen/structure/mansion.rs::room_name floor 1 side room, stairs flag +WoodlandMansionPieces.SecondFloorRoomCollection.get1x2FrontEntrance port crates/horizon-world/src/worldgen/structure/mansion.rs::room_name floor 1 front room, stairs flag +WoodlandMansionPieces.SimpleGrid.set port crates/horizon-world/src/worldgen/structure/mansion.rs::Grid::set +WoodlandMansionPieces.SimpleGrid.get port crates/horizon-world/src/worldgen/structure/mansion.rs::Grid::get outside value for cells off the grid +WoodlandMansionPieces.SimpleGrid.setif port crates/horizon-world/src/worldgen/structure/mansion.rs::Grid::set_if +WoodlandMansionPieces.WoodlandMansionPiece.makeSettings inline crates/horizon-world/src/worldgen/structure/mansion.rs::pieces ignore list and rotation set in pieces +WoodlandMansionPieces.WoodlandMansionPiece.addAdditionalSaveData port crates/horizon-world/src/worldgen/structure/simple.rs::TemplatePiece::saved Rot and Mi +WoodlandMansionPieces.WoodlandMansionPiece.handleDataMarker port crates/horizon-world/src/worldgen/structure/mansion.rs::post_process chests and mob markers +WoodlandMansionStructure.findGenerationPoint port crates/horizon-world/src/worldgen/structure/start.rs::try_generate Mansion arm: corner heights, y >= 60 check +WoodlandMansionStructure.generatePieces port crates/horizon-world/src/worldgen/structure/mansion.rs::pieces +StructurePlaceSettings.copy skip:java copy of a settings record; Rust clones the struct +StructurePlaceSettings.getRandom port crates/horizon-world/src/worldgen/structure/processor.rs::PlaceSettings::with_random shared random, else position-seeded one +StructureTemplate.getSize port crates/horizon-world/src/worldgen/structure/template.rs::StructureTemplate::size rotated size +StructureTemplate.fillFromWorld missing minor: saving a template from the world (structure block save); Horizon only reads templates +StructureTemplate.addToLists inline crates/horizon-world/src/worldgen/structure/template.rs::StructureTemplate::from_nbt block classes sorted inside from_nbt +StructureTemplate.buildInfoList inline crates/horizon-world/src/worldgen/structure/template.rs::StructureTemplate::from_nbt full, other, block-entity order inside from_nbt +StructureTemplate.fillEntityList missing minor: part of template save (structure block); Horizon only reads entities +StructureTemplate.createEntityIgnoreException inline crates/horizon-world/src/worldgen/structure/processor.rs::place_entities entity NBT goes straight to add_entity; no exception catch here +StructureTemplate.loadPalette port crates/horizon-world/src/worldgen/structure/template.rs::StructureTemplate::from_nbt +StructureTemplate.newIntegerList skip:java NBT list builder for template save only +StructureTemplate.newDoubleList skip:java NBT list builder for template save only +StructureTemplate.getJointType inline crates/horizon-world/src/worldgen/structure/template.rs::StructureTemplate::jigsaws joint read from NBT, default from block facing inside jigsaws +StructureTemplate.JigsawBlockInfo.toString skip:debug +StructureTemplate.JigsawBlockInfo.withInfo inline crates/horizon-world/src/worldgen/structure/jigsaw.rs::template_jigsaws moved and rotated jigsaw position and state computed here +StructureTemplate.SimplePalette.idFor missing minor: state to palette id mapping for template save only +StructureTemplate.SimplePalette.stateFor inline crates/horizon-world/src/worldgen/structure/template.rs::StructureTemplate::from_nbt palette index resolved to states inside from_nbt +StructureTemplate.StructureBlockInfo.toString skip:debug +StructureTemplateManager.tryLoad port crates/horizon-world/src/worldgen/structure.rs::StructureResources::template load by id from resources +StructureTemplateManager.save missing minor: writes a template into the world folder (structure block save); not implemented +BlendedNoise.computeFbmRange port crates/horizon-core/src/density/terrain_noise.rs::BlendedNoise::range 16 octaves, the only firstOctave vanilla uses +BlendedNoise.FbmSet.parityConfigString skip:debug test parity dump +NoiseStack.get port crates/horizon-core/src/noise/stack.rs::NoiseStack::get3 weighted layer sum +NoiseStack.Builder.add port crates/horizon-core/src/noise/stack.rs::NoiseStackBuilder::add +NoiseStack.Perlin.get port crates/horizon-core/src/noise/stack.rs::NoiseStack::get3 same layer sum; the subclass only changes dispatch +NoiseStack.SmearedPerlin.get port crates/horizon-core/src/noise/stack.rs::NoiseStack::get3 same layer sum; the subclass only changes dispatch +NormalNoise.computeNormalizationFactor port crates/horizon-core/src/noise/normal.rs::normalization_factor +NormalNoise.estimateDeviation inline crates/horizon-core/src/noise/normal.rs::normalization_factor deviation computed inside normalization_factor +NormalNoise.computeParityBaseAmplitude inline crates/horizon-core/src/noise/normal.rs::NormalNoise::create_parity base amplitude computed inside create_parity +NormalNoise.computeParityNormalizationFactor port crates/horizon-core/src/noise/normal.rs::parity_factor +NormalNoise.parityExpectedDeviation inline crates/horizon-core/src/noise/normal.rs::parity_factor 0.1 * (1 + 1 / span) inside parity_factor +NormalNoise.equals skip:java value equality; Rust derives it +NormalNoise.parityConfigString skip:debug test parity dump +NormalNoise.perlinParityConfigString skip:debug test parity dump +NormalNoise.Parameters.validate port crates/horizon-world/src/worldgen.rs::decode_noise amplitude_modifiers length must match octave_count +PerlinNoise.get port crates/horizon-core/src/noise/perlin.rs::PerlinNoise::get3 +PerlinNoise.sampleAndLerp port crates/horizon-core/src/noise/perlin.rs::PerlinNoise::sample 8 corner gradients, smoothstep lerp +PerlinNoise.sampleWithDerivative port crates/horizon-core/src/noise/perlin.rs::PerlinNoise::noise_with_derivative +PerlinNoise.parityConfigString skip:debug test parity dump +SimplexNoise.getCornerNoise3D port crates/horizon-core/src/noise/simplex.rs::SimplexNoise::corner +SimplexNoise.get port crates/horizon-core/src/noise/simplex.rs::SimplexNoise::get2 2D simplex +SmearedPerlinNoise.get port crates/horizon-core/src/noise/perlin.rs::SmearedPerlinNoise::get3 fudged y +ChunkSkyLightSources.fillFrom inline crates/horizon-client/src/light.rs::light_chunk sky light for a loaded chunk computed in light_chunk +ChunkSkyLightSources.findLowestSourceY port crates/horizon-client/src/light.rs::lowest_sky_source +ChunkSkyLightSources.update inline crates/horizon-client/src/light.rs::relight_region columns of changed cells are recomputed +ChunkSkyLightSources.updateEdge inline crates/horizon-client/src/light.rs::relight_region columns of changed cells are recomputed +ChunkSkyLightSources.findLowestSourceBelow port crates/horizon-client/src/light.rs::lowest_sky_source downward column scan +ChunkSkyLightSources.getHighestLowestSourceY skip:other vanilla skips filling sections below the highest source; light_chunk floods every cell +ChunkSkyLightSources.fill inline crates/horizon-client/src/light.rs::light_chunk empty columns lit inside light_chunk +LevelLightEngine.checkBlock inline crates/horizon-client/src/light.rs::relight changed positions are passed in, no queue +LevelLightEngine.hasLightWork skip:other relight runs synchronously; there is no pending work to query +LevelLightEngine.runLightUpdates inline crates/horizon-client/src/light.rs::relight block then sky pass, run immediately +LevelLightEngine.updateSectionStatus skip:other no section-empty state; relight works cell by cell +LevelLightEngine.setLightEnabled inline crates/horizon-client/src/light.rs::light_chunk chunk lit when loaded +LevelLightEngine.propagateLightSources inline crates/horizon-client/src/light.rs::light_chunk emitters seeded inside light_chunk +LevelLightEngine.getLayerListener skip:other listener API; relight returns the changed positions instead +LevelLightEngine.getDebugData skip:debug +LevelLightEngine.getDebugSectionType skip:debug +LevelLightEngine.retainData skip:other no retained light data for unloaded chunks +LevelLightEngine.getRawBrightness port crates/horizon-client/src/mobs/spawn_rules.rs::raw_brightness +LevelLightEngine.lightOnInColumn skip:other no per-column light readiness flag; chunks are lit whole +LightEngine.hasDifferentLightProperties port crates/horizon-client/src/light.rs::LightTables::differs +LightEngine.getState port crates/horizon-client/src/light.rs::Region::state +LightEngine.getChunk port crates/horizon-client/src/light.rs::Region::load section lookup by key +LightEngine.runLightUpdates port crates/horizon-client/src/light.rs::relight_layer decrease then increase pass +LightEngine.propagateIncreases inline crates/horizon-client/src/light.rs::relight_layer increase queue loop +LightEngine.propagateDecreases inline crates/horizon-client/src/light.rs::relight_layer decrease queue loop +LightEngine.hasLightWork skip:other relight runs synchronously; no queues to inspect +LightEngine.QueueEntry.increaseLightFromEmission inline crates/horizon-client/src/light.rs::relight_layer emission seeds for cleared cells +LightEngine.QueueEntry.increaseSkipOneDirection skip:java packed queue flags; relight_layer checks every neighbour +LightEngine.QueueEntry.increaseOnlyOneDirection skip:java packed queue flags; relight_layer checks every neighbour +LightEngine.QueueEntry.increaseSkySourceInDirections inline crates/horizon-client/src/light.rs::relight_layer sky sources and straight-down rule +LightEngine.QueueEntry.shouldPropagateInDirection port crates/horizon-client/src/light.rs::LightTables::blocked inverse: blocked means no propagation +LightEngine.QueueEntry.withoutDirection skip:java packed queue flags diff --git a/tools/port_map/game2-023.tsv b/tools/port_map/game2-023.tsv new file mode 100644 index 0000000000..e408ec7235 --- /dev/null +++ b/tools/port_map/game2-023.tsv @@ -0,0 +1,150 @@ +FlowingFluid.getFlow port crates/horizon-world/src/fluid_flow.rs::flow_at neighbour currents and falling wall deflection +FlowingFluid.affectsFlow port crates/horizon-world/src/fluid_flow.rs::affects_flow - +FlowingFluid.isSolidFace port crates/horizon-world/src/fluid_flow.rs::solid_face - +FlowingFluid.getFlowing port crates/horizon-world/src/fluid.rs::FluidState::new kind chosen by flowing_of (level/fluids.rs) at the call sites; this builds the state from amount and falling +FlowingFluid.isSourceBlockOfThisType port crates/horizon-world/src/level/fluids.rs::Sim::is_source_of - +FlowingFluid.sourceNeighborCount port crates/horizon-world/src/level/fluids.rs::Sim::source_neighbour_count - +FlowingFluid.getLegacyLevel inline crates/horizon-world/src/fluid.rs::FluidBlockBindings::block the level number (0 for sources, else 8 minus amount plus 8 when falling) is computed inline when picking the liquid block +FlowingFluid.getShape port crates/horizon-world/src/fluid_shape.rs::FluidShapeCache::shape gap: minor: no full-block branch for amount 9 with the same fluid above; the pinned registry has no amount-nine state +FlowingFluid.BlockStatePairKey.equals skip:java - cache key record equality +FlowingFluid.BlockStatePairKey.hashCode skip:java - cache key record hash +FlowingFluid.SpreadContext.getBlockState port crates/horizon-world/src/level/fluids.rs::Sim::context_state - +FlowingFluid.SpreadContext.isHole port crates/horizon-world/src/level/fluids.rs::Sim::context_is_hole - +Fluid.getAABB inline crates/horizon-client/src/arrows.rs::Ticker::collided_with_fluid the fluid box (block to block plus height) is built inside collided_with_fluid for the sweep test +FluidState.isSourceOfType port crates/horizon-world/src/level/fluids.rs::Sim::is_source_of family comparison instead of owner identity; same result for source states +LavaFluid.hasFlammableNeighbours inline crates/horizon-world/src/level/fluids.rs::Sim::lava_random_tick the six-neighbour check is folded into the random tick; uses the plain IGNITED_BY_LAVA flag, no face argument +LavaFluid.isFlammable port crates/horizon-world/src/level/fluids.rs::Sim::lava_flammable deprecated no-face overload; same build-height and chunk check and flag +LavaFluid.canBeReplacedWith port crates/horizon-world/src/level/fluids.rs::Sim::can_be_replaced_with - +LavaFluid.getSpreadDelay port crates/horizon-world/src/level/fluids.rs::Sim::spread_delay - +LavaFluid.spreadTo port crates/horizon-world/src/level/fluids.rs::Sim::spread_to includes the lava-into-water stone conversion and fizz event +LavaFluid.Flowing.createFluidStateDefinition skip:data - state property list, read from data tables +WaterFluid.beforeDestroyingBlock port crates/horizon-world/src/level/update.rs::Sim::drop_resources called from spread_to for water only +WaterFluid.canBeReplacedWith port crates/horizon-world/src/level/fluids.rs::Sim::can_be_replaced_with - +WaterFluid.Flowing.createFluidStateDefinition skip:data - state property list, read from data tables +MapColor.calculateARGBColor port crates/horizon-client/src/game/maps.rs::packed_rgba packed id with brightness in, ARGB out; id 0 is transparent as in vanilla NONE +MapColor.getPackedId inline crates/horizon-client/src/game/maps.rs::Game::render_biome_preview the packed id (color << 2 | brightness) is built where map pixels are written (maps.rs:682 and in update_map) +AmphibiousNodeEvaluator.done skip:other - vanilla sets the mob malus for WATER, WALKABLE and WATER_BORDER in prepare and restores it here; Horizon never writes the mob malus, Finder::malus applies the amphibious values per search +AmphibiousNodeEvaluator.isVerticalNeighborValid inline crates/horizon-client/src/mobs/path.rs::Finder::walk_neighbors the amphibious up and down neighbours need a valid node that is WATER; folded into the amphibious branch +BinaryHeap.remove skip:other - no vanilla caller: the search only uses insert, pop and changeCost, Horizon has heap_insert, heap_pop and change_cost +FlyNodeEvaluator.getNeighbors port crates/horizon-client/src/mobs/path.rs::Finder::fly_neighbors fly neighbours +FlyNodeEvaluator.hasMalus inline crates/horizon-client/src/mobs/path.rs::Finder::fly_neighbors the cost_malus >= 0 test is a closure inside fly_neighbors +FlyNodeEvaluator.getCachedPathType port crates/horizon-client/src/mobs/path.rs::Finder::fly_path_type - +FlyNodeEvaluator.iteratePathfindingStartNodeCandidatePositions inline crates/horizon-client/src/mobs/path.rs::Finder::start gap: minor: flying start candidates are the four corners only; the small-mob branch (ten random positions in the inflated box) is not ported +pathfinder/Node.createDebugStreamCodec skip:debug - stream codec for the debug path overlay +pathfinder/Node.cloneAndMove missing - minor: only used by PathNavigation.trimPath to raise path nodes over cauldrons by one block; that trim is not ported, so mobs may bump on cauldron rims +pathfinder/Node.createHash skip:java - Java map key; Horizon indexes nodes inside Finder +pathfinder/Node.distanceTo port crates/horizon-client/src/mobs/path.rs::Node::distance both overloads: node to node and node to block position +pathfinder/Node.distanceToXZ missing - minor: only the Warden path finder uses horizontal distance as its cost; Horizon costs use 3D distance everywhere (Finder::find) +pathfinder/Node.distanceToSqr skip:other - no vanilla caller in pathfinding or navigation; Horizon has no squared node distance +pathfinder/Node.distanceManhattan port crates/horizon-client/src/mobs/path.rs::Node::manhattan - +pathfinder/Node.asVec3 skip:debug - only the pathfinding debug renderer calls it +pathfinder/Node.equals port crates/horizon-client/src/mobs/path.rs::Path::same_as compares the node lists; Horizon nodes are PathNode values that also compare kind, vanilla compares position only +Path.getEndNode port crates/horizon-client/src/mobs/path.rs::Path::end - +Path.truncateNodes missing - player: RestrictSun sets avoid_sun (goals.rs) and undead use it (undead.rs), but nothing cuts the path at the first sunlit node, so undead wander into sunlight while it is bright +Path.getPreviousNode inline crates/horizon-client/src/mobs/villager_brain.rs::B::try_start path.nodes[path.next - 1] is read directly for the door check (InteractWithDoor) +Path.equals skip:java - Java equals; Path derives PartialEq and navigation compares paths with same_as +Path.copy skip:java - Path derives Clone +PathFinder.reconstructPath inline crates/horizon-client/src/mobs/path.rs::Finder::find the came_from walk is the build closure inside find +Target.updateBest port crates/horizon-client/src/mobs/path.rs::Finder::best_h keeps the best heuristic per target +WalkNodeEvaluator.done inline crates/horizon-client/src/mobs/sniffer.rs::ai_step gap: minor: the per-search caches need no clearing (Horizon caches live in one search); the Sniffer water malus is set from the wet state each tick, while vanilla also sets it to -1 right after each search in onPathfindingDone +WalkNodeEvaluator.doesBlockHavePartialCollision port crates/horizon-client/src/mobs/path.rs::Finder::partial_collision - +WalkNodeEvaluator.getCachedPathType port crates/horizon-client/src/mobs/path.rs::Finder::path_type - +WalkNodeEvaluator.getPathTypeWithinMobBB port crates/horizon-client/src/mobs/path.rs::Finder::within_bb - +MapItemSavedData.type skip:data - saved-data type and codec; Horizon saves map files in save_maps +MapItemSavedData. port crates/horizon-client/src/game/maps.rs::MapData::fresh gap: minor: sets the grid cell, scale and dimension; locked and the tracking flags are set by the caller or the NBT load +MapItemSavedData.createForClient port crates/horizon-client/src/game/maps.rs::Game::remote_map_item_data the client map is created from the packet (fresh map, locked flag from the packet) +MapItemSavedData.mapMatcher inline crates/horizon-client/src/game/maps.rs::Game::tick_maps the carried maps are the map ids in the inventory and offhand, which is what the matcher finds +MapItemSavedData.tickCarriedBy port crates/horizon-client/src/game/maps.rs::Game::tick_maps gap: player: no framed-map marker when the map hangs in an item frame, and no removal of a player who wears map-invisibility gear +MapItemSavedData.hasMapInvisibilityItemEquipped missing - player: a player wearing map-invisibility gear (ItemTags.MAP_INVISIBILITY_EQUIPMENT) is still drawn on other players maps +MapItemSavedData.removeDecoration inline crates/horizon-client/src/game/maps.rs::Game::tick_maps decorations are rebuilt each tick (holder markers kept, the rest replaced) instead of removed by key +MapItemSavedData.addDecoration inline crates/horizon-client/src/game/maps.rs::Game::tick_maps gap: player: only the item target decorations go through here; banner and frame markers never reach it +MapItemSavedData.calculateDecorationLocationAndType inline crates/horizon-client/src/game/maps.rs::Game::tick_maps player branch and item branch both done inline, with clamping +MapItemSavedData.playerDecorationTypeAndRotation inline crates/horizon-client/src/game/maps.rs::Game::tick_maps player marker sprite and rotation chosen inline (decoration_rotation for the rotation) +MapItemSavedData.isInsideMap inline crates/horizon-client/src/game/maps.rs::Game::tick_maps the plus or minus 63 range test is inline +MapItemSavedData.decorationTypeForPlayerOutsideMap inline crates/horizon-client/src/game/maps.rs::Game::tick_maps off-map and off-limits sprite choice inline +MapItemSavedData.setColorsDirty inline crates/horizon-client/src/game/maps.rs::MapData::update_color the holders dirty rectangle is updated in update_color +MapItemSavedData.getHoldingPlayer port crates/horizon-client/src/game/maps.rs::MapData::holder - +MapItemSavedData.toggleBanner missing - player: placing a banner inside a map area does not add a marker to the map (banners saved as an empty list) +MapItemSavedData.checkBanners missing - player: banner markers are not tracked, so a marker would not disappear when its banner is broken +MapItemSavedData.removedFromFrame missing - player: framed-map markers are never added, so breaking an item frame leaves no marker to clear (frames saved as an empty list) +MapItemSavedData.setColor port crates/horizon-client/src/game/maps.rs::MapData::update_color - +MapItemSavedData.addClientSideDecorations port crates/horizon-client/src/game/maps.rs::Game::remote_map_item_data decorations are replaced from the packet list +MapItemSavedData.HoldingPlayer.createPatch port crates/horizon-client/src/game/maps.rs::MapData::update_packet the dirty rectangle is copied into the patch +MapItemSavedData.HoldingPlayer.markColorsDirty inline crates/horizon-client/src/game/maps.rs::MapData::update_color the holder dirty rectangle is widened in update_color +MapItemSavedData.MapPatch.applyToMap port crates/horizon-client/src/game/maps.rs::Game::remote_map_item_data the patch loop writes each colour through update_color +LootContext.createVisitedEntry inline crates/horizon-world/src/loot.rs::Context::table_raw gap: minor: only the loot-table cycle guard (visited list); predicates and modifiers are not tracked +LootContext.EntityTarget.getByName port crates/horizon-world/src/loot.rs::Context::target - +LootPool.addRandomItems inline crates/horizon-world/src/loot.rs::Context::table_raw pool condition, bonus rolls, the rolls loop and the pool modifier are in table_raw +LootPool.validate skip:data - validation of references in datagen tables +LootPool.Builder.addAll skip:java - builder +LootPool.Builder.build skip:java - builder +LootTable.validate skip:data - validation of references in datagen tables +LootTable.getAvailableSlots inline crates/horizon-world/src/loot.rs::LootData::fill_with gap: minor: every slot index is shuffled, not only the empty ones; the callers fill empty containers +LootTable.Builder.build skip:java - builder +LootItem.lootTableItem skip:java - builder factory for a loot entry +LootPoolEntryContainer.commonFields skip:java - codec field group +LootPoolEntryContainer.validate skip:data - validation of references in datagen tables +LootPoolEntryContainer.adjustOutput inline crates/horizon-world/src/loot.rs::Context::create the entry modifier is applied to each created stack +LootPoolEntryContainer.canRun inline crates/horizon-world/src/loot.rs::Context::expand the entry condition check is the first step of expand +LootPoolEntryContainer.Builder.otherwise skip:java - builder +LootPoolEntryContainer.Builder.append skip:java - builder, no vanilla caller +LootPoolEntryContainer.Builder.then skip:java - builder, no vanilla caller +entries/TagEntry.addExpandedEntries port crates/horizon-world/src/loot.rs::Context::expand one choice per tag member when expand is set +entries/TagEntry.tagContents skip:java - builder +entries/TagEntry.expandTag skip:java - builder +EnchantRandomlyFunction.run inline crates/horizon-items/src/items.rs::LootContext::modifiers gap: minor: no ADDITIONAL_TRADE_COST component when includeAdditionalCostComponent is set +EnchantRandomlyFunction.enchantItem inline crates/horizon-items/src/items.rs::LootContext::modifiers gap: minor: book conversion, level roll and enchant are inline; no ADDITIONAL_TRADE_COST component +EnchantRandomlyFunction.Builder.withEnchantment skip:java - builder +EnchantRandomlyFunction.Builder.build skip:java - builder +EnchantWithLevelsFunction.run inline crates/horizon-items/src/items.rs::LootContext::modifiers gap: minor: no ADDITIONAL_TRADE_COST component when the parameter is present +EnchantWithLevelsFunction.Builder.withOptions skip:java - builder +EnchantWithLevelsFunction.Builder.build skip:java - builder +ExplorationMapFunction.claimStructureAt missing - minor: no structure reference is added for the explored structure when the map is filled; Horizon keeps chunk references only (vanillagen.rs) +ExplorationMapFunction.Builder.build skip:java - builder +ListOperation.codec skip:data - codec +ListOperation.Append.apply inline crates/horizon-client/src/game/commands/items.rs::list_operation gap: minor: an append over 256 entries is truncated to 256, vanilla logs an error and keeps the original list +CommandStorage.get port crates/horizon-client/src/command_storage.rs::CommandStorage::get - +CommandStorage.getContainer inline crates/horizon-client/src/command_storage.rs::CommandStorage::get namespace lookup; Horizon keeps every namespace in one map, so no container load +CommandStorage.getOrCreateContainer inline crates/horizon-client/src/command_storage.rs::CommandStorage::set the namespace entry is created on write +CommandStorage.Container.get port crates/horizon-client/src/command_storage.rs::CommandStorage::get - +CommandStorage.Container.put port crates/horizon-client/src/command_storage.rs::CommandStorage::set empty contents remove the entry +CommandStorage.Container.getKeys port crates/horizon-client/src/command_storage.rs::CommandStorage::keys - +LevelStorageSource.LevelStorageAccess.releaseTemporarilyAndRun skip:other - used only by the external datafixer tool (FileFixerUpper); Horizon does not run it +LevelStorageSource.LevelStorageAccess.safeClose skip:other - closes the session lock and logs a failure; Horizon releases its lock when the session drops +LevelStorageSource.LevelStorageAccess.getLevelPath skip:other - path helper for level resources; Horizon builds these paths where they are used (save.rs::saved_data_path) +LevelStorageSource.LevelStorageAccess.collectIssues missing - player: a world with a damaged level.dat gets no recovery screen listing its data-fix issues (RecoverWorldDataScreen) +LevelStorageSource.LevelStorageAccess.fixAndGetSummaryFromTag port crates/horizon-client/src/title.rs::list_worlds gap: minor: the world list reads name and last played directly, without a data fix first +LevelStorageSource.LevelStorageAccess.getUnfixedDataTagWithFallback missing - player: when level.dat is unreadable, the level.dat_old copy is not used as a fallback; the world does not load +LevelStorageSource.LevelStorageAccess.getUnfixedDataTag inline crates/horizon-client/src/save.rs::Level::read the Data compound of level.dat is read inside Level::read +LevelStorageSource.LevelStorageAccess.saveDataTag inline crates/horizon-client/src/save.rs::Level::write level.dat is written inside Level::write +LevelStorageSource.LevelStorageAccess.saveLevelData port crates/horizon-client/src/save.rs::write_dat the temp file, the level.dat_old swap and the write are in write_dat +LevelStorageSource.LevelStorageAccess.getIconFile port crates/horizon-client/src/title.rs::Title::icon gap: minor: no session-lock check; Horizon reads icon.png whenever it is there +LevelStorageSource.LevelStorageAccess.renameLevel port crates/horizon-client/src/title/world_management.rs::rename - +LevelStorageSource.LevelStorageAccess.renameAndDropPlayer skip:other - no caller in the decompiled sources +LevelStorageSource.LevelStorageAccess.modifyLevelDataWithoutDatafix inline crates/horizon-client/src/title/world_management.rs::rename read, change the Data compound and write back; rename is the only edit Horizon makes +LevelStorageSource.LevelStorageAccess.makeWorldBackup port crates/horizon-client/src/title/world_management.rs::backup - +LevelStorageSource.LevelStorageAccess.hasWorldData inline crates/horizon-client/src/title.rs::list_worlds gap: minor: a folder counts as a world only when its level.dat reads; level.dat_old is not checked +LevelStorageSource.LevelStorageAccess.restoreLevelDataFromOld missing - player: the recovery that moves a damaged level.dat aside and puts level.dat_old back is not offered (RecoverWorldDataScreen) +PlayerDataStorage.backup missing - minor: a player file that fails to load is not copied to a _corrupted_ backup first; Horizon leaves it in place +SavedDataStorage.getDataFile port crates/horizon-client/src/save.rs::saved_data_path gap: minor: no check that the path stays inside the data folder; the names come from fixed callers +SavedDataStorage.computeIfAbsent inline crates/horizon-client/src/save.rs::compound_or_empty the default is an empty compound when the file is absent +SavedDataStorage.get port crates/horizon-client/src/save.rs::read_saved_data gap: minor: reads the file on each call; vanilla caches the read per type +SavedDataStorage.isGzip inline crates/horizon-world/src/datafix/files.rs::read_saved_dat the gzip magic test is inline +SavedDataStorage.collectDirtyTagsToSave skip:other - Horizon writes each saved-data file when its owner saves; there is no dirty set +SavedDataStorage.encodeUnchecked inline crates/horizon-client/src/save.rs::write_saved_data DataVersion is added in write_saved_data; callers encode the data +SavedDataStorage.tryWrite port crates/horizon-client/src/save.rs::write_saved_data - +SavedDataStorage.close inline crates/horizon-client/src/save.rs::Level::write the final save of the saved data happens in Level::write +TagValueInput.DecodeFromListFailedProblem.description skip:debug - error text for logs only +TagValueInput.UnexpectedListElementTypeProblem.description skip:debug - error text for logs only +PortalShape.findPortalShape port crates/horizon-world/src/level/portal.rs::Sim::find_empty_portal_shape preferred axis first, then the other; here the predicate is an empty frame with no portal block +PortalShape.calculateBottomLeft inline crates/horizon-world/src/level/portal.rs::Sim::find_portal_shape the fall to the bottom row and the left edge search are inline +PortalShape.calculateWidth inline crates/horizon-world/src/level/portal.rs::Sim::find_portal_shape width check between 2 and 21 is inline +PortalShape.getDistanceUntilEdgeAboveFrame port crates/horizon-world/src/level/portal.rs::Sim::portal_distance_until_edge_above_frame - +PortalShape.calculateHeight inline crates/horizon-world/src/level/portal.rs::Sim::find_portal_shape height rows loop and the 3 to 21 check are inline +PortalShape.hasTopFrame inline crates/horizon-world/src/level/portal.rs::Sim::find_portal_shape the top frame row check is inline +PortalShape.getDistanceUntilTop inline crates/horizon-world/src/level/portal.rs::Sim::find_portal_shape rows loop counts portal blocks as it goes, inline +TeleportTransition.playPortalSound inline crates/horizon-client/src/game/nether_portal.rs::Game::arrive_through_nether_portal level event 1032 is sent on arrival +TeleportTransition.withRotation inline crates/horizon-client/src/game/nether_portal.rs::Game::arrive_through_nether_portal the turned yaw and motion are applied on arrival +TeleportTransition.withPosition inline crates/horizon-client/src/game/nether_portal.rs::Game::arrive_through_nether_portal the exit position is applied on arrival +TeleportTransition.transitionAsPassenger missing - player: a player riding a vehicle cannot use a nether portal here (riding blocks it), while vanilla carries the rider through with the vehicle +DefaultRedstoneWireEvaluator.calculateTargetStrength inline crates/horizon-world/src/level/redstone.rs::Sim::wire_update_power the 15 shortcut and the max with the incoming signal are inline diff --git a/tools/port_map/game2-024.tsv b/tools/port_map/game2-024.tsv new file mode 100644 index 0000000000..f06f5577d0 --- /dev/null +++ b/tools/port_map/game2-024.tsv @@ -0,0 +1,148 @@ +ListOperation.Insert.apply port crates/horizon-client/src/game/commands/items.rs::list_operation gap: minor: an offset past the end is clamped and a result over 256 entries is cut; vanilla returns the original list +ListOperation.ReplaceSection.apply port crates/horizon-client/src/game/commands/items.rs::list_operation gap: minor: an offset past the end is clamped and a result over 256 entries is cut; vanilla returns the original list +ListOperation.StandAlone.codec skip:java codec +SetNameFunction.getReferencedContextParams skip:other loot table validation only: the context params a function reads; no runtime effect +SetNameFunction.createResolver missing minor: a set_name with an entity target keeps its raw text; vanilla resolves selectors and scores against the entity (loot.rs:1230 notes no table uses it) +SetNameFunction.setName skip:data datagen builder for loot tables; Horizon reads the loot JSON +SetNameFunction.Target.component port crates/horizon-world/src/loot.rs::Context::run target becomes the component id minecraft:custom_name or minecraft:item_name +ContextFloatProvider.getFloat port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float gap: minor: the 0.0 fallback for a non-finite result or arithmetic error is not applied; Game::cmd_compute rejects non-finite values instead +ContextFloatProvider.getFloatOrThrow port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float the non-finite throw is in Game::cmd_compute (compute.rs:119) +floats/Power.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float the "pow" arm; 0 to the power of 0 gives NaN as in vanilla +ResolvableFloat.Reference.get port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float gap: minor: a missing registry entry gives 0.0, not the caller's default +ContextIntProvider.floatToIntSafe inline crates/horizon-client/src/game/commands/compute.rs::Game::provider_int the "from_float" arm rejects non-finite values and out-of-range results +ContextIntProvider.longToIntSafe inline crates/horizon-client/src/game/commands/compute.rs::Game::provider_int the long_to_int closure inside provider_int +ints/Power.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int the "pow" arm uses checked_pow; 0 to the power of 0 and negative exponents are errors +ResolvableInt.Reference.get port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int gap: minor: a missing registry entry gives 0, not the caller's default +AABB.of inline crates/horizon-world/src/worldgen/structure.rs::BoundingBox::intersects the max+1 edge is folded into the inclusive compare; the 0.25 deflate and shape joins in JigsawPlacement are not reproduced +AABB.unitCubeFromLowerCorner port crates/horizon-core/src/geometry/mod.rs::Aabb::unit_cube - +AABB.encapsulatingFullBlocks port crates/horizon-core/src/geometry/mod.rs::Aabb::encapsulating_blocks - +AABB.setMinX port crates/horizon-core/src/geometry/mod.rs::Aabb::with_min - +AABB.setMinY port crates/horizon-core/src/geometry/mod.rs::Aabb::with_min - +AABB.setMinZ port crates/horizon-core/src/geometry/mod.rs::Aabb::with_min - +AABB.setMaxX port crates/horizon-core/src/geometry/mod.rs::Aabb::with_max - +AABB.setMaxY port crates/horizon-core/src/geometry/mod.rs::Aabb::with_max - +AABB.setMaxZ port crates/horizon-core/src/geometry/mod.rs::Aabb::with_max - +AABB.equals skip:java Object equality +AABB.hashCode skip:java Hashing +AABB.minmax port crates/horizon-core/src/geometry/mod.rs::Aabb::union_bounds - +AABB.move port crates/horizon-core/src/geometry/mod.rs::Aabb::move_by the BlockPos, Vec3 and Vector3f overloads are the same offset +AABB.getSize port crates/horizon-core/src/geometry/mod.rs::Aabb::average_size - +AABB.getDirection port crates/horizon-core/src/geometry/mod.rs::Aabb::clip_direction same axis order and strict nearest-hit test +AABB.clipPoint inline crates/horizon-core/src/geometry/mod.rs::Aabb::clip_direction the per-face plane test is folded into the axis loop +AABB.toString skip:java String form +AABB.hasNaN port crates/horizon-core/src/geometry/mod.rs::Aabb::has_nan - +AABB.Builder.include skip:java builder: Horizon folds points into min and max directly +AABB.Builder.build skip:java builder +Vec3.dot port crates/horizon-core/src/geometry/mod.rs::Vec3::dot - +Vec3.add port crates/horizon-core/src/geometry/mod.rs::Vec3::add_vector all three add overloads +Vec3.distanceToSqr port crates/horizon-core/src/geometry/mod.rs::Vec3::distance_squared both overloads +Vec3.offsetRandomXZ port crates/horizon-world/src/level/removal.rs::jukebox_pop_position specialised to the 0.7 offset used by the jukebox and composter +Vec3.length port crates/horizon-core/src/geometry/mod.rs::Vec3::length - +Vec3.lengthSqr port crates/horizon-core/src/geometry/mod.rs::Vec3::length_squared - +Vec3.horizontalDistanceSqr port crates/horizon-core/src/geometry/mod.rs::Vec3::horizontal_distance_squared - +Vec3.hashCode skip:java Hashing +Vec3.zRot port crates/horizon-core/src/geometry/mod.rs::Vec3::rotate_z - +Vec3.with port crates/horizon-core/src/geometry/mod.rs::Vec3::with_component - +Vec3.toVector3f skip:java float copy; Horizon keeps f64 vectors and the renderer converts its own +CollisionContext.placementContext port crates/horizon-world/src/collision_context.rs::CollisionContext::placement - +DiscreteVoxelShape.rotate skip:data block shape rotation used only in static block shape definitions; Horizon reads block shapes from the collision table +DiscreteVoxelShape.fixupCoordinate skip:data helper of rotate; block shape definitions only +DiscreteVoxelShape.isFullWide port crates/horizon-core/src/geometry/shape.rs::VoxelShape::is_full both overloads; out-of-range cells are empty +DiscreteVoxelShape.isFull port crates/horizon-core/src/geometry/shape.rs::VoxelShape::is_full the AxisCycle overload is index mapping only +DiscreteVoxelShape.isEmpty port crates/horizon-core/src/geometry/shape.rs::VoxelShape::is_empty - +DiscreteVoxelShape.firstFull port crates/horizon-core/src/geometry/shape.rs::VoxelShape::min_at returns the coordinate, not the index; an empty column gives +infinity +DiscreteVoxelShape.lastFull port crates/horizon-core/src/geometry/shape.rs::VoxelShape::max_at returns the coordinate, not the index; an empty column gives -infinity +DiscreteVoxelShape.forAllEdges skip:data edge outline is precomputed in the outline table (game.rs outline_boxes); no runtime edge walk +DiscreteVoxelShape.forAllAxisEdges skip:data edge outline is precomputed in the outline table (game.rs outline_boxes) +DiscreteVoxelShape.forAllAxisFaces port crates/horizon-world/src/worldgen/tree.rs::update_shape_at_edge the face walk of the tree shape; forAllFaces is used only there +Shapes.box port crates/horizon-core/src/geometry/shape/create.rs::VoxelShape::from_box - +Shapes.or port crates/horizon-core/src/geometry/shape/create.rs::VoxelShape::join OR operation +Shapes.collide port crates/horizon-core/src/geometry/shape.rs::VoxelShape::collide_all same early exit when the distance is tiny +Shapes.createIndexMerger skip:java choice of merge representation for speed; the join gives the same result +Shapes.rotate skip:data block shape definitions only; Horizon reads block shapes from the collision table +Shapes.flipAxisIfNeeded skip:data helper of rotate; block shape definitions only +Shapes.rotateHorizontalAxis skip:data block shape definitions only +Shapes.rotateAllAxis skip:data block shape definitions only +Shapes.rotateHorizontal skip:data block shape definitions only +Shapes.rotateAll skip:data block shape definitions only +Shapes.rotateAttachFace skip:data block shape definitions only +SliceShape.makeSlice inline crates/horizon-world/src/level/fluids.rs::merged_face_occludes the boundary layers are taken as face_shape() samples, not as slice views +SliceShape.getCoords inline crates/horizon-world/src/level/fluids.rs::merged_face_occludes same as makeSlice +VoxelShape.min port crates/horizon-core/src/geometry/shape.rs::VoxelShape::min - +VoxelShape.max port crates/horizon-core/src/geometry/shape.rs::VoxelShape::max - +VoxelShape.singleEncompassing skip:data block shape definition (SegmentableBlock); Horizon reads block shapes from the collision table +VoxelShape.move port crates/horizon-core/src/geometry/shape.rs::VoxelShape::moved all three overloads +VoxelShape.forAllEdges skip:data edge outline is precomputed in the outline table (game.rs outline_boxes) +VoxelShape.forAllBoxes port crates/horizon-core/src/geometry/shape.rs::VoxelShape::boxes the same box decomposition +VoxelShape.toAabbs port crates/horizon-core/src/geometry/shape.rs::VoxelShape::boxes - +VoxelShape.isCubeLike port crates/horizon-core/src/geometry/shape/face.rs::VoxelShape::cube_like_along gap: minor: one axis per call; the three-axis loop is not a separate fn +VoxelShape.isCubeLikeAlong port crates/horizon-core/src/geometry/shape/face.rs::VoxelShape::cube_like_along - +Objective.pack port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::pack objectives are written inside the saved scoreboard +PlayerTeam.pack port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::pack teams are written inside the saved scoreboard +PlayerTeam.setPlayerPrefix port crates/horizon-client/src/scoreboard_cmd.rs::team_modify the team.modify prefix path; the null-to-empty rule is not repeated here +PlayerTeam.setPlayerSuffix port crates/horizon-client/src/scoreboard_cmd.rs::team_modify the team.modify suffix path +PlayerTeam.packOptions port crates/horizon-client/src/lan/scoreboard.rs::View::of the friendly fire and see-invisible flags as the team options byte +PlayerTeam.unpackOptions port crates/horizon-client/src/game/remote.rs::Game::remote_team the two option bits are read from the team packet +ScoreHolder.getFeedbackDisplayName port crates/horizon-client/src/scoreboard_cmd.rs::Holder::named gap: minor: the hover with the scoreboard name is set where a holder is built from an entity, not here +ScoreHolder.forNameOnly port crates/horizon-client/src/scoreboard_cmd.rs::Holder::named gap: minor: "*" is not a wildcard holder; game/scoreboard.rs maps it to the player name +Scoreboard.getOrCreatePlayerScore port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::score_mut gap: minor: the read-only criteria check is in scoreboard_cmd.rs, not here +Scoreboard.getPlayerScoreInfo port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::score - +Scoreboard.listPlayerScores port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::list_scores - +Scoreboard.resetAllPlayerScores port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::reset_all - +Scoreboard.resetSinglePlayerScore port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::reset_one - +Scoreboard.addPlayerTeam port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::add_team - +Scoreboard.removePlayerTeam port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::remove_team - +Scoreboard.packPlayerScores port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::pack scores are written inside the saved scoreboard +Scoreboard.loadPlayerScore port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::unpack - +Scoreboard.loadPlayerTeam port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::unpack - +Scoreboard.loadObjective port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::unpack - +Scoreboard.packDisplaySlots port crates/horizon-client-core/src/scoreboard.rs::Scoreboard::pack display slots are written inside the saved scoreboard +ObjectiveCriteria.registerForEveryTeamColor skip:data criteria registry; Horizon resolves criteria names in criteria_by_name +ObjectiveCriteria.registerCustom skip:data criteria registry; Horizon resolves criteria names in criteria_by_name +ObjectiveCriteria.getStat port crates/horizon-client-core/src/scoreboard.rs::stat_criteria - +LevelTicks.addContainer port crates/horizon-core/src/ticks/mod.rs::LevelTicks::add_chunk - +LevelTicks.removeContainer port crates/horizon-core/src/ticks/mod.rs::LevelTicks::remove_chunk - +LevelTicks.collectTicks port crates/horizon-core/src/ticks/mod.rs::LevelTicks::begin_tick - +LevelTicks.sortContainersToTick inline crates/horizon-core/src/ticks/mod.rs::LevelTicks::begin_tick chunk order is built in begin_tick +LevelTicks.drainContainers inline crates/horizon-core/src/ticks/mod.rs::LevelTicks::begin_tick the per-chunk drain is in begin_tick +LevelTicks.rescheduleLeftoverContainers inline crates/horizon-core/src/ticks/mod.rs::LevelTicks::begin_tick leftover chunks are kept in begin_tick +LevelTicks.drainFromCurrentContainer inline crates/horizon-core/src/ticks/mod.rs::LevelTicks::begin_tick the same-chunk drain is in begin_tick +LevelTicks.runCollectedTicks inline crates/horizon-core/src/ticks/mod.rs::LevelTicks::tick run is the callback loop in tick +LevelTicks.cleanupAfterTick port crates/horizon-core/src/ticks/mod.rs::LevelTicks::finish_tick - +LevelTicks.hasScheduledTick port crates/horizon-core/src/ticks/mod.rs::LevelTicks::has_scheduled - +LevelTicks.calculateTickSetIfNeeded skip:java lazy membership set for speed; will_tick_this_tick answers the same question +LevelTicks.forContainersInArea port crates/horizon-core/src/ticks/mod.rs::LevelTicks::chunks_in_area - +LevelTicks.copyAreaFrom port crates/horizon-core/src/ticks/mod.rs::LevelTicks::copy_area - +ProtoChunkTicks.load port crates/horizon-core/src/ticks/generation.rs::ProtoChunkTicks::from_saved - +SavedTick.codec skip:java codec +ScheduledTick.toSavedTick port crates/horizon-core/src/ticks/mod.rs::ScheduledTick::saved - +Timeline.filterSyncableTracks skip:other network filter for environment attribute tracks; Horizon has no attribute tracks to sync +Timeline.validateRegistry skip:data registry validation +Timeline.validateInternal skip:data data validation of timeline files +Timeline.getPeriodCount missing minor: no day count from a clock period; the day number of /time query and the F3 Day line is not computed +Timeline.getCurrentTicks port crates/horizon-world/src/level/timeline.rs::Sim::day_ticks gap: minor: the overworld day clock only, 24000 ticks +Timeline.registerTimeMarkers skip:data clock time markers are read from the registry data (registries.rs clock_time_markers) +Timeline.createTrackSampler missing unsure: no environment attribute track sampler; the day-time curves are coded per feature (game.rs sky_darken, fog.rs) +Timeline.Builder.addModifierTrack skip:data timeline builder used by data bootstrap +Timeline.Builder.addTimeMarker skip:data timeline builder used by data bootstrap +Timeline.Builder.build skip:data timeline builder used by data bootstrap +TrackedWaypoint.AzimuthWaypoint.update port crates/horizon-client/src/locator_bar.rs::ClientWaypoints::apply only the same kind of waypoint moves +TrackedWaypoint.AzimuthWaypoint.pitchDirectionToCamera port crates/horizon-client/src/locator_bar.rs::View::pitch_direction - +TrackedWaypoint.ChunkWaypoint.update port crates/horizon-client/src/locator_bar.rs::ClientWaypoints::apply only the same kind of waypoint moves +TrackedWaypoint.ChunkWaypoint.yawAngleToCamera port crates/horizon-client/src/locator_bar.rs::View::yaw_angle - +TrackedWaypoint.ChunkWaypoint.pitchDirectionToCamera port crates/horizon-client/src/locator_bar.rs::View::pitch_direction - +TrackedWaypoint.Vec3iWaypoint.update port crates/horizon-client/src/locator_bar.rs::ClientWaypoints::apply only the same kind of waypoint moves +TrackedWaypoint.Vec3iWaypoint.writeContents port crates/horizon-protocol/src/packets/play.rs::TrackedWaypoint::write the block target is written as three varints +TrackedWaypoint.Vec3iWaypoint.yawAngleToCamera port crates/horizon-client/src/locator_bar.rs::View::yaw_angle - +TrackedWaypoint.Vec3iWaypoint.pitchDirectionToCamera port crates/horizon-client/src/locator_bar.rs::View::pitch_direction - +Waypoint.addHideAttribute skip:data item property for the waypoint item, set in item registration +Waypoint.Icon.hasData missing unsure: LivingEntity.java:798 tracks only entities whose icon has data; lan/waypoints.rs make() has no such gate +WaypointTransmitter.EntityAzimuthConnection.isBroken port crates/horizon-client/src/lan/waypoints.rs::Tick::broken - +WaypointTransmitter.EntityAzimuthConnection.connect port crates/horizon-client/src/lan/waypoints.rs::Tick::connect - +WaypointTransmitter.EntityAzimuthConnection.update port crates/horizon-client/src/lan/waypoints.rs::Tick::update - +WaypointTransmitter.EntityBlockConnection.connect port crates/horizon-client/src/lan/waypoints.rs::Tick::connect - +WaypointTransmitter.EntityBlockConnection.update port crates/horizon-client/src/lan/waypoints.rs::Tick::update - +WaypointTransmitter.EntityBlockConnection.isBroken port crates/horizon-client/src/lan/waypoints.rs::Tick::broken - +WaypointTransmitter.EntityChunkConnection.connect port crates/horizon-client/src/lan/waypoints.rs::Tick::connect - +WaypointTransmitter.EntityChunkConnection.update port crates/horizon-client/src/lan/waypoints.rs::Tick::update - +WaypointTransmitter.EntityChunkConnection.isBroken port crates/horizon-client/src/lan/waypoints.rs::Tick::broken - diff --git a/tools/port_map/gui-inventory-1.tsv b/tools/port_map/gui-inventory-1.tsv new file mode 100644 index 0000000000..0298232c26 --- /dev/null +++ b/tools/port_map/gui-inventory-1.tsv @@ -0,0 +1,147 @@ +AbstractCommandBlockEditScreen.getUsageNarration skip:ui narration text of the command suggestions +AbstractCommandBlockEditScreen.resize skip:ui re-init on window resize; Horizon redraws the screen every frame and keeps the text +AbstractCommandBlockEditScreen.updatePreviousOutput port crates/horizon-client/src/game/command_blocks.rs::CommandBlockScreen::show_previous +AbstractCommandBlockEditScreen.onDone port crates/horizon-client/src/game/command_blocks.rs::Game::set_command_block sends the packet and clears last output when not tracking; close is close_command_block +AbstractCommandBlockEditScreen.mouseScrolled missing no command suggestion list in the command block screen, so no wheel scrolling of suggestions +AbstractCommandBlockEditScreen.mouseClicked port crates/horizon-client/src/game/command_blocks.rs::Game::command_block_click buttons only; clicking inside the command box does not move the cursor and there are no suggestions to click +AbstractCommandBlockEditScreen.extractRenderState port crates/horizon-client/src/game/command_blocks.rs::Game::draw_command_block +AbstractContainerScreen.extractRenderState port crates/horizon-client/src/game.rs::Game::draw_inventory contents, carried item, tooltip in vanilla order +AbstractContainerScreen.extractContents inline crates/horizon-client/src/game.rs::Game::draw_inventory labels, slots and hover highlight +AbstractContainerScreen.extractCarriedItem port crates/horizon-client/src/game.rs::Game::draw_carried shows what a quick-craft drag leaves in hand +AbstractContainerScreen.extractSlots port crates/horizon-client/src/game.rs::Game::draw_inventory_slots +AbstractContainerScreen.mouseScrolled port crates/horizon-client/src/game/bundles.rs::Game::bundle_scroll the bundle wheel is the only ItemSlotMouseAction +AbstractContainerScreen.extractSlotHighlightBack missing hover highlight is a flat 50% white square on top of the item; vanilla draws the 24x24 slot_highlight_back sprite behind it, and Slot.isHighlightable is not modelled +AbstractContainerScreen.extractSlotHighlightFront missing no slot_highlight_front sprite over the item (see extractSlotHighlightBack) +AbstractContainerScreen.extractTooltip port crates/horizon-client/src/game/stations.rs::Game::draw_slot_tooltip skipped while a stack is carried, even over a bundle +AbstractContainerScreen.showTooltipWithItemInHand missing hovering a bundle while carrying a stack shows no tooltip; vanilla still shows the bundle contents +AbstractContainerScreen.extractFloatingItem port crates/horizon-client/src/game.rs::Game::draw_carried carried stack drawn at cursor minus 8 +AbstractContainerScreen.extractLabels inline crates/horizon-client/src/game.rs::Game::draw_inventory title and Inventory labels are drawn per screen kind +AbstractContainerScreen.recalculateQuickCraftRemaining port crates/horizon-client/src/menu.rs::Menu::quick_craft_preview returns the count left in hand +AbstractContainerScreen.getHoveredSlot port crates/horizon-client/src/game.rs::Game::slot_at +AbstractContainerScreen.slotClicked port crates/horizon-client/src/game.rs::Game::menu_ops includes the bundle unselect of onMouseClickAction; sent to the server on a remote game +AbstractContainerScreen.mouseClicked port crates/horizon-client/src/game.rs::Game::inventory_press press logic is container_screen::Clicks::press; double click gathers, Shift double click quick-moves all +AbstractContainerScreen.mouseDragged port crates/horizon-client/src/game.rs::Game::inventory_drag_move +AbstractContainerScreen.isHovering port crates/horizon-client/src/game.rs::Game::slot_at hit box is the 16x16 slot; vanilla uses 18x18 (one pixel more each side), so the border between slots hits nothing +AbstractContainerScreen.onMouseClickAction inline crates/horizon-client/src/game.rs::Game::menu_ops Shift click or swap clears a bundle's selection +AbstractContainerScreen.keyPressed port crates/horizon-client/src/game.rs::Game::inventory_hotkey number keys, F and Q over a slot; keyPickItem bound to a keyboard key does not clone the hovered slot +AbstractContainerScreen.checkHotbarKeyPressed port crates/horizon-client/src/game.rs::Game::inventory_hotkey hotbar and off hand swaps while the hand is empty +AbstractContainerScreen.removed port crates/horizon-client/src/game.rs::Game::close_container carried stack and crafting grid go back to the inventory or are thrown +AbstractContainerScreen.tick missing no closeContainer for a dead player: death only swaps to the death screen, so a chest or furnace open at death stays counted as open (lid up) +AbstractContainerScreen.quickCraftToSlots port crates/horizon-client/src/game.rs::Game::inventory_release start, one add per slot, end +AbstractContainerScreen.onClose port crates/horizon-client/src/game.rs::Game::close_container closes the container and clears the bundle selection +AbstractFurnaceScreen.init inline crates/horizon-client/src/game.rs::Game::draw_inventory furnace arm centres the title +AbstractFurnaceScreen.getRecipeBookButtonPosition port crates/horizon-client/src/game/recipe_book.rs::Game::book_button_pos +AbstractFurnaceScreen.extractBackground inline crates/horizon-client/src/game.rs::Game::draw_inventory furnace arm draws the flame and the arrow +AbstractMountInventoryScreen.extractBackground port crates/horizon-client/src/game/mount_menu.rs::Game::draw_mount_background texture, chest slots, saddle and armor slot frames, mount preview +AbstractMountInventoryScreen.extractRenderState inline crates/horizon-client/src/game/mount_menu.rs::Game::draw_mount_background the preview follows the cursor position +AbstractRecipeBookScreen.init port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_shift narrow test and left position shift +AbstractRecipeBookScreen.initButton port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click the green button toggles the book; its place is book_button_pos +AbstractRecipeBookScreen.extractRenderState port crates/horizon-client/src/game/recipe_book.rs::Game::draw_recipe_book in a narrow window with the book open the slots are still drawn; vanilla hides them +AbstractRecipeBookScreen.extractSlots port crates/horizon-client/src/game/recipe_book.rs::Game::draw_ghost ghost recipe among the slots +AbstractRecipeBookScreen.charTyped port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_text +AbstractRecipeBookScreen.keyPressed port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_key +AbstractRecipeBookScreen.isInputCaptured port crates/horizon-client/src/game/recipe_book.rs::Game::recipe_search_focused +AbstractRecipeBookScreen.mouseDragged missing dragging inside the recipe search box does not select text +AbstractRecipeBookScreen.isHovering missing in a narrow window with the book open the slots under the book can still be hovered and clicked; vanilla disables them +AbstractRecipeBookScreen.slotClicked inline crates/horizon-client/src/game/recipe_book.rs::Game::recipe_book_click clicking a crafting slot drops the ghost recipe; done on mouse press only +AbstractSignEditScreen. port crates/horizon-client/src/game/sign_edit.rs::Game::open_sign_editor lines, wood type and text colour come from the sign +AbstractSignEditScreen.init inline crates/horizon-client/src/game/sign_edit.rs::Game::draw_sign_editor Done button is laid out here and handled in sign_edit_click +AbstractSignEditScreen.isValid missing the editor stays open when the sign is broken or the player ends up out of range; vanilla closes it each tick and sends the text +AbstractSignEditScreen.keyPressed port crates/horizon-client/src/game/sign_edit.rs::Game::sign_input Up, Down and Enter move between lines +AbstractSignEditScreen.preeditUpdated missing no IME composition text while typing on a sign +AbstractSignEditScreen.extractRenderState port crates/horizon-client/src/game/sign_edit.rs::Game::draw_sign_editor +AbstractSignEditScreen.extractSign inline crates/horizon-client/src/game/sign_edit.rs::Game::draw_sign_editor sign sprite and text scale +AbstractSignEditScreen.extractSignText inline crates/horizon-client/src/game/sign_edit.rs::Game::draw_sign_editor cursor at the end of a line is a bar; vanilla draws an underscore there; no bidirectional shaping +AnvilScreen.subInit port crates/horizon-client/src/game/stations.rs::anvil_box 50 character unbordered rename box +AnvilScreen.resize skip:ui re-init on window resize +AnvilScreen.extractBackground inline crates/horizon-client/src/game/stations.rs::Game::draw_station text field sprite, disabled without an input +AnvilScreen.extractErrorIcon inline crates/horizon-client/src/game/stations.rs::Game::draw_station error sprite when there is input and no result +BeaconScreen.addBeaconButton skip:ui widget list bookkeeping; buttons() lists the power buttons +BeaconScreen.init port crates/horizon-client/src/game/beacon.rs::buttons tier rows, upgrade, confirm and cancel positions +BeaconScreen.updateButtons inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon active and selected state is worked out each frame and on click +BeaconScreen.extractLabels inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon primary and secondary power labels +BeaconScreen.extractBackground inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon background and the five payment item icons +BeaconScreen.BeaconConfirmButton.onPress port crates/horizon-client/src/game/beacon.rs::Game::confirm_beacon sends the chosen powers on a server and closes +BeaconScreen.BeaconConfirmButton.updateStatus inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon active when a payment is in the slot and a primary power is chosen +BeaconScreen.BeaconPowerButton.setEffect inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon icon and tooltip follow the chosen effect +BeaconScreen.BeaconPowerButton.onPress port crates/horizon-client/src/game/beacon.rs::Game::beacon_click selecting a primary clears a different secondary +BeaconScreen.BeaconPowerButton.extractIcon inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon +BeaconScreen.BeaconPowerButton.updateStatus inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon unlocked by pyramid level, selected by the chosen power +BeaconScreen.BeaconScreenButton.extractContents inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon disabled, selected, highlighted and normal sprites +BeaconScreen.BeaconSpriteScreenButton.extractIcon inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon confirm and cancel icons; their Done and Cancel tooltips are not shown +BeaconScreen.BeaconUpgradePowerButton.updateStatus inline crates/horizon-client/src/game/beacon.rs::Game::draw_beacon shown once a primary is chosen; its tooltip lacks the II suffix +BookEditScreen.init inline crates/horizon-client/src/game/books.rs::Game::book_click page box, page buttons, Sign and Done layout (drawn in draw_book) +BookEditScreen.getNarrationMessage skip:ui narration +BookEditScreen.getPageNumberMessage inline crates/horizon-client/src/game/books.rs::Game::draw_book page indicator text +BookEditScreen.pageBack port crates/horizon-client/src/game/books.rs::Game::turn_page +BookEditScreen.pageForward port crates/horizon-client/src/game/books.rs::Game::turn_page adds a page up to 100 when on the last one +BookEditScreen.updatePageContent inline crates/horizon-client/src/game/books.rs::Game::turn_page loads the page text into the edit box +BookEditScreen.eraseEmptyTrailingPages inline crates/horizon-client/src/game/books.rs::Game::save_book keeps at least one page +BookEditScreen.updateLocalCopy inline crates/horizon-client/src/game/books.rs::Game::write_book the book in hand takes the pages +BookEditScreen.appendPageToBook inline crates/horizon-client/src/game/books.rs::Game::turn_page pushes an empty page below the 100 page cap +BookEditScreen.extractRenderState port crates/horizon-client/src/game/books.rs::Game::draw_book +BookEditScreen.visitText inline crates/horizon-client/src/game/books.rs::Game::draw_book page number at the top right +BookEditScreen.extractBackground inline crates/horizon-client/src/game/books.rs::Game::draw_book book texture +BookSignScreen.init port crates/horizon-client/src/game/books.rs::book_title_box 15 character centred title box; Sign and Cancel buttons are in book_click +BookSignScreen.saveChanges port crates/horizon-client/src/game/books.rs::Game::save_book sign=true sends the title with the pages +BookSignScreen.extractRenderState inline crates/horizon-client/src/game/books.rs::Game::draw_book title prompt, by author line, finalize warning +BookSignScreen.extractBackground inline crates/horizon-client/src/game/books.rs::Game::draw_book +BookViewScreen.setBookAccess port crates/horizon-client/src/game/books.rs::Game::remote_lectern_book lectern book changed +BookViewScreen.setPage port crates/horizon-client/src/game/books.rs::Game::remote_lectern_page lectern page changed +BookViewScreen.getNarrationMessage skip:ui narration +BookViewScreen.createMenuControls inline crates/horizon-client/src/game/books.rs::Game::book_click Done button (Done and Take Book on a lectern) +BookViewScreen.createPageControlButtons inline crates/horizon-client/src/game/books.rs::Game::draw_book page buttons, hit tests in book_click +BookViewScreen.pageBack port crates/horizon-client/src/game/books.rs::Game::turn_page +BookViewScreen.pageForward port crates/horizon-client/src/game/books.rs::Game::turn_page +BookViewScreen.updateButtonVisibility inline crates/horizon-client/src/game/books.rs::Game::draw_book back and forward shown by the page index +BookViewScreen.keyPressed port crates/horizon-client/src/game/books.rs::Game::book_input PageUp and PageDown turn pages +BookViewScreen.extractRenderState port crates/horizon-client/src/game/books.rs::Game::draw_book +BookViewScreen.visitText inline crates/horizon-client/src/game/books.rs::Game::draw_book page lines and page indicator; links are found by book_link_at +BookViewScreen.extractBackground inline crates/horizon-client/src/game/books.rs::Game::draw_book +BookViewScreen.BookAccess.getPage inline crates/horizon-client/src/game/books.rs::Game::draw_book pages.get(page) with an empty page past the end +BookViewScreen.BookAccess.fromItem port crates/horizon-client/src/game/books.rs::view_pages written book components or writable book text +BrewingStandScreen.init inline crates/horizon-client/src/game/stations.rs::Game::draw_station brewing arm centres the title +BrewingStandScreen.extractBackground inline crates/horizon-client/src/game/stations.rs::Game::draw_station fuel bar, progress arrow and bubbles +CartographyTableScreen.extractResultingMap port crates/horizon-client/src/game/cartography.rs::Game::draw_cartography +CartographyTableScreen.extractMap inline crates/horizon-client/src/game/cartography.rs::Game::draw_cartography preview drawn from the map texture +CommandBlockEditScreen.addExtraControls port crates/horizon-client/src/game/command_blocks.rs::CommandBlockScreen::buttons mode, conditional and auto buttons +CommandBlockEditScreen.enableControls skip:other Horizon opens the screen with the block data in hand, so the controls are never disabled while waiting for the server +CommandBlockEditScreen.updateGui inline crates/horizon-client/src/game/command_blocks.rs::Game::open_command_block fills the box and buttons from the block entity +CommandBlockEditScreen.populateAndSendPacket port crates/horizon-client/src/game/command_blocks.rs::Game::set_command_block SetCommandBlock on a server, applied here otherwise +ContainerScreen.extractBackground port crates/horizon-client/src/game/storage.rs::Game::draw_chest_screen +CrafterScreen.init inline crates/horizon-client/src/game/machines/crafter.rs::Game::draw_crafter_screen title centred +CrafterScreen.updateSlotState port crates/horizon-client/src/game/machines/crafter.rs::Game::crafter_slot_clicked menu state, ContainerSlotStateChanged and the click sound +CrafterScreen.extractSlot port crates/horizon-client/src/screen_layout.rs::crafter disabled slot sprite; no pointing-hand cursor over crafter slots +CrafterScreen.extractDisabledSlot port crates/horizon-client/src/screen_layout.rs::crafter +CrafterScreen.extractRenderState port crates/horizon-client/src/game/machines/crafter.rs::Game::draw_crafter_screen the Click to disable slot tooltip is crafter_tooltip +CrafterScreen.extractRedstone port crates/horizon-client/src/screen_layout.rs::crafter powered or unpowered arrow +CraftingScreen.getRecipeBookButtonPosition port crates/horizon-client/src/game/recipe_book.rs::Game::book_button_pos +CraftingScreen.extractBackground inline crates/horizon-client/src/game.rs::Game::draw_inventory crafting table texture +CreativeModeInventoryScreen.tryRefreshInvalidatedTabs missing tabs are read once from creative-inventory.json; they are not rebuilt when feature flags, permissions or datapacks change (no Operator Utilities tab) +CreativeModeInventoryScreen.tryRebuildTabContents missing same as tryRefreshInvalidatedTabs; the search trees are not rebuilt either +CreativeModeInventoryScreen.refreshCurrentTabContents inline crates/horizon-client/src/creative.rs::Creative::items the grid is worked out again when the tab, search or language changed +CreativeModeInventoryScreen.containerTick missing the creative screen stays open if the player is no longer in creative (vanilla swaps to the survival inventory); no tab refresh +CreativeModeInventoryScreen.init port crates/horizon-client/src/creative.rs::Creative::reopen fresh search box and scrollbar when the screen opens +CreativeModeInventoryScreen.resize skip:ui re-init on window resize keeping search text and scroll row +CreativeModeInventoryScreen.removed skip:other unregisters the slot listener; Horizon sends the creative slots to the server from its own tick +CreativeModeInventoryScreen.charTyped port crates/horizon-client/src/game/clipboard.rs::Game::creative_search_edit +CreativeModeInventoryScreen.preeditUpdated missing no IME composition text in the search box +CreativeModeInventoryScreen.isInputCaptured inline crates/horizon-client/src/game/debug_keys.rs::Game::debug_key typing gate for the search tab; vanilla only uses it to stop the friends hotkey +CreativeModeInventoryScreen.updateVisibleTags missing a # tag search does not list the matching tags in item tooltips +CreativeModeInventoryScreen.extractLabels inline crates/horizon-client/src/game.rs::Game::draw_creative tab title at 8,6 except on the inventory tab +CreativeModeInventoryScreen.mouseReleased port crates/horizon-client/src/creative.rs::Creative::release ends the scrollbar drag; tabs are selected on press, not on release +CreativeModeInventoryScreen.mouseScrolled port crates/horizon-client/src/creative.rs::Creative::wheel +CreativeModeInventoryScreen.hasClickedOutside port crates/horizon-client/src/game.rs::Game::clicked_outside creative window size; tab clicks are handled before it in click_creative +CreativeModeInventoryScreen.mouseDragged port crates/horizon-client/src/creative.rs::Creative::drag_scrollbar +CreativeModeInventoryScreen.extractRenderState port crates/horizon-client/src/game.rs::Game::draw_creative trash tooltip text is fixed English; no pointing-hand or resize cursor over tabs and the scrollbar +CreativeModeInventoryScreen.getTooltipFromContainerItem missing item tooltips in the creative screen do not list the tabs that hold the item (blue lines) or, in a # search, the matching tags (purple) +CreativeModeInventoryScreen.getTabY port crates/horizon-client/src/creative.rs::Creative::tab_position click area uses the drawn tab position, 4 px off vanilla's -32 / image height +CreativeModeInventoryScreen.checkTabClicked inline crates/horizon-client/src/game.rs::Game::click_creative loop over the tab rectangles +CreativeModeInventoryScreen.checkTabHovering inline crates/horizon-client/src/game.rs::Game::draw_creative tab title tooltip +CreativeModeInventoryScreen.extractTabButton inline crates/horizon-client/src/game.rs::Game::draw_creative tab sprite and icon item +CreativeModeInventoryScreen.CustomCreativeSlot.mayPickup missing no CREATIVE_SLOT_LOCK or disabled-feature refusal on display slots; only matters for the saved hotbars tab, which Horizon does not have +CreativeModeInventoryScreen.ItemPickerMenu.getRowIndexForScroll port crates/horizon-client/src/creative.rs::Creative::first_row +CreativeModeInventoryScreen.ItemPickerMenu.scrollTo inline crates/horizon-client/src/creative.rs::Creative::first_row display slots are read from the first visible row when drawn and clicked +CyclingSlotBackground missing empty smithing table slots do not cycle hint icons: only a static netherite upgrade template icon shows, none for base item or addition +DispenserScreen.init inline crates/horizon-client/src/game/machines.rs::Game::draw_machine_screen title centred +DispenserScreen.extractBackground port crates/horizon-client/src/game/machines.rs::Game::draw_machine_screen diff --git a/tools/port_map/levelgen-1.tsv b/tools/port_map/levelgen-1.tsv new file mode 100644 index 0000000000..b758ab29ef --- /dev/null +++ b/tools/port_map/levelgen-1.tsv @@ -0,0 +1,104 @@ +Aquifer.NoiseBasedAquifer. port crates/horizon-world/src/worldgen/aquifer/noise_based.rs::NoiseBasedAquifer::new grid bounds, max surface level, skip-above-Y +Aquifer.NoiseBasedAquifer.getIndex port crates/horizon-world/src/worldgen/aquifer/noise_based.rs::NoiseBasedAquifer::grid_index +Aquifer.NoiseBasedAquifer.similarity port crates/horizon-world/src/worldgen/aquifer/noise_based.rs::similarity +Aquifer.NoiseBasedAquifer.calculatePressure port crates/horizon-world/src/worldgen/aquifer/noise_based.rs::NoiseBasedAquifer::pressure +Aquifer.NoiseBasedAquifer.getAquiferStatus port crates/horizon-world/src/worldgen/aquifer/noise_based.rs::NoiseBasedAquifer::status +Aquifer.NoiseBasedAquifer.computeRandomizedFluidSurfaceLevel inline crates/horizon-world/src/worldgen/aquifer/noise_based.rs::NoiseBasedAquifer::compute_surface_level the spread-noise branch +Aquifer.NoiseBasedAquifer.computeFluidType inline crates/horizon-world/src/worldgen/aquifer/noise_based.rs::NoiseBasedAquifer::compute_fluid the lava-noise swap at the end +Beardifier.getBuryContribution port crates/horizon-core/src/density/beardifier.rs::bury +Beardifier.getBeardContribution port crates/horizon-core/src/density/beardifier.rs::beard +Beardifier.computeBeardContribution inline crates/horizon-core/src/density/beardifier.rs::kernel fills the beard kernel table +BelowZeroRetrogen.getBiomeResolver port crates/horizon-world/src/retrogen.rs::RetrogenBiomeResolver::sample keeps lush caves, dripstone caves and deep dark, else the old column biome +BitRandomSource.nextInt port crates/horizon-core/src/random.rs::LegacyRandom::next_int_bound bounded and unbounded forms; only the legacy source is bit based +BitRandomSource.nextDouble port crates/horizon-core/src/random.rs::LegacyRandom::next_double +Column.create inline crates/horizon-world/src/worldgen/dripstone.rs::Speleothem::scan the Column struct holds optional floor and ceiling +Column.scan port crates/horizon-world/src/worldgen/dripstone.rs::Speleothem::scan +Column.scanDirection inline crates/horizon-world/src/worldgen/dripstone.rs::Speleothem::scan the up and down walks are one closure +Column.Ray.toString skip:debug debug text +DebugLevelSource missing debug_all_block_states preset falls back to a normal world (dedicated.rs warns); no block-state grid at y 70 with a barrier floor at y 60 +FlatLevelSource.createState missing flat worlds get no structure state: the default Superflat villages and strongholds (structure_overrides) never generate +FlatLevelSource.buildTerrain port crates/horizon-client/src/flatgen.rs::FlatTerrain::proto stacks the layers from the minimum Y +FlatLevelSource.getBaseHeight missing no flat base-height query; nothing needs it while flat has no structures +FlatLevelSource.getBaseColumn missing no flat base-column query; same as getBaseHeight +LegacyRandomSource.setSeed port crates/horizon-core/src/random.rs::LegacyRandom::set_seed also drops the cached gaussian +LegacyRandomSource.next port crates/horizon-core/src/random.rs::LegacyRandom::bits +LegacyRandomSource.LegacyPositionalRandomFactory.at port crates/horizon-core/src/random/positional.rs::LegacyPositionalRandom::at +LegacyRandomSource.LegacyPositionalRandomFactory.fromHashOf port crates/horizon-core/src/random/positional.rs::LegacyPositionalRandom::from_hash_of +NoiseBasedChunkGenerator.createFluidPicker port crates/horizon-world/src/worldgen/aquifer.rs::GlobalFluidPicker::new lava below -54 or sea level, else the default fluid +NoiseBasedChunkGenerator.createNoiseChunk inline crates/horizon-world/src/worldgen/generator.rs::Pipeline::generate_with_context beardifier field, density volume and aquifer built inline +NoiseBasedChunkGenerator.chunkVolume inline crates/horizon-world/src/worldgen/generator.rs::Pipeline::generate_with_context DensityVolume 16 x height x 16 +NoiseBasedChunkGenerator.addDebugScreenInfo missing F3 has no Density line (N, T, V, C, E, D, W, PV, PS values at the feet) +NoiseBasedChunkGenerator.iterateNoiseColumn port crates/horizon-world/src/worldgen/generator.rs::Pipeline::noise_column base_height and base_column are built on it +NoiseBasedChunkGenerator.buildSurface port crates/horizon-world/src/worldgen/material_stage.rs::build_surface +NoiseBasedChunkGenerator.generateCarvers port crates/horizon-world/src/worldgen/carver/generation.rs::generate_carving_mask the mask is applied by apply_carving_mask; no old-chunk filter +NoiseBasedChunkGenerator.applyCarvingMask port crates/horizon-world/src/worldgen/carving.rs::apply_carving_mask +NoiseBasedChunkGenerator.getBiomeGenerationSettingsForCarver port crates/horizon-world/src/worldgen/generator.rs::Pipeline::carver_biome biome at the source chunk's minimum quart, then its carvers +NoiseBasedChunkGenerator.buildTerrain port crates/horizon-world/src/worldgen/generator.rs::Pipeline::generate_with_context fill, surface, carvers in order +NoiseBasedChunkGenerator.doFill port crates/horizon-world/src/worldgen/terrain.rs::fill_noise +NoiseBasedChunkGenerator.debugPreliminarySurfaceLevel skip:debug DEBUG_AQUIFERS slime and honey markers +NoiseGeneratorSettings skip:data Horizon reads worldgen/noise_settings JSON +NoiseRouter.createClimateSampler port crates/horizon-world/src/worldgen/router.rs::RouterSamplers::climate_sampler +NoiseRouterData skip:data density function and router bootstraps; Horizon reads worldgen/density_function JSON +NoiseRouterData.QuantizedSpaghettiRarity skip:data builds density functions for the density_function JSON +NoiseSettings.guardY inline crates/horizon-world/src/worldgen/settings.rs::NoiseSettings::create upper bound, height and min Y multiples of 16 +Noises.instantiate inline crates/horizon-core/src/density/graph/seeded.rs::SeededContext::noise noise created from the hash of its name +OverworldFunctionSet.map skip:data bootstrap helper +RandomState.create port crates/horizon-core/src/density/graph/seeded.rs::SeededContext::new seed, legacy or Xoroshiro root factory, noise cache +RandomState.getOrCreateNoise port crates/horizon-core/src/density/graph/seeded.rs::SeededContext::noise +RandomState.getOrCreateRandomFactory inline crates/horizon-world/src/worldgen/generator.rs::Pipeline::generate_with_context root.from_hash_of(name).fork_positional() at each use (aquifer, ore), not cached +RandomState.acquireDensityBufferPool skip:java buffer pool +RandomState.releaseDensityBufferPool skip:java buffer pool +RandomState.garbageCollect skip:java buffer pool +RandomSupport.mixStafford13 port crates/horizon-core/src/random.rs::mix_stafford13 +RandomSupport.upgradeSeedTo128bitUnmixed inline crates/horizon-core/src/random.rs::XoroshiroRandom::new seed ^ silver ratio, then + golden ratio +RandomSupport.seedFromHashOf port crates/horizon-core/src/random/hash.rs::seed_from_hash MD5 of the name +RandomSupport.Seed128bit.xor inline crates/horizon-core/src/random/positional.rs::XoroshiroPositionalRandom::from_hash_of lo and hi xored with the factory seed +SingleThreadedRandomSource.setSeed port crates/horizon-core/src/random.rs::LegacyRandom::set_seed same LCG; Rust's LegacyRandom is single threaded +SingleThreadedRandomSource.next port crates/horizon-core/src/random.rs::LegacyRandom::bits +SingleThreadedRandomSource.nextGaussian port crates/horizon-core/src/random.rs::LegacyRandom::next_gaussian +ThreadSafeLegacyRandomSource.next skip:java atomic copy of LegacyRandom::bits; deprecated, no callers, Rust owns its random +VerticalAnchor.merge skip:java codec glue; VerticalAnchor::from_json decodes +VerticalAnchor.split skip:java codec glue +VerticalAnchor.BelowTop.resolveY port crates/horizon-world/src/worldgen/anchor.rs::VerticalAnchor::resolve BelowTop arm +WorldDimensions.keysInOrder skip:other orders the dimension registry; Horizon has the preset's fixed overworld, nether and end +WorldDimensions.replaceOverworldGenerator inline crates/horizon-client/src/save.rs::write_world_gen_settings_with a flat generator replaces the overworld when world_gen_settings is written +WorldDimensions.withOverworld inline crates/horizon-client/src/save.rs::write_world_gen_settings_with same as replaceOverworldGenerator +WorldDimensions.overworld inline crates/horizon-client/src/flatgen.rs::FlatTerrain::from_settings reads the overworld generator from world_gen_settings; a normal one is OverworldGenerator::for_world +WorldDimensions.specialWorldProperty inline crates/horizon-client/src/save.rs::WorldGen::read flat flag only; there is no debug world +WorldDimensions.isVanillaLike skip:other stability only picks the experimental-settings warning; Horizon's preset dimensions are stable +WorldDimensions.isStableOverworld skip:other same as isVanillaLike +WorldDimensions.isStableNether skip:other same as isVanillaLike +WorldDimensions.isStableEnd skip:other same as isVanillaLike +WorldDimensions.bake skip:other builds the dimension registry; Horizon has the preset's fixed dimensions +WorldGenSettings.of inline crates/horizon-client/src/save.rs::write_world_gen_settings_with seed, options and dimensions written together +WorldGenSettings.hashCode skip:java +WorldGenerationContext.of port crates/horizon-world/src/worldgen/anchor.rs::WorldGenerationContext::with_generator the plain-level branch is WorldGenerationContext::new +WorldOptions.withBonusChest inline crates/horizon-client/src/title/create_world.rs::WorldOptions::world_gen never in Hardcore +WorldOptions.withStructures inline crates/horizon-client/src/title/create_world.rs::WorldOptions::world_gen +WorldOptions.withSeed inline crates/horizon-client/src/game/world_loading.rs::WorldRequest::prepare parse_seed in title.rs reads the text; a random seed fills an empty one here +Xoroshiro128PlusPlus.nextLong port crates/horizon-core/src/random.rs::XoroshiroRandom::next_long +XoroshiroRandomSource.setSeed port crates/horizon-core/src/random.rs::XoroshiroRandom::set_seed +XoroshiroRandomSource.nextInt port crates/horizon-core/src/random.rs::XoroshiroRandom::next_int_bound bounded form; next_int is the unbounded one +XoroshiroRandomSource.nextBoolean port crates/horizon-core/src/random.rs::XoroshiroRandom::next_boolean +XoroshiroRandomSource.consumeCount port crates/horizon-core/src/random.rs::RandomSource::consume_count default trait method +XoroshiroRandomSource.XoroshiroPositionalRandomFactory.at port crates/horizon-core/src/random/positional.rs::XoroshiroPositionalRandom::at +XoroshiroRandomSource.XoroshiroPositionalRandomFactory.fromHashOf port crates/horizon-core/src/random/positional.rs::XoroshiroPositionalRandom::from_hash_of +XoroshiroRandomSource.XoroshiroPositionalRandomFactory.fromSeed port crates/horizon-core/src/random/positional.rs::XoroshiroPositionalRandom::from_seed +XoroshiroRandomSource.XoroshiroPositionalRandomFactory.parityConfigString skip:debug test helper +Blender.of missing no old-chunk blending: worlds upgraded from before 1.18 show terrain seams where old chunks meet new ones +Blender.isEmpty port crates/horizon-core/src/density/world_fields.rs::EmptyBlender::is_empty only the empty blender exists +Blender.blendOffsetAndFactor missing old-chunk height blending not implemented; the empty case is alpha 1, offset 0 (WorldField::fallback) +Blender.heightToOffset missing part of old-chunk height blending +Blender.blendDensity missing DensityBlender::blend_density hook exists but only EmptyBlender (returns the input) implements it +Blender.getBlendingDataValue missing no BlendingData is read or kept +Blender.getBiomeResolver missing no biome blending next to old chunks +Blender.blendBiome missing no biome blending next to old chunks +Blender.generateBorderTicks missing no leaf or fluid post-processing at the old/new chunk border +Blender.generateBorderTick missing part of generateBorderTicks +Blender.createAroundOldChunksCarvingMaskFilter missing carvers run unfiltered next to old chunks (apply_carving_mask gets |_| true) +Blender.makeOldChunkDistanceGetter missing part of the carving filter +Blender.makeOffsetOldChunkDistanceGetter missing part of the carving filter +Blender.distanceToCube missing part of the carving filter +Blender.OutputBuffer.getAlpha missing only the empty-blender value exists (alpha 1 via WorldField::fallback) +Blender.OutputBuffer.getOffset missing only the empty-blender value exists (offset 0 via WorldField::fallback) +Blender.OutputBuffer.getIndex missing part of the blend output buffer diff --git a/tools/port_map/rest-001.tsv b/tools/port_map/rest-001.tsv new file mode 100644 index 0000000000..13e0233e15 --- /dev/null +++ b/tools/port_map/rest-001.tsv @@ -0,0 +1,150 @@ +Axis.rotateDegrees skip:other degrees overload of rotate; Horizon converts degrees to radians at each call site +Divisor.nextInt skip:ui even-spacing helper for GridLayout and EqualSpacingLayout; Horizon lays out its UI with its own code +GivensParameters.fromUnnormalized skip:other Givens rotation helper for the SVD split used by Transformation.ensureDecomposed only +GivensParameters.fromPositiveAngle skip:other Givens rotation helper for the SVD split (builds MatrixUtil PI_4) only +GivensParameters.aroundX skip:other Givens rotation helper for the SVD split only +GivensParameters.aroundY skip:other Givens rotation helper for the SVD split only +GivensParameters.aroundZ skip:other Givens rotation helper for the SVD split only +GivensParameters.cos skip:other Givens helper for the SVD split only +MatrixUtil.mulComponentWise missing minor: unsure: the 0.5 GUI and 0.75 first-person foil decal scale of ItemFeatureRenderer.computeFoilDecalPose is not found; glint scale is per context in item_render.rs Glint (see client2-004.tsv:15) +MatrixUtil.approxGivensQuat skip:other Jacobi step helper for the SVD split only +MatrixUtil.qrGivensQuat skip:other QR Givens helper for the SVD split only +MatrixUtil.similarityTransform skip:other Jacobi step helper for the SVD split only +MatrixUtil.stepJacobi skip:other Jacobi eigen step for the SVD split only +MatrixUtil.eigenvalueJacobi skip:other Jacobi eigen solver for the SVD split only +MatrixUtil.svdDecompose skip:other SVD of a matrix-built Transformation; Horizon's display data arrives decomposed (text_display.rs) and item transforms stay matrices (item_model.rs::transformation) +MatrixUtil.checkProperty skip:java JOML matrix property cache check; Horizon's affine matrices are plain values +OctahedralGroup.trace skip:java group enumeration index for the Cayley table; Horizon indexes block rotations by quarter turns +OctahedralGroup.rotate port crates/horizon-assets/src/bake/transform.rs::BlockModelTransform::direction rotates a face direction through the blockstate's quarter-turn matrix (nearest axis) +OctahedralGroup.inverts inline crates/horizon-assets/src/bake/transform.rs::BlockModelTransform::direction the axis sign is read from the matrix inside direction() +Quadrant.parseJson port crates/horizon-assets/src/model.rs::Face::decode face rotation must be a multiple of 90, reduced mod 360 to quarter turns +SymmetricGroup3.permute inline crates/horizon-assets/src/bake/transform.rs::BlockModelTransform::position signed axis permutation is held as the matrix applied by position() +SymmetricGroup3.permuteVector inline crates/horizon-assets/src/bake/transform.rs::BlockModelTransform::position applies the axis permutation to float vectors; the Vector3i overload has no separate port +Transformation.compose port crates/horizon-assets/src/bake/transform.rs::Affine::mul matrix product of two transforms +Transformation.inverse port crates/horizon-assets/src/bake/transform.rs::Affine::inverse gap: minor: no null result for a non-finite inverse +Transformation.ensureDecomposed skip:other SVD split of a matrix-built transform; Horizon never needs it (text displays store decomposed parts) +Transformation.equals skip:java value equality of a Java wrapper +Transformation.slerp port crates/horizon-client/src/text_display.rs::TextDisplayData::blend gap: player: only text displays ease their transform; item and block displays have no renderer here, so their transform changes snap +CharPredicate.and skip:java Java functional combinator +CharPredicate.or skip:java Java functional combinator +ChatFormatting.getByCode inline crates/horizon-client-core/src/text.rs::Text::legacy colour codes come from COLORS, format codes from the k-o and r arms, both in the legacy parser +CrashReport.getDetails inline crates/horizon-client/src/crash.rs::Report::text the report body (details, system section) is built in Report::text +CrashReport.getExceptionMessage port crates/horizon-client/src/crash.rs::stack_lines enhanced stack text for the error +CrashReport.copyProperties skip:debug rebuilds a Throwable for a message rewrite; Rust panics keep their own message +CrashReport.replaceMessage skip:debug rewrites a null NPE, SOE or OOM message to the title; Rust panics carry messages +CrashReport.getFriendlyReport port crates/horizon-client/src/crash.rs::Report::text the friendly report: header, time, description, exception, details +CrashReport.saveToFile port crates/horizon-client/src/crash.rs::Report::save writes the report under crash-reports/ +CrashReport.addCategory skip:debug crash-report category sections; Horizon's report is a flat detail list (crash.rs set_detail) +CrashReport.forThrowable port crates/horizon-client/src/crash.rs::report_error gap: minor: report_error also writes and delivers the file; forThrowable only builds the object +CrashReport.preload skip:java reserves memory and warms the report code so a later crash can be written; Horizon's panic path needs no reserve +CrashReportCategory.formatLocation missing minor: crash details never print world coordinates or chunk and region locations +CrashReportCategory.setDetail port crates/horizon-client/src/crash.rs::set_detail gap: minor: flat detail list with no category section +CrashReportCategory.fillInStackTrace skip:debug stack trimming for a crash category; Rust panic backtraces are filtered in stack_lines +CrashReportCategory.validateStackTrace skip:debug stack trimming for a crash category; see stack_lines +CrashReportCategory.getDetails inline crates/horizon-client/src/crash.rs::Report::text category sections are not kept; Report::text writes the details +CrashReportCategory.populateBlockDetails missing minor: crash details never record the block state or its location +DetectedVersion.createBuiltIn skip:data version constants come from horizon_core::VANILLA_VERSION and the generated tables +DetectedVersion.createFromJson skip:data version.json is not read; version data is compiled in +DetectedVersion.tryDetectVersion skip:data version.json is not read; version data is compiled in +Optionull.first skip:java null-safe Java helper +Optionull.firstOrDefault skip:java null-safe Java helper +Optionull.firstOrElse skip:java null-safe Java helper +Optionull.isNullOrEmpty skip:java null-safe Java helper +net/minecraft/ReportType#getErrorComment inline crates/horizon-client/src/crash.rs::Report::text the header comment is picked from COMMENTS inside Report::text +net/minecraft/ReportType#appendHeader port crates/horizon-client/src/crash.rs::Report::text writes the report header and comment +SharedConstants.booleanProperty skip:debug reads a JVM system property for debug flags +SharedConstants.debugFlag skip:debug debug flag reader +SharedConstants.debugIntValue skip:debug debug value reader +SharedConstants.setVersion skip:data version is a compiled constant +SharedConstants.debugVoidTerrain skip:debug debug world generation switch +SystemReport. port crates/horizon-client/src/crash.rs::system_details gap: minor: no GPU, storage, process or JVM lines +SystemReport.printMemoryUsage skip:other JVM heap usage text; a Rust build has no JVM heap +SystemReport.printJvmFlags skip:other JVM input arguments; a Rust build has none +SystemReport.setDetail port crates/horizon-client/src/crash.rs::set_detail gap: minor: flat detail list +SystemReport.putHardware inline crates/horizon-client/src/crash.rs::system_details gap: minor: CPU and memory are the only hardware lines kept +SystemReport.putSoftware port crates/horizon-client/src/crash.rs::operating_system gap: minor: OS name only; no elevated flag or process details +SystemReport.ignoreErrors skip:debug error isolation for diagnostic collection +SystemReport.putPhysicalMemory port crates/horizon-client/src/crash.rs::memory gap: minor: no per-module memory lines +SystemReport.putVirtualMemory missing minor: no virtual or swap memory lines in the crash details +SystemReport.putMemory port crates/horizon-client/src/crash.rs::memory gap: minor: memory summary only +SystemReport.putGraphics missing minor: no GPU enumeration in the crash details +SystemReport.putProcessor port crates/horizon-client/src/crash.rs::cpu gap: minor: CPU name and thread count only; no frequency or package counts +SystemReport.putStorage missing minor: no free-space lines for the storage paths +SystemReport.putProcessDetails missing minor: no process load or memory lines in the crash details +SystemReport.putSpaceForPath missing minor: no free-space lines for the storage paths +SystemReport.appendToCrashReportString inline crates/horizon-client/src/crash.rs::Report::text the system section is written by Report::text +SystemReport.toLineSeparatedString skip:debug plain-text dump of the system section +TracingExecutor.forName skip:debug Tracy zone and thread naming +TracingExecutor.shutdownAndAwait skip:debug executor shutdown for the profiler wrapper +TracingExecutor.wrapUnnamed skip:debug Tracy zone wrapper +WorldVersion.Simple.packVersion skip:data pack format per pack type; the version tables carry it +Advancement.validate missing minor: shipped advancement JSON is already valid; no validation of background or requirements +Advancement.Builder.parent skip:data datagen builder; Horizon reads advancement JSON in Tree::load and parse +Advancement.Builder.rootDisplay skip:data datagen builder; Horizon reads advancement JSON +Advancement.Builder.display skip:data datagen builder; Horizon reads advancement JSON +Advancement.Builder.rewards skip:data datagen builder; Horizon reads advancement JSON +Advancement.Builder.requirements skip:data datagen builder; Horizon reads advancement JSON +AdvancementHolder.equals skip:java Java equality by id +AdvancementNode.getRoot skip:other no upward root walk: Horizon walks down from each root (Tree::visible, Tree::layout, advancements_screen tabs) +AdvancementNode.equals skip:java Java equality by holder +AdvancementProgress.hasProgress inline crates/horizon-client/src/advancements.rs::Progress::to_json the save keeps only advancements with a criterion obtained (empty entries are skipped) +AdvancementProgress.grantProgress port crates/horizon-client/src/advancements.rs::Progress::grant records the criterion date; false when already obtained +AdvancementProgress.revokeProgress port crates/horizon-client/src/advancements.rs::Progress::revoke removes the criterion; false when not obtained +AdvancementProgress.isCriterionDone port crates/horizon-client/src/advancements.rs::Progress::has criterion obtained +AdvancementProgress.getPercent port crates/horizon-client/src/game/advancements_screen.rs::Game::adv_draw_hover obtained over total requirement groups, drives the progress bar and icon frame +AdvancementProgress.getProgressText port crates/horizon-client/src/game/advancements_screen.rs::Game::adv_draw_hover advancements.progress text, only when there are more than one group +AdvancementProgress.getRemainingCriteria missing minor: /advancement test is not implemented; cmd_advancement only grants and revokes +AdvancementProgress.getCompletedCriteria missing minor: /advancement test is not implemented; cmd_advancement only grants and revokes +AdvancementProgress.getFirstProgressDate skip:java used only by compareTo, which has no caller +AdvancementProgress.compareTo skip:java Comparable with no vanilla caller +AdvancementRequirements.test port crates/horizon-client/src/advancements.rs::Progress::done every requirement group has a criterion obtained +AdvancementRequirements.count port crates/horizon-client/src/advancements.rs::Progress::counts groups with a criterion obtained +AdvancementRequirements.anyMatch inline crates/horizon-client/src/advancements.rs::Progress::done the any() over one group +AdvancementRequirements.validate missing minor: shipped advancement requirements are already valid +AdvancementRequirements.names skip:java builds a name set to pre-create progress entries; Horizon creates them on grant +AdvancementRewards.Builder.build skip:data datagen builder; rewards come from advancement JSON +AdvancementTree.remove inline crates/horizon-client/src/game/advancements.rs::Game::remote_advancements gap: minor: removes the ids the server lists; children are not walked here +AdvancementTree.tryInsert inline crates/horizon-client/src/advancements.rs::Tree::children the pass that inserts an advancement once its parent is in +AdvancementTree.get port crates/horizon-client/src/advancements.rs::Tree::get lookup by id +AdvancementType.createAnnouncement inline crates/horizon-client/src/game/advancements.rs::Game::award the chat line is built in award from the frame's key +DisplayInfo.serializeToNetwork port crates/horizon-client/src/lan/advancements.rs::ServerAdvancements::write_advancement writes title, description, icon, frame, flags and background +DisplayInfo.fromNetwork port crates/horizon-protocol/src/packets/play.rs::UpdateAdvancements::read reads the display and its flags +TreeNodePosition.addChild port crates/horizon-client/src/advancements.rs::Layout::add_child adds visible children in order +TreeNodePosition.firstWalk port crates/horizon-client/src/advancements.rs::Layout::first_walk first pass of the tree layout +TreeNodePosition.secondWalk port crates/horizon-client/src/advancements.rs::Layout::second_walk second pass: absolute positions and minimum +TreeNodePosition.thirdWalk port crates/horizon-client/src/advancements.rs::Layout::third_walk third pass: shift to non-negative +TreeNodePosition.executeShifts port crates/horizon-client/src/advancements.rs::Layout::execute_shifts shifts of the children +TreeNodePosition.previousOrThread port crates/horizon-client/src/advancements.rs::Layout::previous_or_thread previous node or thread +TreeNodePosition.nextOrThread port crates/horizon-client/src/advancements.rs::Layout::next_or_thread next node or thread +TreeNodePosition.apportion port crates/horizon-client/src/advancements.rs::Layout::apportion subtree separation +TreeNodePosition.moveSubtree port crates/horizon-client/src/advancements.rs::Layout::move_subtree moves a subtree right +TreeNodePosition.getAncestor port crates/horizon-client/src/advancements.rs::Layout::ancestor_of ancestor among the siblings +TreeNodePosition.finalizePosition inline crates/horizon-client/src/advancements.rs::Tree::layout positions are copied out in layout's final loop +TreeNodePosition.run port crates/horizon-client/src/advancements.rs::Tree::layout the whole layout for one root +net/minecraft/advancements/predicates/BlockPredicate#matches port crates/horizon-client/src/advancements.rs::block gap: minor: block-entity nbt and components are not checked; no vanilla advancement uses them +net/minecraft/advancements/predicates/BlockPredicate#willMatchBlockEntity missing minor: block-entity nbt and components predicates are not read; no vanilla advancement uses them +net/minecraft/advancements/predicates/BlockPredicate#matchesState inline crates/horizon-client/src/advancements.rs::block block id and state properties +net/minecraft/advancements/predicates/BlockPredicate#matchesBlockEntity missing minor: block-entity nbt and components are not read; no vanilla advancement uses them +net/minecraft/advancements/predicates/BlockPredicate#matchesBlockEntityData missing minor: block-entity nbt is not read; no vanilla advancement uses it +net/minecraft/advancements/predicates/BlockPredicate#matchesComponents missing minor: block-entity components are not read; no vanilla advancement uses them +BlockPredicate.Builder.of skip:data datagen builder +BlockPredicate.Builder.build skip:data datagen builder +CollectionContentsPredicate.of skip:java factory that picks a Zero, Single or Multiple strategy; Horizon checks the JSON in potions() +CollectionContentsPredicate.Multiple.test port crates/horizon-client/src/advancements.rs::potions contains: each listed predicate matched by an effect +CollectionCountsPredicate.of skip:java factory that picks a strategy class +CollectionCountsPredicate.Entry.codec skip:data codec +CollectionCountsPredicate.Multiple.test port crates/horizon-client/src/advancements.rs::potions count entries: each count bound checked against the effects +CollectionPredicate.codec skip:data codec +CollectionPredicate.test port crates/horizon-client/src/advancements.rs::potions gap: minor: only the potion effect list is checked; item container and bundle contents are not +net/minecraft/advancements/predicates/DamagePredicate#matches port crates/horizon-client/src/advancements.rs::damage dealt and taken bounds, source entity, blocked and type +DamagePredicate.Builder.type skip:data datagen builder +DamagePredicate.Builder.build skip:data datagen builder +DamageSourcePredicate.matches port crates/horizon-client/src/advancements.rs::damage the type part: damage type tags, direct and source entity, is_direct +DataComponentMatchers.test port crates/horizon-client/src/advancements.rs::components gap: minor: exact component matches only; partial predicates are not read +DataComponentMatchers.isEmpty inline crates/horizon-client/src/advancements.rs::components an empty expectation passes every component check +DataComponentMatchers.Builder.any skip:data datagen builder +EnchantmentPredicate.containedIn port crates/horizon-client/src/advancements.rs::item the enchantments list and level bounds of the item predicate +EnchantmentPredicate.matchesEnchantment inline crates/horizon-client/src/advancements.rs::item one enchantment and its level +FluidPredicate.matches port crates/horizon-client/src/advancements.rs::location gap: minor: the fluid id and tags only; fluid state properties are not checked; no vanilla advancement uses fluid +FluidPredicate.Builder.of skip:data datagen builder +FoodPredicate.matches missing minor: no food level or saturation condition; no vanilla advancement uses one diff --git a/tools/port_map/rest-002.tsv b/tools/port_map/rest-002.tsv new file mode 100644 index 0000000000..355efd49bb --- /dev/null +++ b/tools/port_map/rest-002.tsv @@ -0,0 +1,149 @@ +InputPredicate.matches missing minor: the player predicate's input key (forward, back, jump, sneak, sprint) is not read; no vanilla advancement uses it +ItemPredicate.Builder.of skip:java builder for item predicates in code +LightPredicate.matches inline crates/horizon-client/src/advancements.rs::location the light range inside location() +LocationPredicate.PositionPredicate.of skip:java factory: empty when no bound is set; location() reads the position key directly +LocationPredicate.PositionPredicate.matches inline crates/horizon-client/src/advancements.rs::location the x, y and z ranges inside location() +MinMaxBounds.validateContainedInRange skip:java codec validation that one range lies inside another +MinMaxBounds.Bounds.isAny inline crates/horizon-client/src/advancements.rs::range_f an empty bound matches any value; range_f treats a missing min or max as open +MinMaxBounds.Bounds.validateSwappedBoundsInCodec skip:java codec check rejecting min above max when data loads +MinMaxBounds.Bounds.areSwapped skip:java helper of the codec check for min above max +MinMaxBounds.Bounds.asRange inline crates/horizon-client/src/advancements.rs::range_f range_f tests min and max directly instead of building a range +MinMaxBounds.Bounds.asPoint skip:java codec writer: a single exact value +MinMaxBounds.Bounds.map skip:java converts bounds between number types in Java +MinMaxBounds.Bounds.createCodec skip:java codec for a number or a min/max object; range_f reads the JSON directly +MinMaxBounds.Bounds.readNumber port crates/horizon-command/src/args.rs::read_bounds reads one number of a range argument +MinMaxBounds.Bounds.isAllowedInputChar inline crates/horizon-command/src/args.rs::read_bounds the digit, minus and single dot test inside read_bounds +MinMaxBounds.Doubles.matchesSqr inline crates/horizon-client/src/advancements.rs::distance takes the square root and tests the range; same result as the squared bounds +MinMaxBounds.Ints.matchesSqr skip:other no caller in vanilla 26.3 +MobEffectsPredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: ambient and visible flags are not checked; no vanilla advancement uses them +MobEffectsPredicate.Builder.and skip:java builder for effect predicates in code +MobEffectsPredicate.MobEffectInstancePredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: ambient and visible flags are not checked; amplifier and duration are +NbtPredicate.matches missing minor: custom_data and entity NBT predicates are not read; no vanilla advancement uses them +SingleComponentItemPredicate.matches inline crates/horizon-client/src/advancements.rs::item gap: minor: only enchantments, damage and potion contents are read; other components are ignored +SlotsPredicate.matches missing minor: slot-range item test is not read; no vanilla advancement uses it +SlotsPredicate.matchSlots missing minor: helper of SlotsPredicate.matches; not read +StatePropertiesPredicate.matches port crates/horizon-client/src/advancements.rs::block the state property test of block() +StatePropertiesPredicate.checkState skip:data load-time check that property names exist +StatePropertiesPredicate.Builder.hasProperty skip:java builder +StatePropertiesPredicate.ExactMatcher.match inline crates/horizon-client/src/advancements.rs::block exact value compare of the state map +StatePropertiesPredicate.PropertyMatcher.match inline crates/horizon-client/src/advancements.rs::block present property compared by block() +StatePropertiesPredicate.PropertyMatcher.checkState skip:data load-time check that the property exists +StatePropertiesPredicate.RangedMatcher.match inline crates/horizon-client/src/advancements.rs::block gap: minor: ranges compare numeric property values only +TagPredicate.codec skip:java codec +TagPredicate.is skip:java builder +TagPredicate.isNot skip:java builder +TagPredicate.matches inline crates/horizon-client/src/advancements.rs::id_matches gap: minor: the #tag form is read; the negated expected=false form is not kept +CubeMobPredicate.matches missing minor: type_specific/cube_mob (slime size) is not read; no vanilla advancement uses it +DistanceToPlayerPredicate.matches port crates/horizon-client/src/advancements.rs::distance the distance key of entity(): from the player to the entity +EntityEquipmentPredicate.captainPredicate skip:data datagen builder for the raid captain predicate (VanillaAdventureAdvancements) +EntityEquipmentPredicate.matches inline crates/horizon-client/src/advancements.rs::entity the equipment key: the seven slots +EntityEquipmentPredicate.Builder.build skip:java builder +EntityFlagsPredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: is_in_water and is_fall_flying are not read; vanilla advancements only use is_baby +EntityFlagsPredicate.Builder.build skip:java builder +EntityLocationPredicate.matches port crates/horizon-client/src/advancements.rs::location the location key of entity() +EntityPartialComponentsPredicate.matches missing minor: the predicates key is accepted and ignored (entity() returns true); no vanilla advancement uses it +EntityPredicate.wrap skip:java wraps a builder into a loot condition +EntityPredicate.createContext inline crates/horizon-client/src/game/advancements.rs::Game::trigger_entity the entity context (this entity, origin at the player) built in each trigger +EntityPredicate.equals skip:java Java equality +EntityPredicate.combine inline crates/horizon-client/src/advancements.rs::entity the AND of the sub-predicates; entity() tests every key in turn +EntityPredicate.Builder.of skip:java builder +EntityPredicate.Builder.equipment skip:java builder +EntitySubPredicate.and inline crates/horizon-client/src/advancements.rs::entity the AND of two sub-predicates; entity() tests every key +EntityTagPredicate.matches missing minor: entity_tags key is not read; no vanilla advancement uses it +EntityTagPredicate.containsAtLeastOne missing minor: helper of entity_tags; not read +EntityTagPredicate.containsAllOf missing minor: helper of entity_tags; not read +EntityTypePredicate.of skip:java builder from an entity type or tag +EntityTypePredicate.matches port crates/horizon-client/src/advancements.rs::id_matches the type key of entity(): an id or #tag +FishingHookPredicate.matches inline crates/horizon-client/src/advancements.rs::entity the type_specific/fishing_hook arm: open-water test +LightningBoltPredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: blocks_set_on_fire is checked against 0, not the bolt's own count; the lightning trigger never fires +MovementAffectedByPredicate.matches missing minor: movement_affected_by key is not read; no vanilla advancement uses it +MovementPredicate.matches missing minor: movement key (speed, fall distance) is not read; no vanilla advancement uses it +PassengerPredicate.matches missing player: husbandry/ride_a_boat_with_a_goat cannot complete: the passenger key inside vehicle falls through entity() to false +PlayerPredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: only gamemode and looking_at are read; level, food, stats, recipes, advancements and input are not +PlayerPredicate.AdvancementCriterionsPredicate.test missing minor: the advancements key of a player predicate is not read; no vanilla advancement uses it +PlayerPredicate.Builder.addStat skip:java builder +PlayerPredicate.Builder.checkAdvancementDone skip:java builder +PlayerPredicate.Builder.checkAdvancementCriterions skip:java builder +PlayerPredicate.Builder.build skip:java builder +PlayerPredicate.StatMatcher.createTypedCodec skip:java codec +RaiderPredicate.matches missing minor: type_specific/raider is not read in entity(); no vanilla advancement uses it +SheepPredicate.matches missing minor: type_specific/sheep is not read; no vanilla advancement uses it +SteppingOnPredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: no on-ground test; the block is read 0.5 below the feet +TargetedEntityPredicate.matches missing minor: targeted_entity key is not read; no vanilla advancement uses it +TeamPredicate.matches missing minor: team key is not read though scoreboard teams exist; no vanilla advancement uses it +AnyBlockInteractionTrigger.trigger missing minor: any_block_use never fires; no vanilla advancement uses it +AnyBlockInteractionTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_block the location test; only the firing of the trigger is missing +AnyBlockInteractionTrigger.TriggerInstance.validate skip:data load-time validation of the location +BeeNestDestroyedTrigger.trigger missing player: breaking a bee nest with bees never fires the criterion; husbandry/silk_touch_nest cannot complete +BeeNestDestroyedTrigger.TriggerInstance.destroyedBeeNest skip:java builder +BeeNestDestroyedTrigger.TriggerInstance.matches missing player: the bee nest test is never run because the trigger never fires +BredAnimalsTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_bred parent, partner and child contexts +BredAnimalsTrigger.TriggerInstance.bredAnimals skip:java builder +BredAnimalsTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_bred parent and partner in either order, then child +BredAnimalsTrigger.TriggerInstance.validate skip:data load-time validation +BrewedPotionTrigger.TriggerInstance.matches port crates/horizon-client/src/advancements.rs::potions the potion test +ChangeDimensionTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_dimension from and to dimensions +ChangeDimensionTrigger.TriggerInstance.changedDimension skip:java builder +ChangeDimensionTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_dimension from and to test +ChanneledLightningTrigger.trigger missing player: Channeling strikes lightning (channel_lightning) but the channeled-lightning criterion never fires; adventure/very_very_frightening cannot complete +ChanneledLightningTrigger.TriggerInstance.matches missing player: never run, the trigger never fires +ConstructBeaconTrigger.TriggerInstance.constructedBeacon skip:java builder +ConsumeItemTrigger.TriggerInstance.usedItem skip:java builder +ConsumeItemTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_item the item test; consume.rs passes the stack +Criterion.criterionCodec skip:java codec +CuredZombieVillagerTrigger.trigger port crates/horizon-client/src/game/zombie_villagers.rs::Game::trigger_cured_zombie_villager zombie and villager contexts +CuredZombieVillagerTrigger.TriggerInstance.matches port crates/horizon-client/src/game/zombie_villagers.rs::Game::trigger_cured_zombie_villager zombie then villager test +CuredZombieVillagerTrigger.TriggerInstance.validate skip:data load-time validation +DefaultBlockInteractionTrigger.trigger missing minor: default_block_use (block use with an empty hand) never fires; no vanilla advancement uses it +DefaultBlockInteractionTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_block the location test +DefaultBlockInteractionTrigger.TriggerInstance.validate skip:data load-time validation +DistanceTrigger.trigger missing player: fall_from_height and ride_entity_in_lava never fire (no fall or lava-ride tracking); ride_strider_in_overworld_lava and fall_from_world_height cannot complete +DistanceTrigger.TriggerInstance.fallFromHeight skip:java builder +DistanceTrigger.TriggerInstance.rideEntityInLava skip:java builder +DistanceTrigger.TriggerInstance.matches missing player: the start-position and distance test is never run; the trigger never fires +EffectsChangedTrigger.trigger missing player: effects_changed never fires; nether/all_effects, nether/all_potions and husbandry/kill_axolotl_target cannot complete +EffectsChangedTrigger.TriggerInstance.hasEffects skip:java builder +EffectsChangedTrigger.TriggerInstance.gotEffectsFrom skip:java builder +EffectsChangedTrigger.TriggerInstance.matches missing player: never run; the effects and source tests are not in Horizon +EnchantedItemTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_enchanted_item the enchanted item and its levels +EnchantedItemTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_enchanted_item item and levels test +EnterBlockTrigger.TriggerInstance.entersBlock skip:java builder +EnterBlockTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_enter_block block and state test +EntityHurtPlayerTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_entity_hurt_player the player and the damage it took +EntityHurtPlayerTrigger.TriggerInstance.matches port crates/horizon-client/src/advancements.rs::damage the damage predicate +FallAfterExplosionTrigger.trigger missing player: fall_after_explosion never fires; adventure/who_needs_rockets cannot complete +FallAfterExplosionTrigger.TriggerInstance.fallAfterExplosion skip:java builder +FallAfterExplosionTrigger.TriggerInstance.matches missing player: never run; the trigger never fires +FilledBucketTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_item the item test; filled_bucket is fired through trigger_item +FishingRodHookedTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_fishing_rod_hooked the hook as the hooked entity and the caught stacks +FishingRodHookedTrigger.TriggerInstance.fishedItem skip:java builder +FishingRodHookedTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_fishing_rod_hooked gap: minor: the stack of a hooked item entity is not tested; only caught stacks are +InventoryChangeTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::check_inventory_trigger slot counts, then the inventory index +InventoryChangeTrigger.TriggerInstance.hasItems skip:java builder +InventoryChangeTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::check_inventory_trigger item predicates: each matched by some stack +InventoryChangeTrigger.TriggerInstance.Slots.matches port crates/horizon-client/src/game/advancements.rs::Game::check_inventory_trigger occupied, full and empty ranges +ItemDurabilityTrigger.trigger missing player: item_durability_changed never fires; nether/ride_strider cannot complete +ItemDurabilityTrigger.TriggerInstance.changedDurability skip:java builder +ItemDurabilityTrigger.TriggerInstance.matches missing player: never run; the trigger never fires +ItemUsedOnLocationTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_block gap: minor: no block entity in the context +ItemUsedOnLocationTrigger.TriggerInstance.placedBlock skip:java builder +ItemUsedOnLocationTrigger.TriggerInstance.placedBlockWithProperties skip:java builder +ItemUsedOnLocationTrigger.TriggerInstance.itemUsedOnLocation skip:java builder +ItemUsedOnLocationTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_block the location test +ItemUsedOnLocationTrigger.TriggerInstance.validate skip:data load-time validation +KilledByArrowTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_killed_by_arrow victims and the weapon +KilledByArrowTrigger.TriggerInstance.crossbowKilled skip:java builder +KilledTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_entity gap: player: the killing_blow condition is never tested; sniper_duel, blowback and return_to_sender complete on any matching kill +KilledTrigger.TriggerInstance.playerKilledEntity skip:java builder +KilledTrigger.TriggerInstance.entityKilledPlayer skip:java builder +KilledTrigger.TriggerInstance.matches inline crates/horizon-client/src/game/advancements.rs::Game::trigger_entity gap: player: killing_blow not tested (see KilledTrigger.trigger) +LevitationTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_levitation start, duration and the player +LevitationTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_levitation distance and duration tests +LightningStrikeTrigger.trigger missing player: lightning_strike never fires; adventure/lightning_rod_with_villager_no_fire cannot complete +LightningStrikeTrigger.TriggerInstance.lightningStrike skip:java builder +LightningStrikeTrigger.TriggerInstance.matches missing player: never run; the bystander test is not in Horizon +LightningStrikeTrigger.TriggerInstance.validate skip:data load-time validation +LootTableTrigger.TriggerInstance.lootTableUsed skip:java builder +PickedUpItemTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_item_entity gap: player: only the sulfur cube absorb fires the entity variant; thrown_item_picked_up_by_player never fires (husbandry/allay_deliver_item_to_player) +PickedUpItemTrigger.TriggerInstance.matches port crates/horizon-client/src/advancements.rs::item_entity player, entity and item tests +PlayerHurtEntityTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_player_hurt_entity the victim, the damage and the player diff --git a/tools/port_map/rest-003.tsv b/tools/port_map/rest-003.tsv new file mode 100644 index 0000000000..8103fd1f37 --- /dev/null +++ b/tools/port_map/rest-003.tsv @@ -0,0 +1,143 @@ +PlayerHurtEntityTrigger.TriggerInstance.playerHurtEntityWithDamage skip:data criterion factory; Horizon reads the criterion from the advancement JSON +PlayerHurtEntityTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_player_hurt_entity player, damage and victim predicates through advancements.rs damage and conditions +PlayerInteractTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_item_entity gap: player: fires player_interacted_with_entity only; player_sheared_equipment (remove_wolf_armor) is never fired +PlayerInteractTrigger.TriggerInstance.itemUsedOnEntity skip:data criterion factory +PlayerInteractTrigger.TriggerInstance.equipmentSheared skip:data criterion factory; the sheared trigger it feeds is never fired, see PlayerInteractTrigger.trigger +PlayerInteractTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_item_entity item and entity predicates +PlayerTrigger.TriggerInstance.located skip:data criterion factory +PlayerTrigger.TriggerInstance.walkOnBlockWithEquipment skip:data criterion factory +RecipeCraftedTrigger.trigger port crates/horizon-client/src/game/machines/crafter.rs::Game::crafter_eject gap: player: only the crafter block fires; player crafting never fires recipe_crafted, so the trim and decorated pot advancements are not granted +RecipeCraftedTrigger.TriggerInstance.craftedItem skip:data criterion factory +RecipeCraftedTrigger.TriggerInstance.matches port crates/horizon-client/src/game/machines/crafter.rs::recipe_matches gap: minor: recipe id only; ingredient predicates are not checked +RecipeUnlockedTrigger.unlocked skip:data criterion factory +RecipeUnlockedTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::recipe_unlocked matched by recipe id +ShotCrossbowTrigger.TriggerInstance.shotCrossbow skip:data criterion factory +ShotCrossbowTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_item fired from crossbow.rs with the shot item +SlideDownBlockTrigger.TriggerInstance.slidesDownBlock skip:data criterion factory +SlideDownBlockTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_honey_slide block and state match +SpearMobsTrigger.TriggerInstance.matches missing player: the spear_many_mobs advancement is never granted; spear hits do not count the mobs they hit (spear.rs fires no trigger) +SummonedEntityTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_entity summoned_entity fired from wither.rs +SummonedEntityTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_entity entity predicate +TameAnimalTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_entity tame_animal fired from creatures.rs, nautili.rs and mounts.rs +TameAnimalTrigger.TriggerInstance.tamedAnimal skip:data criterion factory +TameAnimalTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_entity entity predicate +TargetBlockTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_target_hit fired from creatures.rs on projectile hit +TargetBlockTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_target_hit signal strength and projectile predicates +TradeTrigger.trigger port crates/horizon-client/src/game/advancements.rs::Game::trigger_trade fired from trading.rs +TradeTrigger.TriggerInstance.tradedWithVillager skip:data criterion factory +TradeTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_trade villager and item predicates +UsedEnderEyeTrigger.trigger missing minor: no 26.3 advancement uses used_ender_eye; ender eye use (end.rs) fires no trigger +UsedTotemTrigger.TriggerInstance.usedTotem skip:data criterion factory +UsedTotemTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_item fired from totem.rs +UsingItemTrigger.TriggerInstance.lookingAt skip:data criterion factory +UsingItemTrigger.TriggerInstance.matches port crates/horizon-client/src/game/advancements.rs::Game::trigger_using_item item and looking-at predicates +Camera.createProjectionMatrixForCulling port crates/horizon-client/src/gfx.rs::Renderer::camera_frustum gap: minor: the max(FOV, option FOV) rule is not in this fn +Camera.prepareCullFrustum port crates/horizon-client/src/gfx.rs::Renderer::camera_frustum gap: minor: no captured frustum (F3+U kill and capture) +Camera.setPosition inline crates/horizon-client/src/camera.rs::Camera::align_with_entity the position is stored there +Camera.getViewRotationMatrix port crates/horizon-client/src/camera.rs::view_matrix +Camera.NearPlane.getPointOnPlane inline crates/horizon-client/src/game/fishing.rs::Game::fishing_line only the fishing line's anchor uses it +ClientBootstrap.bootstrap skip:data registry bootstrap +ClientRecipeBook.rebuildCollections port crates/horizon-client/src/recipe_book.rs::ClientBook::collections tab collections built per call +ClientRecipeBook.categorizeAndGroupRecipes port crates/horizon-client/src/recipe_book.rs::ClientBook::collections grouping by category and group id +CommandHistory.addCommand port crates/horizon-client/src/chat.rs::Chat::add_recent no repeats; keeps the last 50 saved commands +CommandHistory.save inline crates/horizon-client/src/chat.rs::Chat::add_recent writes command_history.txt there +ComponentCollector.getResult skip:java collector helper +DebugQueryHandler.handleResponse skip:debug F3 NBT query: packet ids exist in horizon-protocol but nothing sends them +DebugQueryHandler.startTransaction skip:debug F3 NBT query transaction; see handleResponse +DebugQueryHandler.queryEntityTag skip:debug F3 entity NBT query; see handleResponse +DebugQueryHandler.queryBlockEntityTag skip:debug F3 block entity NBT query; see handleResponse +DeltaTracker.Timer.pause port crates/horizon-client/src/clock.rs::Timer::update_pause_state residual kept while paused +DeltaTracker.Timer.unPause port crates/horizon-client/src/clock.rs::Timer::update_pause_state residual restored on resume +GameNarrator missing minor: no text-to-speech; the narrator option is stored but nothing is spoken +HotbarManager.get port crates/horizon-client/src/hotbar.rs::load file loaded when the creative hotbar opens (creative_hotbar.rs) +KeyMapping.forAllKeyMappings port crates/horizon-client-input/src/keybinds.rs::KeyBinds::mappings_for one key drives every mapping bound to it +KeyMapping.setAll missing player: held keys are not re-read when the window regains focus; after alt-tab with a move key held, the walk stops until the key is pressed again +KeyMapping.resetMapping skip:other no lookup table to rebuild; mappings_for reads the current keys +KeyMapping.compareTo skip:ui controls list order is the MAPPINGS table order in keybinds.rs +KeyMapping.createNameSupplier skip:ui key label supplier +KeyMapping.matches port crates/horizon-client-input/src/keybinds.rs::KeyBinds::mappings_for keyboard key to mapping +KeyMapping.matchesMouse port crates/horizon-client-input/src/keybinds.rs::KeyBinds::mappings_for mouse buttons bound by name +KeyMapping.registerMapping skip:java mapping table registration +KeyMapping.Category.register skip:data category registry +KeyboardHandler.handleChunkDebugKeys missing minor: F3+E, F, L, M, O, U, V, W do nothing (section paths, fog, smart cull, multi-draw, octree, frustum, section visibility, wireframe); no keybinds exist for them +KeyboardHandler.copyCreateBlockCommand port crates/horizon-client/src/game/debug_keys.rs::Game::copy_recreate_command +KeyboardHandler.copyCreateEntityCommand port crates/horizon-client/src/game/debug_keys.rs::Game::copy_recreate_command +KeyboardHandler.charTyped port crates/horizon-client/src/game.rs::Game::window_event typed text routed to the open screen +KeyboardHandler.textInput port crates/horizon-client/src/game.rs::Game::window_event typed text routed to the open screen +KeyboardHandler.textEditing missing player: no IME composition; Japanese or Chinese text shows nothing until it is committed +KeyboardHandler.submitPreeditEvent missing player: no IME preedit event reaches any screen +Minecraft. inline crates/horizon-client/src/game.rs::run_loading gap: minor: no unclean-start recovery; startedCleanly is only a default in options_file.rs +Minecraft.onGameLoadFinished missing minor: startedCleanly is never written after load +Minecraft.createTitle missing minor: the window title is always Horizon (with_title in game.rs), no version or world type +Minecraft.createUserApiService skip:service +Minecraft.abortResourcePackRecovery missing minor: no automatic abort when a pack keeps failing; errors stay on the Packs page +Minecraft.addResourcePackLoadFailToast port crates/horizon-client/src/game/packs.rs::Game::reload_packs gap: minor: the failure is a Packs-page error, not a toast +Minecraft.triggerResourcePackRecovery missing minor: a failing pack at runtime is not removed; reload_packs only reports reload errors +Minecraft.run port crates/horizon-client/src/game.rs::run the main loop +Minecraft.saveReport port crates/horizon-client/src/crash.rs::Report::save +Minecraft.saveReportAndShutdownSoundManager port crates/horizon-client/src/crash.rs::Report::save gap: minor: sound shutdown is not part of Report::save +Minecraft.selfTest skip:debug dev self-test, disabled in vanilla (the IS_RUNNING_IN_IDE && false check) +Minecraft.exitWorldAndClose port crates/horizon-client/src/game.rs::Game::close_world +Minecraft.close port crates/horizon-client/src/game.rs::Game::finish_close_world shutdown of the session +Minecraft.renderFrame port crates/horizon-client/src/game.rs::Game::render +Minecraft.pauseIfInactive port crates/horizon-client/src/game.rs::Game::window_event gap: minor: pauses at the focus-lost event, not after 500 ms without focus +Minecraft.constructProfiler skip:debug profiler plumbing +Minecraft.finishProfilers skip:debug profiler plumbing +Minecraft.resizeGui port crates/horizon-client-input/src/window.rs::calculate_scale gap: minor: GUI scale only; no screen resize or mouse resync in this fn +Minecraft.emergencySave missing unsure: no world save before a crash report was found; the panic path only writes the report +Minecraft.debugClientMetricsStart skip:debug metrics recording +Minecraft.archiveProfilingReport skip:debug profiling archive +Minecraft.isLevelRunningNormally port crates/horizon-client/src/clock.rs::TickRateManager::runs_normally +Minecraft.isMultiplayerServer skip:other no single predicate; remote and LAN code test their own state +Minecraft.getQuickActionsDialog missing minor: no key.quickActions binding, so the quick actions dialog cannot open +Minecraft.disconnectFromWorld port crates/horizon-client/src/game/remote.rs::Game::leave_server gap: minor: only the remote exit; local worlds exit via close_world +Minecraft.disconnectWithProgressScreen inline crates/horizon-client/src/game.rs::Game::close_world the exit is folded into the world close +Minecraft.clearClientLevel inline crates/horizon-client/src/game/remote.rs::Game::leave_server level teardown is part of leave_server +Minecraft.setScreenAndShow skip:other Horizon switches the screen field; no forced frame render needed +Minecraft.updateLevelInEngines skip:other one world state in Horizon; no engines to update +Minecraft.telemetryOptInExtra skip:service +Minecraft.extraTelemetryAvailable skip:service +Minecraft.allowsTelemetry skip:service +Minecraft.allowsMultiplayer skip:service +Minecraft.allowsRealms skip:service +Minecraft.friendsEnabled skip:service +Minecraft.handleGlobalKeyPress port crates/horizon-client/src/game.rs::Game::key_pressed F11 and F2 shortcuts; the friends key is service +Minecraft.toggleFullscreen port crates/horizon-client/src/game/options.rs::Game::toggle_fullscreen +Minecraft.toggleFriendsScreen skip:service +Minecraft.allowChatOnlyWithFriend skip:service +Minecraft.isNameBanned skip:service +Minecraft.isBlocked skip:service +Minecraft.isFriendOnlyRestricted skip:service +Minecraft.isLocalOrUnknownPlayer skip:service +Minecraft.fillReport skip:debug crash report details; crash.rs writes its own report +Minecraft.formatSeconds skip:debug report helper +Minecraft.fillUptime skip:debug report helper +Minecraft.fillSystemReport skip:debug system report text +Minecraft.updateReportEnvironment skip:service reporting context for chat reports +Minecraft.getGameProfile skip:service Mojang profile fetch; Horizon's account is local (auth.rs) +Minecraft.showOnlyReducedInfo port crates/horizon-client/src/game/debug_keys.rs::Game::reduced_debug_info +Minecraft.grabPanoramixScreenshot skip:debug only runs with SharedConstants.DEBUG_PANORAMA_SCREENSHOT +Minecraft.onTextInputFocusChange missing minor: no IME, so nothing to re-submit on focus +Minecraft.getTickTargetMillis port crates/horizon-client/src/clock.rs::TickRateManager::milliseconds_per_tick gap: minor: the max with the default tick time is not in this fn +MouseHandler.fillMousePositionDetails skip:debug crash report mouse details +MouseHandler.onDrop missing minor: dropping files on the window does nothing; no drop event is handled +MouseHandler.getScaledYPos port crates/horizon-client-input/src/window.rs::scaled_pos +MouseHandler.resyncMousePosition skip:other winit reports the cursor position; no SDL global-state resync +MouseHandler.releaseMouse port crates/horizon-client/src/game.rs::Game::capture release path (capture false) +MouseHandler.drawDebugMouseInfo skip:debug F3 mouse coordinates +NarratorStatus.shouldNarrateSystemOrChat missing minor: no narrator output +OptionInstance.createBoolean skip:data option definitions live in options_file.rs +OptionInstance.createButton skip:ui widget creation +OptionInstance.AltEnum.validateValue port crates/horizon-client/src/options_file.rs::Spec::validate +OptionInstance.ClampingLazyMaxIntRange.codec skip:java codec +OptionInstance.ClampingLazyMaxIntRange.valueListSupplier skip:ui widget value list +OptionInstance.CycleableValueSet.createButton port crates/horizon-client/src/options_ui.rs::Opt::cycle gap: minor: cycle() does not save; the caller decides +OptionInstance.IntRange.validateValue port crates/horizon-client/src/options_file.rs::Spec::validate +OptionInstance.IntRangeBase.toSliderValue port crates/horizon-client/src/options_ui.rs::Opt::slider_position +OptionInstance.IntRangeBase.fromSliderValue port crates/horizon-client/src/options_ui.rs::Opt::set_slider +OptionInstance.OptionInstanceSliderButton.updateMessage skip:ui label and tooltip text +OptionInstance.OptionInstanceSliderButton.applyValue port crates/horizon-client/src/options_ui.rs::delays_apply gap: minor: values apply at once; the 600 ms delay only holds the handle +OptionInstance.OptionInstanceSliderButton.applyUnsavedValue port crates/horizon-client/src/options_ui.rs::set_slider +OptionInstance.OptionInstanceSliderButton.resetValue port crates/horizon-client/src/options_ui.rs::OptionsUi::slider_shown +OptionInstance.OptionInstanceSliderButton.extractWidgetRenderState port crates/horizon-client/src/options_ui.rs::slider_unsnapped gap: minor: no deferred apply after 600 ms; only the handle display +OptionInstance.OptionInstanceSliderButton.keyPressed missing unsure: no arrow-key slider control found in options_ui.rs or game.rs diff --git a/tools/port_map/rest-004.tsv b/tools/port_map/rest-004.tsv new file mode 100644 index 0000000000..d33081e0af --- /dev/null +++ b/tools/port_map/rest-004.tsv @@ -0,0 +1,148 @@ +OptionInstance.SliderableEnum.toSliderValue port crates/horizon-client/src/options_ui.rs::Opt::kind slider position of the graphics preset (index / 3) +OptionInstance.SliderableEnum.next missing minor: unsure: no next-value step for the graphics preset slider; Opt::cycle has no Preset arm +OptionInstance.SliderableEnum.previous missing minor: unsure: no previous-value step for the graphics preset slider; Opt::cycle has no Preset arm +OptionInstance.SliderableEnum.fromSliderValue port crates/horizon-client/src/options_ui.rs::Opt::set_slider gap: minor: rounds v*3 where vanilla floors v*4 +OptionInstance.SliderableEnum.validateValue port crates/horizon-client/src/options_file.rs::Spec::validate named spec accepts the four preset indexes +OptionInstance.SliderableOrCyclableValueSet.createButton port crates/horizon-client/src/options_ui.rs::option_widget cycle or slider button by option kind +OptionInstance.SliderableValueSet.createButton port crates/horizon-client/src/options_ui.rs::option_widget slider button +OptionInstance.UnitDouble.validateValue port crates/horizon-client/src/options_file.rs::Spec::validate unit range 0..1 +OptionInstance.UnitDouble.codec port crates/horizon-client/src/options_file.rs::codec_double +Options.updateResourcePacks port crates/horizon-client/src/game/options.rs::Game::handle_option_events gap: minor: saves and reloads on every packs event; vanilla only when the list changed +Options.maxAnisotropyValue missing player: no anisotropic filtering setting; textures are sampled without anisotropy +Options.createSoundSliderOptionInstance port crates/horizon-client/src/audio.rs::Audio::sync_volumes gap: player: no sound preview outside a level and no now-playing toast when master or music changes +Options.setGraphicsPresetToCustom inline crates/horizon-client/src/options_ui.rs::Opt::set_slider gap: minor: cycle buttons set custom in Opt::cycle as well +Options.processOptions inline crates/horizon-client/src/options_file.rs::OptionsFile::parse gap: minor: the save direction is OptionsFile::serialize +Options.dataFix port crates/horizon-client/src/options_fixes.rs::update +Options.getFullscreenVideoModeString missing player: the window's fullscreen mode is never saved to options.txt (fullscreen_resolution is only read back) +Options.buildPlayerInformation port crates/horizon-client/src/net.rs::run gap: player: only view distance and chat visibility are sent; language, skin parts, main hand, particles and text filtering stay at defaults +Options.setModelPart missing player: skin layer toggles (hat, jacket, sleeves, pants legs, cape) do not change the drawn skin or the info sent to the server +Options.loadSelectedResourcePacks port crates/horizon-client/src/packs.rs::enabled gap: minor: dropped packs are not written back to resourcePacks +Options.readListOfStrings port crates/horizon-client/src/options_file.rs::read_list +Options.dumpOptionsForReport skip:debug options dump for the debug report +Options.pixelValueLabel port crates/horizon-client/src/options_ui.rs::chat_px +Options.percentValueLabel port crates/horizon-client/src/options_ui.rs::percent +Options.genericValueLabel port crates/horizon-client/src/options_ui.rs::generic +Options.percentValueOrOffLabel port crates/horizon-client/src/options_ui.rs::Opt::value_text gap: minor: OFF text is only drawn for sound volumes +PeriodicNotificationManager.prepare skip:other regional play-time notices (REGIONAL_COMPLIANCIES, country check); none ship with the 26.3 assets and Horizon has no equivalent +PeriodicNotificationManager.apply skip:other regional play-time notices (REGIONAL_COMPLIANCIES, country check); none ship with the 26.3 assets and Horizon has no equivalent +PeriodicNotificationManager.calculateOptimalPeriod skip:other regional play-time notices (REGIONAL_COMPLIANCIES, country check); none ship with the 26.3 assets and Horizon has no equivalent +PeriodicNotificationManager.calculateInitialDelay skip:other regional play-time notices (REGIONAL_COMPLIANCIES, country check); none ship with the 26.3 assets and Horizon has no equivalent +PeriodicNotificationManager.NotificationTask.reset skip:other regional play-time notices; see the manager +PeriodicNotificationManager.NotificationTask.run skip:other regional play-time notices; see the manager +PreferredGraphicsApi.getBackendsToTry skip:other GL is not a Horizon backend; the Vulkan then DX12 order lives in launcher/src/backend.rs +ResourceLoadStateTracker.startReload skip:debug crash-report state of the last resource reload +ResourceLoadStateTracker.startRecovery skip:debug crash-report state of the last resource reload +ResourceLoadStateTracker.finishReload skip:debug crash-report state of the last resource reload +ResourceLoadStateTracker.fillCrashReport skip:debug crash-report state of the last resource reload +ResourceLoadStateTracker.RecoveryInfo.fillCrashInfo skip:debug crash report detail +ResourceLoadStateTracker.ReloadState.fillCrashInfo skip:debug crash report detail +RotatingSectionStorage. skip:java ring of section nodes; Horizon keys dirty sections by coordinates (DirtySections) +RotatingSectionStorage.repositionCenter skip:java ring of section nodes; Horizon keys dirty sections by coordinates (DirtySections) +RotatingSectionStorage.getValue skip:java ring of section nodes; Horizon keys dirty sections by coordinates (DirtySections) +RotatingSectionStorage.containsSection skip:java ring of section nodes; Horizon keys dirty sections by coordinates (DirtySections) +RotatingSectionStorage.getSectionIndex skip:java ring of section nodes; Horizon keys dirty sections by coordinates (DirtySections) +RotatingSectionStorage.forEach skip:java ring of section nodes; Horizon keys dirty sections by coordinates (DirtySections) +Screenshot.grab port crates/horizon-client/src/game/chat.rs::Game::take_screenshot gap: minor: no downscale factor and no callback; the result line comes from finish_screenshot +Screenshot.takeScreenshot port crates/horizon-client/src/gfx.rs::Renderer::write_screenshot gap: minor: full size only, no downscale factor +Screenshot.getFile port crates/horizon-client/src/game/chat.rs::screenshot_name minor: UTC instead of local time in the name +ScrollWheelHandler.getNextScrollWheelSelection port crates/horizon-client-input/src/mouse.rs::ScrollWheelHandler::next_selection +SectionUpdateTracker.setDirty port crates/horizon-client/src/world.rs::DirtySections::insert gap: minor: the from-player flag that orders remeshing is not kept +SectionUpdateTracker.hasAllNeighbors missing minor: unsure: no separate neighbour-chunk check found; the mesher works from a 3x3 region +SectionUpdateTracker.doesChunkExistAt missing minor: unsure: see hasAllNeighbors +SectionUpdateTracker.SectionDirtyState.setDirty port crates/horizon-client/src/world.rs::DirtySections::insert gap: minor: the from-player flag is not kept +SectionUpdateTracker.SectionDirtyState.setSectionNode skip:java section node exists only for the rotating storage; Horizon keys sections by coordinates +StringSplitter.stringWidth port crates/horizon-client/src/font.rs::Font::width +StringSplitter.plainIndexAtWidth port crates/horizon-client/src/screen_draw.rs::Measure::plain gap: minor: measures without the style (bold widths) +StringSplitter.plainTailByWidth port crates/horizon-client/src/screen_draw.rs::Measure::plain gap: minor: same as plainIndexAtWidth, from the end +StringSplitter.findLineBreak port crates/horizon-client/src/block_entity_render.rs::first_line gap: minor: returns the line text rather than the break index; plain text only +StringSplitter.getWordPosition port crates/horizon-client/src/ui.rs::word_position gap: minor: always strips spaces; no newline stop +StringSplitter.FlatComponents.getRemainder inline crates/horizon-client/src/font.rs::Font::split the remainder is produced inside split +StringSplitter.LineBreakFinder.accept port crates/horizon-client/src/font.rs::Font::split_lines +StringSplitter.LineComponent.visit skip:java visitor plumbing over FormattedText +StringSplitter.WidthLimitedCharSink.accept port crates/horizon-client/src/screen_draw.rs::Measure::head +AnimationDefinition.Builder.addAnimation skip:java builder for code-made animations; Horizon parses animation JSON +KeyframeAnimation.bake inline crates/horizon-client/src/mobs/keyframes.rs::Animation::apply gap: minor: bones are looked up per frame; no check that a bone exists +KeyframeAnimation.getElapsedSeconds inline crates/horizon-client/src/mobs/keyframes.rs::Animation::apply seconds modulo the length when looping +KeyframeAnimations.degreeVec missing minor: unsure: no degree-to-radian conversion found in mobs/keyframes.rs or horizon-assets; check where keyframe rotations are converted +KeyframeAnimations.scaleVec missing minor: unsure: no scale-offset conversion found; see degreeVec +ColorLerper.getLerpedColor missing player: a sheep named jeb_ keeps one colour; no colour cycling exists in Horizon +ColorLerper.getModifiedColor missing minor: only builds ColorLerper's colour tables +BlockColors.getTintSource port crates/horizon-client/src/mesher.rs::Mesher::tint +BlockColors.register skip:data tint sources come from block data in Horizon +BlockColors.getColoringProperties skip:other only ModelGroupCollector uses it to group block states; Horizon bakes each state +BlockTintCache.getColor port crates/horizon-client/src/mesher.rs::Tints::get +BlockTintCache.invalidateForChunk skip:java the tint cache lives per section job (Tints::new); nothing to invalidate +BlockTintCache.invalidateAll skip:java see invalidateForChunk +BlockTintCache.findOrCreateChunkCache skip:java see invalidateForChunk +BlockTintCache.CacheData.getLayer skip:java see invalidateForChunk +BlockTintSources.constant port crates/horizon-client/src/mesher.rs::Mesher::tint Tint::Constant arm; the source comes from block data +CustomModelDataSource.calculate port crates/horizon-assets/src/item_model.rs::Resolve::tint +Firework.calculate port crates/horizon-assets/src/item_model.rs::Resolve::tint +ItemTintSources.bootstrap skip:data item tint ids are registered from data +net/minecraft/client/color/item/Potion#calculate port crates/horizon-assets/src/item_model.rs::Resolve::tint +net/minecraft/client/color/item/TeamColor#calculate port crates/horizon-assets/src/item_model.rs::Resolve::tint +ClientAvatarState.moveCloak missing minor: no cape lag; capes are not drawn by Horizon's renderer +ClientMannequin.registerOverrides skip:other client-side constructor override; Horizon creates mannequins from the species table +ClientMannequin.tick missing minor: unsure: the mannequin avatar state (walk bob, cloak) is not ticked; skins come from skins.rs +AbstractSelectionList.getRowLeft port crates/horizon-client/src/pack_screen.rs::List::row_left +CycleButton.setValue skip:ui widgets read the settings each frame; no separate value setter +CreateFlatWorldScreen.DetailsList.LayerEntry.getNarration skip:ui screen-reader text +InputQuirks.isQuitShortcutDown missing player: the quitShortcuts option is stored but never read; with it off vanilla ignores the quit shortcut +InputWithModifiers.isSelection missing minor: unsure: Space and Enter do not activate focused buttons on the screens checked +InputWithModifiers.isConfirmation missing minor: unsure: Enter checks are per screen (options_ui.rs key, chat.rs); no shared predicate +InputWithModifiers.isCopy port crates/horizon-client-input/src/clipboard.rs::edit_shortcut +InputWithModifiers.isPaste port crates/horizon-client-input/src/clipboard.rs::edit_shortcut +InputWithModifiers.isCut port crates/horizon-client-input/src/clipboard.rs::edit_shortcut +PreeditEvent.fromSdlTextEditing missing player: IME composition text (Japanese or Chinese input) is not shown while composing; no IME events are handled +PreeditEvent.toFormattedText missing player: same as fromSdlTextEditing +GameConfig.FolderData.getExternalAssetSource skip:data Horizon reads assets from the prepared resources folder +net/minecraft/client/main/Main#getQuickPlayVariant port crates/horizon-client/src/game.rs::Options::read gap: minor: only the quick play flags of the launch parser; realms branch not checked +net/minecraft/client/main/Main#parseArgument inline crates/horizon-client/src/game.rs::Options::read +net/minecraft/client/main/Main#hasValidUuid missing minor: unsure: no --uuid argument found in the client +net/minecraft/client/main/Main#isUuidValid missing minor: unsure: see hasValidUuid +AbstractClientPlayer.getPlayerInfo missing minor: unsure: no per-player tab entry lookup found on the client +AbstractClientPlayer.getSkin missing minor: unsure: remote player skins are not tied to a player info; skins.rs has the lookup +AbstractClientPlayer.getParrotVariantOnShoulder missing player: a parrot on your shoulder is never drawn (no shoulder parrot code) +ClientInput.makeJump inline crates/horizon-client/src/motion.rs::MotionWorld::controlled_simulation_tick gap: minor: carries the auto-jump flag; no separate jump input +FirstPersonHandsAndItems.extractRenderState inline crates/horizon-client/src/game/hand.rs::Game::visible_hand_items gap: minor: bob, swing and map state are read elsewhere; no render-state struct +FirstPersonHandsAndItems.shouldInstantlyReplaceVisibleItem missing minor: unsure: no instant swap rule found in first_person.rs; HandHeights::tick takes a changed flag +FirstPersonHandsAndItems.extractMapRenderState missing minor: unsure: map-in-hand data lookup not found; one_handed_map_pose only sets the pose +FirstPersonHandsAndItems.isChargedCrossbow inline crates/horizon-client/src/first_person.rs::hands_to_render +ItemActivation.tick missing player: the totem-of-undying item pop is never drawn; game/totem.rs draws particles and sound only +ItemActivation.activate missing player: same as tick +KeyboardInput.calculateImpulse inline crates/horizon-client-input/src/input.rs::keyboard_direction +LocalPlayer. inline crates/horizon-client/src/ambience.rs::Ambience::new gap: minor: the three ambient handlers are the Ambience loops; no connection set-up +LocalPlayer.sendChanges port crates/horizon-client/src/game/remote.rs::Game::remote_send_state +LocalPlayer.getCurrentMood inline crates/horizon-client/src/ambience.rs::Ambience::tick_biome gap: minor: moodiness is built here; no getter is exposed +LocalPlayer.sendIsSprintingIfNeeded inline crates/horizon-client/src/game/remote.rs::Game::remote_send_state sprint start and stop commands +LocalPlayer.closeContainer port crates/horizon-client/src/game/remote.rs::Game::remote_close_container +LocalPlayer.isSuppressingSlidingDownLadder port crates/horizon-client/src/motion.rs::MotionWorld::suppressing_ladder_slide gap: minor: sneak only; vanilla also requires not flying +LocalPlayer.canSpawnSprintParticle port crates/horizon-client/src/game/client_ambient.rs::Game::sprint_dust gap: minor: flying check is the caller's +LocalPlayer.sendRidingJump missing player: mounted jumps (horses, camels) never send START_RIDING_JUMP, so the server ignores the jump charge +LocalPlayer.removeRecipeHighlight port crates/horizon-client/src/recipe_book.rs::Settings::remove_highlight gap: minor: the seen-recipe packet is not sent, so the server keeps the highlight +LocalPlayer.moveTowardsClosestSpace missing player: you are not nudged out of blocks at your four corners each tick (aiStep) +LocalPlayer.suffocatesAt missing player: no suffocation test for the corner nudge (see moveTowardsClosestSpace) +LocalPlayer.setExperienceDisplayStartTickToTickCount inline crates/horizon-client/src/game/locator_bar.rs::Game::track_experience_display +LocalPlayer.handleEntityEvent port crates/horizon-client/src/game/remote.rs::Game::handle_packet gap: minor: only the permission events 24 to 28 of the local player +LocalPlayer.startUsingItem port crates/horizon-client/src/game.rs::Game::use_hand +LocalPlayer.isSlowDueToUsingItem missing player: sprinting is not blocked while eating or drawing a bow; sprint_input_tick never sets slow_item +LocalPlayer.onSyncedDataUpdated missing minor: unsure: server living flags and the elytra start sound are not applied to the local player +LocalPlayer.jumpableVehicle inline crates/horizon-client/src/game/mounts.rs::Game::charge_mount_jump gap: minor: saddle and jump-capable check only; no separate vehicle lookup +LocalPlayer.openTextEdit port crates/horizon-client/src/game/sign_edit.rs::Game::open_sign_editor +LocalPlayer.modifyInputSpeedForSquareMovement port crates/horizon-client-input/src/input.rs::square_movement +LocalPlayer.distanceToUnitSquare inline crates/horizon-client-input/src/input.rs::square_movement +LocalPlayer.resetPos inline crates/horizon-client/src/game/screens.rs::Game::respawn_player gap: minor: no free-height search, pose, rotation or velocity reset +LocalPlayer.shouldStopRunSprinting port crates/horizon-client/src/sprint.rs::Conditions::should_stop gap: player: minor_horizontal_collision is always false, so wall slides stop the sprint +LocalPlayer.shouldStopSwimSprinting port crates/horizon-client/src/sprint.rs::Conditions::should_stop +LocalPlayer.tickDeath missing minor: unsure: the dead local player is not removed after 20 ticks; the death screen flow covers it +LocalPlayer.rideTick missing minor: unsure: no code found that feeds movement keys into a boat's paddling +LocalPlayer.isHorizontalCollisionMinor missing player: sprinting into a wall at an angle stops the sprint (motion.rs minor_horizontal_collision is a stub returning false) +LocalPlayer.isMoving inline crates/horizon-client/src/auto_jump.rs::can_auto_jump +LocalPlayer.isSprintingPossible port crates/horizon-client/src/sprint.rs::Conditions::possible +LocalPlayer.canStartSprinting port crates/horizon-client/src/sprint.rs::Conditions::can_start gap: player: slow_item is never set, so eating does not block starting a sprint +LocalPlayer.onGameModeChanged missing minor: switching to spectator keeps vertical momentum +LocalPlayer.getRopeHoldPosition missing player: no leash or lead rope exists in Horizon, so no rope is drawn +LocalPlayer.raycastHitResult port crates/horizon-client/src/game/hand.rs::GameHands::hit gap: minor: block fallback is Game::pick; no separate item attack-range pass +LocalPlayer.filterHitResult inline crates/horizon-client/src/game.rs::Game::pick gap: minor: reach is applied inside the clip; no miss direction is returned +LocalPlayerResolver.fetchByName missing minor: unsure: no player-list lookup by name on the client +LocalPlayerResolver.fetchById missing minor: unsure: no player-list lookup by id on the client diff --git a/tools/port_map/rest-005.tsv b/tools/port_map/rest-005.tsv new file mode 100644 index 0000000000..a3aa7cda9c --- /dev/null +++ b/tools/port_map/rest-005.tsv @@ -0,0 +1,149 @@ +RemotePlayer.shouldRenderAtSqrDistance missing unsure: no remote-player cull distance (box x 10 x 64 x view scale) found in game/remote.rs +RemotePlayer.aiStep missing unsure: remote players are drawn from Mob records in game/remote.rs; no per-tick motion lerp or push step found +RemotePlayer.lerpMotion missing unsure: no lerp of received remote-player motion found in game/remote.rs +Hotbar.storeFrom port crates/horizon-client/src/game/creative_hotbar.rs::Game::hotbar_load_or_save save copies the nine slots into the hotbar.nbt row +Hotbar.isEmpty missing player: an empty creative Toolbar row shows no paper hint with the save key (no inventory.hotbarInfo in Horizon) +ClientMetricsSamplersProvider.registerStaticSamplers skip:debug F3 metrics samplers +ClientMetricsSamplersProvider.samplers skip:debug F3 metrics samplers +QuickPlay.connect port crates/horizon-client/src/game.rs::Options::read gap: minor: no Realms variant and no latest-world fallback +QuickPlay.getLatestSingleplayerWorld missing minor: a quick play with no world id does not pick the newest world +QuickPlay.joinMultiplayerWorld port crates/horizon-client/src/game.rs::Options::read gap: minor: the server is not added to the server list +QuickPlay.joinRealmsWorld skip:service Realms +QuickPlayLog.log skip:other writes the launcher's quick play session file; Horizon keeps none +FullTextSearchTree.searchIdentifier port crates/horizon-client/src/recipe_book.rs::Book::collection_matches recipe-book namespace:path search; the creative search is Creative::search_results +IdSearchTree.search port crates/horizon-client/src/creative.rs::Creative::search_results # tag branch; plain text goes to the name search +IdSearchTree.searchIdentifier port crates/horizon-client/src/creative.rs::Creative::search_results #namespace:path tag search +IdentifierSearchTree.create inline crates/horizon-client/src/creative.rs::Creative::from_legacy builds the search index with the item list +IntersectionIterator.computeNext inline crates/horizon-client/src/creative.rs::Creative::search_results namespace and path matches intersected by filter +MergingUniqueIterator.computeNext inline crates/horizon-client/src/creative.rs::Creative::search_results one match per item, found by any/filter +SearchTree.plainText inline crates/horizon-client/src/creative.rs::Creative::search_results name search over tooltip lines +SuffixArray.add skip:java suffix array index build; Horizon scans the item list +SuffixArray.generate skip:java suffix array construction; Horizon scans the item list +SuffixArray.print skip:debug suffix array debug dump +SuffixArray.getString skip:debug suffix array debug text +SuffixArray.compare skip:java binary-search helper of the suffix array +SuffixArray.search port crates/horizon-client/src/creative.rs::Creative::search_results substring match by linear scan, same matches in tab order +IntegratedPlayerList.canPlayerLogin inline crates/horizon-client/src/lan/mod.rs::Shared::try_admit gap: minor: no owner-name check; duplicate logins are matched by uuid +IntegratedServer.processPacketsAndTick skip:debug gizmo collector for debug draws +IntegratedServer.fillServerSystemReport skip:debug system report for crash reports +IntegratedServer.getPersonalGameMode port crates/horizon-client/src/game/integrated.rs::Game::server_options owner's game mode, read from the guest record +IntegratedServer.updateGameModeBasedOnPermission inline crates/horizon-client/src/game/lan.rs::Game::lan_guest_joined gap: player: a guest already in the world keeps their mode when force-gamemode or the default mode changes; only a join applies it +IntegratedServer.updatePermissions port crates/horizon-client/src/lan/hub.rs::Hub::resend_commands permission level and commands sent to every guest +IntegratedServer.setDefaultGameType port crates/horizon-client/src/game/lan.rs::Game::set_default_game_mode +IntegratedServer.isSingleplayerOwner missing unsure: the owner is kept by uuid in hub.singleplayer_owner; no name-based check found +IntegratedServer.getScaledTrackingDistance missing player: the Entity Distance option is not applied to what a LAN host tracks +IntegratedServer.getForcedGameType inline crates/horizon-client/src/game/lan.rs::Game::lan_guest_joined forced mode is sent when a guest joins +IntegratedServer.selectLevelLoadFocusPos missing unsure: no load focus at the owner's saved position found in game/integrated.rs open_integrated +IntegratedServer.reportChunkLoadFailure missing player: no low-disk-space toast when a chunk fails to load (LOW_DISK_SPACE_WARNING is only an id constant) +IntegratedServer.reportChunkSaveFailure missing player: no low-disk-space toast when a chunk fails to save +LanServerDetection.LanServerDetector.run port crates/horizon-client/src/lan/discovery.rs::listen receive loop for LAN announcements +LanServerDetection.LanServerList.takeDirtyServers skip:java dirty flag; Scanner::games returns a copy on each call +LanServerPinger.run port crates/horizon-client/src/lan/discovery.rs::Pinger::start announce loop on a thread until dropped +ChannelAccess.createHandle port crates/horizon-client/src/audio.rs::MixerBackend::create gap: minor: the streaming flag is ignored +ChannelAccess.executeOnChannels skip:other sound executor thread; the mixer is shared under a lock +ChannelAccess.ChannelHandle.execute port crates/horizon-client/src/audio.rs::MixerBackend::with runs the action on the channel when it exists +ChunkedSampleByteBuf.accept skip:java 16-bit PCM buffer; Horizon mixes f32 samples +ChunkedSampleByteBuf.get skip:java 16-bit PCM buffer; Horizon mixes f32 samples +FloatSampleSource.read inline crates/horizon-client/src/audio.rs::decode decoded buffer; decode reads the whole file +FloatSampleSource.readAll inline crates/horizon-client/src/audio.rs::decode decoded buffer; decode reads the whole file +JOrbisAudioStream. port crates/horizon-client/src/audio.rs::decode Ogg decoded by symphonia, not JOrbis +JOrbisAudioStream.readToBuffer inline crates/horizon-client/src/audio.rs::decode Ogg reading folded into decode +JOrbisAudioStream.readPage inline crates/horizon-client/src/audio.rs::decode Ogg reading folded into decode +JOrbisAudioStream.readIdentificationPacket inline crates/horizon-client/src/audio.rs::decode Ogg reading folded into decode +JOrbisAudioStream.readPacket inline crates/horizon-client/src/audio.rs::decode Ogg reading folded into decode +JOrbisAudioStream.getSamplesToWrite inline crates/horizon-client/src/audio.rs::decode Ogg reading folded into decode +JOrbisAudioStream.readChunk inline crates/horizon-client/src/audio.rs::decode Ogg reading folded into decode +JOrbisAudioStream.copyAnyChannels inline crates/horizon-client/src/audio.rs::decode Ogg reading folded into decode +JOrbisAudioStream.copyMono inline crates/horizon-client/src/audio.rs::decode Ogg reading folded into decode +JOrbisAudioStream.copyStereo inline crates/horizon-client/src/audio.rs::decode Ogg reading folded into decode +LoopingAudioStream.read port crates/horizon-client/src/audio.rs::attach gap: minor: the looping flag is set on the channel; the buffer loops, no stream is reopened +MusicManager.canReplace inline crates/horizon-client/src/music.rs::MusicManager::tick replace only when the new track differs from the playing one +MusicManager.getCurrentMusicTranslationKey port crates/horizon-client/src/music.rs::MusicManager::current gap: minor: returns the sound id; the short translation key is not built +SoundBufferLibrary.getCompleteBuffer port crates/horizon-client/src/audio.rs::decode cached in SoundCache +SoundBufferLibrary.getStream inline crates/horizon-client/src/audio.rs::decode gap: minor: the whole file is decoded; no stream +SoundBufferLibrary.clear inline crates/horizon-client/src/audio.rs::Audio::reload the cache is emptied on reload +SoundBufferLibrary.preload skip:java sounds decode on demand +SoundBufferLibrary.enumerate port crates/horizon-client/src/audio.rs::Audio::census sound buffer census for debug output +SoundBufferLibrary.DebugOutput.Counter.accountBuffer inline crates/horizon-client/src/audio.rs::Audio::census counts buffers and sizes +SoundEngine.reload port crates/horizon-client/src/audio.rs::Audio::reload stops sounds and reads the new resource root +SoundEngine.loadLibrary port crates/horizon-client/src/audio.rs::Audio::with_output opens the output device +SoundEngine.shouldChangeDevice missing player: when the output device is lost or the system default changes, vanilla reopens sound output; Horizon's Audio has no device watch +SoundEngine.notifyListeners port crates/horizon-client/src/sound_engine.rs::SoundEngine::notify subtitle and listener notification with range +SoundEngine.updateSource port crates/horizon-client/src/audio.rs::Audio::set_listener listener position and orientation +SoundEngineExecutor.createThread skip:other sound thread creation; Horizon's audio runs on the cpal callback +SoundEngineExecutor.run skip:other sound thread loop; Horizon's audio runs on the cpal callback +SoundEngineExecutor.shutDown skip:other sound thread shutdown; Horizon's audio runs on the cpal callback +SoundManager.prepare port crates/horizon-client/src/sound_events.rs::SoundRegistry::load reads sounds.json per namespace +SoundManager.apply port crates/horizon-client/src/audio.rs::Audio::reload applies the registry and reloads the engine +SoundManager.validateSoundResource inline crates/horizon-client/src/sound_events.rs::SoundRegistry::add_file file existence check through the exists closure +SoundManager.Preparations.handleRegistration port crates/horizon-client/src/sound_events.rs::SoundRegistry::add_file replace flag and sound entries +SoundManager.Preparations.apply inline crates/horizon-client/src/sound_events.rs::SoundRegistry::load registry replaced with the prepared one +SoundPreviewHandler.preview missing player: moving a category volume slider in Options plays no preview sound (Options.java calls SoundPreviewHandler.preview) +SoundPreviewHandler.stopOtherCategoryPreview missing minor: part of the volume preview +WeighedSoundEvents.preloadIfRequired skip:java preload is a cache; Horizon decodes on demand +CraftPlanksTutorialStep.tick missing player: a new survival world shows no craft-planks tutorial toast; Horizon has no tutorial steps (tutorialStep is only stored in options) +CraftPlanksTutorialStep.hasCraftedPlanksPreviously missing minor: part of the craft-planks tutorial +FindTreeTutorialStepInstance.tick missing player: no find-a-tree tutorial toast +FindTreeTutorialStepInstance.onLookAt missing minor: part of the find-a-tree tutorial +FindTreeTutorialStepInstance.hasPunchedTreesPreviously missing minor: part of the find-a-tree tutorial +MovementTutorialStepInstance.tick missing player: no move and look tutorial toasts +MovementTutorialStepInstance.clear missing minor: part of the movement tutorial +MovementTutorialStepInstance.onInput missing minor: part of the movement tutorial +MovementTutorialStepInstance.onMouse missing minor: part of the movement tutorial +OpenInventoryTutorialStep.tick missing player: no open-inventory tutorial toast +PunchTreeTutorialStepInstance.tick missing player: no punch-a-tree tutorial toast +PunchTreeTutorialStepInstance.onDestroyBlock missing minor: part of the punch-a-tree tutorial +Tutorial.onLookAt missing minor: tutorial driver; no tutorial exists +Tutorial.onDestroyBlock missing minor: tutorial driver; no tutorial exists +Tutorial.stop missing minor: tutorial driver; no tutorial exists +Tutorial.start missing minor: tutorial driver; no tutorial exists +Tutorial.tick missing player: the tutorial driver that shows the first toasts is absent +Tutorial.setStep missing minor: tutorial driver; no tutorial exists +Tutorial.isSurvival missing minor: only the tutorial asks whether the player is in survival +TutorialSteps.getByName port crates/horizon-client/src/options_file.rs::OptionsFile::try_apply tutorialStep name read from options +ClientWaypointManager.updateWaypoint port crates/horizon-client/src/locator_bar.rs::ClientWaypoints::apply updates a tracked waypoint by id +ClientWaypointManager.forEachWaypoint port crates/horizon-client/src/locator_bar.rs::ClientWaypoints::draw drawn farthest first +ArgumentVisitor.visitArguments port crates/horizon-client/src/game/lan_chat.rs::Game::signable_arguments collects the message arguments of a command +ArgumentVisitor.visitNodeArguments inline crates/horizon-client/src/game/lan_chat.rs::Game::signable_arguments per-node walk folded into the collector +CacheableFunction.get skip:java caches the resolved function; no behaviour of its own +CacheableFunction.equals skip:java identity helper for the function cache +CommandResultCallback.chain inline crates/horizon-client/src/game/commands/execute.rs::Game::deliver callbacks are chained and called in deliver +CommandSourceStack.withSource port crates/horizon-client/src/game/commands/execute.rs::Game::with_source source swapped for a call +CommandSourceStack.withEntity inline crates/horizon-client/src/game/commands/execute.rs::Game::modify execute as +CommandSourceStack.withPosition inline crates/horizon-client/src/game/commands/execute.rs::Game::modify execute at and positioned +CommandSourceStack.withRotation inline crates/horizon-client/src/game/commands/execute.rs::Game::modify execute rotated +CommandSourceStack.withCallback inline crates/horizon-client/src/game/commands/execute.rs::Game::run_leaf result callback set per leaf +CommandSourceStack.withPermission missing minor: unsure: no per-source permission level found in execute.rs +CommandSourceStack.withAnchor inline crates/horizon-client/src/game/commands/execute.rs::Game::modify execute anchored +CommandSourceStack.withSigningContext skip:service chat message signing +CommandSourceStack.getPlayer port crates/horizon-client/src/game/commands/players.rs::Game::source_entity the source entity; callers check for a player +CommandSourceStack.shouldFilterMessageTo skip:service chat text filtering +CommandSourceStack.sendChatMessage port crates/horizon-client/src/lan/hub.rs::Hub::chat gap: minor: console and command block sources are not sent here +CommandSourceStack.suggestRegistryElements port crates/horizon-command/src/args.rs::suggest_resources registry tags and elements +CommandSourceStack.getLookup skip:data registry lookup; registries come from tables +Commands. port crates/horizon-command/src/vanilla.rs::build registers the command tree +Commands.mapSource inline crates/horizon-client/src/game/commands/execute.rs::Game::with_source source mapped onto the parse +Commands.performPrefixedCommand port crates/horizon-client/src/game/commands.rs::Game::run_command the leading slash is trimmed by the callers +Commands.finishParsing port crates/horizon-client/src/game/commands.rs::Game::syntax_failure parse error with the <--[HERE] context +Commands.executeCommandInContext port crates/horizon-client/src/game/commands/execute.rs::Game::run_chain gap: minor: max_command_forks is not enforced +Commands.validate skip:debug startup check of the command tree +Commands.createCompilationContext skip:data datapack compile-time context +ParserUtils.readWhile port crates/horizon-command/src/lib.rs::Reader::read_unquoted_string reads while characters are allowed +SharedSuggestionProvider.suggestRegistryElements port crates/horizon-command/src/args.rs::suggest_resources registry tags and elements +SharedSuggestionProvider.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest suggestions per argument type +SharedSuggestionProvider.suggest2DCoordinates port crates/horizon-command/src/args.rs::suggest_coordinates x z suggestions +AngleArgument.SingleAngle.getAngle port crates/horizon-command/src/args.rs::Coord::get gap: minor: the wrap to -180..180 is not confirmed +ArgumentSignatures.signCommand skip:service chat message signing +DimensionArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest dimension suggestions +DimensionArgument.getDimension inline crates/horizon-client/src/game/commands/execute.rs::Game::modify dimension resolved where execute in reads it +EntityAnchorArgument.parse port crates/horizon-command/src/args.rs::ArgType::parse eyes or feet +EntityArgument.parse port crates/horizon-command/src/args.rs::ArgType::parse the selector is read by selector.rs::parse +EntityArgument.listSuggestions port crates/horizon-command/src/selector.rs::suggest selector and player name suggestions +EntityArgument.Info.serializeToNetwork port crates/horizon-client/src/lan/commands.rs::properties entity flags: single and players only +EntityArgument.Info.deserializeFromNetwork port crates/horizon-command/src/network.rs::argument_type entity flags of a received command tree +EntityArgument.Info.serializeToJson skip:data JSON of the argument template; Horizon reads tree data from files +GameModeArgument.parse port crates/horizon-command/src/args.rs::ArgType::parse game mode name +GameModeArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest game mode suggestions +GameProfileArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest player name suggestions +GameProfileArgument.SelectorResult.getNames missing player: op, deop, ban and whitelist are not in Horizon's command tree (no such literal in horizon-command) +HeightmapTypeArgument.keptTypes port crates/horizon-command/src/network.rs::argument_type heightmap type choices diff --git a/tools/port_map/rest-006.tsv b/tools/port_map/rest-006.tsv new file mode 100644 index 0000000000..8ff7b1a0cc --- /dev/null +++ b/tools/port_map/rest-006.tsv @@ -0,0 +1,150 @@ +MessageArgument.getMessage port crates/horizon-client/src/game/commands.rs::Game::expand_selectors gap: minor: plain text only, no chat signing or text filter +MessageArgument.resolveChatMessage port crates/horizon-client/src/game/commands.rs::Game::cmd_say gap: minor: no chat signing, decorator or text filter +MessageArgument.resolveSignedMessage missing minor: signed chat (signature, ChatDecorator, text filter) is not ported; /say sends plain text +MessageArgument.resolveDisguisedMessage missing minor: ChatDecorator for unsigned messages is not ported +MessageArgument.filterPlainText missing minor: text filtering service is not ported +MessageArgument.Message.toComponent port crates/horizon-client/src/game/commands.rs::Game::expand_selectors @-selector expansion into names +NbtPathArgument.readUnquotedName port crates/horizon-client-core/src/nbt_path.rs::parse_node unquoted node name read in the bare-name arm of parse_node +NbtPathArgument.isAllowedInUnquotedName inline crates/horizon-client-core/src/nbt_path.rs::parse_node the excluded-character set of parse_node's bare-name arm +NbtPathArgument.AllElementsNode.getTag port crates/horizon-client-core/src/nbt_path.rs::NbtPath::step Node::AllElements arm of step +NbtPathArgument.AllElementsNode.getOrCreateTag port crates/horizon-client-core/src/nbt_path.rs::step_mut Node::AllElements arm of step_mut +NbtPathArgument.CompoundChildNode.getTag port crates/horizon-client-core/src/nbt_path.rs::NbtPath::step Node::Child arm of step +NbtPathArgument.CompoundChildNode.getOrCreateTag port crates/horizon-client-core/src/nbt_path.rs::step_mut Node::Child arm of step_mut +NbtPathArgument.IndexedElementNode.getTag port crates/horizon-client-core/src/nbt_path.rs::NbtPath::step Node::Index arm of step +NbtPathArgument.MatchElementNode.getTag port crates/horizon-client-core/src/nbt_path.rs::NbtPath::step Node::MatchElement arm of step +NbtPathArgument.MatchElementNode.getOrCreateTag port crates/horizon-client-core/src/nbt_path.rs::step_mut Node::MatchElement arm of step_mut +NbtPathArgument.MatchObjectNode.getTag port crates/horizon-client-core/src/nbt_path.rs::NbtPath::step Node::MatchChild arm of step +NbtPathArgument.MatchObjectNode.getOrCreateTag port crates/horizon-client-core/src/nbt_path.rs::step_mut Node::MatchChild arm of step_mut +NbtPathArgument.MatchRootObjectNode.getTag port crates/horizon-client-core/src/nbt_path.rs::NbtPath::step Node::MatchRoot arm of step +NbtPathArgument.NbtPath.get port crates/horizon-client-core/src/nbt_path.rs::NbtPath::get +NbtPathArgument.NbtPath.countMatching port crates/horizon-client-core/src/nbt_path.rs::NbtPath::count +NbtPathArgument.NbtPath.getOrCreateParents port crates/horizon-client-core/src/nbt_path.rs::NbtPath::visit_parents parents of the last node, created when missing +NbtPathArgument.NbtPath.apply inline crates/horizon-client-core/src/nbt_path.rs::NbtPath::set per-parent sum of changes; remove sums the same way +NbtPathArgument.NbtPath.isTooDeep port crates/horizon-client-core/src/nbt_path.rs::too_deep +NbtPathArgument.Node.getOrCreate inline crates/horizon-client-core/src/nbt_path.rs::NbtPath::descend walk that creates missing tags node by node +NbtPathArgument.Node.collect inline crates/horizon-client-core/src/nbt_path.rs::NbtPath::read_steps per-node collection loop of the read walk +ObjectiveArgument.getObjective port crates/horizon-client/src/scoreboard_cmd.rs::objective_name +ObjectiveArgument.getWritableObjective port crates/horizon-client/src/scoreboard_cmd.rs::writable_objective +ObjectiveArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest ArgType::Objective arm +ObjectiveCriteriaArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest ArgType::Criteria arm: custom, team kill and stat criteria +OperationArgument.getOperation port crates/horizon-client/src/scoreboard_cmd.rs::players gap: minor: the operation string is read inside players, no separate accessor +OperationArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest ArgType::Operation arm (OPERATIONS) +OperationArgument.getSimpleOperation port crates/horizon-client/src/scoreboard_cmd.rs::apply_operation floor division and positive modulo as in Mth +ParticleArgument.readParticleType port crates/horizon-command/src/args.rs::ArgType::parse ArgType::Particle arm: id, registry check, options +ParticleArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest ArgType::Particle arm +ResourceArgument.getResource port crates/horizon-command/src/args.rs::ArgType::parse ArgType::Resource arm checks the registry +ResourceKeyArgument.key skip:java builder: Horizon names the registry in the ArgType::ResKey variant +ResourceKeyArgument.getRegistryKey port crates/horizon-command/src/args.rs::ArgType::parse gap: minor: ResKey arm does not check membership; consumers do +ResourceKeyArgument.resolveKey port crates/horizon-command/src/args.rs::ArgType::parse gap: minor: registry lookup is done by the consumer (cmd_recipe, cmd_advancement) +ResourceKeyArgument.getRecipe port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_recipe gap: minor: checks learnable recipes, not every non-special recipe +ResourceKeyArgument.getAdvancement port crates/horizon-client/src/game/commands.rs::Game::cmd_advancement advancementNotFound lookup +ResourceOrIdArgument.createGrammar port crates/horizon-command/src/codec.rs::resource_or_id id or inline value +ResourceOrTagArgument.getResourceOrTag port crates/horizon-command/src/args.rs::ArgType::parse ArgType::ResOrTag arm +ResourceOrTagArgument.ResourceResult.cast skip:java generic unchecked cast; the registry check is in the ArgType::ResOrTag arm +ResourceOrTagArgument.TagResult.cast skip:java generic unchecked cast; the registry check is in the ArgType::ResOrTag arm +ResourceOrTagKeyArgument.getResourceOrTagKey port crates/horizon-command/src/args.rs::ArgType::parse ArgType::ResOrTagKey arm (key only) +ResourceSelectorArgument.readPattern port crates/horizon-command/src/args.rs::ArgType::parse ArgType::ResourceSelector arm reads the pattern +ResourceSelectorArgument.isAllowedPatternCharacter inline crates/horizon-command/src/args.rs::ArgType::parse the id-char or * or ? test in the ResourceSelector arm +ScoreHolderArgument.getNamesWithDefaultWildcard port crates/horizon-client/src/scoreboard_cmd.rs::holders default_wildcard true +ScoreHolderArgument.Info.serializeToNetwork port crates/horizon-client/src/lan/commands.rs::properties score_holder flag written as u8 +ScoreHolderArgument.Info.deserializeFromNetwork port crates/horizon-command/src/network.rs::argument_type score_holder multiple flag read from the packet +ScoreHolderArgument.SelectorResult.getNames port crates/horizon-client/src/scoreboard_cmd.rs::resolve_holders selector holders; empty is an error +SlotSourceArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest ArgType::SlotSource arm +StringRepresentableArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest ArgType::Enum and StrEnum arms +StringRepresentableArgument.getExamples skip:other Brigadier examples API; no player-visible output in vanilla play +SwingAnimationArgument.parse port crates/horizon-command/src/args.rs::ArgType::parse StrEnum arm with argument.swing_animation.invalid +SwingAnimationArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest StrEnum arm +TeamArgument.getTeam port crates/horizon-client/src/scoreboard_cmd.rs::team_name +TeamArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest ArgType::Team arm +TimeArgument.time skip:java builder: Horizon names ArgType::Time in the command tree +TimeArgument.listSuggestions port crates/horizon-command/src/args.rs::ArgType::suggest ArgType::Time arm +BlockInput.test port crates/horizon-client/src/game/commands.rs::Game::block_matches gap: minor: block-entity NBT of the input is not compared +BlockInput.overwriteWithDefinedProperties missing unsure: vanilla BlockInput.place keeps neighbour-updated shape properties when the command sets only some; Horizon resolves the given state directly (Game::block_state) +BlockPredicateArgument.parse port crates/horizon-command/src/block.rs::parse +BlockPredicateArgument.BlockPredicate.test port crates/horizon-client/src/game/commands.rs::Game::block_matches gap: minor: block-entity NBT is not compared +BlockPredicateArgument.TagPredicate.test port crates/horizon-client/src/game/commands.rs::Game::block_matches gap: minor: block-entity NBT is not compared +BlockStateArgument.parse port crates/horizon-command/src/block.rs::parse +BlockStateParser.parseForTesting port crates/horizon-command/src/block.rs::parse for_testing = true, tags allowed +BlockStateParser.suggestPropertyNameOrEnd inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions property name suggestions and ] +BlockStateParser.suggestVaguePropertyNameOrEnd inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions vague (tag) property name suggestions +BlockStateParser.suggestPropertyName inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions +BlockStateParser.suggestVaguePropertyName inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions +BlockStateParser.suggestOpenNbt inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions { suggestion for block entities +BlockStateParser.suggestEquals inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions +BlockStateParser.suggestNextPropertyOrEnd inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions +BlockStateParser.addSuggestions port crates/horizon-command/src/block.rs::add_values +BlockStateParser.suggestVaguePropertyValue inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions +BlockStateParser.suggestOpenVaguePropertiesOrNbt inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions +BlockStateParser.suggestOpenPropertiesOrNbt inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions +BlockStateParser.suggestBlockIdOrTag inline crates/horizon-command/src/block.rs::BlockParser::fill_suggestions BlockIdOrTag suggestion +BlockStateParser.setValue inline crates/horizon-command/src/block.rs::BlockParser::read_properties value check and store into properties +BlockStateParser.serialize port crates/horizon-client/src/game/blockctl.rs::Game::state_text name[prop=value,...] +BlockPosArgument.getSpawnablePos missing minor: no spawnable-bounds check for /spawnpoint and /setworldspawn positions +BlockPosArgument.parse port crates/horizon-command/src/args.rs::ArgType::parse ArgType::BlockPos arm +BlockPosArgument.listSuggestions port crates/horizon-command/src/args.rs::suggest_coordinates 3D block coordinates +ColumnPosArgument.getColumnPos port crates/horizon-client/src/game/commands.rs::Game::block_pos_arg gap: minor: returns the full block position; callers drop y +ColumnPosArgument.listSuggestions port crates/horizon-command/src/args.rs::suggest_coordinates 2D coordinates +LocalCoordinates.apply port crates/horizon-command/src/args.rs::Coordinates::position Local arm: ^ offsets from the source position and rotation +Vec2Argument.getVec2 port crates/horizon-client/src/game/commands.rs::Game::resolve_coords gap: minor: returns the 3D position; callers keep x and z +Vec2Argument.listSuggestions port crates/horizon-command/src/args.rs::suggest_coordinates 2D coordinates +Vec3Argument.vec3 skip:java builder: Horizon names ArgType::Vec3 in the command tree +Vec3Argument.parse port crates/horizon-command/src/args.rs::ArgType::parse ArgType::Vec3 arm (local or world) +Vec3Argument.listSuggestions port crates/horizon-command/src/args.rs::suggest_coordinates 3D coordinates +WorldCoordinate.get port crates/horizon-command/src/args.rs::Coord::get +WorldCoordinate.parseDouble port crates/horizon-command/src/args.rs::parse_coord_double center correction for whole numbers +WorldCoordinates.getPosition port crates/horizon-command/src/args.rs::Coordinates::position +WorldCoordinates.getRotation port crates/horizon-command/src/args.rs::Coordinates::rotation +WorldCoordinates.parseInt port crates/horizon-command/src/args.rs::parse_world block = true +WorldCoordinates.parseDouble port crates/horizon-command/src/args.rs::parse_world block = false, center per axis +WorldCoordinates.absolute skip:java builder: Horizon constructs Coordinates::World with absolute Coord values +FunctionArgument.getFunction port crates/horizon-client/src/game/functions.rs::Game::functions_named arguments.function.unknown +FunctionArgument.getFunctionTag port crates/horizon-client/src/game/functions.rs::Game::functions_named player: an unknown #tag is empty here, so /function says no functions; vanilla says arguments.function.tag.unknown +ItemInput.createItemStack missing player: /item replace ... with accepts counts up to 99 above the item's max stack; vanilla rejects them (arguments.item.overstacked) +ItemParser.State.readItem port crates/horizon-command/src/item.rs::ItemState::parse item id check +ItemParser.State.readComponent inline crates/horizon-command/src/item.rs::ItemState::read_components component value decode and malformed error +ItemParser.State.suggestStartComponents inline crates/horizon-command/src/item.rs::ItemState::fill [ suggestion +ItemParser.State.suggestNextOrEndComponents inline crates/horizon-command/src/item.rs::ItemState::fill , and ] suggestions +ItemParser.State.suggestAssignment inline crates/horizon-command/src/item.rs::ItemState::fill = suggestion +ItemParser.State.suggestComponentAssignmentOrRemoval port crates/horizon-command/src/item.rs::ItemState::suggest_component gap: minor: the ! removal suggestion is in ItemState::fill +ItemPredicateArgument.createComponentExistencePredicate port crates/horizon-command/src/item.rs::predicate_grammar exists: predicate for a component id +ItemPredicateArgument.ComponentWrapper.create port crates/horizon-command/src/item.rs::predicate_grammar component value predicate built from the codec +ItemPredicateArgument.ComponentWrapper.decode port crates/horizon-command/src/item.rs::decode_component +ItemPredicateArgument.Context.forElementType port crates/horizon-client/src/game/commands.rs::Game::item_matches item id match +ItemPredicateArgument.Context.forTagType port crates/horizon-client/src/game/commands.rs::Game::item_matches item tag match +ItemPredicateArgument.Context.lookupComponentType port crates/horizon-command/src/item.rs::lookup component id lookup and validation +ItemPredicateArgument.Context.lookupPredicateType port crates/horizon-command/src/item.rs::lookup predicate id lookup and validation +ItemPredicateArgument.Context.listComponentTypes port crates/horizon-command/src/item.rs::ItemState::suggest_component persistent (non-transient) component names +ItemPredicateArgument.PredicateWrapper.decode port crates/horizon-command/src/item.rs::decode_predicate +net/minecraft/commands/arguments/selector/EntitySelector#checkPermissions inline crates/horizon-command/src/selector.rs::SelectorParser::parse @ selectors refused without permission, checked at parse time +net/minecraft/commands/arguments/selector/EntitySelector#findSingleEntity port crates/horizon-client/src/game/commands.rs::Game::entities gap: minor: no single-entity check here; callers check argument.entity.toomany +net/minecraft/commands/arguments/selector/EntitySelector#findEntities port crates/horizon-client/src/game/commands.rs::Game::select +net/minecraft/commands/arguments/selector/EntitySelector#addEntities inline crates/horizon-client/src/game/commands.rs::Game::select per-level collection with the result limit +net/minecraft/commands/arguments/selector/EntitySelector#findSinglePlayer port crates/horizon-client/src/game/commands.rs::Game::entities gap: minor: no single-player check here +net/minecraft/commands/arguments/selector/EntitySelector#findPlayers port crates/horizon-client/src/game/commands.rs::Game::select players only when the selector has no entities +net/minecraft/commands/arguments/selector/EntitySelector#getPredicate port crates/horizon-client/src/game/commands.rs::Game::select_in_level type, name, gamemode, level, distance and box filters +net/minecraft/commands/arguments/selector/EntitySelector#sortAndLimit inline crates/horizon-client/src/game/commands.rs::Game::select sort kinds and the limit +EntitySelectorParser.allowSelectors inline crates/horizon-command/src/args.rs::ArgType::parse reg.permission >= 2 at each selector call site +EntitySelectorParser.createAabb inline crates/horizon-client/src/game/commands.rs::Game::select_in_level dx, dy, dz box from the origin +EntitySelectorParser.finalizePredicates port crates/horizon-client/src/game/commands.rs::Game::select_in_level player: x_rotation and y_rotation are parsed but never filter the selection; the level filter is here +EntitySelectorParser.createRotationPredicate missing player: x_rotation and y_rotation ranges are parsed but never applied, so @e[y_rotation=...] selects regardless of facing +EntitySelectorParser.shouldInvertValue port crates/horizon-command/src/selector.rs::SelectorParser::should_invert +EntitySelectorParser.fillSelectorSuggestions port crates/horizon-command/src/selector.rs::SelectorParser::fill_selectors +EntitySelectorParser.suggestNameOrSelector inline crates/horizon-command/src/selector.rs::SelectorParser::fill_suggestions +EntitySelectorParser.suggestName inline crates/horizon-command/src/selector.rs::SelectorParser::fill_suggestions +EntitySelectorParser.suggestSelector inline crates/horizon-command/src/selector.rs::SelectorParser::fill_suggestions +EntitySelectorParser.suggestOptionsKeyOrClose inline crates/horizon-command/src/selector.rs::SelectorParser::fill_suggestions option names and ] +EntitySelectorParser.suggestOptionsNextOrClose inline crates/horizon-command/src/selector.rs::SelectorParser::fill_suggestions , and ] suggestions +EntitySelectorOptions.register skip:data option table is a const list (OPTIONS) in selector.rs +EntitySelectorOptions.rollbackAndThrow port crates/horizon-command/src/selector.rs::rollback +EntitySelectorOptions.get inline crates/horizon-command/src/selector.rs::SelectorParser::parse_options unknown and inapplicable option checks via can_use +ChainModifiers.setFlag inline crates/horizon-client/src/game/commands/execute.rs::Game::run_execute forked flag set while walking the chain +CustomCommandExecutor.WithErrorHandling.run port crates/horizon-client/src/game/commands/execute.rs::Game::run_leaf error sends failure to the store callbacks +ExecutionContext.createTopFrame skip:java frame object of the queue; Horizon keeps the return value and depth in exec state +ExecutionContext.queueInitialFunctionCall port crates/horizon-client/src/game/functions.rs::Game::run_function_by_id +ExecutionContext.queueInitialCommandExecution port crates/horizon-client/src/game/commands.rs::Game::run_command +ExecutionContext.queueNext skip:java work queue of the 26.x engine; Horizon recurses through run_chain +ExecutionContext.discardAtDepthOrHigher port crates/horizon-client/src/game/commands/execute.rs::Game::finish_return return discards the rest of the frame +ExecutionContext.runCommandQueue port crates/horizon-client/src/game/commands/execute.rs::Game::run_chain gap: minor: stops with command.failed at max_command_sequence_length; no queue overflow limit +ExecutionContext.pushNewCommands skip:java queue bookkeeping of the 26.x engine +BuildContexts.execute port crates/horizon-client/src/game/commands/execute.rs::Game::run_execute walk of the execute modifiers, then the leaf command +BuildContexts.traceCommandStart skip:debug command tracer callback diff --git a/tools/port_map/rest-007.tsv b/tools/port_map/rest-007.tsv new file mode 100644 index 0000000000..1efc79b835 --- /dev/null +++ b/tools/port_map/rest-007.tsv @@ -0,0 +1,149 @@ +BuildContexts.Continuation.execute inline crates/horizon-client/src/game/commands/execute.rs::Game::run_execute the rest of a modifier chain runs as a loop over the sources in run_execute, not as a queued continuation +BuildContexts.TopLevel.execute port crates/horizon-client/src/game/commands/execute.rs::Game::execute_line gap: minor: no trace-start event (only the /debug tracer uses it) +BuildContexts.Unbound.execute port crates/horizon-client/src/game/commands/execute.rs::Game::execute_line gap: minor: no trace-start event (only the /debug tracer uses it) +CallFunction.execute port crates/horizon-client/src/game/functions.rs::Game::run_function gap: minor: the lines run in one loop, not through the command queue; no tracer onCall +ContinuationTask.execute inline crates/horizon-client/src/game/commands/execute.rs::Game::run_execute the one-source-at-a-time task queue is the loop over sources in run_execute +ContinuationTask.schedule inline crates/horizon-client/src/game/commands/execute.rs::Game::run_execute no queue of continuation tasks: run_execute walks the sources in a loop +net/minecraft/commands/execution/tasks/ExecuteCommand#execute port crates/horizon-client/src/game/commands/execute.rs::Game::run_execute gap: minor: profiler and tracer.onReturn are not reported; forked errors are silenced in the run branch +IsolatedCall.execute inline crates/horizon-client/src/game/functions.rs::Game::run_function the new depth and the isolated return slot are set up in run_function; if function reaches it through call_function_ref +CommandFunction.shouldConcatenateNextLine inline crates/horizon-client/src/datapack.rs::parse_function the trailing-backslash join is the continuation loop in parse_function +CommandFunction.checkCommandLineLength missing minor: no 2,000,000-character limit on a command line; a longer line is not refused +CommandFunction.parseCommand missing minor: lines are parsed when they run (execute_line), not when the datapack loads; a syntax error fails at run time +FunctionBuilder.addCommand inline crates/horizon-client/src/datapack.rs::parse_function plain lines are collected in parse_function +FunctionBuilder.getArgumentIndex inline crates/horizon-client/src/datapack.rs::parse_function macro parameter names are collected in order without duplicates; names, not indices +FunctionBuilder.convertToIndices inline crates/horizon-client/src/datapack.rs::Function::instantiate parameters are looked up by name in instantiate; there is no index list +FunctionBuilder.addMacro inline crates/horizon-client/src/datapack.rs::parse_function the $ line branch of parse_function, split by macro_parameters +FunctionBuilder.build port crates/horizon-client/src/datapack.rs::parse_function returns one Function; Function::is_macro tells a plain function from a macro one +MacroFunction.lookupValues inline crates/horizon-client/src/datapack.rs::Function::instantiate values are picked by parameter name in instantiate +MacroFunction.substituteAndParse port crates/horizon-client/src/datapack.rs::Function::instantiate gap: minor: substitutes only; no new function with a per-call id, and lines are parsed when they run +MacroFunction.MacroEntry.instantiate port crates/horizon-client/src/datapack.rs::Function::instantiate gap: minor: substitutes only; a parse error is raised when the line runs, not as commands.function.error.parse +StringTemplate.fromString port crates/horizon-client/src/datapack.rs::macro_parameters gap: minor: keeps the variable names only, and accepts a $ line with no $(name) (vanilla rejects it) +StringTemplate.isValidVariableName inline crates/horizon-client/src/datapack.rs::macro_parameters checks ASCII letters, digits and _; Java also accepts other Unicode letters and digits +StringTemplate.substitute port crates/horizon-client/src/datapack.rs::substitute gap: minor: no 2,000,000-character length check +ArgumentTypeInfos.register skip:data bootstrap registration of argument type infos +ArgumentTypeInfos.byClass skip:java lookup by Java class; Horizon's argument types are an enum +ArgumentUtils.createNumberFlags inline crates/horizon-client/src/lan/commands.rs::properties the min and max presence bits are written inline in properties +ArgumentUtils.serializeArgumentToJson skip:data JSON of the commands report written by datagen; Horizon reads commands.json +ArgumentUtils.findUsedArgumentTypes skip:data bootstrap check that every used argument type is registered +SuggestionProviders.register skip:data bootstrap registration; the ids Horizon knows are the match in network.rs::provider +SuggestionProviders.getName inline crates/horizon-client/src/lan/commands.rs::CommandTree::packet_with gap: minor: the suggestion id is found by node path in SUGGESTIONS, not from the provider object +DoubleArgumentInfo.serializeToNetwork port crates/horizon-client/src/lan/commands.rs::properties writes the min and max presence byte, then each bound +DoubleArgumentInfo.deserializeFromNetwork port crates/horizon-protocol/src/packets/play.rs::ArgumentProperties::read reads the presence byte and the bounds +DoubleArgumentInfo.serializeToJson skip:data the min and max JSON of the datagen report; Horizon reads commands.json +FloatArgumentInfo.serializeToNetwork port crates/horizon-client/src/lan/commands.rs::properties writes the min and max presence byte, then each bound +FloatArgumentInfo.deserializeFromNetwork port crates/horizon-protocol/src/packets/play.rs::ArgumentProperties::read reads the presence byte and the bounds +FloatArgumentInfo.serializeToJson skip:data the min and max JSON of the datagen report; Horizon reads commands.json +IntegerArgumentInfo.serializeToNetwork port crates/horizon-client/src/lan/commands.rs::properties writes the min and max presence byte, then each bound +IntegerArgumentInfo.deserializeFromNetwork port crates/horizon-protocol/src/packets/play.rs::ArgumentProperties::read reads the presence byte and the bounds +IntegerArgumentInfo.serializeToJson skip:data the min and max JSON of the datagen report; Horizon reads commands.json +LongArgumentInfo.serializeToNetwork port crates/horizon-client/src/lan/commands.rs::properties writes the min and max presence byte, then each bound +LongArgumentInfo.deserializeFromNetwork port crates/horizon-protocol/src/packets/play.rs::ArgumentProperties::read reads the presence byte and the bounds +LongArgumentInfo.serializeToJson skip:data the min and max JSON of the datagen report; Horizon reads commands.json +StringArgumentSerializer.deserializeFromNetwork port crates/horizon-protocol/src/packets/play.rs::ArgumentProperties::read reads the string type as a varint +StringArgumentSerializer.Template.instantiate port crates/horizon-command/src/network.rs::argument_type word, phrase and greedy string types +BlockBox.of skip:other no caller outside BlockBox in the 26.3 sources +BlockBox.include skip:other no caller outside BlockBox in the 26.3 sources +BlockBox.contains skip:other no caller outside BlockBox in the 26.3 sources +BlockBox.extend skip:other no caller outside BlockBox in the 26.3 sources +BlockBox.move skip:other no caller outside BlockBox in the 26.3 sources +BlockBox.offset skip:other no caller outside BlockBox in the 26.3 sources +BlockMath.blockCenterToCorner skip:other no callers in the 26.3 sources +BlockMath.blockCornerToCenter skip:other no callers in the 26.3 sources +BlockMath.getFaceTransformation port crates/horizon-assets/src/bake/transform.rs::BlockModelTransform::inverse_uv_rows the per-face UV matrix; Horizon returns identity unless the model is uv-locked +BlockPos.getX port crates/horizon-core/src/position.rs::BlockPos::unpack static getter of the packed long; unpack gives all three axes +BlockPos.getY port crates/horizon-core/src/position.rs::BlockPos::unpack static getter of the packed long; unpack gives all three axes +BlockPos.getZ port crates/horizon-core/src/position.rs::BlockPos::unpack static getter of the packed long; unpack gives all three axes +BlockPos.min skip:other only BlockBox uses it, and nothing outside BlockBox calls BlockBox +BlockPos.max skip:other only BlockBox uses it, and nothing outside BlockBox calls BlockBox +BlockPos.asLong port crates/horizon-core/src/position.rs::BlockPos::pack packs the three axes +BlockPos.subtract inline crates/horizon-core/src/position.rs::BlockPos::offset callers pass the negated vector +BlockPos.multiply skip:other only the MutableBlockPos override calls it; nothing scales a block position in gameplay +BlockPos.rotate skip:other only the MutableBlockPos override calls it in 26.3; structure rotation is StructureTemplate.transform +BlockPos.cross skip:other no callers in the 26.3 sources +BlockPos.randomInCube inline crates/horizon-world/src/level/tick.rs::Sim::lightning_clear_copper the 3x3x3 random picks of the lightning copper walk; WeavingMobEffect's cobwebs draw the same way in game/potions.rs +BlockPos.randomBetweenClosed inline crates/horizon-world/src/worldgen/projected.rs::SteppedColumnClusterFeature::place gap: minor: FlyNodeEvaluator.getStarts' random start points are not ported; path.rs Finder::start uses one start +BlockPos.withinClippedManhattan skip:other no callers in the 26.3 sources +BlockPos.manhattanOrdered skip:other only withinClippedManhattan calls it, and nothing calls that +BlockPos.betweenClosedStream port crates/horizon-core/src/position/iter.rs::BlockPos::between_closed an iterator instead of a stream +BlockPos.neighborColumn port crates/horizon-client/src/mobs/goals.rs::BreathAir::find_air gap: minor: searches only the mob's own column; vanilla also searches the four neighbouring columns +BlockPos.MutableBlockPos.set skip:java a reusable cursor; Horizon's BlockPos is a copied value +BlockPos.MutableBlockPos.setWithOffset skip:java a reusable cursor; Horizon's BlockPos is a copied value +BlockPos.MutableBlockPos.move skip:java a reusable cursor; Horizon's BlockPos is a copied value +BlockPos.MutableBlockPos.clamp skip:java a reusable cursor; Horizon's BlockPos is a copied value +Cloner.clone skip:data codec copy between registry layers; Horizon reads data from tables +Cloner.Factory.addCodec skip:data codec registration for the copy above +CompositeDirection.compose missing minor: Horizon has no old-world chunk upgrade (UpgradeData) and no worldgen blending (BlendingData, Blender) +Cursor3D.advance port crates/horizon-world/src/block_collision_scan.rs::BlockCollisionScan::find_next steps the cursor in x, y, z order and stops at the end +Cursor3D.getNextType inline crates/horizon-world/src/block_collision_scan.rs::BlockCollisionScan::find_next the boundary count is computed inside find_next, which skips corners and gates edges and pistons +DefaultedMappedRegistry.register skip:data registry bookkeeping; Horizon's tables fix the registry contents +DefaultedMappedRegistry.getId skip:data registry bookkeeping; Horizon's tables fix the registry contents +DefaultedMappedRegistry.getKey skip:data registry bookkeeping; Horizon's tables fix the registry contents +DefaultedMappedRegistry.getValue skip:data registry bookkeeping; Horizon's tables fix the registry contents +DefaultedMappedRegistry.byId skip:data registry bookkeeping; Horizon's tables fix the registry contents +Direction.makeDirectionArray skip:java builds an array of the six directions +Direction.rotate port crates/horizon-assets/src/bake/transform.rs::BlockModelTransform::direction a facing turned by a block model's rotation, then snapped to the nearest direction +Direction.getYRot inline crates/horizon-client/src/game/ghasts.rs::Game::spawn_ghastling the facing-to-yaw table is inline there: north 180, east 270, west 90, others 0 +Direction.getFacingAxis port crates/horizon-world/src/level/place.rs::Ctx::nearest_looking_vertical the Y axis only, which is the only axis vanilla asks for +Direction.getClockWiseX skip:other reached only from getClockWise(Axis), which no 26.3 code calls +Direction.getCounterClockWiseX skip:other reached only from getClockWise(Axis), which no 26.3 code calls +Direction.getClockWiseZ skip:other reached only from getClockWise(Axis), which no 26.3 code calls +Direction.getCounterClockWiseZ skip:other reached only from getClockWise(Axis), which no 26.3 code calls +Direction.fromAxisAndDirection inline crates/horizon-world/src/level/sculk.rs::Sim::sculk_unobstructed_move gap: minor: the axis-and-sign pick is done for sculk's movement check; the other callers (voxel shape faces, poplar foliage) are not checked +Direction.getNearest missing minor: only the experimental redstone orientation uses it, and Horizon has no orientation code +Direction.verifyVertical skip:data codec check for vertical directions +Direction.get inline crates/horizon-world/src/level/portal_travel.rs::largest_rectangle_around the positive side is negative(axis).opposite() there; the PortalForcer call is not traced +Direction.Axis.getPositive inline crates/horizon-world/src/level/portal_travel.rs::largest_rectangle_around the positive side is negative(axis).opposite() there +Direction.Axis.getNegative port crates/horizon-world/src/level/portal_travel.rs::negative +Direction.Axis.getDirections port crates/horizon-world/src/level/creaking.rs::axis_directions gap: minor: returns the negative direction first; vanilla returns the positive one first +Direction.Axis.getPlane port crates/horizon-world/src/level/query.rs::is_horizontal takes a direction and checks its axis is X or Z +Direction.Axis.choose port crates/horizon-core/src/geometry/mod.rs::Vec3::component gap: minor: selects a double component of a Vec3; no integer version +Direction.Plane.test port crates/horizon-world/src/level/query.rs::is_horizontal the horizontal plane only; the vertical plane has no test +GlobalPos.isCloseEnough missing player: an allay never remembers a liked jukebox or note block, so the same-dimension, 1024-block check never runs (see AllayAi.hearNoteblock) +Holder.direct skip:data wraps a value as a registry holder; Horizon has no holders +Holder.Reference.key skip:data registry holder bookkeeping +Holder.Reference.value skip:data registry holder bookkeeping +Holder.Reference.is skip:data registry holder bookkeeping +Holder.Reference.boundTags skip:data registry holder bookkeeping +Holder.Reference.bindKey skip:data registry holder bookkeeping +Holder.Reference.bindValue skip:data registry holder bookkeeping +HolderGetter.getOrThrow skip:data registry lookup helper +HolderGetter.Provider.lookupOrThrow skip:data registry lookup helper +HolderGetter.Provider.get skip:data registry lookup helper +HolderGetter.Provider.getOrThrow skip:data registry lookup helper +HolderLookup.Provider.lookupOrThrow skip:data registry lookup helper +HolderLookup.Provider.create skip:data registry lookup helper +HolderLookup.RegistryLookup.filterFeatures skip:data feature-flag filter of registries; Horizon reads data from tables +HolderLookup.RegistryLookup.Delegate.get skip:data registry lookup delegate +HolderSet.direct skip:data builds a direct holder set +HolderSet.Direct.contains skip:java a cached set for membership tests +HolderSet.Direct.equals skip:java equality of two lists +HolderSet.Named.contents skip:data tag contents bound at load +IdMap.byIdOrThrow skip:data registry id helper +IdMap.getIdOrThrow skip:data registry id helper +IdMapper.addMapping skip:data registry id table filled at bootstrap +IdMapper.byId skip:data registry id table lookup +LayeredRegistryAccess.getLayerIndexOrThrow skip:data layered registry bookkeeping +LayeredRegistryAccess.getAccessFrom skip:data layered registry bookkeeping +LayeredRegistryAccess.getCompositeAccessForLayers skip:data layered registry bookkeeping +LayeredRegistryAccess.replaceFrom skip:data layered registry bookkeeping +LayeredRegistryAccess.collectRegistries skip:data layered registry bookkeeping +MappedRegistry.validateWrite skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.register skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.getKey skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.getValue skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.byId skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.get skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.getAny skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.wrapAsHolder skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.getOrCreateHolderOrThrow skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.containsKey skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.freeze skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.createIntrusiveHolder skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.validateAndUnwrapTagElement skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.bindTags skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.refreshTagsInHolders skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.bindAllTagsToEmpty skip:data registry bookkeeping; Horizon's tables fix the registry contents +MappedRegistry.prepareTagReload skip:data registry bookkeeping; Horizon's tables fix the registry contents +NonNullList.withSize skip:java a list filled with a default value +NonNullList.clear skip:java a list reset to its default values +PositionAndRotation.Mutable.addRotation missing minor: when an entity turns itself while steps are pending, vanilla shifts those steps by the same delta (AbstractInterpolationHandler); Horizon does not diff --git a/tools/port_map/rest-008.tsv b/tools/port_map/rest-008.tsv new file mode 100644 index 0000000000..c6a15e73e7 --- /dev/null +++ b/tools/port_map/rest-008.tsv @@ -0,0 +1,149 @@ +Registry.byNameCodec skip:java codec over the registry by name +Registry.referenceHolderWithLifecycle skip:java codec helper with lifecycle +Registry.safeCastToReference skip:java codec helper +Registry.keys skip:java DynamicOps key stream, no ops in Horizon +Registry.getOptional skip:java Optional wrapper of get +Registry.getValueOrThrow skip:java throwing lookup wrapper +Registry.register skip:data bootstrap registration of built-in entries; Horizon reads registry tables +Registry.registerForHolder skip:data bootstrap registration of built-in entries; Horizon reads registry tables +RegistryAccess.lookupOrThrow skip:java lookup wrapper +RegistryAccess.ImmutableRegistryAccess.lookup skip:java map lookup over registries +RegistrySetBuilder.add skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.buildProviderWithContext skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.build skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.findRegistriesMissingFromPatch skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.buildPatch skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.BootstrappedRegistryState.create skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.BootstrappedRegistryState.bindHolders skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.BootstrappedRegistryState.errorOnMissingHolders skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.BootstrappedRegistryState.validatePatchHolders skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.BuildState.createAndApply skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.BuildState.create skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.BuildState.throwOnError skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySetBuilder.BuildState.createBootstrap skip:data bootstrap and datagen registry-set building; Horizon reads registry data from tables +RegistrySynchronization.packRegistries inline crates/horizon-client/src/lan/data.rs::SyncData::registry_packets gap: minor: packs every registry SyncData holds; the synchronized-registry list is not checked here +RegistrySynchronization.packRegistry port crates/horizon-client/src/lan/data.rs::SyncData::registry_packets gap: minor: known_pack leaves out contents for every entry; vanilla checks each entry's known pack +RegistrySynchronization.networkSafeRegistries inline crates/horizon-client/src/lan/data.rs::SyncData::registry_packets gap: minor: unsure which registries count as networkable; decided when SyncData is built +SectionPos.offset inline crates/horizon-client/src/light.rs::Region::slot gap: minor: the light engine reaches neighbour sections by block coordinate; the SectionTracker walk is not checked +SectionPos.sectionRelativePos port crates/horizon-core/src/position.rs::BlockPos::section_relative +SectionPos.relativeToBlockPos port crates/horizon-core/src/position.rs::SectionPos::relative_to_block +SectionPos.blockToSection port crates/horizon-core/src/position.rs::BlockPos::section on the unpacked position; the long is SectionPos::pack of it +SectionPos.origin inline crates/horizon-client/src/gfx/resident.rs::origin gap: minor: camera-relative origin; the block corner is the same sum without the camera +SectionPos.asLong port crates/horizon-core/src/position.rs::SectionPos::pack asLong(BlockPos): BlockPos::section then this pack; bit layout matches +SectionPos.blocksInside inline crates/horizon-client/src/mobs/villager_brain.rs::chunk_pois the POI scan visits every block of each section +SectionPos.aroundChunk missing minor: unsure: ensureLoadedAndValid pre-loads POI chunks around a point; Horizon's chunk_pois reads loaded chunks only +SectionPos.betweenClosedStream skip:java Java stream over a 3-D section box; the loops in pois_in_range and chunk_pois do the iteration +SectionPos.aroundAndAtBlockPos port crates/horizon-client/src/light.rs::LightChanges::dirty_sections the sections within one block of a changed cell +TypedInstance.is skip:java holder type checks; Horizon compares registry names +UUIDUtil.leastMostToIntArray port crates/horizon-client/src/lan/playerdata.rs::uuid_ints +UUIDUtil.uuidToByteArray port crates/horizon-protocol/src/buf.rs::Writer::uuid writes into the buffer instead of returning bytes +UUIDUtil.readUUID port crates/horizon-client/src/lan/playerdata.rs::ints_uuid gap: minor: no length check here; the input is already [i32; 4] +Vec3i.offsetCodec skip:java codec with a range check +Vec3i.equals skip:java derived equality on BlockPos +Vec3i.subtract skip:other trivial: offset by the negated vector; Horizon has no integer subtract +Vec3i.multiply inline crates/horizon-world/src/worldgen/structure/features.rs::TemplateFeature::place gap: minor: only the scalar form, unit normal times size/2 in the template pivot step +Vec3i.cross inline crates/horizon-client/src/game/golems.rs::Game::find_golem gap: minor: the pattern's right axis is enumerated as X or Z, not computed; the redstone Orientation caller is not checked +Vec3i.distToCenterSqr inline crates/horizon-client/src/mobs/doors.rs::closer_to_center gap: minor: returns only the closer-than test, not the squared distance +Vec3i.distToLowCornerSqr inline crates/horizon-world/src/worldgen/end_spike.rs::EndSpikeFeature::place_spike the spike radius test +Vec3i.distChessboard inline crates/horizon-world/src/level/sculk.rs::Sim::sculk_update_cursors gap: minor: only the sculk cursor distance; GlobalPos.isWithinDistance is not covered +Vec3i.differsHorizontally missing minor: unsure: used by TryFindLand-style AI filters; no horizontal-differ check found in Horizon's mob AI +Vec3i.toString skip:debug debug string +CauldronInteraction.Dispatcher.put skip:java item-to-interaction table; Horizon matches items in cauldron.rs::interact +CauldronInteractions.newDispatcher skip:java table constructor +CauldronInteractions.bootStrap inline crates/horizon-client/src/cauldron.rs::interact gap: minor: posts no FLUID_PICKUP/FLUID_PLACE game event (Game::use_on_cauldron) +CauldronInteractions.addDefaultInteractions inline crates/horizon-client/src/cauldron.rs::interact the lava, water and powder snow bucket arms for every cauldron kind +CauldronInteractions.fillBucket inline crates/horizon-client/src/cauldron.rs::interact gap: minor: posts no FLUID_PICKUP/FLUID_PLACE game event (Game::use_on_cauldron) +CauldronInteractions.emptyBucket inline crates/horizon-client/src/cauldron.rs::interact gap: minor: posts no FLUID_PICKUP/FLUID_PLACE game event (Game::use_on_cauldron) +CauldronInteractions.fillWaterInteraction inline crates/horizon-client/src/cauldron.rs::interact gap: minor: posts no FLUID_PICKUP/FLUID_PLACE game event (Game::use_on_cauldron) +CauldronInteractions.fillLavaInteraction inline crates/horizon-client/src/cauldron.rs::interact gap: minor: posts no FLUID_PICKUP/FLUID_PLACE game event (Game::use_on_cauldron) +CauldronInteractions.fillPowderSnowInteraction inline crates/horizon-client/src/cauldron.rs::interact gap: minor: posts no FLUID_PICKUP/FLUID_PLACE game event (Game::use_on_cauldron) +CauldronInteractions.shulkerBoxInteraction inline crates/horizon-client/src/cauldron.rs::interact gap: minor: posts no FLUID_PICKUP/FLUID_PLACE game event (Game::use_on_cauldron) +CauldronInteractions.bannerInteraction inline crates/horizon-client/src/cauldron.rs::interact gap: minor: posts no FLUID_PICKUP/FLUID_PLACE game event (Game::use_on_cauldron) +CauldronInteractions.dyedItemIteration inline crates/horizon-client/src/cauldron.rs::interact gap: minor: posts no FLUID_PICKUP/FLUID_PLACE game event (Game::use_on_cauldron) +BlockTransformer.playerHasBlockingItemUseIntent port crates/horizon-client/src/game.rs::Game::block_transform gap: minor: checks the off-hand shield for any hand; vanilla checks the main hand only +BlockTransformer.BlockTransformData.builder skip:java builder +BlockTransformer.BlockTransformData.Builder.build skip:java builder +DataComponentExactPredicate.someOf skip:data builds an exact predicate from a stack; used for datagen-made predicates +DataComponentExactPredicate.equals skip:java equality +DataComponentExactPredicate.Builder.expect skip:java builder +DataComponentGetter.getTyped skip:java typed read; Horizon reads typed StackData fields +DataComponentHolder.getAllOfType skip:java stream over components +DataComponentInitializers.runInitializers skip:data item default component initializers; Horizon reads item defaults from its tables +DataComponentInitializers.registryEmpty skip:data item default component initializers; Horizon reads item defaults from its tables +DataComponentInitializers.addBuilder skip:data item default component initializers; Horizon reads item defaults from its tables +DataComponentInitializers.build skip:data item default component initializers; Horizon reads item defaults from its tables +DataComponentInitializers.createInitializerForRegistry skip:data item default component initializers; Horizon reads item defaults from its tables +DataComponentInitializers.Initializer.andThen skip:data item default component initializers; Horizon reads item defaults from its tables +DataComponentInitializers.Initializer.add skip:data item default component initializers; Horizon reads item defaults from its tables +DataComponentInitializers.SingleComponentInitializer.asInitializer skip:data item default component initializers; Horizon reads item defaults from its tables +DataComponentLookup.getOrCreateStorage skip:java cache +DataComponentLookup.scanForComponents skip:java index build behind the lookup +DataComponentLookup.findAll missing minor: unsure: DyeColor mixing and the recipe fuel list read it; Horizon finds dyes and fuels by name and table +DataComponentLookup.ComponentStorage.findMatching missing minor: unsure: spawn egg from an entity type; Horizon builds egg ids by name +DataComponentMap.makeCodecFromMap skip:java codec +DataComponentMap.iterator skip:java iterator +DataComponentMap.Builder.setUnchecked skip:java builder +DataComponentMap.Builder.addAll skip:java builder +DataComponentMap.Builder.buildFromMapTrusted skip:java builder +DataComponentPatch.getFromPatchAndPrototype skip:java patch-or-default read; typed StackData fields plus the item table cover it +DataComponentPatch.forget missing minor: unsure: BlockEntity.setComponents strips implicit components with forget; not checked in Horizon's block entity placement +DataComponentPatch.split skip:java added and removed parts kept as separate fields (protocol Stack.removed, StackData) +DataComponentPatch.equals skip:java equality +DataComponentPatch.toString skip:debug debug string +DataComponentPatch.Builder.set skip:java builder +DataComponentPatch.Builder.build skip:java builder +DataComponentType.codecOrThrow skip:java codec +DataComponentType.Builder.build skip:data component type registration +PatchedDataComponentMap.fromPatch port crates/horizon-items/src/item_data.rs::decode_components gap: minor: defaults come from the item table, not a prototype map +PatchedDataComponentMap.isPatchSanitized skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.set skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.remove skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.applyPatch skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.restorePatch skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.setAll skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.ensureMapOwnership skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.keySet skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.iterator skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.size skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.asPatch skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.copy skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +PatchedDataComponentMap.equals skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data +TypedDataComponent.encodeValue skip:java codec +AttributeModifiersPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly +AttributeModifiersPredicate.EntryPredicate.test missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly +BundlePredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly +ContainerPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly +net/minecraft/core/component/predicates/DamagePredicate#matches inline crates/horizon-client/src/advancements.rs::item gap: minor: only the damage range; the durability range (max damage minus damage) is not checked +DataComponentPredicate.Single.wrapCodec skip:java codec +DataComponentPredicate.Type.unpackType skip:java codec helper +EnchantmentsPredicate.matches inline crates/horizon-client/src/advancements.rs::item the enchantments predicate inside item() +FireworkExplosionPredicate.FireworkPredicate.test missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly +FireworksPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly +JukeboxPlayablePredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly +PotionsPredicate.matches port crates/horizon-client/src/advancements.rs::potions +WritableBookPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly +WrittenBookPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly +BoatDispenseItemBehavior.execute port crates/horizon-client/src/game/dispenser.rs::Game::dispense_boat +DefaultDispenseItemBehavior.dispense port crates/horizon-client/src/game/dispenser.rs::Game::dispense_default the click and dispense events of dispense_default +DefaultDispenseItemBehavior.addToInventoryOrDispense port crates/horizon-client/src/game/dispenser.rs::Game::consume_with_remainder +EquipmentDispenseItemBehavior.execute inline crates/horizon-client/src/game/dispenser.rs::Game::dispense_default_method the equippable branch of dispense_default_method +FlintAndSteelDispenseItemBehavior.execute port crates/horizon-client/src/game/dispenser.rs::Game::dispense_flint_and_steel +FlintAndSteelDispenseItemBehavior.tryIgniteExplosiveEntities inline crates/horizon-client/src/game/dispenser.rs::Game::dispense_flint_and_steel the sulfur cube priming branch +MinecartDispenseItemBehavior.execute port crates/horizon-client/src/game/dispenser.rs::Game::dispense_minecart +MinecartDispenseItemBehavior.getRailShape inline crates/horizon-client/src/game/dispenser.rs::Game::dispense_minecart the slope test on the rail +ProjectileDispenseBehavior.execute port crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile +ProjectileDispenseBehavior.playSound inline crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile returns the level event that dispense_item posts +ShearsDispenseItemBehavior.execute port crates/horizon-client/src/game/dispenser.rs::Game::dispense_shears +ShearsDispenseItemBehavior.tryShearBeehive port crates/horizon-client/src/game/dispenser.rs::Game::shear_beehive +ShulkerBoxDispenseBehavior.execute port crates/horizon-client/src/game/dispenser.rs::Game::dispense_shulker_box +SpawnEggItemBehavior.execute port crates/horizon-client/src/game/dispenser.rs::Game::dispense_spawn_egg +SulfurCubeBlockDispenseItemBehavior.execute inline crates/horizon-client/src/game/dispenser.rs::Game::dispense_default_method the swallow branch of dispense_default_method +BlockParticleOption.codec skip:java codec; the field check is horizon-command args.rs::particle_options +BlockParticleOption.streamCodec port crates/horizon-protocol/src/entity_data.rs::Particle::read read side of the stream codec +ColorParticleOption.codec skip:java codec; the field check is horizon-command args.rs::particle_options +ColorParticleOption.streamCodec port crates/horizon-protocol/src/entity_data.rs::Particle::read read side of the stream codec +GeyserBaseParticleOptions.streamCodec port crates/horizon-protocol/src/entity_data.rs::Particle::read read side of the stream codec +GeyserParticleOptions.codec skip:java codec; the field check is horizon-command args.rs::particle_options +GeyserParticleOptions.streamCodec port crates/horizon-protocol/src/entity_data.rs::Particle::read read side of the stream codec +ItemParticleOption.codec skip:java codec; the field check is horizon-command args.rs::particle_options +ItemParticleOption.streamCodec port crates/horizon-protocol/src/entity_data.rs::Particle::read read side of the stream codec diff --git a/tools/port_map/rest-009.tsv b/tools/port_map/rest-009.tsv new file mode 100644 index 0000000000..563ddbe418 --- /dev/null +++ b/tools/port_map/rest-009.tsv @@ -0,0 +1,149 @@ +PowerParticleOption.codec skip:java codec of the command and NBT form, default power 1.0 +PowerParticleOption.streamCodec port crates/horizon-protocol/src/entity_data.rs::read_value gap: minor: decode side only (the Scalar arm used by dragon_breath) +SpellParticleOption.codec skip:java codec of the command and NBT form: optional colour and power +SpellParticleOption.streamCodec port crates/horizon-protocol/src/entity_data.rs::read_value gap: minor: decode side only (the ColorPower arm) +SpellParticleOption.create port crates/horizon-client/src/particles.rs::ParticleOptions::colored gap: minor: takes RGBA with no power argument; power is set by the caller through scale +BootstrapRegistry.get skip:data bootstrap registry used at datagen; Horizon reads registry data from tables +BootstrapRegistry.FrozenStorage.get skip:data bootstrap registry storage at datagen; Horizon reads registry data from tables +BootstrapRegistry.RegistrationStorage.get skip:data bootstrap registry storage at datagen; Horizon reads registry data from tables +BuiltInRegistries.registerDefaulted skip:data startup registration of built-in registries; Horizon ships registry data as tables +BuiltInRegistries.registerDefaultedWithIntrusiveHolders skip:data startup registration of built-in registries; Horizon ships registry data as tables +BuiltInRegistries.internalRegister skip:data startup registration of built-in registries; Horizon ships registry data as tables +BuiltInRegistries.createContents skip:data bootstrap loader run; Horizon ships registry data as tables +BuiltInRegistries.freeze skip:data bootstrap freeze of built-in registries; Horizon ships registry data as tables +BuiltInRegistries.validate skip:data startup sanity check of registry contents; Horizon ships registry data as tables +ConcurrentHolderGetter.get skip:java thread-safe cache over a holder getter +EmptyTagLookupWrapper.wrap skip:data registry lookup with tags stripped, used when vanilla builds registries from patches; Horizon does not build registries +PatchedRegistry.get skip:data patched registry from data packs; Horizon reads registry data from tables +PatchedRegistry.createLazyFullPatchedRegistries skip:data builds patched registries from data packs; Horizon reads registry data from tables +PatchedRegistry.applyPatches skip:data applies data-pack registry patches; Horizon reads registry data from tables +PatchedRegistry.LazyHolder.value skip:java lazy clone cache for a patched holder +HolderSetCodec.directCodec skip:java codec helper for holder lists +HolderSetCodec.decode skip:java codec decode of tag or list references +HolderSetCodec.lookupTag skip:data resolves a tag key against the registry; Horizon resolves tag references by name in its data readers (loot.rs::holder_set) +HolderSetCodec.encode skip:java codec encode of holder sets +RegistryCodecs.holder skip:java codec factory for registry references +RegistryFileCodec.encode skip:java codec encode of registry file references +RegistryFileCodec.decode skip:java codec decode of registry file references (inline definitions allowed) +RegistryFixedCodec.encode skip:java codec encode of fixed registry references +RegistryFixedCodec.decode skip:java codec decode of fixed registry references +CircleGizmo.emit missing minor: debug circle gizmo (F3 debug renderers only); gizmos.rs has no circle primitive +GizmoStyle.hasStroke skip:other style predicate; Horizon debug lines always carry one width (gizmos.rs::Line), so there is no stroke to test +Gizmos.addGizmo skip:other registration into vanilla's thread-local collector; Horizon's gizmos.rs::Gizmos holds the lines directly +Gizmos.rect missing minor: debug rectangles (F3 debug renderers only); gizmos.rs draws cuboid edges only, no faces +Gizmos.billboardTextOverBlock missing minor: debug text above a block (debug renderers only); gizmos.rs has no billboard text +Gizmos.billboardTextOverMob missing minor: debug text above a mob (debug renderers only); gizmos.rs has no billboard text +Gizmos.TemporaryCollection.close skip:other restores vanilla's thread-local gizmo collector; Horizon has no collector stack +LineGizmo.emit port crates/horizon-client/src/gizmos.rs::Gizmos::line gap: minor: no alpha multiplier, so debug lines do not fade +PointGizmo.emit missing minor: debug point gizmo (F3 debug renderers only); gizmos.rs has no point primitive +RectGizmo.fromCuboidFace missing minor: face rectangle for debug rects (F3 debug renderers only); not ported +RectGizmo.emit missing minor: filled and outlined debug quads (F3 debug renderers only); gizmos.rs has no quad primitive +SimpleGizmoCollector.add skip:other adds to vanilla's per-frame gizmo collector; Horizon has no collector object, gizmos.rs::Gizmos holds the lines +SimpleGizmoCollector.drainGizmos skip:other per-frame hand-off and expiry of vanilla's collector; Horizon's debug gizmos have no expiry +SimpleGizmoCollector.GizmoInstance.persistForMillis missing minor: debug gizmos that stay on screen for a set time (F3 debug renderers only); gizmos.rs has no expiry +SimpleGizmoCollector.GizmoInstance.getAlphaMultiplier missing minor: fade-out of persisted debug gizmos; gizmos.rs has no fade +TextGizmo.emit missing minor: debug text gizmo (F3 debug renderers only); gizmos.rs has no text primitive +TextGizmo.Style.withLeftAlignment missing minor: left-aligned debug label text (debug renderers only); no text style in gizmos.rs +DeprecatedTranslationsInfo.loadFromJson port crates/horizon-client-core/src/lang.rs::apply_deprecated parses and applies the deprecated list in one fn; vanilla throws on a bad file, Horizon ignores it +DeprecatedTranslationsInfo.loadFromResource inline crates/horizon-client-core/src/lang.rs::Language::load reads assets/minecraft/lang/deprecated.json from the first source that has it, then applies it +Language.parseTranslations port crates/horizon-client-core/src/lang.rs::Language::load reads each lang file from every source; a file that fails to parse is skipped +Language.loadFromJson port crates/horizon-client-core/src/lang.rs::Language::load gap: minor: component-valued entries (arrays or objects) are not kept; serde_json rejects the whole file, so its other entries are lost +ByteArrayTag.copy skip:java Clone on Tag replaces copy() +ByteArrayTag.equals skip:java PartialEq derive replaces equals() +ByteArrayTag.setTag port crates/horizon-client-core/src/nbt_path.rs::set_tag gap: minor: writes through an NBT path node; returns a change count, not a boolean for non-numeric tags +ByteArrayTag.addTag port crates/horizon-client-core/src/nbt_path.rs::NbtPath::insert gap: minor: inserts through the /data path, not a per-array add +ByteArrayTag.remove port crates/horizon-client-core/src/nbt_path.rs::remove_tag gap: minor: removal through an index node; returns a count, not the removed tag +CompoundTag.write port crates/horizon-world/src/nbt.rs::Writer::payload the Compound arm writes each entry and the End byte +CompoundTag.sizeInBytes skip:other memory-size estimate for vanilla's NbtAccounter quota; Horizon's Limits count wire bytes and nodes +CompoundTag.getByteOr port crates/horizon-world/src/datafix/dynamic.rs::as_number gap: minor: the default and the i8 narrowing (Number::byte_value) are left to the caller +CompoundTag.getShortOr port crates/horizon-world/src/datafix/dynamic.rs::as_number gap: minor: the default and the i16 narrowing are left to the caller +CompoundTag.getLongOr port crates/horizon-world/src/datafix/dynamic.rs::as_long the key lookup is done by the caller +CompoundTag.getFloatOr port crates/horizon-world/src/datafix/dynamic.rs::as_float the key lookup is done by the caller +CompoundTag.getDoubleOr port crates/horizon-world/src/datafix/dynamic.rs::as_double the key lookup is done by the caller +CompoundTag.getStringOr port crates/horizon-world/src/datafix/dynamic.rs::as_string the key lookup is done by the caller +CompoundTag.getByteArray port crates/horizon-world/src/datafix/dynamic.rs::as_byte_buffer minor: also accepts number lists; vanilla takes a ByteArrayTag only +CompoundTag.getLongArray port crates/horizon-world/src/datafix/dynamic.rs::as_long_stream minor: also accepts number lists; vanilla takes a LongArrayTag only +CompoundTag.getCompound port crates/horizon-world/src/datafix/dynamic.rs::map the key lookup is done by the caller (dynamic.rs::get) +CompoundTag.getList port crates/horizon-world/src/datafix/dynamic.rs::stream gap: minor: returns copies of the elements, not the list tag; number arrays also pass +CompoundTag.getBooleanOr port crates/horizon-client-core/src/block_entity_nbt.rs::flag minor: reads the int value of any numeric tag; vanilla reads the byte, which differs only beyond a byte +CompoundTag.copy skip:java Clone on Tag replaces copy() +CompoundTag.equals skip:java PartialEq derive replaces equals() +CompoundTag.writeNamedTag port crates/horizon-world/src/nbt.rs::Writer::payload the Compound arm writes each entry's id and name before its payload +CompoundTag.readNamedTagData port crates/horizon-world/src/nbt.rs::Reader::payload the read error is an Error value, not a crash report +CompoundTag.accept port crates/horizon-client-core/src/snbt.rs::visit gap: minor: the StreamTagVisitor overload (entry-by-entry walk) is not ported +CompoundTag.store skip:java codec-based save; Horizon writes fields explicitly (block_entity_nbt.rs Fields::put) +CompoundTag.storeNullable skip:java codec-based save of an optional value; Horizon writes fields explicitly +CompoundTag.read skip:java codec-based load; Horizon reads fields explicitly (block_entity_nbt.rs helpers) +IntArrayTag.write port crates/horizon-world/src/nbt.rs::Writer::payload the IntArray arm writes the count, then the ints +IntArrayTag.copy skip:java Clone on Tag replaces copy() +IntArrayTag.equals skip:java PartialEq derive replaces equals() +IntArrayTag.setTag port crates/horizon-client-core/src/nbt_path.rs::set_tag gap: minor: writes through an NBT path node; returns a change count +IntArrayTag.addTag port crates/horizon-client-core/src/nbt_path.rs::NbtPath::insert gap: minor: inserts through the /data path, not a per-array add +IntArrayTag.remove port crates/horizon-client-core/src/nbt_path.rs::remove_tag gap: minor: removal through an index node; returns a count, not the removed tag +ListTag.tryUnwrap inline crates/horizon-world/src/nbt.rs::Reader::payload a list element that is a single "" compound is unwrapped while it is read +ListTag.isWrapper inline crates/horizon-world/src/nbt.rs::Reader::payload same single-""-key test as the read-side unwrap; the write side tests it in Writer::payload +ListTag.write port crates/horizon-world/src/nbt.rs::Writer::payload the List arm writes the element type, the count, then the elements, wrapping mixed-list compounds +ListTag.identifyRawElementType port crates/horizon-world/src/nbt.rs::Writer::payload the List arm uses the first element's type, or 10 when the elements differ +ListTag.addAndUnwrap inline crates/horizon-world/src/nbt.rs::Reader::payload elements are unwrapped as they are read; there is no in-memory add that unwraps +ListTag.sizeInBytes skip:other memory-size estimate for vanilla's NbtAccounter; Horizon counts wire bytes and nodes +ListTag.getCompound port crates/horizon-world/src/datafix/dynamic.rs::map the index lookup is done by the caller +ListTag.getList port crates/horizon-world/src/datafix/dynamic.rs::stream gap: minor: returns copies of the elements, not the list tag; number arrays also pass +ListTag.getShortOr port crates/horizon-world/src/datafix/dynamic.rs::as_number gap: minor: the default and the i16 narrowing are left to the caller +ListTag.getIntOr port crates/horizon-world/src/datafix/dynamic.rs::as_int the index lookup is done by the caller +ListTag.getIntArray port crates/horizon-world/src/datafix/dynamic.rs::as_int_stream minor: also accepts number lists +ListTag.getLongArray port crates/horizon-world/src/datafix/dynamic.rs::as_long_stream minor: also accepts number lists +ListTag.getDoubleOr port crates/horizon-world/src/datafix/dynamic.rs::as_double the index lookup is done by the caller +ListTag.getFloatOr port crates/horizon-world/src/datafix/dynamic.rs::as_float the index lookup is done by the caller +ListTag.getStringOr port crates/horizon-world/src/datafix/dynamic.rs::as_string the index lookup is done by the caller +ListTag.getNullable skip:java bounds-checked element access; Rust slice get and Option +ListTag.copy skip:java Clone on Tag replaces copy() +ListTag.equals skip:java PartialEq derive replaces equals() +ListTag.compoundStream port crates/horizon-world/src/datafix/dynamic.rs::stream gap: minor: returns the elements; the compound-only filter is done by the caller +ListTag.accept port crates/horizon-client-core/src/snbt.rs::visit gap: minor: the StreamTagVisitor overload (entry-by-entry walk) is not ported +LongArrayTag.write port crates/horizon-world/src/nbt.rs::Writer::payload the LongArray arm writes the count, then the longs +LongArrayTag.copy skip:java Clone on Tag replaces copy() +LongArrayTag.equals skip:java PartialEq derive replaces equals() +LongArrayTag.setTag port crates/horizon-client-core/src/nbt_path.rs::set_tag gap: minor: writes through an NBT path node; returns a change count +LongArrayTag.addTag port crates/horizon-client-core/src/nbt_path.rs::NbtPath::insert gap: minor: inserts through the /data path, not a per-array add +LongArrayTag.remove port crates/horizon-client-core/src/nbt_path.rs::remove_tag gap: minor: removal through an index node; returns a count, not the removed tag +NbtAccounter.accountBytes inline crates/horizon-world/src/nbt.rs::Reader::take gap: minor: counts wire bytes against max_bytes; vanilla counts an in-memory size estimate, so the same limit trips at other sizes +NbtAccounter.pushDepth inline crates/horizon-world/src/nbt.rs::Reader::payload the depth check for list and compound arms; depth is passed down as a parameter +NbtAccounter.popDepth inline crates/horizon-world/src/nbt.rs::Reader::payload nothing to pop: depth is a parameter, not a counter +NbtIo.createDecompressorStream port crates/horizon-world/src/compression.rs::Compression::decode gap: minor: decodes the whole buffer at once, not a stream +NbtIo.createCompressorStream port crates/horizon-world/src/compression.rs::Compression::encode gap: minor: encodes the whole buffer at once, not a stream +NbtIo.parseCompressed port crates/horizon-world/src/nbt.rs::read_named_streaming gap: minor: decompression is the caller's Compression::decode; the visitor only streams lists under one key +NbtIo.parse port crates/horizon-world/src/nbt.rs::read_named_streaming gap: minor: visitor callbacks are not generic; only lists under one key are streamed +NbtIo.writeAnyTag port crates/horizon-world/src/nbt.rs::write_network type byte then the payload, no name +NbtIo.writeUnnamedTag port crates/horizon-world/src/nbt.rs::write_named written with an empty name +NbtIo.readUnnamedTag port crates/horizon-world/src/nbt.rs::read_network type byte then the payload, no name +NbtIo.readTagSafe port crates/horizon-world/src/nbt.rs::read_network an IOException becomes an Error value, not a crash report +NbtIo.StringFallbackDataOutput.writeUTF port crates/horizon-world/src/nbt.rs::Writer::string gap: minor: a string over 65535 bytes fails the write; vanilla writes "" and logs +NbtOps.getNumberValue port crates/horizon-world/src/datafix/dynamic.rs::as_number +NbtOps.getStringValue port crates/horizon-world/src/datafix/dynamic.rs::as_str +NbtOps.mergeToList skip:java DynamicOps list builder used by codecs; Horizon builds lists directly in datafix +NbtOps.mergeToMap port crates/horizon-world/src/datafix/dynamic.rs::set gap: minor: one field; returns no error for a non-map or a non-string key +NbtOps.getMapValues port crates/horizon-world/src/datafix/dynamic.rs::map the (key, value) pairs are built by the caller +NbtOps.getMapEntries port crates/horizon-world/src/datafix/dynamic.rs::map the entries are walked by the caller +NbtOps.getMap port crates/horizon-world/src/datafix/dynamic.rs::map the MapLike view is built by the caller +NbtOps.createMap port crates/horizon-world/src/nbt.rs::Tag::compound builds a compound from name and value pairs +NbtOps.getList port crates/horizon-world/src/datafix/dynamic.rs::stream gap: minor: returns copies of the elements; number arrays also pass +NbtOps.getByteBuffer port crates/horizon-world/src/datafix/dynamic.rs::as_byte_buffer +NbtOps.createByteList skip:java builds a Tag::ByteArray from a buffer; Horizon constructs the array directly +NbtOps.getIntStream port crates/horizon-world/src/datafix/dynamic.rs::as_int_stream +NbtOps.getLongStream port crates/horizon-world/src/datafix/dynamic.rs::as_long_stream +NbtOps.remove port crates/horizon-world/src/datafix/dynamic.rs::remove +NbtOps.createCollector skip:java typed-list collector for list merges (keeps byte, int and long arrays); Horizon has no DynamicOps list builder +NbtOps.ByteListCollector.accept skip:java collector builder for typed byte lists +NbtOps.IntListCollector.accept skip:java collector builder for typed int lists +NbtOps.ListCollector.acceptAll skip:java collector builder for list merges +NbtOps.LongListCollector.accept skip:java collector builder for typed long lists +NbtOps.NbtRecordBuilder.build skip:java record builder for codecs +NbtUtils.setValueHelper port crates/horizon-world/src/worldgen/structure/template.rs::read_state a valid property value is set; a bad one keeps the default and the warning is not logged +NbtUtils.writeStateProperties port crates/horizon-world/src/datafix/block_state_data.rs::State::to_tag Properties written only when the state has any +NbtUtils.writeBlockState port crates/horizon-world/src/datafix/block_state_data.rs::State::to_tag {Name, Properties} +NbtUtils.packStructureTemplate missing minor: writes the packed palette form of a structure template (string palette, sorted blocks); Horizon's template code only reads templates +NbtUtils.unpackStructureTemplate port crates/horizon-world/src/worldgen/structure/template.rs::StructureTemplate::from_nbt gap: minor: reads the unpacked palette and blocks only; a packed string palette with its data list is not read and its states load as air +NbtUtils.packBlockState missing minor: writes name{key:value} palette strings; Horizon has no state-to-string writer for templates +NbtUtils.unpackBlockState port crates/horizon-world/src/worldgen/structure/template.rs::parse_state_string gap: minor: resolves the text to a state id, not an NBT state; invalid text gives None +NbtUtils.addCurrentDataVersion inline crates/horizon-world/src/datafix/files.rs::add_versions gap: minor: add_versions stamps level.dat only, and it also sets LastPlayed and Version +NbtUtils.addDataVersion inline crates/horizon-world/src/datafix/mod.rs::update_chunk_to gap: minor: stamps chunks only; the same stamp is in update_region_tag_to and update_player_to diff --git a/tools/port_map/rest-010.tsv b/tools/port_map/rest-010.tsv new file mode 100644 index 0000000000..a889290885 --- /dev/null +++ b/tools/port_map/rest-010.tsv @@ -0,0 +1,150 @@ +SnbtGrammar.cleanAndAppend inline crates/horizon-command/src/snbt.rs::grammar float text: underscores dropped in the float action +SnbtGrammar.parseUnsignedShort inline crates/horizon-command/src/snbt.rs::IntLit::create unsigned short arm: value must fit 16 bits +SnbtGrammar.createFloat port crates/horizon-command/src/snbt.rs::float_tag gap: minor: the text is built in the grammar's float action; float_tag types it +SnbtGrammar.convertFloat inline crates/horizon-command/src/snbt.rs::float_tag float arm; non-finite values rejected +SnbtGrammar.convertDouble inline crates/horizon-command/src/snbt.rs::float_tag double arm; non-finite values rejected +SnbtGrammar.joinList skip:java joins parser parts as a string +SnbtGrammar.ArrayPrefix.isAllowed inline crates/horizon-command/src/snbt.rs::array allowed element suffixes per array kind +SnbtGrammar.ArrayPrefix.buildNumber inline crates/horizon-command/src/snbt.rs::array element type check, then the value +SnbtGrammar.ArrayPrefix.computeType inline crates/horizon-command/src/snbt.rs::array default type when the element has no suffix +SnbtGrammar.IntegerLiteral.cleanupDigits port crates/horizon-command/src/snbt.rs::IntLit::cleaned sign and underscores +SnbtPrinterTagVisitor.visitByteArray skip:data datagen .snbt writer only (NbtUtils.structureToSnbt); Horizon writes no .snbt +SnbtPrinterTagVisitor.visitIntArray skip:data datagen .snbt writer only (NbtUtils.structureToSnbt); Horizon writes no .snbt +SnbtPrinterTagVisitor.visitLongArray skip:data datagen .snbt writer only (NbtUtils.structureToSnbt); Horizon writes no .snbt +SnbtPrinterTagVisitor.visitList skip:data datagen .snbt writer only (NbtUtils.structureToSnbt); Horizon writes no .snbt +SnbtPrinterTagVisitor.visitCompound skip:data datagen .snbt writer only (NbtUtils.structureToSnbt); Horizon writes no .snbt +SnbtPrinterTagVisitor.getKeys skip:data structure key order for .snbt files; Horizon writes no .snbt +StringTagVisitor.visitByteArray inline crates/horizon-client-core/src/snbt.rs::visit compact [B;..B] form +StringTagVisitor.visitIntArray inline crates/horizon-client-core/src/snbt.rs::visit compact [I;..] form +StringTagVisitor.visitLongArray inline crates/horizon-client-core/src/snbt.rs::visit compact [L;..L] form +StringTagVisitor.visitList inline crates/horizon-client-core/src/snbt.rs::visit compact list form +StringTagVisitor.visitCompound inline crates/horizon-client-core/src/snbt.rs::visit compact form; keys sorted +StringTagVisitor.handleKeyEscape port crates/horizon-client-core/src/snbt.rs::unquoted_key decides bare or quoted key; quote_and_escape does the quoting +Tag.acceptAsRoot skip:java streaming visitor entry point; Horizon reads whole trees +Tag.asBoolean port crates/horizon-world/src/datafix/dynamic.rs::as_bool_opt non-zero number is true +TagParser.castToCompoundOrThrow inline crates/horizon-command/src/snbt.rs::parse_compound_argument expected-compound error +TagParser.parseCompoundFully port crates/horizon-command/src/snbt.rs::parse_fully gap: minor: returns any tag; callers check for a compound +TagType.parseRoot skip:java streaming root parse; Horizon reads whole trees +TagType.StaticSize.skip skip:java streaming skip; Horizon reads whole trees +TagType.VariableSize.skip skip:java streaming skip; Horizon reads whole trees +TagTypes.getType inline crates/horizon-world/src/nbt.rs::Reader::payload tag id dispatch; unknown id is an error +TextComponentTagVisitor.visitByte inline crates/horizon-client-core/src/snbt.rs::Pretty::visit number arm: gold value, red suffix +TextComponentTagVisitor.visitShort inline crates/horizon-client-core/src/snbt.rs::Pretty::visit number arm: gold value, red suffix +TextComponentTagVisitor.visitLong inline crates/horizon-client-core/src/snbt.rs::Pretty::visit number arm: gold value, red suffix +TextComponentTagVisitor.visitFloat inline crates/horizon-client-core/src/snbt.rs::Pretty::visit number arm: gold value, red suffix +TextComponentTagVisitor.visitDouble inline crates/horizon-client-core/src/snbt.rs::Pretty::visit number arm: gold value, red suffix +TextComponentTagVisitor.visitByteArray port crates/horizon-client-core/src/snbt.rs::Pretty::array 128-element cap with <...> +TextComponentTagVisitor.visitIntArray port crates/horizon-client-core/src/snbt.rs::Pretty::array 128-element cap with <...> +TextComponentTagVisitor.visitLongArray port crates/horizon-client-core/src/snbt.rs::Pretty::array 128-element cap with <...> +TextComponentTagVisitor.shouldWrapListElements inline crates/horizon-client-core/src/snbt.rs::Pretty::list wrap when under 8 elements and one is not numeric +TextComponentTagVisitor.appendSubTag port crates/horizon-client-core/src/snbt.rs::Pretty::sub depth and indent tracking +TextComponentTagVisitor.RichStyling.overrideToken inline crates/horizon-client-core/src/snbt.rs::Pretty::number token colours are fixed in code; nothing to override +CollectFields.visitEntry skip:java streaming field filter for speed; Horizon reads whole tags +CollectFields.visitContainerEnd skip:java streaming field filter for speed; Horizon reads whole tags +CollectToTag.visit skip:java streaming tag builder; Horizon parses to a tree directly +CollectToTag.visitEntry skip:java streaming tag builder; Horizon parses to a tree directly +CollectToTag.enterContainerIfNeeded skip:java streaming tag builder; Horizon parses to a tree directly +CollectToTag.visitContainerEnd skip:java streaming tag builder; Horizon parses to a tree directly +FieldTree.addEntry skip:java field selection tree for streaming reads +SkipFields.visitEntry skip:java streaming field skip for speed; Horizon reads whole tags +SkipFields.visitContainerEnd skip:java streaming field skip for speed; Horizon reads whole tags +CipherBase.bufToByte skip:java Netty buffer to byte array +CipherBase.decipher port crates/horizon-protocol/src/crypto.rs::Cfb8::decrypt inbound CFB8; applied in ConnReader::recv +CipherBase.encipher port crates/horizon-protocol/src/crypto.rs::Cfb8::encrypt outbound CFB8; applied in ConnWriter::send_contents +CompressionDecoder.decode port crates/horizon-protocol/src/frame.rs::decompress threshold and size checks, exact length +CompressionDecoder.setupInflaterInput skip:java ByteBuffer plumbing for the inflater +CompressionDecoder.inflate inline crates/horizon-protocol/src/frame.rs::decompress zlib inflate with the declared-length check +net/minecraft/network/Connection#channelActive skip:other Netty channel open; the blocking Connection is connected before use +net/minecraft/network/Connection#exceptionCaught inline crates/horizon-client/src/net.rs::run gap: player: a failed read or malformed packet shows Horizon's raw error text, not vanilla's Timed out or Internal Exception disconnect +net/minecraft/network/Connection#channelRead0 port crates/horizon-client/src/net.rs::receive_play play-phase dispatch; gap: minor: configuration packets dispatch in configure +net/minecraft/network/Connection#validateListener skip:other the per-phase decode enums fix the listener's side and protocol +net/minecraft/network/Connection#syncAfterConfigurationChange skip:other Netty future sync +net/minecraft/network/Connection#setupInboundProtocol inline crates/horizon-client/src/net.rs::run phase switch: the decode enum used for each phase +net/minecraft/network/Connection#setupOutboundProtocol inline crates/horizon-client/src/net.rs::run phase switch: the encode enum used for each phase +net/minecraft/network/Connection#setListenerForServerboundHandshake inline crates/horizon-client/src/lan/conn.rs::serve_connection the handshake intent picks the path +net/minecraft/network/Connection#initiateServerboundPlayConnection port crates/horizon-client/src/net.rs::run handshake intention and login hello; transfer intent via the TRANSFER flag +net/minecraft/network/Connection#initiateServerboundConnection port crates/horizon-client/src/net.rs::run handshake intention and login hello +net/minecraft/network/Connection#runOnceConnected inline crates/horizon-client/src/net.rs::run the action runs at once: the socket is connected before run sends +net/minecraft/network/Connection#sendPacket port crates/horizon-protocol/src/connection.rs::ConnWriter::send compression framing, then the write +net/minecraft/network/Connection#doSendPacket port crates/horizon-protocol/src/connection.rs::ConnWriter::send_contents write, count and encrypt +net/minecraft/network/Connection#flushChannel inline crates/horizon-protocol/src/connection.rs::ConnWriter::send_contents every send writes at once (TCP_NODELAY); no flush step +net/minecraft/network/Connection#flush inline crates/horizon-protocol/src/connection.rs::ConnWriter::send_contents every send writes at once (TCP_NODELAY); no flush step +net/minecraft/network/Connection#flushQueue skip:other pending actions exist only while disconnected; run is always connected +net/minecraft/network/Connection#tick skip:other the receive thread blocks in recv instead of ticking +net/minecraft/network/Connection#tickSecond skip:debug packet averages for the F3 screen; debug_keys samples raw counters per tick +net/minecraft/network/Connection#getLoggableAddress port crates/horizon-client/src/lan/conn.rs::user_name gap: minor: name with address or withheld; no local form +net/minecraft/network/Connection#disconnect port crates/horizon-client/src/lan/conn.rs::PlayerSender::disconnect gap: minor: server-side kick; the client's own close is Session::disconnect +net/minecraft/network/Connection#isMemoryConnection port crates/horizon-protocol/src/connection.rs::Connection::is_memory in-memory pipe +net/minecraft/network/Connection#connectToServer port crates/horizon-protocol/src/connection.rs::Connection::connect_addresses gap: minor: the SRV lookup is in connect_server +net/minecraft/network/Connection#configurePacketHandler skip:other Netty handler workaround +net/minecraft/network/Connection#configureSerialization skip:other Netty pipeline; framing is in frame.rs and ConnReader/ConnWriter +net/minecraft/network/Connection#createFrameDecoder skip:other Netty frame decoder choice; in-memory pipes use the same framing +net/minecraft/network/Connection#connectToLocalServer port crates/horizon-protocol/src/connection.rs::Connection::local the pipe comes from lan/mod.rs Lan::connect_local +net/minecraft/network/Connection#fromChannel port crates/horizon-protocol/src/connection.rs::Connection::from_stream gap: minor: the 30 s read timeout is set in lan/conn.rs::serve_connection +net/minecraft/network/Connection#setEncryptionKey port crates/horizon-protocol/src/connection.rs::Connection::enable_encryption CFB8 both directions +net/minecraft/network/Connection#isConnected inline crates/horizon-client/src/net.rs::Session::send no connected flag: sends stop once the writer is gone +net/minecraft/network/Connection#setupCompression port crates/horizon-protocol/src/connection.rs::Connection::set_compression negative threshold turns compression off +net/minecraft/network/Connection#handleDisconnection inline crates/horizon-client/src/net.rs::Session::start the thread ends and reports Disconnected with the reason +FriendlyByteBuf.readWithCodec skip:java DataFixer codec plumbing; Horizon has its typed Codec trait +FriendlyByteBuf.writeWithCodec skip:java DataFixer codec plumbing; Horizon has its typed Codec trait +FriendlyByteBuf.readLenientJsonWithCodec skip:java codec plumbing for lenient JSON +FriendlyByteBuf.writeJsonWithCodec skip:java codec plumbing for JSON +FriendlyByteBuf.readWithCount skip:java count loop; the List codec does it +FriendlyByteBuf.writeEnumSet port crates/horizon-protocol/src/packets/play.rs::PlayerInfoUpdate::write action flags as one byte (8 actions fit) +FriendlyByteBuf.readEnumSet port crates/horizon-protocol/src/packets/play.rs::PlayerInfoUpdate::read action flags as one byte (8 actions fit) +FriendlyByteBuf.writeOptional port crates/horizon-protocol/src/buf.rs::Opt::write boolean flag, then the value +FriendlyByteBuf.writeEither port crates/horizon-protocol/src/packets/play.rs::TrackedWaypoint::write gap: minor: only the waypoint identifier; no generic Either codec +FriendlyByteBuf.readEither port crates/horizon-protocol/src/packets/play.rs::TrackedWaypoint::read gap: minor: only the waypoint identifier; no generic Either codec +FriendlyByteBuf.readNullable port crates/horizon-protocol/src/buf.rs::Opt::read boolean flag, then the value +FriendlyByteBuf.writeNullable port crates/horizon-protocol/src/buf.rs::Opt::write boolean flag, then the value +FriendlyByteBuf.readByteArray port crates/horizon-protocol/src/buf.rs::Reader::byte_array VarInt length, then bytes +FriendlyByteBuf.writeByteArray port crates/horizon-protocol/src/buf.rs::Writer::byte_array VarInt length, then bytes +FriendlyByteBuf.writeVarIntArray port crates/horizon-protocol/src/buf.rs::List::write VarInt count, then VarInts +FriendlyByteBuf.readVarIntArray port crates/horizon-protocol/src/buf.rs::List::read count checked against the bytes left +FriendlyByteBuf.writeLongArray port crates/horizon-protocol/src/buf.rs::Writer::long_array VarInt count, then longs +FriendlyByteBuf.writeFixedSizeLongArray inline crates/horizon-protocol/src/buf.rs::Writer::long_array the element loop of long_array; no separate fixed-size write +FriendlyByteBuf.readLongArray port crates/horizon-protocol/src/buf.rs::Reader::long_array VarInt count, then longs +FriendlyByteBuf.readFixedSizeLongArray port crates/horizon-protocol/src/buf.rs::Reader::fixed_longs longs without a count +FriendlyByteBuf.writeBlockPos port crates/horizon-protocol/src/buf.rs::Pos::write packed x, z, y in one long +FriendlyByteBuf.writeChunkPos port crates/horizon-protocol/src/buf.rs::ChunkPos::write packed x and z in one long +FriendlyByteBuf.readGlobalPos port crates/horizon-protocol/src/buf.rs::Pos::read gap: minor: reads only the block position; the dimension is read by the GlobalPos packet! struct in play.rs +FriendlyByteBuf.readVector3f missing player: item, block and text displays get no server-set scale or translation; Horizon reads no display transforms (no Vector3f codec) +FriendlyByteBuf.writeVector3f missing player: item, block and text displays get no server-set scale or translation; Horizon reads no display transforms (no Vector3f codec) +FriendlyByteBuf.readQuaternion missing player: display entities get no server-set rotation; Horizon has no Quaternion codec +FriendlyByteBuf.writeQuaternion missing player: display entities get no server-set rotation; Horizon has no Quaternion codec +FriendlyByteBuf.writeUUID port crates/horizon-protocol/src/buf.rs::Writer::uuid two longs +FriendlyByteBuf.readUUID port crates/horizon-protocol/src/buf.rs::Reader::uuid two longs +FriendlyByteBuf.writeNbt port crates/horizon-protocol/src/buf.rs::Writer::nbt null is written as End +FriendlyByteBuf.readNbt port crates/horizon-protocol/src/buf.rs::Reader::nbt gap: minor: accepts any root tag; no compound-only check +FriendlyByteBuf.writeUtf port crates/horizon-protocol/src/buf.rs::Writer::string gap: minor: no max-length check on write +FriendlyByteBuf.readFixedBitSet port crates/horizon-protocol/src/packets/play.rs::Fixed3::read gap: minor: only the 3-byte set LastSeenUpdate uses; the action set reads a flag byte +FriendlyByteBuf.writeFixedBitSet port crates/horizon-protocol/src/packets/play.rs::Fixed3::write gap: minor: only the 3-byte set LastSeenUpdate uses +FriendlyByteBuf.writeContainerId port crates/horizon-protocol/src/buf.rs::Writer::varint container id as a VarInt +FriendlyByteBuf.ensureWritable skip:java Netty buffer capacity +FriendlyByteBuf.getBytes skip:java Netty buffer accessor +FriendlyByteBuf.setBytes skip:java Netty buffer accessor +FriendlyByteBuf.readBytes skip:java Netty buffer accessor +FriendlyByteBuf.writeBytes skip:java Netty buffer accessor +FriendlyByteBuf.bytesBefore skip:java Netty buffer search +FriendlyByteBuf.forEachByte skip:java Netty buffer search +FriendlyByteBuf.forEachByteDesc skip:java Netty buffer search +FriendlyByteBuf.toString skip:java Netty buffer text +FriendlyByteBuf.retain skip:java Netty reference counting +FriendlyByteBuf.touch skip:java Netty leak detection +HashedPatchMap.create port crates/horizon-protocol/src/item.rs::Hashed::of gap: player: component hashes are never sent (added stays empty), so a vanilla server resyncs a slot that holds components +HashedPatchMap.matches port crates/horizon-client/src/lan/containers.rs::Hub::hashed_matches gap: minor: compares item id and count only, not the component patch +HashedStack.create port crates/horizon-protocol/src/item.rs::Hashed::of gap: player: component hashes are never sent, so a vanilla server resyncs a slot that holds components +HashedStack.ActualItem.matches port crates/horizon-client/src/lan/containers.rs::Hub::hashed_matches gap: minor: compares item id and count only, not the component patch +HiddenByteBuf.pack skip:java Netty in-memory channel wrapper +HiddenByteBuf.unpack skip:java Netty in-memory channel wrapper +HiddenByteBuf.retain skip:java Netty reference counting +HiddenByteBuf.touch skip:java Netty leak detection +MonitoredLocalFrameDecoder.channelRead port crates/horizon-protocol/src/connection.rs::ConnReader::recv bytes_in counter for the bandwidth chart +PacketBundlePacker.decode inline crates/horizon-client/src/net.rs::receive_play gap: minor: BundleDelimiter is dropped; bundled packets are dispatched one by one +PacketBundleUnpacker.encode port crates/horizon-client/src/lan/entity.rs::ServerEntity::send_changes gap: minor: delimiters only around the projectile motion and power pair +PacketDecoder.decode port crates/horizon-protocol/src/packets/mod.rs::decode rejects leftover bytes (larger than expected) +PacketEncoder.encode port crates/horizon-protocol/src/packets/mod.rs::encode gap: minor: no error log and no skippable-packet path +PacketListener.fillCrashReport skip:debug crash report category +PacketProcessor.scheduleIfPossible port crates/horizon-client/src/lan/conn.rs::read_loop gap: minor: no shutdown rejection here; the hand-off is Hub::poll +PacketProcessor.processQueuedPackets port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox per guest; at most 400 packets per tick, the rest wait +PacketProcessor.ListenerAndPacket.handle inline crates/horizon-client/src/lan/hub.rs::Hub::process_inbox gap: minor: no per-packet error catch; a failing handler does not become a disconnect diff --git a/tools/port_map/rest-011.tsv b/tools/port_map/rest-011.tsv new file mode 100644 index 0000000000..098a4a2c2d --- /dev/null +++ b/tools/port_map/rest-011.tsv @@ -0,0 +1,150 @@ +ProtocolSwapHandler.handleInboundTerminalPacket port crates/horizon-client/src/net.rs::configure the configuration phase ends on FinishConfiguration and play starts in run; no Netty handler swap +ProtocolSwapHandler.handleOutboundTerminalPacket port crates/horizon-client/src/net.rs::configure the client's FinishConfiguration reply is sent inside configure +RateKickingConnection.tickSecond missing minor: server.properties rate-limit (default 10) is read but no connection is kicked for a packet flood +UnconfiguredPipelineHandler.Inbound.channelRead skip:other Netty guard for packets before a protocol is set; Horizon's connection loops only read their own phase +UnconfiguredPipelineHandler.Inbound.write skip:other Netty guard for configuration tasks on the inbound pipeline; no pipeline in Horizon +UnconfiguredPipelineHandler.Outbound.write skip:other Netty guard for packets sent before a protocol is set; no pipeline in Horizon +VarInt.getByteSize port crates/horizon-protocol/src/buf.rs::varint_size +VarLong.getByteSize skip:java byte-size helper with no caller here; Horizon writes varlongs and measures the buffer +Varint21FrameDecoder.copyVarint inline crates/horizon-protocol/src/frame.rs::split the 3-byte length read and the 'length wider than 21 bits' error +Varint21FrameDecoder.decode port crates/horizon-protocol/src/frame.rs::split gap: minor: a zero frame length is not rejected (vanilla throws 'Frame length cannot be zero') +ChatType.bind port crates/horizon-client/src/lan/hub/chat.rs::Hub::handle_chat the bound takes the chat type and the sender's display name +ChatTypeDecoration.incomingDirectMessage skip:data chat_type registry bootstrap; the msg decorations come from the chat_type data +ChatTypeDecoration.outgoingDirectMessage skip:data chat_type registry bootstrap; the msg decorations come from the chat_type data +ChatTypeDecoration.decorate port crates/horizon-client/src/game/remote.rs::Game::format_chat the chat type's translation with its style, on the client when a PlayerChat or DisguisedChat arrives +ChatTypeDecoration.resolveParameters port crates/horizon-client/src/game/remote.rs::Game::format_chat the parameter list (sender, target, content) built in format_chat +ClickEvent.Action.filterForSerialization port crates/horizon-client-core/src/component.rs::object_style gap: minor: a disallowed action (open_file) is dropped; vanilla refuses the whole component +CommonComponents.optionStatus port crates/horizon-client/src/options_ui.rs::on_off options.on / options.off text +CommonComponents.joinForNarration skip:other screen-reader narration text; Horizon has no narrator output (the narrator option is only stored) +Component.tryCollapseToString skip:java JSON shape only: Text always writes the object form, which reads the same +Component.copy skip:java clone; Text is Clone +Component.visit port crates/horizon-client-core/src/text.rs::Text::chars gap: minor: no early exit on the first consumer result; the styled characters are the same +Component.contains skip:other no caller in the 26.3 sources; the only match is the definition +Component.translatableEscape port crates/horizon-client-core/src/text.rs::Text::tr non-text arguments become literal strings +Component.translatableWithFallback inline crates/horizon-client-core/src/component.rs::to_text_with the fallback key is read from the JSON and used when the key is missing +Component.score skip:java builder of a score component; Horizon reads score components from JSON in resolve_score_component +Component.object missing minor: an object component (sprite or player head) draws nothing and its fallback text is not shown +Component.translationArg skip:java Date-to-text helper; Horizon has no Date argument type +ComponentSerialization.createFromList port crates/horizon-client-core/src/component.rs::to_text_with a list: the first element, then the rest as siblings in its style +ComponentSerialization.createLegacyComponentMatcher skip:java codec dispatch builder; to_text_with reads both forms directly +ComponentSerialization.createCodec port crates/horizon-client-core/src/component.rs::to_text_with JSON to text for strings, lists and objects +ComponentSerialization.FuzzyCodec.decode port crates/horizon-client-core/src/component.rs::to_text_with the first matching content key wins, as the object branch does +ComponentSerialization.FuzzyCodec.encode skip:java encode side; Text::component writes the JSON +ComponentSerialization.FuzzyCodec.keys skip:java codec key listing +ComponentSerialization.StrictEither.decode port crates/horizon-client-core/src/component.rs::to_text_with gap: minor: an explicit type field is not read; the content is matched by its key +ComponentSerialization.StrictEither.keys skip:java codec key listing +ComponentUtils.resolve port crates/horizon-client/src/game/commands/text_commands.rs::Game::component_arg gap: minor: hover text selectors stay unresolved (resolveStyle is missing) +ComponentUtils.resolveStyle missing minor: a selector inside a show_text hover is not resolved for the source +ComponentUtils.formatAndSortList port crates/horizon-client/src/scoreboard_cmd.rs::format_names gap: minor: the name formatter is fixed (green names); the list is sorted with gray commas +ComponentUtils.fromMessage port crates/horizon-client-core/src/text.rs::Text::from a plain message becomes a literal +ComponentUtils.isTranslationResolvable port crates/horizon-client-core/src/lang.rs::has gap: minor: the fallback case is handled in component.rs to_text_with, not here +FilterMask.apply missing minor: the server's text-filter masks are ignored, so filtered words show in full (only a text filter service sends masks) +FilterMask.applyWithFormatting missing minor: masks are ignored, so filtered words are not replaced with # in gray (only a text filter service sends masks) +FilterMask.equals skip:java value equality +FilterMask.hashCode skip:java hashing +FormattedText.of skip:java adapter from a String to FormattedText +FormattedText.getString port crates/horizon-client-core/src/text.rs::Text::plain +HoverEvent.Action.filterForSerialization port crates/horizon-client-core/src/component.rs::object_style gap: minor: show_entity is accepted by vanilla and dropped here +HoverEvent.EntityTooltipInfo.getTooltipLines missing player: hovering a player or entity name in chat shows no tooltip with name, type and UUID; show_entity hovers are dropped +HoverEvent.EntityTooltipInfo.equals skip:java value equality +HoverEvent.EntityTooltipInfo.hashCode skip:java hashing +LastSeenMessages.updateSignature inline crates/horizon-protocol/src/secure_chat.rs::signed_payload the last-seen list written into the signed payload +LastSeenMessages.Packed.unpack missing minor: the client does not unpack the packed last-seen list of other players' messages +LastSeenMessages.Update.verifyChecksum inline crates/horizon-protocol/src/secure_chat.rs::LastSeenValidator::apply_update the checksum check; 0 means not checked +LastSeenMessagesTracker.addPending missing minor: the client-side last-seen tracker is absent; Horizon's client sends an empty last-seen update +LastSeenMessagesTracker.addEntry missing minor: part of the client-side last-seen tracker, which is absent +LastSeenMessagesTracker.ignorePending missing minor: part of the client-side last-seen tracker, which is absent +LastSeenMessagesTracker.generateAndApplyUpdate missing minor: the client never builds an acknowledgement update for the server +LastSeenTrackedEntry.acknowledge missing minor: part of the client-side last-seen tracker, which is absent +LocalChatSession.createMessageEncoder missing player: Horizon's client never signs chat (no chat key); on a server that enforces secure chat, its messages are refused +MessageSignature.equals skip:java value equality +MessageSignature.Packed.unpack missing minor: the client does not unpack packed signatures from other players' messages +MessageSignatureCache.push port crates/horizon-protocol/src/secure_chat.rs::SignatureCache::push +MutableComponent.withColor port crates/horizon-client-core/src/text.rs::Text::with_color +MutableComponent.getVisualOrderText skip:ui bidi reordering for drawing; Horizon lays text out in logical order +MutableComponent.equals skip:java value equality +MutableComponent.hashCode skip:java hashing +MutableComponent.toString skip:debug debug text +OutgoingChatMessage.create port crates/horizon-client/src/lan/hub/chat.rs::Hub::broadcast_command_chat a message without signing context goes out as a disguised chat; a signed one as player chat +OutgoingChatMessage.Player.sendToPlayer port crates/horizon-client/src/lan/hub/chat.rs::Hub::send_chat gap: minor: the filter is always pass-through; the message goes to players who show chat +PlayerChatMessage.withUnsignedContent missing minor: the hub never sets unsigned content (there is no chat decorator), so decorated text is not sent +PlayerChatMessage.removeUnsignedContent missing minor: unsigned content is never set, so there is nothing to remove +PlayerChatMessage.filter missing minor: the hub sends pass-through filter masks; there is no text filter +PlayerChatMessage.removeSignature port crates/horizon-client/src/lan/hub/chat.rs::ChatMessage::new the unsigned branch keeps the content and drops the signature and the last seen +PlayerChatMessage.verify port crates/horizon-protocol/src/secure_chat.rs::Chain::unpack the signature check inside the chain check +PlayerChatMessage.hasSignatureFrom missing minor: only decides whether a text filter runs for the message; Horizon runs no text filter +PlayerChatMessage.describeSigned skip:debug log text for a rejected message +RemoteChatSession.createMessageValidator port crates/horizon-protocol/src/secure_chat.rs::Chain::unpack gap: minor: the profile key's expiry takes no grace period (has_expired(now) only) +ResolutionContext.Builder.build skip:java builder +SignableCommand.getArgument port crates/horizon-client/src/lan/hub/chat.rs::Hub::signed_arguments the command's message arguments, looked up by name +SignedMessageBody.updateSignature inline crates/horizon-protocol/src/secure_chat.rs::signed_payload salt, time, content and last seen in the signed payload +SignedMessageBody.Packed.unpack missing minor: the client does not unpack packed bodies of other players' messages +SignedMessageChain.encoder missing player: client-side signing; Horizon's client never signs, so chat is refused on servers that enforce secure chat +SignedMessageChain.Decoder.unsigned port crates/horizon-protocol/src/secure_chat.rs::Chain::unpack no session: unsigned, or refused when the server enforces secure chat +SignedMessageLink.updateSignature inline crates/horizon-protocol/src/secure_chat.rs::signed_payload sender, session and index in the signed payload +SignedMessageLink.isDescendantOf inline crates/horizon-protocol/src/secure_chat.rs::Chain::unpack Horizon requires the exact next index, which is stricter than a descendant check +SignedMessageLink.advance inline crates/horizon-protocol/src/secure_chat.rs::Chain::unpack the next index is index + 1, kept in the chain +SignedMessageValidator.KeyBased.validateChain inline crates/horizon-protocol/src/secure_chat.rs::Chain::unpack the order check (timestamp and index) inside the chain check +SignedMessageValidator.KeyBased.validate port crates/horizon-protocol/src/secure_chat.rs::Chain::unpack expiry, signature and order, in that order +SignedMessageValidator.KeyBased.updateAndValidate port crates/horizon-protocol/src/secure_chat.rs::Chain::unpack a failed link breaks the chain (next index cleared) +Style.create skip:java builder from optionals; Style is a plain struct +Style.checkEmptyAfterChange skip:java canonical empty style instance +Style.withShadowColor missing minor: Style has no shadow colour, so a component's shadow_color is dropped +Style.withBold port crates/horizon-client-core/src/text.rs::Text::bold gap: minor: sets true only; a null or false setter is not kept +Style.withItalic port crates/horizon-client-core/src/text.rs::Text::italic gap: minor: sets true only; a null or false setter is not kept +Style.withUnderlined port crates/horizon-client-core/src/text.rs::Text::underlined gap: minor: sets true only; a null or false setter is not kept +Style.withStrikethrough skip:java field setter; the field is set in object_style and legacy +Style.withObfuscated skip:java field setter; the field is parsed but nothing draws it +Style.withClickEvent skip:java field setter; the click field is set in object_style +Style.withHoverEvent skip:java field setter; the hover field is set in object_style +Style.withInsertion skip:java field setter; the insertion field is set in object_style +Style.withFont skip:java field setter; the font field is set in object_style +Style.applyFormat port crates/horizon-client-core/src/text.rs::Text::with_style gap: minor: the format is a style over the text; fields already set are kept, not overwritten +Style.applyFormats port crates/horizon-client-core/src/text.rs::Text::with_style gap: minor: the formats are a style over the text; fields already set are kept, not overwritten +Style.toString skip:debug debug text +Style.equals skip:java value equality +Style.hashCode skip:java hashing +SubStringSource.substring skip:ui splits text into style runs for drawing; the HUD draws each span with its own style +SubStringSource.create skip:ui builds the per-character styles for drawing +TextColor.named skip:data named colour table (COLORS) +TextColor.formatValue port crates/horizon-client-core/src/text.rs::style_json a colour is written as its name or #RRGGBB +TextColor.equals skip:java value equality +TextColor.hashCode skip:java hashing +TextColor.fromLegacyFormat port crates/horizon-client-core/src/text.rs::named_color looked up by name: formatting names are the colour names +TextColor.parseColor port crates/horizon-client-core/src/component.rs::object_style gap: minor: no range check on #hex; vanilla rejects values above #FFFFFF +KeybindContents.getNestedComponent port crates/horizon-client-core/src/key_display.rs::display_name the key's display text +KeybindContents.visit port crates/horizon-client-core/src/key_display.rs::display_name visits the nested key text +KeybindContents.equals skip:java value equality +ObjectContents.resolve missing minor: object components (sprite or player head) are not resolved; the fallback text is not shown +ObjectContents.visit missing minor: object components draw nothing +PlainTextContents.LiteralContents.visit skip:java leaf of the visit walk; Text holds the literal in its spans +ScoreContents.findTargetName inline crates/horizon-client/src/game/commands/text_commands.rs::Game::resolve_score_component a selector matching one entity is that entity, none is the name, more than one is an error +ScoreContents.getScore port crates/horizon-client/src/game/commands/text_commands.rs::Game::resolve_score_component the objective's value in the holder's format; empty when there is none +SelectorContents.resolve port crates/horizon-client/src/game/commands/text_commands.rs::Game::component_arg gap: minor: the separator is always ', ' +SelectorContents.visit port crates/horizon-client-core/src/component.rs::to_text with a literal resolver the selector text is shown, as visit does +TranslatableContents.filterAllowedArguments skip:java codec filter; Text arguments are typed +TranslatableContents.isAllowedPrimitiveArgument skip:java Text arguments are typed +TranslatableContents.adjustArgs skip:java argument list builder +TranslatableContents.create skip:java codec factory +TranslatableContents.decompose port crates/horizon-client-core/src/text.rs::format_translation the template parsed into parts; the fallback is handled in to_text_with +TranslatableContents.getArgument inline crates/horizon-client-core/src/text.rs::format_translation a missing argument index shows the raw template +TranslatableContents.visit inline crates/horizon-client-core/src/text.rs::format_translation the parts in order; no reference-cycle guard is needed for eager text +TranslatableContents.resolve port crates/horizon-client/src/game/commands/text_commands.rs::Game::component_arg the arguments are resolved with the rest of the tree +TranslatableContents.equals skip:java value equality +TranslatableContents.hashCode skip:java hashing +TranslatableContents.toString skip:debug debug text +BlockDataSource.getData inline crates/horizon-client/src/game/commands/text_commands.rs::Game::resolve_nbt_component the block branch: the block's data for an nbt component +EntityDataSource.getData inline crates/horizon-client/src/game/commands/text_commands.rs::Game::resolve_nbt_component the entity branch: the entity's data for an nbt component +AtlasSprite.toShortName missing minor: sprite object components are not drawn, so their short names are not shown +AtlasSprite.defaultFallback missing minor: the '[sprite]' fallback text of a sprite object is not produced +PlayerSprite.defaultFallback missing minor: the '[name head]' fallback text of a player head is not produced +NumberFormatTypes.bootstrap skip:data registry bootstrap of number format types +ByteBufCodecs.compoundTagCodec port crates/horizon-protocol/src/buf.rs::Nbt::read gap: minor: any NBT tag is accepted where a compound is required +ByteBufCodecs.fromCodec skip:java generic codec adapter +ByteBufCodecs.fromCodecWithRegistries skip:java generic codec adapter +ByteBufCodecs.collection port crates/horizon-protocol/src/buf.rs::List::read a VarInt count then the elements; the count is bounded by the bytes left +ByteBufCodecs.fixedSizeCollection skip:java fixed-length list codec; Horizon reads fixed arrays with their own codecs (Vec3d, Pos, fixed_longs) +ByteBufCodecs.map skip:java generic map codec; each packet spells its map fields out +ByteBufCodecs.lengthPrefixed skip:java codec wrapper; the size limits are checked per field by count and byte_array(max) +ByteBufCodecs.idMapper skip:java generic enum-id codec; each packet reads its own VarInt ids +ByteBufCodecs.registry port crates/horizon-protocol/src/packets/play.rs::HolderId::read gap: minor: inline holder values are refused; vanilla accepts them diff --git a/tools/port_map/rest-012.tsv b/tools/port_map/rest-012.tsv new file mode 100644 index 0000000000..d4ec2e8c9c --- /dev/null +++ b/tools/port_map/rest-012.tsv @@ -0,0 +1,149 @@ +IdDispatchCodec.decode skip:java codec dispatch; packet_enum! matches the id in packets/mod.rs +IdDispatchCodec.encode skip:java codec dispatch; packet_enum! encode in packets/mod.rs +IdDispatchCodec.Builder.add skip:java codec registration builder +IdDispatchCodec.Builder.build skip:java codec registration builder +StreamCodec.composite skip:java codec combinator; packet! macro in buf.rs does the field mapping +PacketUtils.ensureRunningOnSameThread skip:other packets are handled on the game loop; no reschedule path needed +PacketUtils.makeReportedException skip:debug crash report wrapper +PacketUtils.fillCrashReport skip:debug crash report details +ProtocolCodecBuilder.add skip:java codec registration builder +ProtocolInfoBuilder.addPacket skip:java protocol registration; packet ids are static in ids.rs +ProtocolInfoBuilder.withBundlePacket missing minor: bundle delimiter is a no-op (net.rs BundleDelimiter arm); bundled packets are applied one by one +ProtocolInfoBuilder.buildPacketCodec skip:java codec construction +ProtocolInfoBuilder.buildUnbound skip:java protocol construction; enum variants in packet_enum! +ProtocolInfoBuilder.protocol skip:java protocol builder entry point +ProtocolInfoBuilder.contextProtocol skip:java protocol builder entry point +ProtocolInfoBuilder.CodecEntry.addToBuilder skip:java codec construction +ClientboundStoreCookiePacket.write port crates/horizon-protocol/src/buf.rs::write packet! StoreCookie {key Ident, payload Bytes} +ClientboundTransferPacket.write port crates/horizon-protocol/src/buf.rs::write packet! Transfer {host Str, port VarInt} +ServerboundResourcePackPacket.write port crates/horizon-protocol/src/buf.rs::write packet! ResourcePackResponse {id Uuid, action VarInt} +CustomPacketPayload.codec skip:java StreamCodec factory; packet! macro and Codec trait cover it +DiscardedPayload.codec missing minor: no 32767-byte cap on unknown custom payloads; Unknown keeps the raw bytes +ServerboundCookieResponsePacket.write port crates/horizon-protocol/src/buf.rs::write packet! CookieResponse {key Ident, payload Option} +ClientboundAddEntityPacket. port crates/horizon-client/src/lan/entity.rs::ServerEntity::pairing builds the spawn packet from the entity state +ClientboundAddEntityPacket.write port crates/horizon-protocol/src/buf.rs::write packet! AddEntity (play.rs) in the vanilla field order +ClientboundAddTransientBlockPacket.write missing player: a temporary block the server sends never appears; Horizon has no such packet (ids.rs only) +ClientboundAnimatePacket.write port crates/horizon-protocol/src/buf.rs::write packet! Animate {id VarInt, action UByte} +ClientboundBlockDestructionPacket.write port crates/horizon-protocol/src/buf.rs::write packet! BlockDestruction {id, pos, progress} +ClientboundBlockEntityDataPacket.create missing player: block entity changes after load (sign text, banner, chest name) are not sent to other players; the LAN host has no BlockEntityData sender +ClientboundBlockEventPacket.write port crates/horizon-protocol/src/buf.rs::write packet! BlockEvent {pos, b0, b1, block} +ClientboundBossEventPacket.createUpdateProgressPacket missing player: boss bar progress is never sent to other players; the LAN host has no BossEvent sender +ClientboundBossEventPacket.createUpdateNamePacket missing player: boss bar name changes are never sent to other players; the LAN host has no BossEvent sender +ClientboundBossEventPacket.createUpdateStylePacket missing player: boss bar colour and overlay changes are never sent to other players; the LAN host has no BossEvent sender +ClientboundBossEventPacket.createUpdatePropertiesPacket missing player: boss bar fog, music and darkening flags are never sent to other players; the LAN host has no BossEvent sender +ClientboundBossEventPacket.encodeProperties port crates/horizon-protocol/src/packets/play.rs::boss_flags the three flag bits (darken, music, fog) +ClientboundBossEventPacket.AddOperation.dispatch missing player: boss bars from a server (wither, dragon fight) never appear; remote.rs has no BossEvent arm +ClientboundChunksBiomesPacket.ChunkBiomeData.calculateChunkSize missing minor: biome-only chunk updates are never sent; no ChunksBiomes packet in ids.rs +ClientboundChunksBiomesPacket.ChunkBiomeData.extractChunkData missing minor: biome-only chunk updates are never sent; no ChunksBiomes packet in ids.rs +ClientboundCommandSuggestionsPacket.toSuggestions port crates/horizon-client/src/game/remote.rs::Game::handle_command_suggestions start, length and tooltip become the suggestion range +ClientboundCommandsPacket.validateEntries missing minor: an impossible command tree is not rejected; from_packet skips bad child indexes +ClientboundCommandsPacket.enumerateNodes port crates/horizon-client/src/lan/commands.rs::CommandTree::packet_with breadth-first node ids on the LAN host +ClientboundCommandsPacket.createEntries port crates/horizon-client/src/lan/commands.rs::CommandTree::packet_with builds the node list for the LAN host's Commands packet +ClientboundCommandsPacket.createEntry inline crates/horizon-client/src/lan/commands.rs::CommandTree::packet_with flags, redirect and children are set in the final node map +ClientboundCommandsPacket.getRoot port crates/horizon-command/src/network.rs::from_packet the packet's root becomes the client's dispatcher root +ClientboundCommandsPacket.ArgumentNodeStub.serializeCap port crates/horizon-client/src/lan/commands.rs::properties argument parser property bytes +ClientboundCommandsPacket.Entry.canBuild inline crates/horizon-command/src/network.rs::from_packet gap: minor: no build-order check; children and redirects resolve recursively +ClientboundCommandsPacket.Entry.canResolve inline crates/horizon-command/src/network.rs::from_packet gap: minor: no build-order check; children and redirects resolve recursively +ClientboundCommandsPacket.NodeResolver.resolve port crates/horizon-command/src/network.rs::from_packet builds the client's tree from the packet nodes +ClientboundContainerSetDataPacket.write port crates/horizon-protocol/src/buf.rs::write packet! ContainerSetData {container VarInt, id Short, value Short} +ClientboundContainerSetSlotPacket.write port crates/horizon-protocol/src/buf.rs::write packet! ContainerSetSlot {container, state, slot, item Slot} +ClientboundDamageEventPacket.getSource inline crates/horizon-client/src/game/remote.rs::Game::handle_packet gap: minor: DamageSource is not rebuilt; the hurt sound comes from the host's sound packet +ClientboundDebugSamplePacket.write skip:debug F3 debug chart sample; Horizon has no debug subscription +ClientboundEntityEventPacket.write port crates/horizon-protocol/src/buf.rs::write packet! EntityEvent {id Int, event Byte} +ClientboundEntityPositionSyncPacket.of port crates/horizon-client/src/lan/entity.rs::ServerEntity::send_changes builds the position sync for the LAN host +ClientboundGameEventPacket.write port crates/horizon-protocol/src/buf.rs::write packet! GameEvent {event UByte, param Float} +ClientboundHurtAnimationPacket.write port crates/horizon-protocol/src/buf.rs::write packet! HurtAnimation {id VarInt, yaw Float} +ClientboundLevelChunkPacketData.calculateChunkSize skip:java buffer sizing; Vec grows as sections are written +ClientboundLevelChunkPacketData.extractChunkData port crates/horizon-protocol/src/chunk.rs::ChunkData::from_sections gap: minor: no writer-index length check +ClientboundLevelChunkPacketData.forEachBlockEntityTag port crates/horizon-client/src/game/remote_chunks.rs::Game::remote_chunk block entities of a chunk are applied when the chunk arrives +ClientboundLevelEventPacket.write port crates/horizon-protocol/src/buf.rs::write packet! LevelEvent {kind Int, pos, data Int, global Bool} +ClientboundLightUpdatePacketData.prepareSectionData port crates/horizon-client/src/lan/chunks.rs::light_data builds the light layers the section mask marks +ClientboundMapItemDataPacket.applyToMap port crates/horizon-client/src/game/maps.rs::Game::remote_map_item_data decorations and colour patch applied to the map +ClientboundMoveEntityPacket.Pos.read port crates/horizon-protocol/src/packets/play.rs::read_inner MoveEntityPos read +ClientboundMoveEntityPacket.Pos.write port crates/horizon-protocol/src/packets/play.rs::write_inner MoveEntityPos write +ClientboundMoveEntityPacket.PosRot.read port crates/horizon-protocol/src/packets/play.rs::read_inner MoveEntityPosRot read +ClientboundMoveEntityPacket.PosRot.write port crates/horizon-protocol/src/packets/play.rs::write_inner MoveEntityPosRot write +ClientboundMoveEntityPacket.Rot.read port crates/horizon-protocol/src/packets/play.rs::read_inner MoveEntityRot read +ClientboundMoveEntityPacket.Rot.write port crates/horizon-protocol/src/packets/play.rs::write_inner MoveEntityRot write +ClientboundPlayerAbilitiesPacket.write port crates/horizon-protocol/src/buf.rs::write packet! PlayerAbilities {flags Byte, flying speed, walking speed}; flag bits packed by the sender +ClientboundPlayerInfoUpdatePacket.createPlayerInitializing port crates/horizon-client/src/lan/hub.rs::Hub::place_new_player sends every action (INFO_ACTIONS) to a newcomer +ClientboundPlayerInfoUpdatePacket.entryCodec port crates/horizon-protocol/src/packets/play.rs::PlayerInfoUpdate::write per-entry write in action order +ClientboundPlayerInfoUpdatePacket.toString skip:debug debug string +ClientboundPlayerInfoUpdatePacket.EntryBuilder.build inline crates/horizon-protocol/src/packets/play.rs::PlayerInfoUpdate::read entry fields read in action order +ClientboundPlayerLookAtPacket. missing player: a server's facing command (e.g. teleport facing an entity) does not turn the client's view +ClientboundPlayerLookAtPacket.write missing player: same as the constructor; the view is never turned by the server +ClientboundPlayerLookAtPacket.getPosition missing player: same as the constructor; the view is never turned by the server +ClientboundRotateHeadPacket.write port crates/horizon-protocol/src/buf.rs::write packet! RotateHead {id VarInt, head_yaw Byte} +ClientboundSectionBlocksUpdatePacket. port crates/horizon-client/src/lan/hub.rs::Hub::broadcast_block_changes builds the section's change list from the changed blocks +ClientboundSectionBlocksUpdatePacket.write port crates/horizon-protocol/src/buf.rs::write packet! SectionBlocksUpdate (play.rs) with SectionPos and VarLong changes +ClientboundSectionBlocksUpdatePacket.runUpdates inline crates/horizon-client/src/game/remote.rs::Game::handle_packet applies each change at its section position +ClientboundSetChunkCacheCenterPacket.write port crates/horizon-protocol/src/buf.rs::write packet! SetChunkCacheCenter {x VarInt, z VarInt} +ClientboundSetEntityDataPacket.pack port crates/horizon-protocol/src/entity_data.rs::EntityData::write entries then 0xFF terminator +ClientboundSetEntityDataPacket.unpack port crates/horizon-protocol/src/entity_data.rs::EntityData::read entries until 0xFF +ClientboundSetExperiencePacket.write port crates/horizon-protocol/src/buf.rs::write packet! SetExperience {progress Float, level, total VarInt} +ClientboundSetHealthPacket.write port crates/horizon-protocol/src/buf.rs::write packet! SetHealth {health, food VarInt, saturation} +ClientboundSetPlayerTeamPacket.createAddOrModifyPacket missing player: team changes made on the host (name, colours, friendly fire, members) do not reach guests; the LAN host never sends team packets +ClientboundSetPlayerTeamPacket.createPlayerPacket missing player: adding or removing a player from a team does not reach guests; the LAN host never sends team packets +ClientboundSetPlayerTeamPacket.shouldHavePlayerList port crates/horizon-protocol/src/packets/play.rs::SetPlayerTeam::read the player list is read only for methods 0, 3 and 4 +ClientboundSetTitlesAnimationPacket.write port crates/horizon-protocol/src/buf.rs::write packet! TitlesAnimation {fade_in, stay, fade_out Int} +ClientboundSoundEntityPacket.write port crates/horizon-protocol/src/buf.rs::write packet! SoundEntity with the sound holder +ClientboundSoundPacket.write port crates/horizon-protocol/src/buf.rs::write packet! Sound with the sound holder and fixed-point position +ClientboundTagQueryPacket.write missing minor: answers to F3+I NBT queries are not handled; Horizon has no tag_query packet (ids.rs only) +ClientboundTakeItemEntityPacket.write port crates/horizon-protocol/src/buf.rs::write packet! TakeItemEntity {item, player, amount VarInt} +DebugEntityNameGenerator.getEntityName skip:debug debug name for entities +ServerboundBlockEntityTagQueryPacket.write missing minor: the F3+I block NBT query is not sent; Horizon has no such packet (ids.rs only) +ServerboundEntityTagQueryPacket.write missing minor: the F3+I entity NBT query is not sent; Horizon has no such packet (ids.rs only) +ServerboundJigsawGeneratePacket.write missing minor: no jigsaw generate packet; the client has no jigsaw screen (only block_entity_nbt reads jigsaw data) +ServerboundMovePlayerPacket.packFlags inline crates/horizon-client/src/player_sync.rs::PositionSender::update on-ground and horizontal-collision bits set when the move is sent +ServerboundPlayerActionPacket.write port crates/horizon-protocol/src/buf.rs::write packet! PlayerAction {action VarInt, pos, direction UByte, sequence VarInt} +ServerboundPlayerCommandPacket.write port crates/horizon-protocol/src/buf.rs::write packet! PlayerCommand {id, action, data VarInt} +ServerboundSeenAdvancementsPacket.write missing minor: the advancement tab the player opened is not sent; Horizon has no seen_advancements packet (ids.rs only) +ServerboundSetCommandMinecartPacket.write missing minor: minecart command block edits are not sent; Horizon has no such packet (ids.rs only) +ServerboundSetCommandMinecartPacket.getCommandBlock missing minor: minecart command block edits are not sent; Horizon has no such packet (ids.rs only) +ServerboundSetJigsawBlockPacket.write missing minor: jigsaw block edits are not sent; no packet and no jigsaw screen in the client +ServerboundSetStructureBlockPacket. missing minor: structure block settings are not sent; no packet and no structure block screen in the client +ServerboundSetStructureBlockPacket.write missing minor: structure block settings are not sent; no packet and no structure block screen in the client +VecDelta.Stepped.hasDeltaX skip:other no callers in vanilla +VecDelta.Stepped.hasDeltaZ skip:other no callers in vanilla +VecDelta.Stepped.tryEncode missing minor: the LAN host sends only linear deltas (send_changes); no stepped path is encoded +VecDeltaCodec.isDeltaTooBig inline crates/horizon-client/src/lan/entity.rs::ServerEntity::send_changes the i16 range check on each delta +VecDeltaCodec.tryEncode inline crates/horizon-client/src/lan/entity.rs::ServerEntity::send_changes gap: minor: linear positions only; stepped paths are not encoded +ClientIntent.byId missing minor: an unknown handshake intent is not rejected; the intent stays a raw number in handshake.rs +ClientIntent.id skip:other constants STATUS, LOGIN and TRANSFER in handshake.rs +ClientIntentionPacket.write port crates/horizon-protocol/src/buf.rs::write packet! Intention {protocol, host, port, intent} +ClientboundCustomQueryPacket.readUnknownPayload missing minor: no 1 MiB cap on unknown login query payloads; CustomQuery keeps the raw data +ClientboundCustomQueryPacket.write port crates/horizon-protocol/src/buf.rs::write packet! CustomQuery {transaction, channel Ident, data Rest} +ClientboundHelloPacket.write port crates/horizon-protocol/src/buf.rs::write login hello packet! fields in vanilla order +ServerboundCustomQueryAnswerPacket.read port crates/horizon-protocol/src/buf.rs::read gap: minor: payload stays raw (Opt); no per-channel reader +ServerboundCustomQueryAnswerPacket.readUnknownPayload missing minor: no 1 MiB cap on unknown login query answers; the payload stays raw +ServerboundCustomQueryAnswerPacket.write port crates/horizon-protocol/src/buf.rs::write packet! CustomQueryAnswer {transaction, payload Opt} +ServerboundHelloPacket.write port crates/horizon-protocol/src/buf.rs::write login start packet! name and profile id +ServerboundKeyPacket.write port crates/horizon-protocol/src/buf.rs::write login key packet! encrypted secret and challenge +net/minecraft/network/syncher/EntityDataAccessor#equals skip:java typed accessor equality; Rust derives it +SynchedEntityData.defineId skip:other entity data ids are fixed in code per entity type, not defined at class load +SynchedEntityData.set inline crates/horizon-client/src/lan/entity.rs::ServerEntity::send_changes gap: minor: no per-value dirty flag; the metadata list is compared each tick +SynchedEntityData.packDirty port crates/horizon-client/src/lan/entity.rs::ServerEntity::send_changes sends the changed metadata values +SynchedEntityData.getNonDefaultValues port crates/horizon-client/src/lan/entity.rs::ServerEntity::pairing non-empty metadata goes out with the spawn +SynchedEntityData.assignValues inline crates/horizon-client/src/game/remote.rs::Game::handle_packet applies the values to the entity by index +SynchedEntityData.assignValue inline crates/horizon-client/src/game/remote.rs::Game::handle_packet gap: minor: no serializer type check +SynchedEntityData.Builder.define skip:other entity data ids and defaults are fixed per entity type in Horizon +SynchedEntityData.Builder.build skip:other entity data ids and defaults are fixed per entity type in Horizon +SynchedEntityData.DataValue.create skip:java copies a value; Rust owns the value +SynchedEntityData.DataValue.write port crates/horizon-protocol/src/entity_data.rs::EntityData::write id, serializer id and value +SynchedEntityData.DataValue.read port crates/horizon-protocol/src/entity_data.rs::EntityData::read id, serializer id and value +RealmsConnect.connect skip:service Realms +RealmsConnect.abort skip:service Realms +RealmsConnect.tick skip:service Realms +RepeatedNarrator.narrate skip:other accessibility narration; Horizon has no narrator +ServerPlaceRecipe.tryPlaceRecipe port crates/horizon-client/src/menu/place.rs::Menu::place_recipe places when craftable, else clears the grid and shows the ghost +ServerPlaceRecipe.clearGrid port crates/horizon-client/src/menu/place.rs::Menu::clear_grid returns the grid to the inventory +ServerPlaceRecipe.clampToMaxStackSize inline crates/horizon-client/src/menu/place.rs::Menu::place gap: minor: clamps by the slot's stack, not by each candidate item +ServerPlaceRecipe.calculateAmountToCraft port crates/horizon-client/src/menu/place.rs::Menu::place the amount placed from the grid's smallest stack +ServerPlaceRecipe.moveItemToGrid port crates/horizon-client/src/menu/place.rs::Menu::move_item_to_grid moves one ingredient stack into a grid slot +ServerPlaceRecipe.testClearGrid port crates/horizon-client/src/menu/place.rs::Menu::test_clear_grid checks the grid can be emptied into the inventory +ServerPlaceRecipe.getAmountOfFreeSlotsInInventory inline crates/horizon-client/src/menu/place.rs::Menu::test_clear_grid free_in_inventory is counted there +BlockItemId.create skip:data registry keys; Horizon names items by string id +DelegatingOps.convertTo skip:java codec ops wrapper +DelegatingOps.mergeToList skip:java codec ops wrapper +DelegatingOps.mergeToMap skip:java codec ops wrapper +DelegatingOps.createMap skip:java codec ops wrapper diff --git a/tools/port_map/rest-013.tsv b/tools/port_map/rest-013.tsv new file mode 100644 index 0000000000..94b848ad6b --- /dev/null +++ b/tools/port_map/rest-013.tsv @@ -0,0 +1,142 @@ +DelegatingOps.DelegateListBuilder.build skip:java forwards to the original ListBuilder; codec plumbing +DelegatingOps.DelegateListBuilder.add skip:java forwards to the original ListBuilder; codec plumbing +DelegatingOps.DelegateListBuilder.addAll skip:java encodes values into the original ListBuilder; codec plumbing +DelegatingOps.DelegateRecordBuilder.add skip:java forwards to the original RecordBuilder; codec plumbing +DelegatingOps.DelegateRecordBuilder.build skip:java forwards to the original RecordBuilder; codec plumbing +FileToIdConverter.fileToId port crates/horizon-client/src/datapack.rs::resource_id resource path to id with the folder prefix and extension stripped +FileToIdConverter.matches inline crates/horizon-client/src/datapack.rs::resource_id prefix and extension test is inside resource_id +Identifier.tryBuild port crates/horizon-world/src/identifier.rs::Identifier::from_parts validated constructor; Err instead of null +Identifier.tryBySeparator port crates/horizon-world/src/identifier.rs::Identifier::parse splits on ':'; minecraft namespace when it is empty or missing +Identifier.withPath port crates/horizon-world/src/identifier.rs::Identifier::from_parts no helper; callers rebuild the id with from_parts(namespace, path) +Identifier.equals skip:java derived PartialEq on namespace and path +Identifier.compareTo port crates/horizon-world/src/identifier.rs::Identifier::cmp path first, then namespace +Identifier.resolveAgainst missing unsure: no identifier-to-file-path guard found; callers join paths directly +Identifier.toLanguageKey skip:other string helper; translation keys are built where a Text is made +Identifier.toShortLanguageKey skip:other string helper; translation keys are built where a Text is made +Identifier.toShortString skip:other string helper; the minecraft: prefix is stripped at each display site +Identifier.readGreedy port crates/horizon-command/src/args.rs::read_identifier reads id characters while they are allowed +Identifier.isAllowedInIdentifier port crates/horizon-command/src/args.rs::is_id_char same character set +Identifier.isValidPath inline crates/horizon-world/src/identifier.rs::Identifier::from_parts path check inside from_parts; '/' is allowed +Identifier.isValidNamespace port crates/horizon-world/src/identifier.rs::Identifier::from_parts gap: minor: vanilla rejects the namespace '..'; from_parts accepts it (horizon-client-core parse_identifier has that check) +Identifier.assertValidNamespace inline crates/horizon-world/src/identifier.rs::Identifier::from_parts returns Err(Namespace) instead of throwing +Identifier.validPathChar inline crates/horizon-world/src/identifier.rs::Identifier::from_parts path bytes: valid() or '/' +Identifier.validNamespaceChar port crates/horizon-world/src/identifier.rs::valid same character set for namespaces +Identifier.assertValidPath inline crates/horizon-world/src/identifier.rs::Identifier::from_parts returns Err(Path) instead of throwing +NetworkRegistryLoadTask.load port crates/horizon-protocol/src/registry.rs::Dynamic::add gap: minor: keeps the raw entries of a RegistryData packet; no codec decode, no known-pack fallback, no tag binding +RegistryDataLoader.load skip:data loads registries from resources; Horizon reads its data from tables and files +RegistryDataLoader.createContext skip:data registry lookup context for data loading +RegistryDataLoader.printFullDetailsToLog skip:debug logs registry load errors +RegistryDataLoader.createReportWithBriefInfo skip:debug crash report text for registry load errors +RegistryLoadTask.readOnlyRegistry skip:data registry loading step; Horizon builds its registries from tables +RegistryLoadTask.registerElements skip:data registry loading step; Horizon builds its registries from tables +RegistryLoadTask.registerTags skip:data registry loading step; Horizon builds its registries from tables +RegistryLoadTask.freezeRegistry skip:data registry loading step; Horizon builds its registries from tables +RegistryLoadTask.validateRegistry skip:data registry loading step; Horizon builds its registries from tables +RegistryLoadTask.PendingRegistration.loadFromResource skip:data parses one registry entry during loading; Horizon reads its data from tables +RegistryLoadTask.PendingRegistration.findAndLoadFromResource skip:data parses one registry entry during loading; Horizon reads its data from tables +RegistryLoadTask.PendingRegistration.loadFromNetwork skip:data parses one registry entry during loading; Horizon reads its data from tables +RegistryOps.create skip:data codec context for registry lookups during data decoding +RegistryOps.withParent skip:data codec context for registry lookups during data decoding +RegistryOps.equals skip:data codec context for registry lookups during data decoding +RegistryOps.retrieveGetter skip:data codec context for registry lookups during data decoding +RegistryOps.retrieveElement skip:data codec context for registry lookups during data decoding +RegistryOps.HolderLookupAdapter.lookup skip:data registry lookup adapter for data decoding +RegistryOps.HolderLookupAdapter.equals skip:data registry lookup adapter for data decoding +ResourceKey.create skip:java interned key; Horizon uses plain identifiers +ResourceKey.dependent skip:data derived registry key; the ids come from Horizon's tables +ResourceManagerRegistryLoadTask.load skip:data registry loading from the resource manager +Bootstrap.bootStrap skip:data startup bootstrap of the registries and data +Bootstrap.checkTranslations skip:debug IDE-only translation check +Bootstrap.checkGameruleTranslations skip:debug IDE-only game rule translation check +Bootstrap.getMissingTranslations skip:debug IDE-only missing translation report +Bootstrap.createBootstrapException skip:debug error text for a call before bootstrap +Bootstrap.validate skip:debug IDE-only validation +Bootstrap.wrapStreams skip:debug wraps stdout and stderr for logging +ChainedJsonException.setFilenameAndFlush skip:data JSON error path messages for data-pack parse errors +ChainedJsonException.getMessage skip:data JSON error path messages for data-pack parse errors +ChainedJsonException.forException skip:data JSON error path messages for data-pack parse errors +ChainedJsonException.Entry.toString skip:data JSON error path entry for data-pack parse errors +DebugLoggedPrintStream.logLine skip:debug debug-only stream logging +Eula.saveDefaults port crates/horizon-server-files/src/eula.rs::default_file default eula.txt content; the write happens in agreed() +net/minecraft/server/Main#createNewWorldData port crates/horizon-client/src/game/dedicated.rs::Game::write_world_gen gap: minor: writes the world gen settings for a new world; --bonusChest is accepted and ignored (preflight); level settings come from the save code +net/minecraft/server/Main#writePidFile port crates/horizon-client/src/game/dedicated.rs::preflight --pidFile writes the process id while the options are read +net/minecraft/server/Main#loadOrCreateConfig port crates/horizon-client/src/game/functions.rs::Game::reload_datapacks gap: minor: reads the saved datapack settings; no feature-flag check and no new-world default config +net/minecraft/server/Main#forceUpgrade missing minor: --forceUpgrade is accepted and ignored (dedicated.rs preflight); worlds are upgraded on open (save.rs upgrade_older_world) +MinecraftServer. inline crates/horizon-client/src/game/dedicated.rs::Game::run_dedicated gap: minor: setup is spread over run_dedicated and the Game fields +MinecraftServer.loadLevel inline crates/horizon-client/src/game/dedicated.rs::Game::run_dedicated gap: minor: the world is opened in run_dedicated; no JFR load profiling +MinecraftServer.setupDebugLevel skip:debug debug world preset is not offered +MinecraftServer.prepareLevels port crates/horizon-client/src/game/initial_spawn.rs::Game::find_world_spawn gap: minor: waits only for the spawn chunks (find_world_spawn returns None while one loads); no progress stage for other levels, no mob-spawn flag update here +MinecraftServer.saveEverything port crates/horizon-client/src/game.rs::Game::save_game_with gap: minor: no low-disk warning +MinecraftServer.finishMeasuringTaskExecutionTime skip:server tick timing for the profiler +MinecraftServer.constructOrExtractCrashReport skip:debug crash report wrapping +MinecraftServer.haveTime inline crates/horizon-client/src/game/levels.rs::Game::wait_for_next_server_tick gap: minor: no time budget for queued tasks +MinecraftServer.waitUntilNextTick port crates/horizon-client/src/game/levels.rs::Game::wait_for_next_server_tick gap: minor: queued tasks are not run before the wait +MinecraftServer.waitForTasks inline crates/horizon-client/src/game/levels.rs::Game::wait_for_next_server_tick idle sleep until the next tick +MinecraftServer.shouldRun missing minor: no per-task deferral; queued tasks run in one pass +MinecraftServer.pollTaskInternal inline crates/horizon-client/src/game/integrated.rs::Game::run_tasks gap: minor: only the task queue; chunk-source tasks are not polled here +MinecraftServer.loadStatusIcon inline crates/horizon-client/src/game/dedicated.rs::Game::run_dedicated gap: minor: only server-icon.png; no world icon fallback +MinecraftServer.processPacketsAndTick port crates/horizon-client/src/game/levels.rs::Game::tick_server reads the network, then ticks the levels +MinecraftServer.autoSave inline crates/horizon-client/src/game.rs::Game::tick gap: minor: the save runs every 6000 ticks; /save-off is checked in the dedicated loop, where the check does nothing, so it does not stop the save +MinecraftServer.logTickMethodTime skip:server tick timing log for the profiler +MinecraftServer.computeNextAutosaveInterval missing minor: the save runs every 6000 ticks (game.rs tick), not 300 s scaled by the tick rate +MinecraftServer.onTickRateChanged missing minor: the autosave interval does not follow /tick rate +MinecraftServer.buildPlayerStatus port crates/horizon-client/src/lan/mod.rs::Shared::status_json up to 12 sampled player names in the status JSON +MinecraftServer.updateEffectiveRespawnData inline crates/horizon-client/src/game/initial_spawn.rs::Game::spawn_finder gap: minor: no cached respawn data; the radius is clamped to the border when the spawn is found +MinecraftServer.forceGameTimeSynchronization port crates/horizon-client/src/lan/hub.rs::Hub::sync_world game time sent to guests with SetTime +MinecraftServer.fillSystemReport skip:debug system report for crash and debug output +MinecraftServer.setDifficulty port crates/horizon-client/src/game/commands.rs::Game::cmd_difficulty gap: player: LAN guests are not sent ChangeDifficulty when /difficulty changes; they get it on join, respawn or dimension change +MinecraftServer.updateMobSpawningFlags inline crates/horizon-client/src/game/natural_spawning.rs::Game::spawn_natural rules are read at each spawn; no cached spawn flag +MinecraftServer.sendDifficultyUpdate inline crates/horizon-client/src/lan/hub.rs::Hub::place_new_player gap: player: sent when a guest joins, respawns or changes dimension; see setDifficulty +MinecraftServer.enforceGameTypeForPlayers inline crates/horizon-client/src/game/lan.rs::Game::lan_apply_settings gap: player: guests already online keep their mode when force changes; the default applies at join (lan_guest_joined) +MinecraftServer.executeIfPossible skip:java rejects tasks during shutdown +MinecraftServer.reloadResources port crates/horizon-client/src/game/functions.rs::Game::cmd_reload gap: minor: data packs only, via reload_datapacks +MinecraftServer.configurePackRepository port crates/horizon-client/src/game/functions.rs::Game::reload_datapacks gap: minor: picks the enabled packs and adds new ones; no feature-flag check and no safe mode +MinecraftServer.configureRepositoryWithSelection inline crates/horizon-client/src/game/functions.rs::Game::reload_datapacks gap: minor: sets the enabled list only; no forced features +MinecraftServer.enableForcedFeaturePacks missing minor: Horizon's datapacks have no feature flags; a pack for a required feature is never forced on +MinecraftServer.getSelectedPacks port crates/horizon-client/src/game/functions.rs::Game::save_datapack_settings gap: minor: writes the enabled and disabled lists; no feature check +MinecraftServer.findRespawnDimension inline crates/horizon-client/src/game/server_travel.rs::Game::world_spawn_trip gap: minor: always the Overworld; /setworldspawn keeps no dimension +MinecraftServer.setRespawnData port crates/horizon-client/src/game/commands.rs::Game::cmd_setworldspawn gap: minor: the new spawn is not sent to guests here; they get it on join +MinecraftServer.getCommandStorage skip:java getter for the command storage +MinecraftServer.getStopwatches skip:java getter for the stopwatches +MinecraftServer.setWeatherParameters port crates/horizon-client/src/game/commands.rs::Game::cmd_weather sets the weather timers and state +MinecraftServer.getAverageTickTimeNanos port crates/horizon-client/src/clock.rs::FixedStep::average_nanos average of the recent tick times +MinecraftServer.saveDebugReport skip:debug debug report output +MinecraftServer.dumpMiscStats skip:debug debug report output +MinecraftServer.dumpGameRules skip:debug debug report output +MinecraftServer.dumpClasspath skip:debug debug report output +MinecraftServer.dumpThreads skip:debug debug report output +MinecraftServer.dumpNativeModules skip:debug debug report output +MinecraftServer.createProfiler skip:server profiler setup +MinecraftServer.startRecordingMetrics skip:server metrics recording for the profiler +MinecraftServer.createGameModeForPlayer missing minor: demo mode is not implemented; --demo is accepted and ignored (dedicated.rs preflight) +MinecraftServer.stopTimeProfiler skip:debug debug profiler stop +MinecraftServer.setAutoSave inline crates/horizon-client/src/game/dedicated.rs::Game::console_command gap: minor: the save-on/save-off flag is kept, but the tick's autosave does not read it +MinecraftServer.isAutoSave inline crates/horizon-client/src/game/dedicated.rs::Game::console_command reads the same flag for the save-on reply only +MinecraftServer.storeChunkIoError skip:debug writes a chunk IO crash report +MinecraftServer.reportChunkLoadFailure missing unsure: no chunk-load failure log or report found in save.rs or streaming.rs +MinecraftServer.reportChunkSaveFailure port crates/horizon-client/src/save.rs::Persistent::save_finished gap: minor: logs only; no debug report and no low-disk warning +PlayerAdvancements.reload missing minor: /reload rebuilds datapack functions and settings only (cmd_reload); the advancement tree is kept for the session +PlayerAdvancements.registerListeners skip:other no per-criterion listener map; trigger() tests every criterion when an event fires +PlayerAdvancements.checkForAutomaticTriggers missing minor: criteria-less advancements from data packs are not granted when a player loads +PlayerAdvancements.asData inline crates/horizon-client/src/game/advancements.rs::Game::save_advancements writes the progress that has any progress +PlayerAdvancements.revoke port crates/horizon-client/src/game/advancements.rs::Game::revoke_advancement revokes one criterion or all of them +PlayerAdvancements.markForVisibilityUpdate inline crates/horizon-client/src/lan/advancements.rs::ServerAdvancements::update visibility is recomputed for the whole tree on each update +PlayerAdvancements.unregisterListeners skip:other no per-criterion listener map; trigger() tests every criterion when an event fires +PlayerAdvancements.setSelectedTab missing unsure: the server keeps no selected advancement tab and sends no select-tab packet (only the SELECT_ADVANCEMENTS_TAB id exists) +PlayerAdvancements.getOrStartProgress skip:java map get-or-create +PlayerAdvancements.startProgress skip:java initialises progress for an advancement +PlayerAdvancements.updateTreeVisibility port crates/horizon-client/src/advancements.rs::Tree::visible gap: minor: returns the visible flags; the added and removed lists are built in lan/advancements.rs +PlayerAdvancements.removeListener skip:other no per-criterion listener map; trigger() tests every criterion when an event fires +PlayerAdvancements.Data.forEach skip:java sorted iteration over loaded progress; no game behaviour of its own +RegistryLayer.createRegistryAccess skip:data registry layers; Horizon's registries come from its tables +ReloadableServerRegistries.reload skip:data reloadable registry loading; Horizon's data comes from tables and files +ReloadableServerRegistries.createAndValidateFullContext skip:data registry context assembly and validation at load +ReloadableServerRegistries.validateLootRegistries skip:data loot table validation at load +ReloadableServerResources.loadResources skip:data server resource loading +ServerAdvancementManager.validate skip:data advancement validation logging at load +ServerFunctionLibrary.reload port crates/horizon-client/src/game/functions.rs::Game::rebuild_store rebuilds the function store from the data packs +ServerFunctionLibrary.readLines inline crates/horizon-client/src/datapack.rs::parse_function splits the function text into lines +ServerFunctionManager.executeTagFunctions port crates/horizon-client/src/game/functions.rs::Game::tick_functions runs the functions of a tag +ServerFunctionManager.execute port crates/horizon-client/src/game/functions.rs::Game::run_function runs one function as the sender +ServerLinks.findKnownType missing minor: no lookup by known link type; only the bug-report link is sent (dedicated.rs) +ServerLinks.untrust inline crates/horizon-client/src/game/dedicated.rs::Game::run_dedicated links are built as ServerLink entries for the net config, after valid_untrusted_uri diff --git a/tools/port_map/rest-014.tsv b/tools/port_map/rest-014.tsv new file mode 100644 index 0000000000..08237ab59b --- /dev/null +++ b/tools/port_map/rest-014.tsv @@ -0,0 +1,149 @@ +ServerScoreboard.onScoreChanged port crates/horizon-client/src/lan/scoreboard.rs::View::changes score packet for tracked objectives, sent as the diff +ServerScoreboard.onPlayerRemoved port crates/horizon-client/src/lan/scoreboard.rs::View::changes reset-all becomes one ResetScore per tracked objective; same client result +ServerScoreboard.onPlayerScoreRemoved port crates/horizon-client/src/lan/scoreboard.rs::View::changes ResetScore for one tracked objective +ServerScoreboard.addPlayerToTeam port crates/horizon-client/src/lan/scoreboard.rs::View::changes team method 3 (join) for the new member +ServerScoreboard.removePlayerFromTeam port crates/horizon-client/src/lan/scoreboard.rs::View::changes team method 4 (leave) +ServerScoreboard.onObjectiveChanged port crates/horizon-client/src/lan/scoreboard.rs::View::changes objective method 2 when its display changes +ServerScoreboard.onObjectiveRemoved port crates/horizon-client/src/lan/scoreboard.rs::View::changes objective method 1 when it leaves every display slot +ServerScoreboard.onTeamAdded port crates/horizon-client/src/lan/scoreboard.rs::View::changes team method 0 (create) to everyone +ServerScoreboard.onTeamChanged port crates/horizon-client/src/lan/scoreboard.rs::View::changes team method 2 (update parameters); waypoints in Waypoints::tick_level +ServerScoreboard.onTeamRemoved port crates/horizon-client/src/lan/scoreboard.rs::View::changes team method 1 (remove) +ServerScoreboard.storeToSaveDataIfDirty port crates/horizon-client/src/game/scoreboard.rs::Game::save_scoreboard gap: minor: saves the board on every save, no dirty flag; same file +ServerScoreboard.getStartTrackingPackets inline crates/horizon-client/src/lan/scoreboard.rs::View::changes objective, display slot and score packets come from the diff +ServerScoreboard.startTrackingObjective inline crates/horizon-client/src/lan/scoreboard.rs::View::changes no per-objective call; a newly displayed objective appears in the next diff +ServerScoreboard.getStopTrackingPackets inline crates/horizon-client/src/lan/scoreboard.rs::View::changes objective method 1 and display resets come from the diff +ServerScoreboard.stopTrackingObjective inline crates/horizon-client/src/lan/scoreboard.rs::View::changes objective removed from the next diff +ServerScoreboard.getObjectiveDisplaySlotCount inline crates/horizon-client/src/lan/scoreboard.rs::View::of tracked set = objectives in any display slot +ServerScoreboard.updatePlayerWaypoint port crates/horizon-client/src/lan/waypoints.rs::Waypoints::tick_level team change remakes the player's waypoint connections, found by the per-tick diff +ServerScoreboard.updateTeamWaypoints port crates/horizon-client/src/lan/waypoints.rs::Waypoints::tick_level every player on a changed team, via the per-tick diff +ServerTickRateManager.updateJoiningPlayer port crates/horizon-client/src/lan/hub.rs::Hub::send_level_info sends ticking state and step to the joining guest +SuppressedExceptionCollector.addEntry skip:server feeds the system report's suppressed-exception section; no player-facing behaviour +SuppressedExceptionCollector.dump skip:server system report section only +WorldLoader.PackConfig.createResourceManager port crates/horizon-client/src/game/functions.rs::Game::reload_datapacks discovers packs and rebuilds the data store; no resource manager object +AdvancementVisibilityEvaluator.evaluateVisibility port crates/horizon-client/src/advancements.rs::Tree::visible done, ancestors and near unhidden advancements in one walk +AdvancementVisibilityEvaluator.evaluateVisibilityRule inline crates/horizon-client/src/advancements.rs::Tree::visible rule match inside the walk +AdvancementVisibilityEvaluator.evaluateVisiblityForUnfinishedNode inline crates/horizon-client/src/advancements.rs::Tree::visible two-level check on the top three rules, in the walk +CustomBossEvent.addPlayer port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set players arm stores the UUID set; viewer packets from publish_boss_bars +CustomBossEvent.removePlayer port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set players arm replaces the set without the removed UUID +CustomBossEvent.removeAllPlayers inline crates/horizon-client/src/game/bossbars.rs::Game::bossbar_command bossbar remove drops the bar for every viewer on the next publish; no separate clear +CustomBossEvent.setValue port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set value arm; progress is clamped in BossBar::progress +CustomBossEvent.setMax port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set max arm; progress clamped in BossBar::progress +CustomBossEvent.setPlayers port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set players arm +CustomBossEvent.load port crates/horizon-client-core/src/bossbar.rs::BossBars::unpack restores saved bars; UUIDs minted from the id +CustomBossEvents.onPlayerDisconnect port crates/horizon-client/src/game/bossbars.rs::Game::publish_boss_bars a disconnected viewer leaves boss_viewers and the sent map +ChaseClient.start skip:debug /chase remote-control client, a developer tool +ChaseClient.stop skip:debug /chase remote-control client, a developer tool +ChaseClient.run skip:debug /chase remote-control client, a developer tool +ChaseClient.handleMessage skip:debug /chase remote-control client, a developer tool +ChaseClient.handleTeleport skip:debug /chase remote-control client, a developer tool +ChaseClient.parseTarget skip:debug /chase remote-control client, a developer tool +ChaseClient.executeCommand skip:debug /chase remote-control client, a developer tool +ChaseServer.start skip:debug /chase remote-control server, a developer tool +ChaseServer.runSender skip:debug /chase remote-control server, a developer tool +ChaseServer.runAcceptor skip:debug /chase remote-control server, a developer tool +ChaseServer.getPlayerPosition skip:debug /chase remote-control server, a developer tool +ChaseServer.PlayerPosition.format skip:debug /chase remote-control wire format, a developer tool +AdvancementCommands.performCriterion port crates/horizon-client/src/game/advancements.rs::Game::grant_advancement grant path with a criterion; the revoke path is Game::revoke_advancement +AdvancementCommands.getAdvancements inline crates/horizon-client/src/game/commands.rs::Game::cmd_advancement parents, target, then children, walked inside cmd_advancement +AdvancementCommands.addChildren inline crates/horizon-client/src/game/commands.rs::Game::cmd_advancement subtree walk (nested add fn) inside cmd_advancement +AdvancementCommands.Action. inline crates/horizon-client/src/game/commands.rs::Game::cmd_advancement per-action message keys built from grant/revoke inside cmd_advancement +AdvancementCommands.Action.perform port crates/horizon-client/src/game/commands.rs::Game::cmd_advancement loops the targets, counts changes, quiet when no single advancement +ArgProvider.buildList skip:java list builder; tree_data.rs providers builds the entity, block and storage branches directly +AttributeCommand.register port crates/horizon-command/src/tree_world.rs::attribute attribute syntax tree +AttributeCommand.getAttributeInstance inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute instance lookup and no-attribute error inside cmd_attribute +AttributeCommand.getLivingEntity inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute non-living target error inside cmd_attribute +AttributeCommand.getEntityWithAttribute inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute has-attribute check inside cmd_attribute +AttributeCommand.getAttributeValue port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute get arm, value times scale +AttributeCommand.getAttributeBase port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute base get arm, base times scale +AttributeCommand.getAttributeModifier port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute modifier value get arm +AttributeCommand.setAttributeBase port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute base set arm +AttributeCommand.resetAttributeBase port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute base reset arm +AttributeCommand.addModifier port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute modifier add arm, already-present error +AttributeCommand.removeModifier port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_attribute modifier remove arm +BanIpCommands.register missing minor: /ban-ip is not in the command tree; Horizon has no ban list +BanIpCommands.banIpOrName missing minor: operator ban-by-IP or name; no ban list in Horizon +BanListCommands.register missing minor: /banlist is not in the command tree; Horizon has no ban list +BanPlayerCommands.register missing minor: /ban is not in the command tree; Horizon has no ban list +BossBarCommands.register port crates/horizon-command/src/tree_misc.rs::bossbar bossbar syntax tree +BossBarCommands.getValue port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_command bossbar.get value arm +BossBarCommands.getMax port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_command bossbar.get max arm +BossBarCommands.setValue port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set value arm; unchanged error +BossBarCommands.setMax port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set max arm; unchanged error +BossBarCommands.setColor port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set color arm; unchanged error +BossBarCommands.setStyle port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set style arm; unchanged error +BossBarCommands.setName port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_set name arm; unchanged error +BossBarCommands.createBar port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_command bossbar.add arm, already-exists error +BossBarCommands.removeBar port crates/horizon-client/src/game/bossbars.rs::Game::bossbar_command bossbar.remove arm +BossBarCommands.getBossBar port crates/horizon-client/src/game/bossbars.rs::Game::bar_checked lookup with does-not-exist error +ChaseCommand.register skip:debug /chase syntax, a developer tool +ChaseCommand.stop skip:debug /chase stop, a developer tool +ChaseCommand.alreadyRunning skip:debug /chase state check, a developer tool +ChaseCommand.lead skip:debug /chase lead, a developer tool +ChaseCommand.follow skip:debug /chase follow, a developer tool +ClearInventoryCommands.register port crates/horizon-command/src/vanilla.rs::registered /clear syntax (vanilla.rs line 319) +CloneCommands.register port crates/horizon-command/src/vanilla.rs::registered /clone syntax root +CloneCommands.beginEndDestinationAndModeSuffix port crates/horizon-command/src/vanilla.rs::clone_begin begin, end, to dimension +CloneCommands.getLoadedDimensionAndPosition inline crates/horizon-client/src/game/commands.rs::Game::cmd_clone position resolve (block_pos_arg) inside cmd_clone +CloneCommands.destinationAndStrictSuffix port crates/horizon-command/src/vanilla.rs::clone_destination destination and strict +CloneCommands.modeSuffix port crates/horizon-command/src/vanilla.rs::clone_mask replace, masked, filtered +CloneCommands.wrapWithCloneMode port crates/horizon-command/src/vanilla.rs::clone_mode force, move, normal +CommandResponseTracker.track skip:java result aggregation; each command counts its own targets +CommandResponseTracker.firstElement skip:java single-target check; each command decides singular or plural text +CommandResponseTracker.elementCount skip:java target count; each command counts its own targets +CommandResponseTracker.messages skip:java message builder; Horizon writes each command's feedback inline +CommandResponseTracker.dispatch skip:java singular or plural message choice; done per command +CommandResponseTracker.sendFeedback skip:java zero-target failure and feedback are written per command +CommandResponseTracker.MessagesWithArg.throwIfZero skip:java zero-target error; written per command +CommandResponseTracker.MessagesWithArgs.throwIfZero skip:java zero-target error; written per command +ComputeCommand.computeAsFloat port crates/horizon-client/src/game/commands/compute.rs::Game::cmd_compute float value, floor(value * scale), exact and inexact feedback +ComputeCommand.throwInvalidValue inline crates/horizon-client/src/game/commands/compute.rs::Game::cmd_compute invalid-value message closure inside cmd_compute +DamageCommand.register port crates/horizon-command/src/tree_world.rs::damage damage syntax tree +DataPackCommand.createPack missing minor: /datapack create is in the tree but writes no pack folder or pack.mcmeta +DataPackCommand.enablePack port crates/horizon-client/src/game/functions.rs::Game::cmd_datapack datapack.enable arm; apply_pack_change reloads +DataPackCommand.disablePack port crates/horizon-client/src/game/functions.rs::Game::cmd_datapack disable branch; apply_pack_change reloads +DataPackCommand.getPack port crates/horizon-client/src/game/functions.rs::Game::cmd_datapack unknown, already enabled or disabled, and feature checks +DeOpCommands.register missing minor: /deop is not in the in-game tree; op lists are edited by the standalone server CLI +DebugCommand.start port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_debug profiler start, already-running error +DebugCommand.stop port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_debug profiler stop, ticks and tps +DebugCommand.TraceCustomExecutor.runGuarded port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_debug_function trace file under debug/ with one line per call +DebugCommand.Tracer.printIndent inline crates/horizon-client/src/game/commands/extra.rs::Game::trace_function indent closure inside trace_function +DebugCommand.Tracer.onCommand port crates/horizon-client/src/game/commands/extra.rs::Game::trace_function [C] line +DebugCommand.Tracer.onReturn inline crates/horizon-client/src/game/commands/extra.rs::Game::trace_function result appended to the [C] line +DebugCommand.Tracer.onCall port crates/horizon-client/src/game/commands/extra.rs::Game::trace_function [F] line with size +DebugCommand.Tracer.onError port crates/horizon-client/src/game/commands/extra.rs::Game::trace_function [E] line +DebugCommand.Tracer.sendSystemMessage missing minor: no [M] lines for system messages in the trace file +DebugConfigCommand.register skip:debug /debugconfig, a developer tool +DebugConfigCommand.getUuidsInConfig skip:debug /debugconfig, a developer tool +DebugConfigCommand.config skip:debug /debugconfig, a developer tool +DebugConfigCommand.findConfigPlayer skip:debug /debugconfig, a developer tool +DebugConfigCommand.unconfig skip:debug /debugconfig, a developer tool +DebugConfigCommand.showDialog skip:debug /debugconfig, a developer tool +DebugMobSpawningCommand.register skip:debug /debugmobspawning, a developer tool +DebugPathCommand.register skip:debug /debugpath, a developer tool +DebugPathCommand.fillBlocks skip:debug /debugpath, a developer tool +DefaultGameModeCommands.setMode port crates/horizon-client/src/game/commands.rs::Game::execute_command gap: unsure: calls set_default_game_mode (lan.rs), which also sets the host's creative flag; vanilla only changes the default for new players +DialogCommand.showDialog port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_dialog gap: player: open_dialog only sets the host's dialog state; other targets are counted but never sent the dialog +DialogCommand.clearDialog port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_dialog gap: player: close_dialog only closes the host's dialog; guests get no clear packet +DifficultyCommand.register port crates/horizon-command/src/vanilla.rs::registered difficulty syntax (vanilla.rs line 284) +DifficultyCommand.setDifficulty port crates/horizon-client/src/game/commands.rs::Game::cmd_difficulty already-same error, set, hostile mobs removed on peaceful +EffectCommands.register port crates/horizon-command/src/vanilla.rs::registered effect syntax +EffectCommands.computeDurationInTicks port crates/horizon-client/src/game/commands/effects.rs::Game::cmd_effect_give duration arm inside cmd_effect_give +EffectCommands.clearEffect port crates/horizon-client/src/game/commands/effects.rs::Game::cmd_effect_clear clear one effect; tracker feedback +EmoteCommands.register port crates/horizon-command/src/vanilla.rs::registered /me syntax; emote text in cmd_say (commands.rs) +EnchantCommand.register port crates/horizon-command/src/vanilla.rs::registered enchant syntax (vanilla.rs line 189) +net/minecraft/server/commands/ExecuteCommand#wrapStores port crates/horizon-command/src/tree_execute.rs::store execute store syntax +net/minecraft/server/commands/ExecuteCommand#storeValue port crates/horizon-client/src/game/commands/execute.rs::Game::store_value score and bossbar store targets +net/minecraft/server/commands/ExecuteCommand#storeData port crates/horizon-client/src/game/commands/execute.rs::Game::store_value data target store with int, float, short, long, double tags +net/minecraft/server/commands/ExecuteCommand#isChunkLoaded port crates/horizon-client/src/game/commands/execute.rs::Game::condition_matches gap: unsure: tests the streaming lit set; vanilla needs ENTITY_TICKING status and loaded entities +net/minecraft/server/commands/ExecuteCommand#countItems port crates/horizon-client/src/game/commands/items.rs::Game::condition_items item count predicate +net/minecraft/server/commands/ExecuteCommand#checkScore port crates/horizon-client/src/game/commands/execute.rs::Game::condition_score score comparison, both score holders +net/minecraft/server/commands/ExecuteCommand#checkStopwatch port crates/horizon-client/src/game/commands/execute.rs::Game::condition_stopwatch stopwatch range check +net/minecraft/server/commands/ExecuteCommand#checkCustomPredicate port crates/horizon-client/src/game/functions.rs::Game::check_predicate predicate check +net/minecraft/server/commands/ExecuteCommand#addConditional port crates/horizon-client/src/game/commands/execute.rs::Game::run_conditional_terminal pass and fail messages, count +net/minecraft/server/commands/ExecuteCommand#addIfBlocksConditional port crates/horizon-client/src/game/commands/execute.rs::Game::run_conditional_terminal if and unless blocks through the shared terminal +net/minecraft/server/commands/ExecuteCommand#checkIfRegions inline crates/horizon-client/src/game/commands/execute.rs::Game::run_conditional_terminal pass_count message folded into the terminal +net/minecraft/server/commands/ExecuteCommand#checkUnlessRegions inline crates/horizon-client/src/game/commands/execute.rs::Game::run_conditional_terminal fail_count error folded into the terminal +net/minecraft/server/commands/ExecuteCommand#checkRegions port crates/horizon-client/src/game/commands/execute.rs::Game::condition_blocks 32768 area limit, block and block-entity compare +net/minecraft/server/commands/ExecuteCommand#spawnEntityAndRedirect port crates/horizon-client/src/game/commands/execute.rs::Game::modify summon arm, then the redirected source +net/minecraft/server/commands/ExecuteCommand#scheduleFunctionConditionsAndTest port crates/horizon-client/src/game/commands/execute.rs::Game::condition_function gap: minor: runs the function inline and keeps sources by its result; no queued per-source instantiation +ExecuteCommand.ExecuteIfFunctionCustomModifier.apply port crates/horizon-client/src/game/commands/execute.rs::Game::condition_function function condition as a modifier diff --git a/tools/port_map/rest-015.tsv b/tools/port_map/rest-015.tsv new file mode 100644 index 0000000000..c328616fe0 --- /dev/null +++ b/tools/port_map/rest-015.tsv @@ -0,0 +1,149 @@ +ExperienceCommand.addExperience port crates/horizon-client/src/game/commands.rs::Game::change_experience add branch of the per-player change; loop and feedback in cmd_experience +ExperienceCommand.setExperience port crates/horizon-client/src/game/commands.rs::Game::change_experience set branch; refuses points that do not fit the level; loop and feedback in cmd_experience +ExperienceCommand.Type. inline crates/horizon-client/src/game/commands.rs::Game::cmd_experience per-unit (points or levels) keys chosen in cmd_experience +ExperienceCommand.Type.queryResponse inline crates/horizon-client/src/game/commands.rs::Game::cmd_experience query message built in the query branch of cmd_experience +FetchProfileCommand.resolveName missing player: /fetchprofile name always reports the profile as not found; no profile lookup +FetchProfileCommand.resolveId missing player: /fetchprofile id always reports the profile as not found; no profile lookup +FetchProfileCommand.printForEntity port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_fetchprofile entity branch: player profile links; other entities get no_profile error +FillBiomeCommand.register port crates/horizon-command/src/tree_world.rs::fillbiome syntax of /fillbiome incl. replace filter +FillBiomeCommand.quantize inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_fillbiome quart snap (v >> 2) << 2 done in cmd_fillbiome +FillBiomeCommand.makeResolver inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_fillbiome biome cells inside the box set in cmd_fillbiome, filter honoured +FillCommand.register port crates/horizon-command/src/vanilla.rs::registered fill literal with replace/keep/filter/mode branches +FillCommand.fillBlocks port crates/horizon-client/src/game/commands.rs::Game::cmd_fill fill with area limit, hollow/outline/destroy/strict/keep modes +ForceLoadCommand.register port crates/horizon-command/src/tree_world.rs::forceload syntax of /forceload add|remove|query +ForceLoadCommand.queryForceLoad port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_forceload query branch of cmd_forceload +ForceLoadCommand.removeAll port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_forceload remove.all branch of cmd_forceload +ForceLoadCommand.changeForceLoad port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_forceload add/remove over a chunk box with the 256-chunk limit +FunctionCommand.register port crates/horizon-command/src/tree_misc.rs::function syntax of /function incl. with [path] +FunctionCommand.getArgumentTag inline crates/horizon-client/src/game/functions.rs::Game::cmd_function with branch reads the tag and rejects non-compound +FunctionCommand.queueFunctions inline crates/horizon-client/src/game/functions.rs::Game::cmd_function no-return path runs in cmd_function; return path goes through run_return_run +FunctionCommand.instantiateAndQueueFunctions port crates/horizon-client/src/game/functions.rs::Game::run_function instantiates with arguments and runs; instantiation failure reported +FunctionCommand.decorateOutputIfNeeded inline crates/horizon-client/src/game/functions.rs::Game::cmd_function per-function result message when a value comes back +FunctionCommand.queueFunctionsAsReturn port crates/horizon-client/src/game/commands/execute.rs::Game::run_return_run gap: minor: several functions under 'return run' return their sum; vanilla hands each result to the frame +FunctionCommand.queueFunctionsNoReturn inline crates/horizon-client/src/game/functions.rs::Game::cmd_function sums results into the caller's callback +GameModeCommand.register port crates/horizon-command/src/vanilla.rs::registered gamemode literal with gamemode and target arguments +GameModeCommand.setMode port crates/horizon-client/src/game/commands.rs::Game::cmd_gamemode sets each target's mode and counts changes +GameRuleCommand.buildRuleArguments port crates/horizon-command/src/vanilla.rs::registered one literal per rule (name and minecraft: id) with a value argument +GameRuleCommand.setRule port crates/horizon-client/src/game/commands.rs::Game::cmd_gamerule set branch: refuses an unchanged value +GameRuleCommand.queryRule port crates/horizon-client/src/game/commands.rs::Game::cmd_gamerule query branch of cmd_gamerule +GiveCommand.register port crates/horizon-command/src/vanilla.rs::registered give literal with targets, item and count +GiveCommand.giveItem port crates/horizon-client/src/game/commands.rs::Game::cmd_give stack limit x100, overflow dropped at the player, pickup sound +HelpCommand.register port crates/horizon-command/src/vanilla.rs::registered help literal; usage text is Game::cmd_help +JfrCommand.startJfr skip:server JVM flight recorder profiler; Horizon has its own /jfr in extra.rs +KillCommand.register port crates/horizon-command/src/vanilla.rs::registered kill literal with optional targets +KillCommand.kill port crates/horizon-client/src/game/commands.rs::Game::cmd_kill kills each target and reports the count +ListPlayersCommand.register port crates/horizon-command/src/vanilla.rs::registered list literal with uuids branch +ListPlayersCommand.listPlayersWithUuids inline crates/horizon-client/src/game/commands.rs::Game::execute_command gap: minor: the 'list' arm always shows the one local player out of 8 (hard-coded), no server player list +ListPlayersCommand.format inline crates/horizon-client/src/game/commands.rs::Game::execute_command gap: minor: same hard-coded 1 of 8 summary as the list arm +LocateCommand.getHolders port crates/horizon-client/src/game/commands.rs::Game::structure_tag gap: minor: resolves a #tag to its structures; an unknown id is not validated here +LocateCommand.showLocateResult inline crates/horizon-client/src/game/commands.rs::Game::cmd_locate distance (2D for structures, 3D for biomes) and the success message +LocateCommand.dist inline crates/horizon-client/src/game/commands.rs::Game::cmd_locate floored horizontal distance computed in cmd_locate +LookAt.LookAtEntity.perform port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_rotate facing-entity branch of cmd_rotate; look_at does the yaw and pitch math +LootCommand.register port crates/horizon-command/src/tree_world.rs::loot loot literal with loot/mine/kill/fish/replace/insert/give/spawn +LootCommand.addTargets port crates/horizon-command/src/tree_world.rs::loot replace entity|block, insert, give, spawn subcommands +LootCommand.blockDistribute inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot insert branch of cmd_loot +LootCommand.distributeToContainer inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot gap: minor: insert branch fills slots without container.canPlaceItem checks +LootCommand.blockReplace inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot replace block branch of cmd_loot +LootCommand.canMergeItems inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot same item and components check inside the insert branch +LootCommand.playerGive inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot give branch of cmd_loot +LootCommand.setSlots inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot replace entity branch of cmd_loot +LootCommand.entityReplace inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot replace entity branch of cmd_loot +LootCommand.dropInWorld inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot spawn branch: items with pickup delay 10 +LootCommand.getSourceHandItem port crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::tool_stack mainhand or offhand of the source, or the tool argument +LootCommand.dropBlockLoot inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot loot.mine branch of cmd_loot +LootCommand.dropKillLoot inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot loot.kill branch of cmd_loot +LootCommand.dropChestLoot inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot loot.loot branch of cmd_loot +LootCommand.dropFishingLoot inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot loot.fish branch of cmd_loot +LootCommand.drop inline crates/horizon-client/src/game/commands/terrain_cmds.rs::Game::cmd_loot shared drop and feedback at the end of cmd_loot +LootContextSources.ContextDecorator.createParams port crates/horizon-client/src/game/commands/compute.rs::Game::compute_context gap: minor: keeps only the block and entity picks; no origin or this-entity loot params +LootContextSources.ContextDecorator.createContext inline crates/horizon-client/src/game/commands/compute.rs::Game::compute_context gap: minor: no LootContext is built +MsgCommand.register port crates/horizon-command/src/vanilla.rs::registered msg literal with tell and w aliases +OpCommand.register missing minor: no in-game /op literal; ops are set with the horizon-server CLI +OpCommand.opPlayers port crates/horizon-server/src/lists.rs::run gap: minor: CLI op only (ops.json); no in-game /op +PardonCommand.register missing minor: no in-game /pardon literal +PardonCommand.pardonPlayers missing minor: no pardon path; ban lists exist only as server files (horizon-server-files user_lists.rs) +PardonIpCommand.register missing minor: no in-game /pardon-ip literal +PardonIpCommand.unban missing minor: no pardon-ip path; ban lists exist only as server files +ParticleCommand.register port crates/horizon-command/src/tree_world.rs::particle particle syntax with viewers, force and normal; execution in cmd_particle +PerfCommand.register skip:server profiler for the dedicated server; not a game behaviour +PerfCommand.startProfilingDedicatedServer skip:server profiler for the dedicated server; not a game behaviour +PerfCommand.stopProfilingDedicatedServer skip:server profiler for the dedicated server; not a game behaviour +PerfCommand.saveResults skip:server profiler for the dedicated server; not a game behaviour +PerfCommand.whenStopped skip:server profiler for the dedicated server; not a game behaviour +PlaceCommand.placeJigsaw port crates/horizon-client/src/game/commands/place.rs::Game::cmd_place_generated place.jigsaw branch: pool check, generation, success message +PlaceCommand.placeStructure missing player: /place structure checks the id then always fails (Structure.generate is not wired to the live level) +PlaceCommand.placeTemplate port crates/horizon-client/src/game/commands/place.rs::Game::cmd_place template placement with rotation, mirror, integrity and strict +PlaySoundCommand.register port crates/horizon-command/src/tree_world.rs::sounds playsound literal with a sound-source literal per source +PlaySoundCommand.source port crates/horizon-command/src/tree_world.rs::sounds per-source literal with targets, pos, volume, pitch, minVolume +PlaySoundCommand.playSound port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_playsound range 16 (16 x volume above 1), minVolume fallback, one seed for all players +PostEffectCommand.addPostEffect port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_posteffect gap: player: the effect is stored and listed, but nothing draws it +PostEffectCommand.clearPostEffect port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_posteffect gap: player: the cleared list is never drawn anyway +PostEffectCommand.removePostEffect port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_posteffect gap: player: the removed effect was never drawn +PublishCommand.getSuccessMessage port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_publish published-port message with a copy-on-click port +RaidCommand.glow port crates/horizon-client/src/game/raids.rs::Game::raid_glow glowing for 1000 ticks, amplifier 1, on the raid's raiders (via cmd_raid raid.glow) +RaidCommand.setRaidOmenLevel port crates/horizon-client/src/game/commands/raid.rs::Game::cmd_raid raid.setomen branch +RaidCommand.spawnLeader port crates/horizon-client/src/game/commands/raid.rs::Game::cmd_raid raid.spawnleader branch: patrol leader and banner +RaidCommand.playSound port crates/horizon-client/src/game/commands/raid.rs::Game::cmd_raid gap: minor: plays the horn whatever the type argument says; vanilla plays it only for 'local' +RaidCommand.start port crates/horizon-client/src/game/commands/raid.rs::Game::cmd_raid raid.start branch +RaidCommand.stop port crates/horizon-client/src/game/commands/raid.rs::Game::cmd_raid raid.stop branch +RaidCommand.check port crates/horizon-client/src/game/commands/raid.rs::Game::cmd_raid raid.check branch with the status line +RandomCommand.register port crates/horizon-command/src/tree_misc.rs::register random literal: value, roll and reset with sequence and seed syntax +RandomCommand.drawRandomValueTree port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_random value and roll branches of cmd_random +RandomCommand.suggestRandomSequence port crates/horizon-command/src/args.rs::ArgType::suggest gap: minor: suggests the registry's sequence ids, not the sequences this world has created +RandomCommand.resetSequence port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_random reset with a sequence (and seed) branch +RandomCommand.resetAllSequences port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_random reset.* branch without seed +RandomCommand.resetAllSequencesAndSetNewDefaults port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_random reset.* branch with a seed sets the defaults then clears +RecipeCommand.register port crates/horizon-command/src/tree_world.rs::recipe recipe give|take literals with recipe and * arguments +RecipeCommand.giveRecipes port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_recipe give branch; recipes added through change_recipes +RecipeCommand.takeRecipes port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_recipe take branch; recipes removed through change_recipes +ReloadCommand.discoverNewPacks port crates/horizon-client/src/game/functions.rs::Game::reload_datapacks gap: minor: enables newly found packs that are not disabled; no NeoForge reorder of new packs +ReloadCommand.register port crates/horizon-command/src/tree_misc.rs::register reload literal +ReturnCommand.ReturnFailCustomExecutor.run port crates/horizon-client/src/game/functions.rs::Game::cmd_return return fail branch: finish_return with no value +ReturnCommand.ReturnFromCommandCustomModifier.apply port crates/horizon-client/src/game/commands/execute.rs::Game::run_return_run return run: the command's result becomes the frame's return +ReturnCommand.ReturnValueCustomExecutor.run port crates/horizon-client/src/game/functions.rs::Game::cmd_return return value branch: finish_return with the value +RideCommand.mount port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_ride mount branch with already-riding, player and loop checks +RideCommand.dismount port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_ride dismount branch +RotateCommand.register port crates/horizon-command/src/tree_world.rs::rotate_swing rotate literal with entity, facing and rotation syntax +SaveAllCommand.register port crates/horizon-client/src/game.rs::Game::save_game_metadata gap: minor: the full save exists here (level, advancements, scoreboard, world state) but no /save-all literal reaches it +SayCommand.register port crates/horizon-command/src/vanilla.rs::registered say literal; message delivery is Game::cmd_say +ScheduleCommand.remove port crates/horizon-client/src/game/functions.rs::Game::cmd_schedule schedule.clear branch: removes by id, failure when none +ScoreboardCommand.createRenderTypeModify port crates/horizon-command/src/tree_scoreboard.rs::scoreboard rendertype literal per render type +ScoreboardCommand.suggestTriggers port crates/horizon-command/src/lib.rs::Suggests::suggest lists trigger objectives that are unlocked for the targets +ScoreboardCommand.getScore port crates/horizon-client/src/scoreboard_cmd.rs::players players get branch +ScoreboardCommand.performOperation port crates/horizon-client/src/scoreboard_cmd.rs::players players operation branch applying apply_operation per target and source +ScoreboardCommand.enableTrigger port crates/horizon-client/src/scoreboard_cmd.rs::players players enable branch +ScoreboardCommand.resetScores port crates/horizon-client/src/scoreboard_cmd.rs::players players reset without objective +ScoreboardCommand.resetScore port crates/horizon-client/src/scoreboard_cmd.rs::players players reset with objective +ScoreboardCommand.setScore port crates/horizon-client/src/scoreboard_cmd.rs::players players set branch +ScoreboardCommand.setScoreDisplay port crates/horizon-client/src/scoreboard_cmd.rs::players players display name branch +ScoreboardCommand.setScoreNumberFormat port crates/horizon-client/src/scoreboard_cmd.rs::players players display numberformat branch +ScoreboardCommand.addScore port crates/horizon-client/src/scoreboard_cmd.rs::players players add branch +ScoreboardCommand.removeScore port crates/horizon-client/src/scoreboard_cmd.rs::players players remove branch +ScoreboardCommand.clearDisplaySlot port crates/horizon-client/src/scoreboard_cmd.rs::scoreboard objectives setdisplay branch without objective +ScoreboardCommand.setDisplaySlot port crates/horizon-client/src/scoreboard_cmd.rs::scoreboard objectives setdisplay branch with objective +ScoreboardCommand.setDisplayName port crates/horizon-client/src/scoreboard_cmd.rs::scoreboard objectives modify displayname branch +ScoreboardCommand.setDisplayAutoUpdate port crates/horizon-client/src/scoreboard_cmd.rs::scoreboard objectives modify displayautoupdate branch +ScoreboardCommand.setObjectiveFormat port crates/horizon-client/src/scoreboard_cmd.rs::scoreboard objectives modify numberformat branch +ScoreboardCommand.setRenderType port crates/horizon-client/src/scoreboard_cmd.rs::scoreboard objectives modify rendertype branch +ScoreboardCommand.removeObjective port crates/horizon-client/src/scoreboard_cmd.rs::scoreboard objectives remove branch +ScoreboardCommand.addObjective port crates/horizon-client/src/scoreboard_cmd.rs::scoreboard objectives add branch +SeedCommand.register port crates/horizon-command/src/vanilla.rs::registered seed literal; the seed text is the seed arm of execute_command +ServerPackCommand.register missing minor: no /serverpack literal +ServerPackCommand.sendToAllConnections missing minor: no pack packets are sent to connected clients by any command +ServerPackCommand.pushPack missing minor: no push; ResourcePackPush exists only in horizon-protocol +ServerPackCommand.popPack missing minor: no pop; ResourcePackPop exists only in horizon-protocol +SetBlockCommand.register port crates/horizon-command/src/vanilla.rs::registered setblock literal with destroy, keep, replace and strict +SetBlockCommand.setBlock port crates/horizon-client/src/game/commands.rs::Game::cmd_setblock destroy, keep, replace, strict and block entity NBT +SetPlayerIdleTimeoutCommand.register missing minor: no /setidletimeout literal; server.properties keeps player-idle-timeout only +SetPlayerIdleTimeoutCommand.setIdleTimeout missing minor: no in-game idle timeout change +SetSpawnCommand.setSpawn port crates/horizon-client/src/game/commands.rs::Game::cmd_spawnpoint forced spawn per target, overworld spawn or respawn anchor by dimension +SetWorldSpawnCommand.setSpawn port crates/horizon-client/src/game/commands.rs::Game::cmd_setworldspawn world spawn with angle and the fallback respawn update +SpawnArmorTrimsCommand.register missing minor: no /spawn_armor_trims literal (test command for trim patterns) +SpawnArmorTrimsCommand.spawnArmorTrim missing minor: no armor stands are spawned for trim patterns +SpawnArmorTrimsCommand.spawnArmorTrims missing minor: no armor stands are spawned for trim patterns +SpawnArmorTrimsCommand.findEquippableItemsWithAssets missing minor: no scan of equippable armor items for trims +SpreadPlayersCommand.spreadPlayers port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers spreads targets around the centre with spread distance, max range and max height +SpreadPlayersCommand.getNumberOfTeams inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers team count via team_of in respectTeams mode +SpreadPlayersCommand.spreadPositions inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers collision loop with the 10000-iteration cap, clamp and safe-spot check +SpreadPlayersCommand.setPlayerPositions inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers teleports each target and computes the average distance +SpreadPlayersCommand.createInitialPositions inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers random start points in the box diff --git a/tools/port_map/rest-016.tsv b/tools/port_map/rest-016.tsv new file mode 100644 index 0000000000..71b850f140 --- /dev/null +++ b/tools/port_map/rest-016.tsv @@ -0,0 +1,123 @@ +SpreadPlayersCommand.Position.dist inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers helper folded into the spread loop +SpreadPlayersCommand.Position.normalize inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers helper folded into the spread loop +SpreadPlayersCommand.Position.getLength inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers helper folded into the spread loop +SpreadPlayersCommand.Position.moveAway inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers helper folded into the spread loop +SpreadPlayersCommand.Position.clamp inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers helper folded into the spread loop +SpreadPlayersCommand.Position.randomize inline crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_spreadplayers helper folded into the spread loop +StopCommand.register port crates/horizon-client/src/game/dedicated.rs::Game::console_command gap: minor: /stop only from the dedicated console; no in-game /stop node +StopSoundCommand.register port crates/horizon-command/src/tree_world.rs::sounds /stopsound command tree +StopwatchCommand.createStopwatch port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_stopwatch stopwatch.create arm +StopwatchCommand.queryStopwatch port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_stopwatch stopwatch.query arm +StopwatchCommand.restartStopwatch port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_stopwatch stopwatch.restart arm +StopwatchCommand.removeStopwatch port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_stopwatch default arm: remove +SummonCommand.register port crates/horizon-command/src/vanilla.rs::registered root summon node +SummonCommand.spawnEntity port crates/horizon-client/src/game/commands.rs::Game::cmd_summon commands.summon.success +SwingCommand.register port crates/horizon-command/src/tree_world.rs::rotate_swing /swing command tree +SwingCommand.handSwing port crates/horizon-command/src/tree_world.rs::rotate_swing gap: minor: hand literal is parsed but cmd_swing ignores it; only the main-hand swing plays +SwingCommand.swing port crates/horizon-client/src/game/commands/world_cmds.rs::Game::cmd_swing gap: minor: ignores hand, animation and duration; mobs get the swing flag, the player swings the main arm +TagCommand.getTags inline crates/horizon-client/src/game/commands/data.rs::Game::cmd_tag union of tags over targets for list +TagCommand.addTag port crates/horizon-client/src/game/commands/data.rs::Game::cmd_tag tag.add arm +TagCommand.removeTag port crates/horizon-client/src/game/commands/data.rs::Game::cmd_tag tag.remove arm +TagCommand.listTags port crates/horizon-client/src/game/commands/data.rs::Game::cmd_tag default arm: list +TeamCommand.leaveTeam port crates/horizon-client/src/scoreboard_cmd.rs::team team.leave arm +TeamCommand.joinTeam port crates/horizon-client/src/scoreboard_cmd.rs::team team.join arm +TeamCommand.setNameTagVisibility port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.setDeathMessageVisibility port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.setCollision port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.setFriendlySight port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.setFriendlyFire port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.setDisplayName port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.setColor port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.clearColor port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.setPrefix port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.setSuffix port crates/horizon-client/src/scoreboard_cmd.rs::team_modify team.modify arm +TeamCommand.emptyTeam port crates/horizon-client/src/scoreboard_cmd.rs::team team.empty arm +TeamCommand.deleteTeam port crates/horizon-client/src/scoreboard_cmd.rs::team team.remove arm +TeamMsgCommand.sendMessage port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_teammsg gap: minor: only the local player can receive; no per-receiver chat filtering +TeleportCommand.register port crates/horizon-command/src/vanilla.rs::registered teleport and tp nodes +TeleportCommand.teleportToPos port crates/horizon-client/src/game/commands.rs::Game::cmd_teleport location arm +TeleportCommand.getRelatives inline crates/horizon-client/src/game/commands.rs::Game::cmd_teleport relative coords resolved from the source up front; same end position +TeleportCommand.performTeleport port crates/horizon-client/src/game/commands.rs::Game::teleport_to_in_level gap: minor: zeroes all velocity and clears on_ground for a player; vanilla keeps horizontal motion, sets on_ground, stops mob navigation, skips Y reset while fall-flying +TickCommand.nanosToMilisString port crates/horizon-client/src/game/commands/misc.rs::millis one-decimal millisecond string +TickCommand.setTickingRate port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_tick tick.rate arm +TickCommand.step port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_tick tick.step arm +TickCommand.stopStepping port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_tick tick.step.stop default arm +TickCommand.stopSprinting port crates/horizon-client/src/game/commands/misc.rs::Game::cmd_tick tick.sprint.stop arm +TimeCommand.suggestTimelines port crates/horizon-command/src/args.rs::ArgType::suggest gap: minor: lists every timeline id, not only the ids of the parsed clock +TimeCommand.queryGameTime port crates/horizon-client/src/game/commands.rs::Game::cmd_time time.query.gametime arm +TimeCommand.queryTime port crates/horizon-client/src/game/commands.rs::Game::cmd_time time.query.time arm +TimeCommand.queryTimelineTicks port crates/horizon-client/src/game/commands.rs::Game::cmd_time time.query.timeline arm +TimeCommand.queryTimelineRepetitions port crates/horizon-client/src/game/commands.rs::Game::cmd_time time.query.repetition arm +TimeCommand.setTotalTicks port crates/horizon-client/src/game/commands.rs::Game::cmd_time time.set arm +TimeCommand.addTime port crates/horizon-client/src/game/commands.rs::Game::cmd_time time.add arm +TimeCommand.setTimeToTimeMarker port crates/horizon-client/src/game/commands.rs::Game::cmd_time time.set.marker arm +TimeCommand.setRate port crates/horizon-client/src/game/commands.rs::Game::cmd_time time.rate arm +TimeCommand.getDefaultClock inline crates/horizon-client/src/game/commands.rs::Game::cmd_time overworld clock; the Nether has no default clock +TitleCommand.sendPacketToPlayers port crates/horizon-client/src/game/commands/text_commands.rs::Game::titles_or_packet per-player send +TitleCommand.showTitle port crates/horizon-client/src/game/commands/text_commands.rs::Game::cmd_title title, subtitle and actionbar +TitleCommand.setTimes port crates/horizon-client/src/game/commands/text_commands.rs::Game::cmd_title times arm +TransferCommand.register missing minor: no /transfer node; the client follows an incoming transfer (net.rs) but no command sends one +TransferCommand.transfer missing minor: no command sends a ClientboundTransferPacket to a player +TriggerCommand.suggestObjectives port crates/horizon-command/src/lib.rs::Suggests::suggest Suggests::Triggers arm +TriggerCommand.addValue port crates/horizon-client/src/scoreboard_cmd.rs::trigger trigger.add arm +TriggerCommand.setValue port crates/horizon-client/src/scoreboard_cmd.rs::trigger trigger.set arm +TriggerCommand.simpleTrigger port crates/horizon-client/src/scoreboard_cmd.rs::trigger trigger arm without a sub-command +TriggerCommand.getScore inline crates/horizon-client/src/scoreboard_cmd.rs::trigger primed check and lock +VersionCommand.register port crates/horizon-command/src/vanilla.rs::registered version node +WardenSpawnTrackerCommand.setWarningLevel port crates/horizon-client/src/game/sculk.rs::Game::cmd_warden_spawn_tracker set arm +WardenSpawnTrackerCommand.resetTracker port crates/horizon-client/src/game/sculk.rs::Game::cmd_warden_spawn_tracker clear arm +WaypointCommand.setWaypointStyle port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_waypoint style arm of the modify branch +WaypointCommand.setWaypointColor port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_waypoint color arm of the modify branch +WaypointCommand.resetWaypointColor port crates/horizon-client/src/game/commands/extra.rs::Game::cmd_waypoint color reset arm +WeatherCommand.getDuration inline crates/horizon-client/src/game/commands.rs::Game::cmd_weather default duration sample +WeatherCommand.setClear port crates/horizon-client/src/game/commands.rs::Game::cmd_weather clear branch +WeatherCommand.setRain port crates/horizon-client/src/game/commands.rs::Game::cmd_weather rain branch +WeatherCommand.setThunder port crates/horizon-client/src/game/commands.rs::Game::cmd_weather thunder branch +WhitelistCommand.register port crates/horizon-client/src/game/dedicated.rs::Game::console_command gap: minor: console only; no in-game /whitelist node +WhitelistCommand.reload port crates/horizon-client/src/game/dedicated.rs::Game::console_whitelist reload arm +WhitelistCommand.addPlayers port crates/horizon-client/src/game/dedicated.rs::Game::console_whitelist gap: minor: one name per command, not a target list +WhitelistCommand.removePlayers port crates/horizon-client/src/game/dedicated.rs::Game::console_whitelist gap: minor: one name per command, not a target list +WhitelistCommand.enableWhitelist port crates/horizon-client/src/game/dedicated.rs::Game::console_whitelist on arm +WhitelistCommand.disableWhitelist port crates/horizon-client/src/game/dedicated.rs::Game::console_whitelist off arm +WorldBorderCommand.setDamageBuffer port crates/horizon-client/src/game/commands/terrain_cmds.rs::cmd_worldborder damage buffer arm +WorldBorderCommand.setDamageAmount port crates/horizon-client/src/game/commands/terrain_cmds.rs::cmd_worldborder damage amount arm +WorldBorderCommand.setWarningTime port crates/horizon-client/src/game/commands/terrain_cmds.rs::cmd_worldborder warning time arm +WorldBorderCommand.setWarningDistance port crates/horizon-client/src/game/commands/terrain_cmds.rs::cmd_worldborder warning distance arm +WorldBorderCommand.getSize port crates/horizon-client/src/game/commands/terrain_cmds.rs::cmd_worldborder worldborder.get arm +WorldBorderCommand.formatTicksToSeconds port crates/horizon-client/src/game/commands/terrain_cmds.rs::ticks_to_seconds %.2f of ticks / 20 +BlockDataAccessor.setData port crates/horizon-client/src/game/commands/data.rs::Game::set_data_of block arm +BlockDataAccessor.getModifiedSuccess port crates/horizon-client/src/game/commands/data.rs::Game::modified_text block message +BlockDataAccessor.getPrintSuccess port crates/horizon-client/src/game/commands/data.rs::Game::query_text block query message +DataCommands.stringifyTagList inline crates/horizon-client/src/game/commands/data.rs::Game::cmd_data_modify string branch of modify +DataCommands.validatedSubstring port crates/horizon-client/src/data_cmd.rs::substring +DataCommands.resolveSourcePath inline crates/horizon-client/src/game/commands/data.rs::Game::cmd_data_modify sourcePath resolution +DataCommands.manipulateData port crates/horizon-client/src/game/commands/data.rs::Game::cmd_data_modify modify arm +DataCommands.removeData port crates/horizon-client/src/game/commands/data.rs::Game::cmd_data_in data.remove arm +DataCommands.getData port crates/horizon-client/src/data_cmd.rs::get_value count of list, compound, string or numeric +DataCommands.getNumeric port crates/horizon-client/src/data_cmd.rs::scaled_value floor of numeric times scale +DataCommands.mergeData port crates/horizon-client/src/game/commands/data.rs::Game::cmd_data_in data.merge arm +net/minecraft/server/commands/data/EntityDataAccessor#setData port crates/horizon-client/src/game/commands/data.rs::Game::apply_entity_nbt refuses players +net/minecraft/server/commands/data/EntityDataAccessor#getModifiedSuccess port crates/horizon-client/src/game/commands/data.rs::Game::modified_text entity message +net/minecraft/server/commands/data/EntityDataAccessor#getPrintSuccess port crates/horizon-client/src/game/commands/data.rs::Game::query_text entity query message +StorageDataAccessor.getModifiedSuccess port crates/horizon-client/src/game/commands/data.rs::Game::modified_text storage message +StorageDataAccessor.getPrintSuccess port crates/horizon-client/src/game/commands/data.rs::Game::query_text storage query message +BlockItemAccessor.getBlockSlots inline crates/horizon-client/src/game/commands/items.rs::Game::slots_named block owner branch +BlockItemAccessor.setItems inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item block write +BlockItemAccessor.getReplaceSuccess inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item block replace feedback +BlockItemAccessor.getContainer inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item not_a_container check +EntityItemAccessor.getSlots port crates/horizon-client/src/game/commands/items.rs::Game::slots_named entity owner branch +EntityItemAccessor.setItems inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item entity write +EntityItemAccessor.getReplaceSuccess inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item entity replace feedback +ItemCommands.wrapSetItems port crates/horizon-command/src/tree_misc.rs::item /item tree +ItemCommands.setItems port crates/horizon-client/src/game/commands/items.rs::Game::cmd_item item replace +ItemCommands.modifyItems port crates/horizon-client/src/game/commands/items.rs::Game::cmd_item_modify item modify +ItemCommands.getItems inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item from-source branch +ItemCommands.getSlotsFromProvider port crates/horizon-client/src/game/commands/items.rs::Game::slots_named slot name matching +DedicatedPlayerList.saveIpBanList port crates/horizon-server-files/src/user_lists.rs::UserList::save ban and list saves go through UserList::save +DedicatedPlayerList.saveUserBanList port crates/horizon-server-files/src/user_lists.rs::UserList::save +DedicatedPlayerList.loadIpBanList port crates/horizon-server-files/src/user_lists.rs::UserList::load +DedicatedPlayerList.loadUserBanList port crates/horizon-server-files/src/user_lists.rs::UserList::load +DedicatedPlayerList.saveOps port crates/horizon-server-files/src/user_lists.rs::UserList::save +DedicatedPlayerList.loadWhiteList port crates/horizon-server-files/src/user_lists.rs::UserList::load startup and console reload +DedicatedPlayerList.saveWhiteList port crates/horizon-server-files/src/user_lists.rs::UserList::save +DedicatedPlayerList.isWhiteListed port crates/horizon-client/src/game/dedicated.rs::Lists::whitelisted diff --git a/tools/port_map/rest-017.tsv b/tools/port_map/rest-017.tsv new file mode 100644 index 0000000000..5a5e2d0d22 --- /dev/null +++ b/tools/port_map/rest-017.tsv @@ -0,0 +1,107 @@ +DedicatedServer.readCodeOfConducts missing player: with enable-code-of-conduct=true a joining player gets no code of conduct; the codeofconduct folder is never read +DedicatedServer.setEnforceWhitelist skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.setUsingWhitelist port crates/horizon-client/src/game/dedicated.rs::Game::console_whitelist console whitelist on|off; store_property writes white-list back +DedicatedServer.saveAllChunks port crates/horizon-client/src/game.rs::Game::save_game gap: minor: no save notifications to the JSON-RPC management API +DedicatedServer.sendLowDiskSpaceWarning missing minor: admin-level players get no low disk space warning; LOW_DISK_SPACE_WARNING exists only as a packet id in ids.rs +DedicatedServer.setAllowFlight skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.setDifficulty port crates/horizon-client/src/game/commands.rs::Game::cmd_difficulty the /difficulty command; gap: minor: the new value is not written back to server.properties +DedicatedServer.setViewDistance skip:server JSON-RPC management API only; view-distance is read at start +DedicatedServer.setSimulationDistance skip:server JSON-RPC management API only; simulation-distance is read at start +DedicatedServer.fillServerSystemReport skip:debug adds rows to the system report of a server crash report; Horizon writes no server crash report +DedicatedServer.dumpServerProperties skip:debug writes server.properties.txt into the debug report only +DedicatedServer.onServerExit inline crates/horizon-client/src/game/dedicated.rs::Game::stop_dedicated gap: minor: closes no RCON, query, GUI or JSON-RPC; the LAN pinger and listener are closed by lan_close +DedicatedServer.setMaxPlayers skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.setSpawnProtectionRadius skip:server JSON-RPC management API only; spawn-protection is read at start and used by Game::under_spawn_protection +DedicatedServer.setRepliesToStatus skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.setHidesOnlinePlayers skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.setOperatorUserPermissions skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.setPlayerIdleTimeout missing minor: no /setidletimeout command in Horizon's command tree; player-idle-timeout is read once at start +DedicatedServer.setStatusHeartbeatInterval skip:server JSON-RPC management API only (status heartbeat) +DedicatedServer.setMotd skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.enforceSecureProfile inline crates/horizon-client/src/game/dedicated.rs::Game::run_dedicated gap: minor: services.canValidateProfileKeys() is not part of the check +DedicatedServer.convertOldUsers skip:other one-time import of pre-1.7 .txt user lists; Horizon reads only the json lists in user_lists.rs +DedicatedServer.runCommand skip:server RCON only; Horizon has no RCON (the stdin console is Game::console_command) +DedicatedServer.setEntityBroadcastRangePercentage skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.createTextFilterForPlayer skip:service external chat filter service; Horizon has no text filter +DedicatedServer.setForceGameMode skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.setGameMode port crates/horizon-client/src/game/lan.rs::Game::set_default_game_mode the /defaultgamemode command; gap: minor: the value is not written back to server.properties +DedicatedServer.setAcceptsTransfers skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.setPauseWhenEmptySeconds skip:server JSON-RPC management API only (ServerSettingsService); Horizon has no such API +DedicatedServer.createServerLinks inline crates/horizon-client/src/game/dedicated.rs::Game::run_dedicated builds the bug-report server link from bug-report-link (dedicated.rs 636-646) +DedicatedServer.parseBugReportLink port crates/horizon-client/src/game/server_links.rs::valid_untrusted_uri a yes or no check of the link; the caller builds the link in run_dedicated +DedicatedServerProperties.getDatapackConfig inline crates/horizon-server-files/src/properties.rs::ServerProperties::new splits initial-enabled-packs and initial-disabled-packs +DedicatedServerProperties.deserializePermission port crates/horizon-server-files/src/properties.rs::Reader::permission gap: minor: numbers clamp to 0 to 4; PermissionLevel.byId is not compared +DedicatedServerProperties.WorldDimensionData.create port crates/horizon-client/src/game/dedicated.rs::Game::write_world_gen gap: minor: only the preset pick by level-type is mapped; the flat-settings replacement is not compared +ServerWatchdog.run missing minor: max-tick-time is read (properties.rs) but no watchdog runs; a stuck tick is never reported or stopped +ServerWatchdog.createWatchdogCrashReport missing minor: builds the thread-dump crash report for the watchdog only; Horizon has no watchdog +ServerWatchdog.exit missing minor: halts the process 10 s after a stuck tick (watchdog only) +Settings.dispatchNumberOrString inline crates/horizon-server-files/src/properties.rs::ServerProperties::new the difficulty parse in ServerProperties::new does this job inline +Settings.getLegacy port crates/horizon-server-files/src/properties.rs::Reader::legacy returns the raw value and removes the key; the caller parses it +Settings.get port crates/horizon-server-files/src/properties.rs::Reader::get reads with a default and writes the value back to the properties map +Settings.getMutable port crates/horizon-server-files/src/properties.rs::Reader::get gap: minor: no mutable handle; ServerProperties::update changes one key instead +Dialogs.bootstrap skip:data registry bootstrap of the built-in dialogs; Horizon builds its dialogs in code +Action.ValueGetter.of skip:other fixed-string overload with no vanilla caller in the sources (only the Supplier overload is used); live values come from Control::substitution +CommandTemplate.createAction port crates/horizon-client/src/dialog.rs::Action::click the RunCommand arm: substitute over the input values +CustomAll.createAction port crates/horizon-client/src/dialog.rs::Action::click the Custom arm: additions plus each input's tag +ParsedTemplate.parse inline crates/horizon-client/src/dialog.rs::Action::parse gap: minor: the dynamic/run_command template is kept without a StringTemplate syntax check +DialogBodyTypes.bootstrap skip:data registers the item and plain_message body codecs; Body::parse dispatches them in dialog.rs +NumberRangeInput.computeLabel missing unsure: label_format is parsed (dialog.rs) but no code formats it, so the slider value label may be missing +NumberRangeInput.RangeInfo.isOutOfRange inline crates/horizon-client/src/dialog.rs::Control::scaled the range test that keeps the snapped value inside the slider +NumberRangeInput.RangeInfo.initialScaledValue inline crates/horizon-client/src/dialog.rs::Control::scaled the first value (initial or the midpoint) used by the step snap +MinecraftServerGui.showFrameFor skip:server server window (Swing); Horizon has no server GUI +MinecraftServerGui.buildInfoPanel skip:server server window (Swing); Horizon has no server GUI +MinecraftServerGui.buildPlayerPanel skip:server server window (Swing); Horizon has no server GUI +MinecraftServerGui.buildChatPanel skip:server server window (Swing); Horizon has no server GUI +MinecraftServerGui.print skip:server server window (Swing); Horizon has no server GUI +PlayerListComponent.tick skip:server server window (Swing); Horizon has no server GUI +StatsComponent.tick skip:server server window (Swing); Horizon has no server GUI +StatsComponent.paint skip:server server window (Swing); Horizon has no server GUI +EventLoopGroupHolder.createThreadFactory skip:java Netty thread factory; Horizon uses std threads +EventLoopGroupHolder.eventLoopGroup skip:java lazy Netty event loop group; Horizon has no Netty +Filterable.codec skip:data NBT codec for the raw and filtered pair; Horizon keeps only the raw text +Filterable.from inline crates/horizon-protocol/src/item.rs::filterable gap: minor: the filtered copy is read and dropped; only the raw text is kept +Filterable.get skip:service returns the filtered copy, which only the external text filter makes; Horizon uses the raw text +Filterable.map skip:java pure helper over the raw and filtered texts +Filterable.resolve skip:java pure helper over the raw and filtered texts +LegacyProtocolUtils.writeLegacyString inline crates/horizon-client/src/lan/conn.rs::serve_legacy the length-prefixed UTF-16BE string of the reply +LegacyProtocolUtils.readLegacyString inline crates/horizon-client/src/lan/conn.rs::serve_legacy the in_string reader of the 1.6 payload +LegacyQueryHandler.channelRead port crates/horizon-client/src/lan/conn.rs::serve_legacy gap: minor: runs on the first byte peeked by serve(), not as a Netty handler; replies only while status is on +LegacyQueryHandler.readCustomPayloadPacket inline crates/horizon-client/src/lan/conn.rs::serve_legacy the MC|PingHost check and its length rules +LegacyQueryHandler.createVersion0Response inline crates/horizon-client/src/lan/conn.rs::serve_legacy the motd, count and max reply when only 0xFE arrived +LegacyQueryHandler.createVersion1Response inline crates/horizon-client/src/lan/conn.rs::serve_legacy the 0xFF-marked version reply with protocol 127 +LegacyQueryHandler.createLegacyDisconnectPacket inline crates/horizon-client/src/lan/conn.rs::serve_legacy the 0xFF packet with the length-prefixed string +LegacyTextFilter.createTextFilterFromConfig skip:service external chat filter (text-filtering-config); Horizon has no text filter +LegacyTextFilter.processJoinOrLeave skip:service external chat filter service; Horizon has no text filter +LegacyTextFilter.processRequest skip:service external chat filter service; Horizon has no text filter +LegacyTextFilter.filterText skip:service external chat filter service; Horizon has no text filter +MemoryServerHandshakePacketListenerImpl.handleIntention inline crates/horizon-client/src/lan/conn.rs::serve_connection gap: minor: the in-memory connection accepts every intention; vanilla throws for anything but login +PlayerChunkSender.sendChunk inline crates/horizon-client/src/lan/hub.rs::Hub::update_chunks gap: minor: the debug synchronizer and the chunk-sent event are not ported +PlayerChunkSender.collectChunksToSend port crates/horizon-client/src/lan/hub.rs::Hub::next_batch closest first within the batch quota; gap: minor: the in-memory player is cut to the quota too, vanilla sends it all pending chunks +PlayerSafetyServiceTextFilter.createTextFilterFromConfig skip:service external chat filter service (Microsoft identity, text-filtering-config); Horizon has no text filter +PlayerSafetyServiceTextFilter.filterText skip:service external chat filter service; Horizon has no text filter +ServerCommandSuggestionsProvider.tick inline crates/horizon-client/src/game/lan_actions.rs::Game::guest_suggestions gap: minor: no per-tick budget; every request is answered when it arrives +ServerCommandSuggestionsProvider.tryProcessRequest port crates/horizon-client/src/game/lan_actions.rs::Game::guest_suggestions parses the command (after an optional slash) and gets its suggestions +ServerCommandSuggestionsProvider.limitSuggestionCount missing minor: vanilla keeps the first 1000 suggestions; Horizon sends them all +ServerCommandSuggestionsProvider.trySchedule inline crates/horizon-client/src/game/lan_actions.rs::Game::guest_suggestions gap: minor: no hop to the server thread; the request is handled on its packet +ServerCommandSuggestionsProvider.request port crates/horizon-client/src/game/lan_actions.rs::Game::guest_suggestions gap: minor: no coalescing of rapid requests; vanilla keeps only the newest text +ServerCommonPacketListenerImpl.onDisconnect inline crates/horizon-client/src/game/integrated.rs::Integrated::halt gap: minor: unsure: whether a dropped local connection halts the server is not checked +ServerCommonPacketListenerImpl.onPacketError missing unsure: vanilla records the exception for the crash report (reportPacketHandlingException); Horizon keeps no record of a failing packet +ServerCommonPacketListenerImpl.handleKeepAlive inline crates/horizon-client/src/lan/conn.rs::write_loop gap: minor: read_loop stores the id and write_loop sets the latency; a wrong id is not an instant disconnect +ServerCommonPacketListenerImpl.handleCustomClickAction skip:debug vanilla only logs at debug (the NeoForge hook is mod API); Horizon drops the packet +ServerCommonPacketListenerImpl.keepConnectionAlive port crates/horizon-client/src/lan/conn.rs::write_loop gap: minor: times out after 30 s without an answer; vanilla after 15 s +ServerCommonPacketListenerImpl.checkIfClosed inline crates/horizon-client/src/lan/conn.rs::write_loop gap: minor: the 15 s close timeout is covered by the 30 s keep-alive timeout +ServerCommonPacketListenerImpl.createCookie inline crates/horizon-client/src/lan/conn.rs::login_and_configure builds the Parts (profile, client information, brand); latency and transferred are not kept +ServerConfigurationPacketListenerImpl.returnToWorld inline crates/horizon-client/src/lan/conn.rs::login_and_configure queues the join steps after configuration; play() takes over in Horizon +ServerConfigurationPacketListenerImpl.addOptionalTasks inline crates/horizon-client/src/lan/conn.rs::login_and_configure gap: player: no code of conduct task (enable-code-of-conduct); the resource pack offer is there, mod tasks are not +ServerConfigurationPacketListenerImpl.handleResourcePackResponse inline crates/horizon-client/src/lan/conn.rs::offer_pack the pack answer loop; a terminal answer ends the pack step +ServerConfigurationPacketListenerImpl.handleSelectKnownPacks inline crates/horizon-client/src/lan/conn.rs::login_and_configure reads the client's known packs and sends the registry data for them +ServerConfigurationPacketListenerImpl.handleConfigurationFinished inline crates/horizon-client/src/lan/conn.rs::login_and_configure the FinishConfiguration wait; the join checks run in serve_login +ServerConfigurationPacketListenerImpl.startNextTask inline crates/horizon-client/src/lan/conn.rs::login_and_configure the configuration steps run in order, with no task queue +ServerConfigurationPacketListenerImpl.finishCurrentTask inline crates/horizon-client/src/lan/conn.rs::login_and_configure the configuration steps run in order, with no task queue +ServerConnectionListener.stop port crates/horizon-client/src/lan/mod.rs::Lan::stop gap: minor: stops the listener and sets the shared stop flag; channels are not closed one by one +ServerConnectionListener.stopTcpServerListener port crates/horizon-client/src/lan/mod.rs::Lan::stop_listening closes the network listener; connections already in, the local one included, stay +ServerConnectionListener.tick inline crates/horizon-client/src/lan/mod.rs::Lan::poll gap: minor: reports Left events; no Internal server error disconnect when a packet fails +ServerConnectionListener.addPendingConnections skip:java queue of accepted sockets; Horizon starts one thread per connection +ServerConnectionListener.getSessionId missing minor: vanilla sends one random session id per server run in login finished; Horizon sends the player's uuid +ServerConnectionListener.LatencySimulator.delayDownstream skip:debug dev latency simulator behind SharedConstants.DEBUG_FAKE_LATENCY_MS +ServerConnectionListener.LatencySimulator.onTimeout skip:debug dev latency simulator behind SharedConstants.DEBUG_FAKE_LATENCY_MS diff --git a/tools/port_map/rest-018.tsv b/tools/port_map/rest-018.tsv new file mode 100644 index 0000000000..f6c41fbcce --- /dev/null +++ b/tools/port_map/rest-018.tsv @@ -0,0 +1,147 @@ +ServerGamePacketListenerImpl.getMaximumFlyingTicks missing player: the flying kick (multiplayer.disconnect.flying) for hovering above ground is never checked; the tick limit is not computed +ServerGamePacketListenerImpl.resetPosition inline crates/horizon-client/src/lan/hub.rs::Hub::process_inbox gap: minor: only firstGood is reset at the start of each inbox pass; lastGood is not kept and teleports do not reset it +ServerGamePacketListenerImpl.isAcceptingMessages skip:other the waiting-for-configuration flag does not exist: the host never starts a configuration phase mid-play +ServerGamePacketListenerImpl.shouldHandleMessage skip:other the waiting-for-configuration flag does not exist: the host never starts a configuration phase mid-play +ServerGamePacketListenerImpl.filterTextPacket skip:service text filter / notification service: the LAN host wires no such service +ServerGamePacketListenerImpl.handlePlayerInput port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox PlayerInput arm: keys stored, sneaking set, action time reset once the guest is loaded +ServerGamePacketListenerImpl.containsInvalidValues port crates/horizon-client/src/lan/hub.rs::Hub::move_player NaN and non-finite rotation test, kick with invalid_player_movement; process_inbox runs the same test for AcceptTeleportation +ServerGamePacketListenerImpl.noBlocksAround missing player: the air-below test of the flying check is never run, so a player hovering over empty air is not checked +ServerGamePacketListenerImpl.handleAcceptTeleportPacket port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox gap: minor: an accept with the right id but no teleport awaited is ignored; vanilla disconnects +ServerGamePacketListenerImpl.handleRecipeBookSeenRecipePacket missing minor: the server never clears the new-recipe highlight (RECIPE_BOOK_SEEN_RECIPE has no handler) +ServerGamePacketListenerImpl.handleBundleItemSelectedPacket port crates/horizon-client/src/lan/containers.rs::Hub::select_bundle_item the spectator check is in the SelectBundleItem arm of lan_guest_packet +ServerGamePacketListenerImpl.handleSeenAdvancements missing minor: the advancement tab a player last opened is not stored on the host (SEEN_ADVANCEMENTS has no handler) +ServerGamePacketListenerImpl.handleSetCommandMinecart missing player: a command minecart's command cannot be saved from its edit screen (SET_COMMAND_MINECART has no handler) +ServerGamePacketListenerImpl.handlePickItemFromBlock port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet the reach check and block pick are in the PickItemFromBlock arm; the pick itself is guest_pick_item +ServerGamePacketListenerImpl.handlePickItemFromEntity port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet gap: minor: the op's fetchprofile printout with includeData is not sent; Horizon prints no profiles +ServerGamePacketListenerImpl.handleSetGameRule port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet SetGameRule arm: permission 2 or more, unknown rules ignored, rules synced +ServerGamePacketListenerImpl.setGameRuleValue inline crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet the rule set and the op broadcast are in the SetGameRule arm +ServerGamePacketListenerImpl.broadcastGameRuleChangeToOperators inline crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet the operator message is sent in the SetGameRule arm to guests with permission 2 or more +ServerGamePacketListenerImpl.handleSetStructureBlock missing player: a creative player's structure block edits (save, load, scan) do nothing; SET_STRUCTURE_BLOCK has no handler +ServerGamePacketListenerImpl.handleSetTestBlock missing player: test block edits do not save; SET_TEST_BLOCK has no handler +ServerGamePacketListenerImpl.handleTestInstanceBlockAction missing player: test instance block actions (save, run, reset, export) do nothing; TEST_INSTANCE_BLOCK_ACTION has no handler +ServerGamePacketListenerImpl.handleSetJigsawBlock missing player: jigsaw block edits do not save; SET_JIGSAW_BLOCK has no handler +ServerGamePacketListenerImpl.handleJigsawGenerate missing player: the jigsaw block's Generate does nothing; JIGSAW_GENERATE has no handler +ServerGamePacketListenerImpl.handleSelectTrade port crates/horizon-client/src/game/lan_trading.rs::Game::lan_select_trade +ServerGamePacketListenerImpl.updateBookContents port crates/horizon-client/src/game/books.rs::Game::write_book writes the pages into the writable book in the slot +ServerGamePacketListenerImpl.filterableFromOutgoing skip:service text filter pass-through: with no filter on the LAN host the page is the raw text +ServerGamePacketListenerImpl.handleEntityTagQuery missing player: an op's entity NBT query (ENTITY_TAG_QUERY) gets no reply +ServerGamePacketListenerImpl.handleBlockEntityTagQuery missing player: an op's block entity NBT query (BLOCK_ENTITY_TAG_QUERY) gets no reply +ServerGamePacketListenerImpl.shouldCheckPlayerMovement inline crates/horizon-client/src/lan/hub.rs::Hub::position_change gap: minor: the changing-dimension exemption is not checked; the single-player owner, movement and elytra gates are +ServerGamePacketListenerImpl.forceSendPlayerSupportBlocks missing minor: the blocks under a player standing on air are never sent again (vanilla's desync fix) +ServerGamePacketListenerImpl.updateAwaitingTeleport port crates/horizon-client/src/lan/hub.rs::Hub::move_player gap: minor: vanilla runs it every tick; here it runs only when a move packet arrives +ServerGamePacketListenerImpl.isEntityCollidingWithAnythingNew port crates/horizon-client/src/lan/hub.rs::Hub::collide +ServerGamePacketListenerImpl.wasBlockPlacementAttempt missing minor: only decides the build-limit message, which is not sent (see handleUseItemOn) +ServerGamePacketListenerImpl.handleUseItemOn port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet gap: player: the build-limit message is never sent when placing at the top or bottom of the world +ServerGamePacketListenerImpl.handleUseItem port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet UseItem arm: rotation taken from the packet, then the item is used +ServerGamePacketListenerImpl.handlePaddleBoat port crates/horizon-client/src/game/lan_vehicles.rs::Game::guest_paddle_boat +ServerGamePacketListenerImpl.onDisconnect port crates/horizon-client/src/lan/hub.rs::Hub::remove_guest the reason is kept and the leave is broadcast +ServerGamePacketListenerImpl.handleSetCarriedItem port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet SetCarriedItem arm: hotbar slot set, using item cleared +ServerGamePacketListenerImpl.performUnsignedChatCommand port crates/horizon-client/src/game/lan_chat.rs::Game::guest_chat_command +ServerGamePacketListenerImpl.performSignedChatCommand port crates/horizon-client/src/game/lan_chat.rs::Game::guest_signed_command +ServerGamePacketListenerImpl.collectUnsignedArguments port crates/horizon-client/src/lan/hub/chat.rs::Hub::signed_arguments the empty-arguments branch of signed_arguments +ServerGamePacketListenerImpl.createSignedArgumentMismatchException inline crates/horizon-client/src/lan/hub/chat.rs::Hub::signed_arguments the mismatch error and log are in signed_arguments +ServerGamePacketListenerImpl.parseCommand port crates/horizon-client/src/game/lan_chat.rs::Game::parse_as parse with the guest's permission level +ServerGamePacketListenerImpl.isChatMessageIllegal port crates/horizon-client/src/lan/hub/chat.rs::Hub::try_handle_chat per-character allowed test; kicks with illegal_characters +ServerGamePacketListenerImpl.getSignedMessage port crates/horizon-client/src/lan/hub/chat.rs::Hub::handle_chat the body is unpacked in handle_chat +ServerGamePacketListenerImpl.detectRateSpam missing player: a player who spams chat or commands is never kicked (disconnect.spam); chat-spam-threshold-seconds is read but not used +ServerGamePacketListenerImpl.switchToConfig skip:other the host never starts a configuration phase mid-play +ServerGamePacketListenerImpl.handleAttack port crates/horizon-client/src/game/lan_actions.rs::Game::guest_attack gap: minor: no kick for attacking an item, XP orb, self or a non-attackable arrow; the piercing-weapon skip is not in this fn +ServerGamePacketListenerImpl.handleSpectatorAction missing player: a spectator's entity camera choice (SPECTATOR_ACTION) has no handler and SET_CAMERA is never sent +ServerGamePacketListenerImpl.sendGameRuleValues port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet ClientCommand action 2 arm: the game rule list for ops +ServerGamePacketListenerImpl.addGameRuleValue inline crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet the value list is built in the same arm +ServerGamePacketListenerImpl.handleSetCreativeModeSlot port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet gap: minor: slot 0 (craft result) is accepted where vanilla takes 1 to 45, and the stack max is not checked for a set +ServerGamePacketListenerImpl.updateSignText inline crates/horizon-client/src/game/sign_edit.rs::Game::lan_sign_update the sign write is in lan_sign_update +ServerGamePacketListenerImpl.handlePlayerAbilities port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox PlayerAbilities arm: flying only when the guest may fly +ServerGamePacketListenerImpl.handleClientInformation port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox gap: player: the hat-layer change is not sent to the other players (UPDATE_HAT) +ServerGamePacketListenerImpl.handleChangeDifficulty port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet ChangeDifficulty arm: permission 2 or more, not locked, 0 to 3 +ServerGamePacketListenerImpl.handleChangeGameMode port crates/horizon-client/src/game/debug_keys.rs::Game::change_game_mode reached from the ChangeGameMode arm, which checks permission 2 or more +ServerGamePacketListenerImpl.handleLockDifficulty port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet LockDifficulty arm: permission 2 or more +ServerGamePacketListenerImpl.handleConfigurationAcknowledged skip:other the host never starts a configuration phase mid-play, so there is no acknowledgement to wait for +ServerGamePacketListenerImpl.handleChunkBatchReceived port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox ChunkBatchReceived arm: updates the send quota +ServerGamePacketListenerImpl.handleDebugSubscriptionRequest skip:debug F3 debug subscriptions are not implemented by the LAN host +ServerGamePacketListenerImpl.resetPlayerChatState port crates/horizon-client/src/lan/hub/chat.rs::Hub::chat_session_update gap: minor: the chain swap is immediate and not queued behind pending chat; INITIALIZE_CHAT is broadcast +ServerGamePacketListenerImpl.handleClientTickEnd port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox ClientTickEnd arm clears the per-tick movement flag; known movement is the known_moves copy taken at the start of the pass +ServerGamePacketListenerImpl.handlePlayerKnownMovement port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox a move that goes far enough resets the action time +ServerHandshakePacketListenerImpl.handleIntention port crates/horizon-client/src/lan/conn.rs::serve_connection LOGIN, STATUS and TRANSFER branches; a protocol mismatch sends a disconnect +ServerLoginPacketListenerImpl.tick port crates/horizon-client/src/lan/conn.rs::serve_connection gap: minor: the 600-tick slow-login kick becomes a 30 s socket read timeout that closes without a message +ServerLoginPacketListenerImpl.isPlayerAlreadyInWorld port crates/horizon-client/src/lan/mod.rs::Shared::try_admit the online check and the wait for a duplicate profile to leave +ServerLoginPacketListenerImpl.handleHello port crates/horizon-client/src/lan/conn.rs::serve_login name check, then authenticate online or derive the offline id +ServerLoginPacketListenerImpl.verifyLoginAndFinishConnectionSetup port crates/horizon-client/src/lan/mod.rs::Shared::try_admit gap: minor: canPlayerLogin and the duplicate wait are here; compression is sent in login_and_configure +ServerLoginPacketListenerImpl.finishLoginAndWaitForClient port crates/horizon-client/src/lan/conn.rs::login_and_configure sends LoginFinished +ServerLoginPacketListenerImpl.handleLoginAcknowledgement port crates/horizon-client/src/lan/conn.rs::login_and_configure reads the login acknowledgement, then the configuration phase +ServerStatusPacketListenerImpl.handleStatusRequest port crates/horizon-client/src/lan/conn.rs::serve_status a second request closes the connection +ServerStatusPacketListenerImpl.handlePingRequest port crates/horizon-client/src/lan/conn.rs::serve_status pong, then close +ServerTextFilter.getEndpoint skip:service text filter / notification service: the LAN host wires no such service +ServerTextFilter.createFromConfig skip:service text filter / notification service: the LAN host wires no such service +ServerTextFilter.requestMessageProcessing skip:service text filter / notification service: the LAN host wires no such service +ServerTextFilter.parseMask skip:service text filter / notification service: the LAN host wires no such service +ServerTextFilter.processRequestResponse skip:service text filter / notification service: the LAN host wires no such service +ServerTextFilter.makeRequest skip:service text filter / notification service: the LAN host wires no such service +ServerTextFilter.getURLConnection skip:service text filter / notification service: the LAN host wires no such service +ServerTextFilter.PlayerContext.processMessageBundle skip:service text filter / notification service: the LAN host wires no such service +ServerTextFilter.PlayerContext.processStreamMessage skip:service text filter / notification service: the LAN host wires no such service +PrepareSpawnTask.start port crates/horizon-client/src/game/lan.rs::Game::lan_guest_joined gap: minor: no wait for the spawn chunks; the saved state is applied and a new player is placed by server_player_spawn +PrepareSpawnTask.tick missing minor: no spawn-chunk wait before a joining player is placed (see Preparing.tick) +PrepareSpawnTask.spawnPlayer inline crates/horizon-client/src/game/lan.rs::Game::lan_guest_joined the load and the placement run here, through place_new_player +PrepareSpawnTask.keepAlive skip:other no preparing state to hold spawn chunks for +PrepareSpawnTask.close skip:other no preparing state to cancel +PrepareSpawnTask.Preparing.tick missing minor: no spawn-chunk load (radius 3) and no LOAD_PLAYER_CHUNKS progress before the join +PrepareSpawnTask.Ready.keepAlive skip:other no waiting state to hold spawn chunks for +PrepareSpawnTask.Ready.spawn port crates/horizon-client/src/lan/hub.rs::Hub::place_new_player the saved state is loaded in lan_guest_joined; placeNewPlayer is here +ServerResourcePackConfigurationTask.start port crates/horizon-client/src/lan/conn.rs::offer_pack sends ResourcePackPush; the answer is awaited +SynchronizeRegistriesTask.sendRegistries port crates/horizon-client/src/lan/data.rs::SyncData::registry_packets registry data; the tags are sent by login_and_configure +SynchronizeRegistriesTask.handleResponse port crates/horizon-client/src/lan/conn.rs::login_and_configure known-pack answer: only the core pack is offered, so it is all or the core pack +NotificationManager.setServer skip:service external notification services for dedicated servers +NotificationManager.onGameRuleChanged skip:service external notification services for dedicated servers +ServerActivityMonitor.processWithRateLimit skip:service external notification services for dedicated servers +AbstractPackMetadataResources.getMetadataSection skip:data pack file access; Horizon reads its data from tables and files +AbstractPackMetadataResources.loadMetadata skip:data pack file access; Horizon reads its data from tables and files +DownloadCacheCleaner.listFilesWithModificationTimes inline crates/horizon-client/src/server_packs/download.rs::vacuum the folder walk and modification times are in vacuum +DownloadCacheCleaner.prioritizeFilesInDirs inline crates/horizon-client/src/server_packs/download.rs::vacuum the per-folder rank is in vacuum +DownloadQueue.getFileInfo port crates/horizon-client/src/server_packs/download.rs::run the size and relative name of each downloaded file are in the log entry built by run +DownloadQueue.downloadBatch port crates/horizon-client/src/server_packs/download.rs::Queue::download queues the batch for the download thread, which runs run +FilePackResources.getPathFromLocation skip:data pack file access; Horizon reads its data from tables and files +FilePackResources.getResource skip:data pack file access; Horizon reads its data from tables and files +FilePackResources.addPrefix skip:data pack file access; Horizon reads its data from tables and files +FilePackResources.getNamespaces skip:data pack file access; Horizon reads its data from tables and files +FilePackResources.extractNamespace skip:data pack file access; Horizon reads its data from tables and files +FilePackResources.listResources skip:data pack file access; Horizon reads its data from tables and files +FilePackResources.FileResourcesSupplier.openMetadata skip:data pack file access; Horizon reads its data from tables and files +FilePackResources.FileResourcesSupplier.openResources skip:data pack file access; Horizon reads its data from tables and files +FilePackResources.SharedZipFileAccess.getOrCreateZipFile skip:java lazily opened zip cache +FixedPathPackResources.getRootResource skip:data pack file access; Horizon reads its data from tables and files +FixedPathPackResources.listRawPaths skip:data pack file access; Horizon reads its data from tables and files +FixedPathPackResources.listResources skip:data pack file access; Horizon reads its data from tables and files +FixedPathPackResources.getResource skip:data pack file access; Horizon reads its data from tables and files +FixedPathPackResources.getMetadataSection skip:data pack file access; Horizon reads its data from tables and files +FixedPathPackResources.Builder.validateDirPath skip:java builder step; Horizon has no pack builder +FixedPathPackResources.Builder.pushPathForType skip:java builder step; Horizon has no pack builder +FixedPathPackResources.Builder.build skip:java builder step; Horizon has no pack builder +OverlayMetadataSection.validateOverlayDir skip:java codec validator for overlay directory names +OverlayMetadataSection.codecForPackType skip:java codec construction +OverlayMetadataSection.forPackType skip:java codec type lookup +OverlayMetadataSection.overlaysForVersion skip:data overlay selection by pack format; no pack stack in Horizon +OverlayMetadataSection.OverlayEntry.listCodecForPackType skip:java codec construction +OverlayedPackResources.getResource skip:data pack file access; Horizon reads its data from tables and files +OverlayedPackResources.listResources skip:data pack file access; Horizon reads its data from tables and files +OverlayedPackResources.getNamespaces skip:data pack file access; Horizon reads its data from tables and files +PathPackResources.getRootResource skip:data pack file access; Horizon reads its data from tables and files +PathPackResources.validatePath skip:debug DEBUG_VALIDATE_RESOURCE_PATH_CASE check +PathPackResources.getResource skip:data pack file access; Horizon reads its data from tables and files +PathPackResources.returnFileIfExists skip:data pack file access; Horizon reads its data from tables and files +PathPackResources.listResources skip:data pack file access; Horizon reads its data from tables and files +PathPackResources.listPath skip:data pack file access; Horizon reads its data from tables and files +PathPackResources.isRegularFile skip:debug IDE-only .DS_Store filter +PathPackResources.getNamespaces skip:data pack file access; Horizon reads its data from tables and files +PathPackResources.PathResourcesSupplier.openResources skip:data pack file access; Horizon reads its data from tables and files +VanillaPackResources.asResourceManager skip:data pack file access; Horizon reads its data from tables and files +VanillaPackResourcesBuilder.forLastLayer skip:java builder step; Horizon has no pack builder +VanillaPackResourcesBuilder.forAllLayers skip:java builder step; Horizon has no pack builder +VanillaPackResourcesBuilder.pushPathForType skip:java builder step; Horizon has no pack builder +VanillaPackResourcesBuilder.pushJarResources skip:data resource roots from the jar or classpath; Horizon reads its data from tables and files +VanillaPackResourcesBuilder.pushClasspathResources skip:data resource roots from the jar or classpath; Horizon reads its data from tables and files +VanillaPackResourcesBuilder.pushUniversalPath skip:data resource roots from the jar or classpath; Horizon reads its data from tables and files +VanillaPackResourcesBuilder.setMetadata skip:java builder step; Horizon has no pack builder +VanillaPackResourcesBuilder.exposeNamespace skip:java builder step; Horizon has no pack builder +VanillaPackResourcesBuilder.pushLayer skip:java builder step; Horizon has no pack builder +VanillaPackResourcesBuilder.build skip:java builder step; Horizon has no pack builder diff --git a/tools/port_map/rest-019.tsv b/tools/port_map/rest-019.tsv new file mode 100644 index 0000000000..22bdb92652 --- /dev/null +++ b/tools/port_map/rest-019.tsv @@ -0,0 +1,118 @@ +LinkFSPath skip:other Java path of the in-memory link filesystem; Horizon links host paths (packs.rs::link) +LinkFSProvider skip:other Java NIO provider of the link filesystem; Horizon reads host folders and zips directly +LinkFileSystem skip:other link filesystem and its getPath; Horizon builds overlay folders instead (packs.rs::build_overlay) +LinkFileSystem.Builder skip:other builder of the link filesystem tree; no equivalent, see packs.rs::build_overlay +PackFormat.fullCodec skip:java codec for supported_formats; packs.rs::compatible reads the same array +PackFormat.validateHolderList inline crates/horizon-client/src/packs.rs::overlays_of gap: minor: a broken overlay entry is dropped alone; vanilla rejects the whole overlays list +PackFormat.lastPreMinorVersion missing minor: the 64 and 81 cut-offs feed only the format validators, which Horizon lacks +PackFormat.packCodec skip:java codec wiring; the validation it runs is missing (IntermediaryFormat.validate) +PackFormat.of skip:java constructor; Horizon keeps (major, minor) tuples (packs.rs::RESOURCE_FORMAT) +PackFormat.compareTo skip:java ordering; Horizon compares the (major, minor) tuples directly (packs.rs::compatibility) +PackFormat.toString skip:debug text form of a format, used in validation messages only +PackFormat.IntermediaryFormat.fromRange skip:java encoder back to pack.mcmeta; Horizon never writes pack.mcmeta +PackFormat.IntermediaryFormat.effectiveMinMajorVersion missing minor: only the overlay validator uses it +PackFormat.IntermediaryFormat.validate missing player: vanilla refuses a pack.mcmeta with incomplete format fields (min_format without max_format, or pack_format outside the range); Horizon takes the ranges as written +PackFormat.IntermediaryFormat.validateNewFormat missing player: vanilla refuses min_format above max_format and a missing supported_formats or pack_format where an older game needs them; Horizon does not check +PackFormat.IntermediaryFormat.validateOldFormat missing player: vanilla refuses a supported_formats range above the last pre-minor version, or one without pack_format; Horizon does not check +PackFormat.IntermediaryFormat.validatePackFormatForRange missing player: vanilla refuses a pack_format outside its min to max range, or a multi-version pack below format 15; Horizon does not check +PackMetadataSection.codecForPackType skip:java codec for the pack section; packs.rs::pack reads the same fields +PackMetadataSection.forPackType skip:java picks the section type per pack type; Horizon reads one pack section +BuiltInPackSource.loadPacks port crates/horizon-client/src/packs.rs::discover built-in Programmer Art and High Contrast packs; the vanilla pack is added by game/packs.rs::pack_infos +BuiltInPackSource.listBundledPacks inline crates/horizon-client/src/packs.rs::discover the two bundled ids, found in the resources folder +BuiltInPackSource.discoverPacksInPath inline crates/horizon-client/src/packs.rs::discover bundled folder scan, folded into discover +FolderRepositorySource.loadPacks port crates/horizon-client/src/packs.rs::discover resourcepacks/ folder: folders and zips, sorted by id +FolderRepositorySource.createDiscoveredFilePackInfo inline crates/horizon-client/src/packs.rs::discover pack id file/ and the file name as its title +FolderRepositorySource.discoverPacks port crates/horizon-client/src/packs.rs::discover folder scan with pack detection +FolderRepositorySource.FolderPackDetector.createZipPack inline crates/horizon-client/src/packs.rs::discover zip packs by .zip name; the non-default filesystem check does not apply +Pack.readMetaAndCreate port crates/horizon-client/src/packs.rs::pack reads pack.mcmeta and makes the pack, none when it is missing +Pack.readPackMetadata inline crates/horizon-client/src/packs.rs::pack gap: minor: no required feature flags, no hidden flag, no fallback when the pack section fails to parse +Pack.equals skip:java identity by pack id; the selection model compares ids +PackDetector.detectPackResources inline crates/horizon-client/src/packs.rs::discover gap: minor: no symlink validation; a folder without pack.mcmeta is dropped by pack() +PackRepository.displayPackList skip:debug pack ids for the system report (MinecraftServer) +PackRepository.reload port crates/horizon-client/src/game/packs.rs::Game::refresh_pack_lists re-discovers the packs and resets the selection +PackRepository.discoverAvailable port crates/horizon-client/src/packs.rs::discover scans the sources for packs +PackRepository.isAbleToClearAnyPack missing player: when a pack fails at runtime vanilla clears the selected packs with a toast; Horizon keeps them and shows the error in Options (game/packs.rs::reload_packs) +PackRepository.addPack inline crates/horizon-client/src/packs.rs::SelectionModel::activate gap: minor: vanilla appends by id with no position rules +PackRepository.removePack inline crates/horizon-client/src/packs.rs::SelectionModel::activate gap: minor: vanilla removes by id even when the pack is required; the screen never offers that +PackRepository.getAvailablePacks inline crates/horizon-client/src/packs.rs::SelectionModel::rebuild gap: minor: rebuild also adds the required packs +PackRepository.getRequestedFeatureFlags missing minor: no pack feature flags are read, so a pack's requested features are never checked +PackSource.decorateWithSource port crates/horizon-client/src/pack_screen.rs::PackInfo::extended_description source label after the pack name (built-in, world, server) +ServerPacksSource.createVanillaPackSource port crates/horizon-client/src/datapack.rs::DataPack::builtin the built-in vanilla data source (id vanilla), used by functions.rs +ServerPacksSource.createPackRepository port crates/horizon-client/src/datapack.rs::discover gap: minor: finds world datapacks; the enable list is kept by functions.rs::reload_datapacks +ServerPacksSource.createVanillaTrustedRepository skip:other known-packs reply: Horizon answers SelectKnownPacks with no packs (net.rs), so the server sends every registry entry +FallbackResourceManager.push inline crates/horizon-client/src/packs.rs::build_overlay_uncached packs layered in load order, last wins +FallbackResourceManager.pushInternal inline crates/horizon-client/src/packs.rs::build_overlay_uncached same layering, with an optional content filter +FallbackResourceManager.getResource inline crates/horizon-client/src/packs.rs::build_overlay_uncached the top file per path; a filter stops the search +FallbackResourceManager.wrapForDebug skip:debug leak warning on unclosed resource streams +FallbackResourceManager.getResourceStack inline crates/horizon-client/src/packs.rs::build_overlay_uncached gap: minor: stacks merge only for lang, font, atlases and sounds.json (merge_kind) +FallbackResourceManager.getIdentifierFromMetadata skip:other maps a .mcmeta sidecar to its resource; each feature strips the suffix itself (ui.rs) +FallbackResourceManager.listResources inline crates/horizon-client/src/packs.rs::build_overlay_uncached gap: minor: sidecar metadata is not attached; features read their own .png.mcmeta +FallbackResourceManager.createStackMetadataFinder skip:other finds the top sidecar for a resource; each feature reads its own .png.mcmeta (locator_bar.rs) +FallbackResourceManager.parseMetadata skip:other parses a sidecar; each feature parses its own (ui.rs, mipmap.rs); pack.mcmeta goes through packs.rs::read_meta +FallbackResourceManager.applyPackFiltersToExistingResources inline crates/horizon-client/src/packs.rs::build_overlay_uncached a pack's filter drops files of the packs below it +FallbackResourceManager.listPackResources inline crates/horizon-client/src/packs.rs::build_overlay_uncached pack_files lists each pack's assets, overlay folders included +FallbackResourceManager.listResourceStacks inline crates/horizon-client/src/packs.rs::build_overlay_uncached gap: minor: same merge limit as getResourceStack +FallbackResourceManager.listPacks skip:java stream of the pack list; Horizon passes the slice +FallbackResourceManager.LeakedResourceWarningInputStream.finalize skip:debug leak warning when a stream is garbage collected unclosed +FallbackResourceManager.PackEntry.filterAll inline crates/horizon-client/src/packs.rs::build_overlay_uncached files and stacks are dropped when a filter blocks them +FallbackResourceManager.PackEntry.isFiltered port crates/horizon-client/src/packs.rs::Filter::blocks one pack's filter test +IoSupplier.create skip:java lambda over Files.newInputStream; Horizon reads files directly +MultiPackResourceManager. inline crates/horizon-client/src/packs.rs::build_overlay_uncached gap: minor: namespace-only filters are not kept; filters act on each file path +MultiPackResourceManager.getPackFilterSection port crates/horizon-client/src/packs.rs::filter_of reads the pack's filter section +MultiPackResourceManager.getResource skip:other lookups read the built overlay folder (packs.rs::resource_root); no manager object +MultiPackResourceManager.getResourceStack skip:other same overlay folder lookup +MultiPackResourceManager.listResources skip:other same overlay folder listing +MultiPackResourceManager.listResourceStacks skip:other same overlay folder listing +MultiPackResourceManager.checkTrailingDirectoryPath skip:java argument check for the resource listing API, which Horizon lacks +ProfiledReloadInstance skip:debug reload timing log (profiler), which Horizon does not keep +ReloadableResourceManager.createReload port crates/horizon-client/src/game/packs.rs::Game::stage_selected_packs builds the new overlay root and resources +Resource skip:other lazy stream and sidecar metadata; Horizon reads host files and each feature reads its own .png.mcmeta +ResourceFilterSection.isNamespaceFiltered inline crates/horizon-client/src/packs.rs::Filter::blocks gap: minor: no namespace-only test; blocks checks the namespace inside a file path +ResourceFilterSection.isPathFiltered port crates/horizon-client/src/packs.rs::Filter::blocks path test of a filter entry +ResourceManagerReloadListener.reload port crates/horizon-client/src/game/packs.rs::Game::apply_staged_pack the render-thread apply of a reload +ResourceMetadata.fromJsonStream port crates/horizon-client/src/packs.rs::read_meta gap: minor: reads pack.mcmeta only; sections are read by key in pack() +ResourceMetadata.of skip:java map-backed metadata constructor +ResourceMetadata.getTypedSections skip:java typed view over sections; Horizon reads sections by key +ResourceProvider.getResourceOrThrow skip:java Option instead of a thrown FileNotFound +SimpleJsonResourceReloadListener.prepare port crates/horizon-client/src/datapack.rs::DataStore::load gap: player: only function, item_modifier, predicate and function-tag files are read; recipes, loot tables and advancements from datapacks are not +SimplePreparableReloadListener.reload port crates/horizon-client/src/game/packs.rs::Game::stage_selected_packs gap: minor: the prepare half; apply_staged_pack does the apply +SimpleReloadInstance.of port crates/horizon-client/src/game/packs.rs::Game::reload_selected_packs stage then apply, in one call +SimpleReloadInstance.startTasks inline crates/horizon-client/src/game/packs.rs::Game::reload_packs defers while a world or rest loads; errors go to Options +SimpleReloadInstance.prepareTasks port crates/horizon-client/src/game/packs.rs::Game::stage_selected_packs gap: minor: one staging pass, no per-listener barrier chain +SimpleReloadInstance.getActualProgress skip:ui progress fraction of the reload screen +SimpleReloadInstance.weightProgress skip:ui progress weights of the reload screen +SimpleReloadInstance.create skip:debug chooses profiled or plain reload +LevelBasedPermissionSet.hasPermission inline crates/horizon-command/src/lib.rs::Dispatcher::parse_nodes node levels; entity selectors take reg.permission >= 2 at each selector argument (args.rs) +LevelBasedPermissionSet.union skip:other Horizon keeps one u8 level per sender, so there is no union to build +LevelBasedPermissionSet.forLevel skip:other named levels are the u8 constants in horizon-command/src/vanilla.rs +PermissionCheckTypes.bootstrap skip:data registry bootstrap of the permission check codecs +PermissionSet.union skip:other no permission-set objects; levels are plain u8 +PermissionSetUnion.hasPermission skip:other no unions; a sender has one level +PermissionSetUnion.union skip:other no unions; a sender has one level +PermissionSetUnion.ensureNoUnionsWithinUnions skip:java constructor guard against nested unions +PermissionTypes.bootstrap skip:data registry bootstrap of the permission codecs +BanListEntry.equals port crates/horizon-server-files/src/user_lists.rs::BanEntry::eq same source, expiry, reason and user +BanListEntry.hashCode skip:java hash for map keys; bans are keyed by uuid or ip string +CachedUserNameToIdResolver.lookupGameProfile port crates/horizon-server-files/src/user_cache.rs::UserCache::get_by_name cache first, then the profile lookup, then offline if allowed +CachedUserNameToIdResolver.getTopMRUProfiles inline crates/horizon-server-files/src/user_cache.rs::UserCache::to_json the 1000 most recently used entries +CachedUserNameToIdResolver.writeGameProfile inline crates/horizon-server-files/src/user_cache.rs::UserCache::to_json entry JSON with expiresOn +CachedUserNameToIdResolver.readGameProfile port crates/horizon-server-files/src/user_cache.rs::UserCache::parse reads entries with their expiry dates +IpBanList.isBanned port crates/horizon-client/src/game/dedicated.rs::Lists::can_login the IP ban branch of the login check +IpBanList.getIpFromAddress inline crates/horizon-client/src/game/dedicated.rs::Lists::can_login gap: minor: the address comes from IpAddr, not a cut of the text +IpBanList.add port crates/horizon-client/src/game/dedicated.rs::Game::console_command ban-ip: adds, replies, kicks matching players; gap: minor: no JSON-RPC ipBanned notice +IpBanList.remove port crates/horizon-client/src/game/dedicated.rs::Game::console_command pardon-ip: removes and replies; gap: minor: no JSON-RPC ipUnbanned notice +IpBanList.clear skip:server only the JSON-RPC ban API calls clear (MinecraftBanListServiceImpl); Horizon has no JSON-RPC server +IpBanListEntry.createIpInfo port crates/horizon-server-files/src/user_lists.rs::BanEntry::from_json reads the ip field of an entry +IpBanListEntry.serialize port crates/horizon-server-files/src/user_lists.rs::BanEntry::serialize writes the ip field and the ban fields +OldUsersConverter.readOldListFormat missing minor: reads the pre-1.7 flat ban and op lists; Horizon reads only its json files +OldUsersConverter.lookupPlayers missing minor: name to uuid lookup for the old list conversion +OldUsersConverter.convertUserBanlist missing minor: converts an old banned-players.txt when the server starts +OldUsersConverter.convertIpBanlist missing minor: converts an old banned-ips.txt when the server starts +OldUsersConverter.convertOpsList missing minor: converts an old ops.txt when the server starts +OldUsersConverter.convertWhiteList missing minor: converts an old whitelist.txt when the server starts +OldUsersConverter.convertMobOwnerIfNecessary missing minor: name owners on old tamed mobs are not turned into uuids; datafix handles OwnerUUID only +OldUsersConverter.convertPlayers missing minor: old world/players .dat files are not moved to playerdata +OldUsersConverter.ensureDirectoryExists skip:other helper of the file moves, which Horizon does not do +OldUsersConverter.areOldUserlistsRemoved missing minor: no start-up refusal while old list files remain +OldUsersConverter.renameOldFile missing minor: converted files are not renamed to .converted +OldUsersConverter.parseDate port crates/horizon-server-files/src/user_lists.rs::parse_date parses the ban date format diff --git a/tools/port_map/rest-020.tsv b/tools/port_map/rest-020.tsv new file mode 100644 index 0000000000..14f42aa75b --- /dev/null +++ b/tools/port_map/rest-020.tsv @@ -0,0 +1,149 @@ +PlayerList.updateEntireScoreboard port crates/horizon-client/src/lan/scoreboard.rs::View::changes the join is View::changes against the guest's empty view, sent from game/scoreboard.rs lan_sync_scoreboard +PlayerList.loadPlayerData port crates/horizon-client/src/lan/playerdata.rs::load gap: minor: the single-player owner's saved level is applied in Game::lan_guest_joined instead +PlayerList.save port crates/horizon-client/src/game/lan.rs::Game::save_guest player file, advancements and statistics of a guest +PlayerList.disconnectAllPlayersWithProfile port crates/horizon-client/src/lan/hub.rs::Hub::poll kicks every connection of the uuid on NetEvent::Duplicate; the refusal of a second login is Shared::try_admit (lan/mod.rs) +PlayerList.broadcastSystemToTeam missing player: when a dead player's team hides death messages from other teams, its own members should read the death in chat; Horizon sends it to nobody (game/lan_actions.rs guest_die sends only when the team shows its own death) +PlayerList.broadcastSystemToAllExceptTeam missing player: when a dead player's team hides death messages from its own team, players outside the team should read the death in chat; Horizon sends it to nobody +PlayerList.getPlayerNamesArray skip:server the GS4 query reply's player list; RCON and query server code; Horizon has no RCON or GS4 query port +PlayerList.op inline crates/horizon-client/src/game/dedicated.rs::Game::console_command the op arm: list add, reply, and sync_ops (permission level push to the hub) +PlayerList.deop inline crates/horizon-client/src/game/dedicated.rs::Game::console_command the deop arm: list remove, reply, sync_ops and kick_unlisted_players +PlayerList.saveAll port crates/horizon-client/src/game/dedicated.rs::Game::stop_dedicated gap: minor: /save-all (save-all arm of console_command) writes the world only; players are saved on leave and when the server stops +PlayerList.getPlayersWithAddress inline crates/horizon-client/src/game/dedicated.rs::Game::console_command the ban-ip arm filters the guests by address +PlayerList.getPlayerStats inline crates/horizon-client/src/game/lan.rs::Game::load_guest_records statistics are read when the guest joins, not on first use +PlayerList.locateStatsFile missing minor: a name-based stats or advancements file from old versions is not moved to the uuid file (player_json takes only the uuid) +PlayerList.getPlayerAdvancements inline crates/horizon-client/src/game/lan.rs::Game::load_guest_records advancements are read when the guest joins, not on first use +PlayerList.getPlayer port crates/horizon-client/src/game/commands/players.rs::Game::target_by_uuid gap: minor: the uuid lookup also returns entities (selector fallback); the name overload is Game::player_named +PlayerList.reloadResources port crates/horizon-client/src/game/functions.rs::Game::reload_datapacks gap: player: /reload re-reads the datapacks but sends no tags or recipes to connected players and does not reload advancements; the sync data is loaded once per process (game/lan.rs SYNC), so new tags and recipes wait for a restart +ProfileResolver.Cached.fetchByName port crates/horizon-client/src/game/dedicated.rs::Game::resolve name to profile through the user cache, else the profile service (profiles::find_by_name) +ServerOpList.getUserList skip:ui tab-completion names only (DeOpCommands suggestions); the console has no completion +ServerOpList.add port crates/horizon-server-files/src/user_lists.rs::UserList::add gap: minor: the Opped reply and the permission push are in console_command's op arm +ServerOpList.remove port crates/horizon-server-files/src/user_lists.rs::UserList::remove gap: minor: the Deopped reply, sync_ops and the kick are in console_command's deop arm +ServerOpList.clear skip:server only the JSON-RPC management API calls clear(); Horizon has no such API +ServerOpList.canBypassPlayerLimit port crates/horizon-client/src/game/dedicated.rs::Game::run_dedicated the bypasses_limit closure: an op entry with bypassesPlayerLimit; read by Shared::try_admit +ServerOpListEntry.serialize port crates/horizon-server-files/src/user_lists.rs::OpEntry::serialize level and bypassesPlayerLimit keys +SleepStatus.areEnoughDeepSleeping port crates/horizon-client/src/game/lan_actions.rs::Game::enough_slept counts players asleep for 100 ticks (sleep_counts) against sleepers_needed +StoredUserList.clear skip:server only the JSON-RPC management API calls clear(); Horizon has no such API +StoredUserList.removeExpired port crates/horizon-server-files/src/user_lists.rs::UserList::get expired entries are dropped on each get, as vanilla +UserBanList.getUserList skip:ui tab-completion names only (PardonCommand suggestions) +UserBanList.add port crates/horizon-server-files/src/user_lists.rs::UserList::add gap: minor: the banned notice and the kick are in console_command's ban arm +UserBanList.remove port crates/horizon-server-files/src/user_lists.rs::UserList::remove gap: minor: the unbanned notice is in console_command's pardon arm +UserBanList.clear skip:server only the JSON-RPC management API calls clear(); Horizon has no such API +UserBanListEntry.serialize port crates/horizon-server-files/src/user_lists.rs::BanEntry::serialize the player's name and the ban fields +UserBanListEntry.getDisplayName inline crates/horizon-client/src/game/dedicated.rs::Game::console_command the banlist arm prints the name; Horizon entries always carry one, so no Unknown-user fallback +UserWhiteList.add port crates/horizon-server-files/src/user_lists.rs::UserList::add gap: minor: the allowlist notice is in console_whitelist's add arm +UserWhiteList.remove port crates/horizon-server-files/src/user_lists.rs::UserList::remove gap: minor: the notice is in console_whitelist's remove arm +UserWhiteList.clear skip:server only the JSON-RPC management API calls clear(); Horizon has no such API +UserWhiteList.getUserList skip:ui tab-completion names only (WhitelistCommand suggestions) +NetworkDataOutputStream.writeString skip:server RCON and query server code; Horizon has no RCON or GS4 query port +PktUtils.stringFromByteArray skip:server RCON and query server code; Horizon has no RCON or GS4 query port +PktUtils.intFromByteArray skip:server RCON and query server code; Horizon has no RCON or GS4 query port +PktUtils.intFromNetworkByteArray skip:server RCON and query server code; Horizon has no RCON or GS4 query port +PktUtils.toHexString skip:server RCON and query server code; Horizon has no RCON or GS4 query port +RconConsoleSource.createCommandSourceStack skip:server RCON command source; RCON and query server code; Horizon has no RCON or GS4 query port +GenericThread.start skip:server thread helper of the RCON and query servers; RCON and query server code; Horizon has no RCON or GS4 query port +GenericThread.stop skip:server thread helper of the RCON and query servers; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.create skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.sendTo skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.processPacket skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.buildRuleResponse skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.validChallenge skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.sendChallenge skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.pruneChallenges skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.run skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.start skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.recoverSocketError skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +QueryThreadGs4.initSocket skip:server GS4 query server; RCON and query server code; Horizon has no RCON or GS4 query port +RconClient.run skip:server RCON client connection; RCON and query server code; Horizon has no RCON or GS4 query port +RconClient.send skip:server RCON client connection; RCON and query server code; Horizon has no RCON or GS4 query port +RconClient.sendCmdResponse skip:server RCON client connection; RCON and query server code; Horizon has no RCON or GS4 query port +RconClient.closeSocket skip:server RCON client connection; RCON and query server code; Horizon has no RCON or GS4 query port +RconThread.clearClients skip:server RCON listener; RCON and query server code; Horizon has no RCON or GS4 query port +RconThread.run skip:server RCON listener; RCON and query server code; Horizon has no RCON or GS4 query port +RconThread.create skip:server RCON listener; RCON and query server code; Horizon has no RCON or GS4 query port +RconThread.stop skip:server RCON listener; RCON and query server code; Horizon has no RCON or GS4 query port +RconThread.closeSocket skip:server RCON listener; RCON and query server code; Horizon has no RCON or GS4 query port +ServerWaypointManager.trackWaypoint port crates/horizon-client/src/lan/waypoints.rs::Waypoints::track +ServerWaypointManager.updateWaypoint port crates/horizon-client/src/lan/waypoints.rs::Waypoints::update_waypoint +ServerWaypointManager.untrackWaypoint port crates/horizon-client/src/lan/waypoints.rs::Waypoints::untrack +ServerWaypointManager.addPlayer port crates/horizon-client/src/lan/waypoints.rs::Waypoints::add_player +ServerWaypointManager.updatePlayer port crates/horizon-client/src/lan/waypoints.rs::Waypoints::update_player +ServerWaypointManager.removePlayer port crates/horizon-client/src/lan/waypoints.rs::Waypoints::remove_player +ServerWaypointManager.remakeConnections inline crates/horizon-client/src/lan/waypoints.rs::Waypoints::tick_level called on scoreboard team changes; the per-tick update re-checks every connection instead +ServerWaypointManager.createConnection port crates/horizon-client/src/lan/waypoints.rs::Waypoints::create_connection +ServerWaypointManager.updateConnection port crates/horizon-client/src/lan/waypoints.rs::Waypoints::update_or_create +SoundEvent.create skip:data registry object construction; Horizon's sound_events.rs models sounds.json entries, not SoundEvent objects +SoundEvent.getRange missing minor: no fixed-range sound events (fixed_range is not read anywhere); attenuation comes from the sounds.json value (Sound::attenuation_distance) +SoundEvents.register skip:data registry bootstrap of sound events and variant sets; Horizon has no registry layer +SoundEvents.registerForHolder skip:data registry bootstrap of sound events and variant sets; Horizon has no registry layer +SoundEvents.registerGoatHornSoundVariants skip:data registry bootstrap of sound events and variant sets; Horizon has no registry layer +SoundEvents.registerWolfSoundVariants skip:data registry bootstrap of sound events and variant sets; Horizon has no registry layer +SoundEvents.registerChickenSoundVariants skip:data registry bootstrap of sound events and variant sets; Horizon has no registry layer +SoundEvents.registerCowSoundVariants skip:data registry bootstrap of sound events and variant sets; Horizon has no registry layer +SoundEvents.registerPigSoundVariants skip:data registry bootstrap of sound events and variant sets; Horizon has no registry layer +SoundEvents.registerCatSoundVariants skip:data registry bootstrap of sound events and variant sets; Horizon has no registry layer +RecipeBook.setBookSetting port crates/horizon-client/src/lan/hub.rs::Hub::process_inbox the RecipeBookChangeSettings arm sets open and filtering for the book +RecipeBookSettings.getSettings skip:java accessor: the four books are arrays indexed by BookType::index in recipe_book.rs +RecipeBookSettings.updateSettings inline crates/horizon-client/src/lan/hub.rs::Hub::process_inbox the open and filtering writes of the change-settings arm +RecipeBookSettings.copy skip:java value copy of a plain struct; no separate copy step +RecipeBookSettings.replaceFrom inline crates/horizon-client/src/recipe_book.rs::Settings::read_valid the flags and recipe sets of a loaded book +RecipeBookSettings.TypeSettings.codec skip:java NBT codec builder; Settings::read_valid and Settings::write use the same keys +ServerRecipeBook.loadRecipes port crates/horizon-client/src/recipe_book.rs::Settings::read_valid gap: minor: recipes that fail the validator are dropped without vanilla's error log +ServerRecipeBook.pack port crates/horizon-client/src/recipe_book.rs::Settings::write the saved compound of the book +ServerRecipeBook.apply inline crates/horizon-client/src/recipe_book.rs::Settings::read_valid replaces the book's known and highlighted recipes on load +ServerStatsCounter.createTypedStatsCodec skip:java codec builder for the stats map +ServerStatsCounter.parse port crates/horizon-client/src/game/stats.rs::Stats::read gap: minor: no data fixer runs on old stats files +Stat.equals skip:java Horizon keys stats by name strings; there are no Stat objects to compare +StatType.get skip:java instance cache of Stat objects; Horizon keys stats by name strings +Stats.makeCustomStat skip:data registration of custom stat ids +Stats.makeRegistryStatType skip:data registration of stat types +StatsCounter.increment port crates/horizon-client/src/game/stats.rs::Stats::add saturating add, as vanilla's clamp at Integer.MAX_VALUE +StatsCounter.getValue port crates/horizon-client/src/game/stats.rs::Stats::get the count of a stat, 0 when missing +BlockItemTagId.create skip:data tag key constructors; Horizon reads tag files by name +net/minecraft/tags/TagEntry#visitRequiredDependencies inline crates/horizon-world/src/worldgen/tags.rs::TagDefinitions::build the required tag edges built in TagDefinitions::build +net/minecraft/tags/TagEntry#visitOptionalDependencies inline crates/horizon-world/src/worldgen/tags.rs::TagDefinitions::build the optional tag edges built in TagDefinitions::build +net/minecraft/tags/TagEntry#verifyIfPresent inline crates/horizon-world/src/worldgen/tags.rs::TagDefinitions::build a missing required entry leaves its tag in the missing list +net/minecraft/tags/TagEntry#toString port crates/horizon-world/src/worldgen/tags.rs::TagEntry::display the # prefix, the id and the ? for optional entries +TagKey.hashedCodec skip:java codec for #tag names; TagEntry::from_json parses the same prefix +TagLoader.load port crates/horizon-items/src/items.rs::load_tag_lists gap: minor: the remove list of tag files is not read (TagFile has no remove field) +TagLoader.loadTagsForExistingRegistries skip:data registry-wide reload plumbing; Items::load_with loads each kind by itself +TagLoader.loadTagsForRegistry port crates/horizon-items/src/items.rs::load_tags one registry's tags from the data folder +TagLoader.wrapTags skip:java wraps tag keys in a map; no separate step in Horizon +TagLoader.loadPendingTags skip:data pending-tag reload plumbing (Registry.PendingTags); Horizon has no registry layer +TagLoader.buildUpdatedLookups skip:data registry lookup layer for reloads; Horizon has none +TagLoader.findTagsForRegistry skip:data lookup helper of the reload layer; Horizon has none +TagLoader.ElementLookup.fromGetters skip:java lookup adapter over the registry getters +TagLoader.SortingEntry.visitRequiredDependencies inline crates/horizon-world/src/worldgen/tags.rs::TagDefinitions::build the edges of the sort in TagDefinitions::build +TagLoader.SortingEntry.visitOptionalDependencies inline crates/horizon-world/src/worldgen/tags.rs::TagDefinitions::build the edges of the sort in TagDefinitions::build +TagNetworkSerialization.serializeToNetwork port crates/horizon-client/src/lan/data.rs::SyncData::load_tags tag ids per registry, with # references expanded +TagNetworkSerialization.deserializeTagsFromNetwork missing minor: the client never reads UpdateTags (only lan/conn.rs sends it), so tags from a server are not applied; Horizon loads its own tag files (items.rs load_tag_lists) +ARGB.linearChannelMean port crates/horizon-client/src/mipmap.rs::mean_linear the linear-light mean of four samples for one channel +ARGB.addRgb skip:other int overload has no caller in 26.3; ColorModifier uses the Vector3fc and Vector4fc overloads +ARGB.subtractRgb skip:other int overload has no caller in 26.3; ColorModifier uses the Vector3fc and Vector4fc overloads +ARGB.greyscale skip:other int overload has no caller in 26.3; ColorModifier uses the vector overloads +ARGB.alphaBlend skip:other int overload has no caller in 26.3; ColorModifier uses the Vector4fc overload +ARGB.alphaBlendChannel skip:other private helper of alphaBlend, which has no caller in 26.3 +ARGB.linearLerp skip:other no caller in 26.3 +ARGB.vector4fFromARGB32 port crates/horizon-client/src/game/lan_particles.rs::rgba ARGB int to float colour, used for particle option colours; the colour codecs have no counterpart +ARGB.setVector4fFromARGB32 skip:ui clear colour of the start-up loading overlay; drawing only +ARGB.toABGR port crates/horizon-client/src/hud_layout.rs::abgr swaps red and blue into the HUD's packed colour +AbstractListBuilder.add skip:java DataResult list builder of the codec layer +AbstractListBuilder.withErrorsFrom skip:java DataResult list builder of the codec layer +AbstractListBuilder.build skip:java DataResult list builder of the codec layer +ArrayListDeque.verifyIndexInRange skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.set skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.add skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.grow skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.remove skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.removeIf skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.copyCount skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.replaceAll skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.forEach skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.removeFirst skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.removeLast skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.getFirst skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.getLast skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.removeFirstOccurrence skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.removeLastOccurrence skip:java Java collection internals; Horizon uses std collections +ArrayListDeque.DescendingIterator.next skip:java Java collection iterator +ArrayListDeque.ReversedView.subList skip:java Java collection view +ArrayListDeque.ReversedView.reverseIndex skip:java Java collection view diff --git a/tools/port_map/rest-021.tsv b/tools/port_map/rest-021.tsv new file mode 100644 index 0000000000..24040be388 --- /dev/null +++ b/tools/port_map/rest-021.tsv @@ -0,0 +1,135 @@ +BinaryAnimator.tick port crates/horizon-client/src/mobs/axolotl.rs::Animation::tick one axolotl Animation holds the four animators; old/value pair and the clamp to 10 as vanilla +BinaryAnimator.getFactor port crates/horizon-client/src/mobs/axolotl.rs::Animation::factor lerp of old and value over 10, then the IN_OUT_SINE curve as vanilla +BlockUtil.packDifferenceInPositionInternal port crates/horizon-client/src/mobs/shulker.rs::pack_diff gap: minor: radius fixed at 8 on each axis; the dragon egg packs (16, 8, 16) inline in crates/horizon-world/src/level/interact.rs::Sim::dragon_egg_teleport +BlockUtil.checkDifferenceInBounds skip:other range guard inside packDifferenceInPosition; radii of 8 and 16 cannot exceed 127 +ByIdMap.createMap skip:java id-to-value lookup table; Horizon enums match their own ids +ByIdMap.sparse skip:java id-to-value lookup table; Horizon enums match their own ids +ByIdMap.createSortedArray skip:java id-to-value lookup table; Horizon enums match their own ids +ByIdMap.continuous skip:java id-to-value lookup table; Horizon enums match their own ids +ClassInstanceMultiMap.add skip:java typed collection helper for entity sections; Horizon uses its own containers +ClassInstanceMultiMap.remove skip:java typed collection helper for entity sections; Horizon uses its own containers +ClassInstanceMultiMap.find skip:java typed collection helper for entity sections; Horizon uses its own containers +ClassInstanceMultiMap.iterator skip:java typed collection helper for entity sections; Horizon uses its own containers +ClassTreeIdRegistry.getLastIdFor skip:java synched entity data slot allocator by class; Horizon does not allocate slots this way +ClassTreeIdRegistry.define skip:java synched entity data slot allocator by class; Horizon does not allocate slots this way +CommonLinks.extendRealms skip:service Realms upgrade link +CompilableString.codec skip:java codec +CompilableString.equals skip:java value equality on the source string +CompilableString.CommandParserHelper.apply missing player: a selector, score or NBT text component is not compiled (Horizon has no SelectorContents), so this parse and its trailing-data error are missing +CrudeIncrementalIntIdentityHashBiMap.byId skip:java identity bimap over registry ids; Horizon uses its own id tables +CrudeIncrementalIntIdentityHashBiMap.contains skip:java identity bimap over registry ids; Horizon uses its own id tables +CrudeIncrementalIntIdentityHashBiMap.nextId skip:java identity bimap over registry ids; Horizon uses its own id tables +CrudeIncrementalIntIdentityHashBiMap.grow skip:java identity bimap over registry ids; Horizon uses its own id tables +CrudeIncrementalIntIdentityHashBiMap.addMapping skip:java identity bimap over registry ids; Horizon uses its own id tables +CrudeIncrementalIntIdentityHashBiMap.indexOf skip:java identity bimap over registry ids; Horizon uses its own id tables +CrudeIncrementalIntIdentityHashBiMap.findEmpty skip:java identity bimap over registry ids; Horizon uses its own id tables +CrudeIncrementalIntIdentityHashBiMap.clear skip:java identity bimap over registry ids; Horizon uses its own id tables +CrudeIncrementalIntIdentityHashBiMap.copy skip:java identity bimap over registry ids; Horizon uses its own id tables +Crypt.generateKeyPair port crates/horizon-protocol/src/rsa.rs::RsaPrivateKey::generate RSA key pair with the bit size passed in; vanilla passes 1024 +Crypt.digestData port crates/horizon-protocol/src/crypto.rs::server_hash returns the signed hex string; vanilla returns bytes and the caller converts them +Crypt.rsaStringToKey missing minor: PEM reader for the account chat key pair (AccountProfileKeyPairManager); Horizon has no PEM reader +Crypt.rsaPublicKeyToString missing minor: PEM writer for the account chat public key; Horizon has no PEM writer +Crypt.pemRsaPrivateKeyToString missing minor: PEM writer for the account chat private key; Horizon has no PEM writer +Crypt.byteToPrivateKey missing minor: PKCS#8 private key reader for the account chat key pair; Horizon has none +Crypt.byteToPublicKey port crates/horizon-protocol/src/rsa.rs::RsaPublicKey::from_der X.509 SubjectPublicKeyInfo decode +Crypt.decryptByteToSecretKey port crates/horizon-protocol/src/rsa.rs::RsaPrivateKey::decrypt RSA PKCS#1 v1.5 decrypt of the shared secret; the bytes are used as the AES key as given +Crypt.cipherData port crates/horizon-protocol/src/rsa.rs::RsaPublicKey::encrypt the encrypt half (opMode 1); the decrypt half is RsaPrivateKey::decrypt +Crypt.getCipher port crates/horizon-protocol/src/crypto.rs::Cfb8::new AES CFB8 with the shared secret as both key and IV +Crypt.SaltSignaturePair.write skip:other no caller in the 26.3 source; the salt and signature are packet fields +CsvOutput.writeRow skip:debug CSV dumps for chunk, entity and metrics output +CsvOutput.writeLine skip:debug CSV dumps for chunk, entity and metrics output +CubicSpline.asSampler port crates/horizon-core/src/density/spline.rs::SplineSampler::new compiles the spline into a sampler over its coordinates +CubicSpline.codec skip:java codec +CubicSpline.builder skip:java builder entry point +CubicSpline.Builder.addPoint skip:java builder; the point goes into the point list +CubicSpline.Builder.build port crates/horizon-core/src/density/spline.rs::CubicSpline::multipoint empty input is the error (EmptyPoints); otherwise the same point list +CubicSpline.Constant.parityString skip:debug parity debug string +CubicSpline.Multipoint.range port crates/horizon-core/src/density/spline.rs::CubicSpline::try_range range analysis; the one-argument range is CubicSpline::range +CubicSpline.Multipoint.linearExtend inline crates/horizon-core/src/density/spline.rs::CubicSpline::try_range the extend closure used at both ends +CubicSpline.Multipoint.validateSizes port crates/horizon-core/src/density/spline.rs::CubicSpline::multipoint gap: minor: equal lengths hold by construction; the empty check is EmptyPoints +CubicSpline.Multipoint.findIntervalStart inline crates/horizon-core/src/density/spline.rs::CubicSpline::sample the binary search for the first location greater than the input +CubicSpline.Multipoint.parityString skip:debug parity debug string +CubicSpline.Multipoint.forEachCoordinate port crates/horizon-core/src/density/spline.rs::CubicSpline::coordinates_mut visits the node coordinate and every child coordinate +CubicSpline.Multipoint.equals skip:java value equality +CubicSpline.Multipoint.hashCode skip:java hashing +CubicSpline.Multipoint.codec skip:java codec +CubicSpline.Multipoint.packToPoints skip:java codec helper: nodes to a point list +CubicSpline.Multipoint.createFromPoints skip:java codec helper: point list to nodes +CubicSpline.Multipoint.Point.codec skip:java codec +DelegateDataOutput.write skip:java stream delegate +DependencySorter.visitDependenciesAndElement port crates/horizon-world/src/worldgen/tags.rs::visit depth-first walk that emits dependencies before the element +DependencySorter.isCyclic port crates/horizon-world/src/worldgen/tags.rs::reaches reach test used to refuse a cyclic edge +DependencySorter.addDependencyIfNotCyclic inline crates/horizon-world/src/worldgen/tags.rs::TagDefinitions::build adds the edge only when reaches is false +DependencySorter.orderByDependencies inline crates/horizon-world/src/worldgen/tags.rs::TagDefinitions::build required edges first, then optional ones, then the visit order +DirectoryLock.create port crates/horizon-client/src/save.rs::SessionLock::acquire exclusive file lock on session.lock with the two-byte snowman content +DirectoryLock.isLocked missing player: a world another copy has open is listed as an ordinary world; vanilla flags it as locked in the list (LevelStorageSource.loadLevelSummaries), Horizon has no such check in title.rs list_worlds +Ease.inBack missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.inElastic missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.inExpo missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.inOutBounce missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.inOutCirc missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.inOutQuad missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.inOutQuart missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.inOutQuint missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.outBounce missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.outElastic missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +Ease.outExpo missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only +EasingType.cubicBezier inline crates/horizon-client/src/environment.rs::Environment::load_dimension reads the four control points of a cubic_bezier ease; gap: minor: no range check, see CubicBezierControls.validate +EasingType.CubicBezier.curveFromControls inline crates/horizon-client/src/environment.rs::Easing::apply the curve coefficients closure +EasingType.CubicBezier.solveT inline crates/horizon-client/src/environment.rs::Easing::apply Newton steps with the 1e-5 tolerance +EasingType.CubicBezier.solveTBisect inline crates/horizon-client/src/environment.rs::Easing::apply bisection fallback when Newton does not converge +EasingType.CubicBezier.equals skip:java value equality +EasingType.CubicBezier.toString skip:debug debug string +EasingType.CubicBezier.CubicCurve.sample inline crates/horizon-client/src/environment.rs::Easing::apply the sample closure +EasingType.CubicBezier.CubicCurve.sampleGradient inline crates/horizon-client/src/environment.rs::Easing::apply the gradient closure +EasingType.CubicBezierControls.validate missing minor: vanilla rejects control points outside [0;1] when a timeline loads; Horizon takes any value +EncoderCache.Key.equals skip:java cache key equality +EncoderCache.Key.hashCode skip:java cache key hashing +ExceptionCollector.add skip:java suppressed exception collector +ExtraCodecs.converter skip:java codec or codec validator +ExtraCodecs.hexColor skip:java codec or codec validator +ExtraCodecs.intervalCodec skip:java codec or codec validator +ExtraCodecs.idResolverCodec skip:java codec or codec validator +ExtraCodecs.orCompressed skip:java codec or codec validator +ExtraCodecs.overrideLifecycle skip:java codec or codec validator +ExtraCodecs.compactListCodec skip:java codec or codec validator +ExtraCodecs.intRangeWithMessage skip:java codec or codec validator +ExtraCodecs.intRange skip:java codec or codec validator +ExtraCodecs.longRangeWithMessage skip:java codec or codec validator +ExtraCodecs.longRange skip:java codec or codec validator +ExtraCodecs.floatRangeMinInclusiveWithMessage skip:java codec or codec validator +ExtraCodecs.floatRangeMinExclusiveWithMessage skip:java codec or codec validator +ExtraCodecs.floatRange skip:java codec or codec validator +ExtraCodecs.nonEmptyHolderSet skip:java codec or codec validator +ExtraCodecs.nonEmptyMap skip:java codec or codec validator +ExtraCodecs.temporalCodec skip:java codec or codec validator +ExtraCodecs.gameProfileCodec skip:java codec or codec validator +ExtraCodecs.sizeLimitedMap skip:java codec or codec validator +ExtraCodecs.legacyEnum skip:java codec or codec validator +ExtraCodecs.relativeNormalizedSubPathCodec skip:data path codec for data paths; Horizon reads its data from tables and files +ExtraCodecs.guardedPathCodec skip:data path codec for data paths; Horizon reads its data from tables and files +ExtraCodecs.optionalAlwaysPresentFieldOf skip:java codec or codec validator +ExtraCodecs.LateBoundIdMapper.codec skip:java codec +ExtraCodecs.LateBoundIdMapper.put skip:java builder-style registration into a map +ExtraCodecs.TagOrElementLocation.decoratedId skip:java string for codec error messages +FastBufferedInputStream.read skip:java buffered stream helper; Rust readers do their own buffering +FastBufferedInputStream.skip skip:java buffered stream helper; Rust readers do their own buffering +FastBufferedInputStream.fill skip:java buffered stream helper; Rust readers do their own buffering +FileSystemUtil.safeGetPath skip:other opens a jar or zip filesystem for a URI; Horizon reads archives with the zip crate +FileUtil.sanitizeName port crates/horizon-client/src/title.rs::Title::target_folder gap: minor: replaces every non-alphanumeric character with _; vanilla only replaces illegal file characters, dots, slashes and quotes +FileUtil.isPathPortable skip:other portable path check; no caller in the 26.3 source +FileUtil.decomposePath skip:data resource path handling; Horizon reads its data from tables and files +FileUtil.resolvePath skip:data resource path handling; Horizon reads its data from tables and files +FileUtil.isValidPathSegment skip:data resource path handling; Horizon reads its data from tables and files +FileUtil.validatePath skip:data resource path handling; Horizon reads its data from tables and files +FileUtil.createDirectoriesSafe skip:other directory creation; save.rs uses create_dir_all +FileUtil.isEmptyPath skip:data resource path handling; Horizon reads its data from tables and files +FileZipper.add skip:debug profiling report archive (Minecraft.archiveProfilingReport, PerfCommand); Horizon writes no zip report +FileZipper.close skip:debug profiling report archive (Minecraft.archiveProfilingReport, PerfCommand); Horizon writes no zip report +FormattedCharSequence.forward port crates/horizon-client-core/src/text.rs::Text::styled styled text object; the per-character walk is Text::chars +FormattedCharSequence.backward skip:other two-argument reverse walk; no caller in the 26.3 source (SubStringSource uses the three-argument form) +FormattedCharSequence.decorateOutput missing minor: char-code modifier for reversed text, used only for right-to-left languages; Horizon has no right-to-left layout +FormattedCharSequence.composite port crates/horizon-client-core/src/text.rs::Text::empty zero parts give the empty sequence +FormattedCharSequence.fromPair port crates/horizon-client-core/src/text.rs::Text::append concatenation of two sequences +FutureChain.append skip:other ordering helper for the async chat-signature checks in ServerGamePacketListenerImpl; a scheduling wrapper, no game behaviour of its own +Graph.depthFirstSearch inline crates/horizon-world/src/worldgen/feature_sort.rs::build_features_per_step iterative DFS with colours; finds the cycle and emits the reverse topological order diff --git a/tools/port_map/rest-022.tsv b/tools/port_map/rest-022.tsv new file mode 100644 index 0000000000..8ab09413ba --- /dev/null +++ b/tools/port_map/rest-022.tsv @@ -0,0 +1,149 @@ +GsonHelper.isStringValue skip:java Gson presence and type predicate; Horizon checks the JSON type where it reads the field (item_model.rs) +GsonHelper.isNumberValue skip:java Gson presence and type predicate; Horizon checks the JSON type where it reads the field (item_model.rs) +GsonHelper.isBooleanValue skip:java Gson presence and type predicate; Horizon checks the JSON type where it reads the field (item_model.rs) +GsonHelper.isArrayNode skip:java Gson presence and type predicate; Horizon checks the JSON type where it reads the field (item_model.rs) +GsonHelper.isObjectNode skip:java Gson presence and type predicate; Horizon checks the JSON type where it reads the field (item_model.rs) +GsonHelper.isValidPrimitive skip:java Gson presence and type predicate; Horizon checks the JSON type where it reads the field (item_model.rs) +GsonHelper.isValidNode skip:java Gson presence and type predicate; Horizon checks the JSON type where it reads the field (item_model.rs) +GsonHelper.getNonNull port crates/horizon-assets/src/item_model.rs::required missing field is an error; JSON null counts as absent +GsonHelper.convertToString port crates/horizon-assets/src/item_model.rs::string +GsonHelper.getAsString port crates/horizon-assets/src/item_model.rs::required gap: minor: the string type check is in string(), not here +GsonHelper.convertToItem skip:data datapack item id lookup in recipe and loot JSON; Horizon reads items from its registry tables +GsonHelper.getAsItem skip:data datapack item id lookup in recipe and loot JSON; Horizon reads items from its registry tables +GsonHelper.convertToBoolean port crates/horizon-assets/src/item_model.rs::boolean +GsonHelper.getAsBoolean port crates/horizon-assets/src/item_model.rs::required gap: minor: the boolean type check is in boolean(), not here +GsonHelper.convertToDouble port crates/horizon-assets/src/item_model.rs::float gap: minor: reads f32, not f64 +GsonHelper.getAsDouble port crates/horizon-assets/src/item_model.rs::required gap: minor: the number check is in float(), not here +GsonHelper.convertToFloat port crates/horizon-assets/src/item_model.rs::float +GsonHelper.convertToLong skip:java Java long width accessor; Horizon reads JSON integers with the int reader +GsonHelper.getAsLong skip:java Java long width accessor; Horizon reads JSON integers with the int reader +GsonHelper.convertToInt port crates/horizon-assets/src/item_model.rs::int +GsonHelper.getAsInt port crates/horizon-assets/src/item_model.rs::required gap: minor: the integer type check is in int(), not here +GsonHelper.convertToByte skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.getAsByte skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.convertToCharacter skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.getAsCharacter skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.convertToBigDecimal skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.getAsBigDecimal skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.convertToBigInteger skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.getAsBigInteger skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.convertToShort skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.getAsShort skip:java Java numeric width accessor; Horizon has no byte, char, short or big-number JSON fields +GsonHelper.convertToJsonObject port crates/horizon-assets/src/item_model.rs::object +GsonHelper.getAsJsonObject port crates/horizon-assets/src/item_model.rs::required gap: minor: the object type check is in object(), not here +GsonHelper.convertToJsonArray port crates/horizon-assets/src/item_model.rs::list +GsonHelper.getAsJsonArray port crates/horizon-assets/src/item_model.rs::required gap: minor: the array type check is in list(), not here +GsonHelper.convertToObject skip:java Gson codec deserialisation through a context; Horizon decodes typed records field by field +GsonHelper.getAsObject skip:java Gson codec deserialisation through a context; Horizon decodes typed records field by field +GsonHelper.getType skip:other error-message text for a malformed JSON value; no gameplay effect +GsonHelper.fromJson port crates/horizon-world/src/datafix/json.rs::parse_strict gap: minor: no 'JSON data was null or empty' error; returns None instead +GsonHelper.parseArray port crates/horizon-world/src/datafix/json.rs::parse_strict gap: minor: does not require the top-level value to be an array +GsonHelper.writeValue port crates/horizon-world/src/datafix/json.rs::to_stable_string gap: minor: always sorts keys; vanilla sorts only when a comparator is given +GsonHelper.sortByKeyIfNeeded inline crates/horizon-world/src/datafix/json.rs::to_stable_string key sort folded into the stable writer +GsonHelper.encodesLongerThan missing minor: no 32767-character book page or component length check exists in crates; the limit is not enforced +GsonHelper.CountedAppendable.accountChars skip:java character counter behind encodesLongerThan, which Horizon does not port +GsonHelper.CountedAppendable.append skip:java character counter behind encodesLongerThan, which Horizon does not port +HashOps.createNumeric missing minor: no component hashing; Hashed::of in crates/horizon-protocol/src/item.rs keeps item and count only +HashOps.createString missing minor: no component hashing; Hashed::of in crates/horizon-protocol/src/item.rs keeps item and count only +HashOps.hashMap missing minor: no component hashing; Hashed::of in crates/horizon-protocol/src/item.rs keeps item and count only +HashOps.createMap missing minor: no component hashing; Hashed::of in crates/horizon-protocol/src/item.rs keeps item and count only +HashOps.createList missing minor: no component hashing; Hashed::of in crates/horizon-protocol/src/item.rs keeps item and count only +HashOps.createByteList missing minor: no component hashing; Hashed::of in crates/horizon-protocol/src/item.rs keeps item and count only +HashOps.createIntList missing minor: no component hashing; Hashed::of in crates/horizon-protocol/src/item.rs keeps item and count only +HashOps.createLongList missing minor: no component hashing; Hashed::of in crates/horizon-protocol/src/item.rs keeps item and count only +HashOps.mergeToList skip:java DynamicOps merge into a hashed list; Horizon builds no hashed data incrementally +HashOps.mergeToMap skip:java DynamicOps merge into a hashed map; Horizon builds no hashed data incrementally +HashOps.ListHashBuilder.build skip:java DynamicOps builder for hashed lists; not needed without component hashing +HashOps.MapHashBuilder.build skip:java DynamicOps builder for hashed maps; not needed without component hashing +HttpUtil.updateModificationTime port crates/horizon-client/src/server_packs/download.rs::touch +HttpUtil.hashFile port crates/horizon-client/src/server_packs/download.rs::sha1_of +HttpUtil.checkExistingFile port crates/horizon-client/src/server_packs/download.rs::matches +HttpUtil.downloadAndHash port crates/horizon-client/src/server_packs/download.rs::download_file +HttpUtil.isPortAvailable port crates/horizon-client/src/lan/mod.rs::Lan::bind gap: minor: returns the bind error, not a bool; no 0..65535 range check +InclusiveRange.codec port crates/horizon-client/src/mobs/spawner.rs::light_limit gap: minor: only 0..15 light limits in integer, two-element or min/max form; no generic range codec +InclusiveRange.create inline crates/horizon-client/src/mobs/spawner.rs::light_limit the min <= max check +InclusiveRange.map skip:java Java generic element mapper; Horizon ranges are concrete integers +InclusiveRange.isValueInRange inline crates/horizon-client/src/mobs/spawner.rs::SpawnData::valid_position the light test is written inline +InclusiveRange.contains missing minor: no subrange check; Horizon reads only single light ranges (spawner.rs light_limit) +Interval.sub port crates/horizon-core/src/density/interval.rs::Interval::subtract_range +Interval.mul port crates/horizon-core/src/density/interval.rs::Interval::multiply_range +Interval.abs port crates/horizon-core/src/density/interval.rs::Interval::abs +Interval.pow port crates/horizon-core/src/density/interval.rs::Interval::power +Interval.powPositiveBase inline crates/horizon-core/src/density/interval.rs::Interval::power_base the exponent-case branch is folded into power_base +Interval.powZeroBase inline crates/horizon-core/src/density/interval.rs::Interval::power_base the exponent-case branch is folded into power_base +Interval.powInfiniteExponent inline crates/horizon-core/src/density/interval.rs::Interval::power_base the exponent-case branch is folded into power_base +Interval.powNegativeBase inline crates/horizon-core/src/density/interval.rs::Interval::power_base the exponent-case branch is folded into power_base +Interval.lerpFiniteBounds port crates/horizon-core/src/density/interval.rs::Interval::lerp_values +Interval.lerpInfiniteBounds inline crates/horizon-core/src/density/interval.rs::Interval::lerp_values the infinite-bound branch is folded into lerp_values +Interval.equals port crates/horizon-core/src/density/interval.rs::Interval::eq gap: minor: two NaI intervals compare equal here; vanilla compares NaN bounds, so they differ unless same object +Interval.toString skip:debug debug text of a range; Horizon has no Display for Interval +net/minecraft/util/Keyframe#codec port crates/horizon-client/src/patrol.rs::GameplayAttributes::load gap: minor: only boolean keyframe values are read +KeyframeTrack.mapCodec port crates/horizon-client/src/patrol.rs::GameplayAttributes::load gap: minor: the ease field is not read; boolean tracks are never eased +KeyframeTrack.validateKeyframes missing minor: no check that keyframes are ordered or that more than two share a tick; load only skips an empty track +KeyframeTrack.validatePeriod missing minor: no check that keyframe ticks lie in 0..period on load +KeyframeTrack.Builder.addKeyframe skip:java builder; Horizon builds keyframe lists from JSON +KeyframeTrack.Builder.build skip:java builder; Horizon builds keyframe lists from JSON +KeyframeTrackSampler.bakeSegments inline crates/horizon-client/src/patrol.rs::BoolTrack::sample gap: minor: no periodic wrap segment; with a period and a first keyframe after tick 0, ticks before it read keyframes[0] instead of the last keyframe +KeyframeTrackSampler.addSegmentsFromKeyframes inline crates/horizon-client/src/patrol.rs::BoolTrack::sample segments are not built; sample reads the keyframes directly +KeyframeTrackSampler.getSegmentAt inline crates/horizon-client/src/patrol.rs::BoolTrack::sample the keyframe lookup is a scan in sample +KeyframeTrackSampler.loopTicks inline crates/horizon-client/src/patrol.rs::BoolTrack::sample the period wrap (rem_euclid) inside sample +LightCoordsUtil.addSmoothBlockEmission port crates/horizon-client/src/particles.rs::Particle::light_coords the age-emission arms add the emitted block light capped at 240 +LinearCongruentialGenerator.next inline crates/horizon-core/src/biome.rs::BiomeZoom::quart_at_block the g closure is the LCG step; the coordinate is added separately +MemoryReserve.release skip:other frees a JVM heap reserve for OutOfMemoryError; Horizon is native code +ModCheck.identify skip:other brand and jar-signature check for crash reports; Horizon has no such check +ModCheck.merge skip:other brand and jar-signature check for crash reports; Horizon has no such check +Mth.absMax skip:java pure math helper; Rust abs and max at each use +Mth.nextFloat inline crates/horizon-world/src/loot.rs::Context::float the float range formula; one copy inlined here, others may inline it too +Mth.smallestEncompassingPowerOfTwo skip:java Rust next_power_of_two at each use; differs only for 0 (vanilla 0, Rust 1) +Mth.smallestSquareSide port crates/horizon-client/src/gfx/pack_atlas.rs::PackAtlas::new gap: minor: the shader-pack block atlas grid; vanilla's caller is the GUI item atlas, with no Horizon counterpart found +Mth.isPowerOfTwo skip:java Rust is_power_of_two at each use +Mth.rayIntersectsAABB skip:other minor: no caller in 26.3 (dead helper) +Mth.fastInvCubeRoot skip:other minor: no caller in 26.3 (dead helper) +Mth.murmurHash3Mixer skip:java JVM identity-hash bucketing in CrudeIncrementalIntIdentityHashBiMap; Horizon has no identity-hash maps +Mth.sign skip:java pure math helper; sign is written inline at each use +Mth.clampedMap missing unsure: no inverse-lerp helper or formula found by grep; vanilla has 13 callers and they may inline it +Mth.map missing unsure: no inverse-lerp helper or formula found by grep; vanilla has 8 callers and they may inline it +Mth.wobble skip:other minor: no caller in 26.3 (dead helper) +Mth.lengthSquared skip:java 2-D squared length; Rust arithmetic at each use +NativeModuleLister.listModules skip:other Windows module list for crash reports; Horizon has no such report +NativeModuleLister.tryGetModuleVersion skip:other Windows module list for crash reports; Horizon has no such report +NativeModuleLister.langTableKey skip:other Windows module list for crash reports; Horizon has no such report +NativeModuleLister.findLangAndCodepage skip:other Windows module list for crash reports; Horizon has no such report +NativeModuleLister.queryVersionValue skip:other Windows module list for crash reports; Horizon has no such report +NativeModuleLister.queryVersionString skip:other Windows module list for crash reports; Horizon has no such report +NativeModuleLister.addCrashSection skip:other Windows module list for crash reports; Horizon has no such report +NativeModuleLister.NativeModuleInfo.toString skip:other Windows module list for crash reports; Horizon has no such report +ParticleUtils.spawnParticlesOnBlockFace inline crates/horizon-client/src/particles.rs::on_block_faces_between the per-face count and emission fold into the six-face loop +ParticleUtils.getRandomSpeedRanges inline crates/horizon-client/src/particles.rs::on_block_faces_between the speed draw is written inline in the loop +ParticleUtils.spawnParticleBelow inline crates/horizon-client/src/animate_tick.rs::block_animate_tick the leaves drip and falling-dust arms place a particle at y - 0.05 with zero velocity +PlaceholderLookupProvider.hasRegisteredPlaceholders skip:data datagen placeholder lookup for data-driven registries +PlaceholderLookupProvider.UniversalLookup.get skip:data datagen placeholder lookup for data-driven registries +PlaceholderLookupProvider.UniversalLookup.getOrCreate skip:data datagen placeholder lookup for data-driven registries +PngInfo.fromStream missing minor: no PNG IHDR or signature check in crates (grep finds none); the image decoder rejects bad files +PngInfo.validateHeader missing minor: no PNG IHDR or signature check in crates (grep finds none); the image decoder rejects bad files +PngInfo.validateBounds missing minor: no 16-bit buffer bound check in crates; the image decoder enforces its own limits +ProblemReporter.Collector.hasFatalProblems skip:other log text for load problems; Horizon's loaders report errors through their own results +ProblemReporter.Collector.forEach skip:other log text for load problems; Horizon's loaders report errors through their own results +ProblemReporter.Collector.getReport skip:other log text for load problems; Horizon's loaders report errors through their own results +ProblemReporter.Collector.getTreeReport skip:other log text for load problems; Horizon's loaders report errors through their own results +ProblemReporter.Collector.ProblemTreeNode.getLines skip:other log text for load problems; Horizon's loaders report errors through their own results +ProblemReporter.ScopedCollector.close skip:other log text for load problems; Horizon's loaders report errors through their own results +RandomSource.createThreadLocalInstance port crates/horizon-client/src/game/dispenser.rs::entity_random gap: minor: seeds from a uniquifier and the clock, not ThreadLocalRandom +SegmentedAnglePrecision.isSameAxis skip:other minor: no caller in 26.3 (dead helper) +SegmentedAnglePrecision.fromDirection port crates/horizon-world/src/level/place.rs::rotation_segment_of +SegmentedAnglePrecision.toDegrees inline crates/horizon-client/src/block_entity_render.rs::classify the segment-to-angle step (segment * 22.5); minor: no wrap to -180..180, same rotation +SequencedPriorityIterator.add inline crates/horizon-world/src/worldgen/structure/jigsaw.rs::Placer::children the priority queue insert is inside children +SequencedPriorityIterator.computeNext port crates/horizon-world/src/worldgen/structure/jigsaw.rs::Placer::next gap: minor: the queue is a BTreeMap, not a cache that switches queues +SequencedPriorityIterator.switchCacheToNextHighestPrioQueue inline crates/horizon-world/src/worldgen/structure/jigsaw.rs::Placer::next BTreeMap last_entry finds the highest priority queue +SignatureValidator.verifySignature port crates/horizon-protocol/src/rsa.rs::RsaPublicKey::verify +Signer.from missing unsure: no private-key chat signing exists in crates (grep finds none); unknown whether a secure-chat hub refuses a guest's unsigned messages +SimpleBitStorage. port crates/horizon-core/src/paletted.rs::pack_words packs palette ids into longs, values never spanning a long +SimpleBitStorage.cellIndex port crates/horizon-core/src/paletted.rs::locate the word index of an entry +SimpleBitStorage.getAndSet port crates/horizon-core/src/paletted.rs::PalettedContainer::set gap: minor: also grows the palette; the raw word write is write +SimpleBitStorage.set port crates/horizon-core/src/paletted.rs::write raw word write; no palette growth +SimpleBitStorage.fill port crates/horizon-core/src/paletted.rs::PalettedContainer::new gap: minor: builds a single-value container instead of overwriting each word +SimpleBitStorage.get port crates/horizon-core/src/paletted.rs::read raw word read +SimpleBitStorage.getAll port crates/horizon-core/src/paletted.rs::PalettedContainer::unpack gap: minor: yields palette-resolved values; vanilla yields raw indices +SimpleBitStorage.unpack port crates/horizon-core/src/paletted.rs::PalettedContainer::unpack gap: minor: yields palette-resolved values; vanilla yields raw indices +SimpleBitStorage.copy skip:java Rust Clone on the container +SingleKeyCache.getValue skip:java one-entry memo; Horizon computes the value directly where it needs it diff --git a/tools/port_map/rest-023.tsv b/tools/port_map/rest-023.tsv new file mode 100644 index 0000000000..ae18a2ce5e --- /dev/null +++ b/tools/port_map/rest-023.tsv @@ -0,0 +1,81 @@ +SortedArraySet skip:java sorted unique array list, a java.util collection; Horizon keeps its own ordered containers +SortedArraySet.ArrayIterator skip:java iterator of the sorted array set above +StaticCache2D skip:java chunk-neighbourhood cache; plain arrays in Horizon +StringDecomposer.feedChar inline crates/horizon-client/src/screen_draw.rs::formatted gap: minor: no surrogate replacement; Rust chars are always valid, so each char is passed on as is +StringDecomposer.iterate port crates/horizon-client/src/screen_draw.rs::formatted returns every drawn char with its index and style at once; no early stop, no surrogate handling needed +StringDecomposer.iterateBackwards port crates/horizon-client/src/font.rs::Font::fit_reverse counts characters from the end that fit the width (plain text, no style walk backwards) +StringDecomposer.filterBrokenSurrogates skip:java strips lone UTF-16 surrogates; Rust strings are valid UTF-8 already (java_text.rs keeps the replacement) +StringRepresentable skip:java codec and name-lookup builders; Horizon matches enum names in its own tables +StringRepresentable.EnumCodec skip:java codec byName lookup +StringUtil.truncateStringIfNecessary port crates/horizon-client/src/screen_draw.rs::TextField::set_value gap: minor: no '...' suffix variant (only the brain-dump debug text uses it); cuts by characters +StringUtil.lineCount skip:other no caller in 26.3 (dead helper) +StringUtil.isBlank port crates/horizon-client/src/title.rs::Title::buttons Save of the rename screen is active only for a non-blank name; trim() uses Unicode whitespace, a tiny difference from Java's isWhitespace +ThreadingDetector skip:debug debug check that two threads enter the same structure; Rust's ownership rules rule it out +TickThrottler.isUnderThreshold port crates/horizon-client/src/game/lan_actions.rs::Game::lan_guest_packet gap: player: chat and command spam kicks (disconnect.spam) are missing; the throttle only guards creative drops (limit 1480, decays in tick_guests) +Util.makeDescriptionId inline crates/horizon-client/src/game/commands.rs::Game::stack_display_name gap: minor: each caller builds its own item, block or entity key (creative.rs, brewing.rs, stats_screen.rs, combat.rs); no shared key builder +Util.makeExecutor skip:java background ForkJoin pool with thread names +Util.getMaxThreads skip:java max.bg.threads pool size; Horizon sizes its own pools +Util.makeIoExecutor skip:java cached IO thread pool +Util.throwAsRuntime skip:java Java checked-exception rethrow +Util.onThreadException port crates/horizon-client/src/crash.rs::install gap: player: vanilla then relays a crash report to the main thread and the game ends; Horizon only logs an error from a worker thread +Util.doFetchChoiceType skip:data DataFixer choice-type lookup; Horizon migrates saves in horizon-world datafix +Util.runNamed skip:debug renames the thread and opens a Tracy zone +Util.getRegisteredName skip:debug registry key text for logs and debug output (Ticket.toString) +Util.allOf skip:java predicate combinator +Util.anyOf skip:java predicate combinator +Util.growByHalf skip:java array growth; Rust Vec grows itself +Util.findNextInIterable missing player: an op with a debug stick in creative right-clicks and no block state property cycles to the next value; Horizon has no DebugStickItem behaviour (only datafix and item model names) +Util.findPreviousInIterable missing player: the same debug stick sneak-click does not cycle a property backwards; Horizon has no DebugStickItem behaviour +Util.makeEnumMap skip:java EnumMap builder +Util.mapValues skip:java map stream helper +Util.sequence skip:java future combinator +Util.sequenceFailFast skip:java future combinator +Util.sequenceFailFastAndCancel skip:java future combinator +Util.fallibleSequence skip:java future combinator +Util.ifElse skip:java Optional helper +Util.logAndPauseIfInIde skip:debug IDE breakpoint pause +Util.pauseInIde skip:debug IDE breakpoint pause +Util.doPause skip:debug IDE breakpoint pause +Util.describeError skip:debug error text for game test reports and FreeType errors +Util.executeInSequence missing minor: the steps of a file move are not retried as a unit; a failed move is reported once +Util.runWithRetries missing minor: no retry loop (10 attempts) around file moves for Windows file locks; Horizon's save and datafix moves do not retry +Util.safeMoveFile port crates/horizon-world/src/datafix/files.rs::move_file gap: minor: one attempt, no 10-try retry; still refuses to overwrite an existing target +Util.safeReplaceOrMoveFile port crates/horizon-client/src/save.rs::write_dat gap: minor: no retries and no rollback to the backup when the new file cannot be renamed in; the old file is moved to a *_old name +Util.offsetByCodepoints inline crates/horizon-client/src/screen_draw.rs::TextField::key gap: minor: arrow keys move one character only (no count argument, no surrogate pairs); ctrl-arrows use ui word_position +Util.startTimerHackThread skip:other JVM Windows timer-resolution thread; Rust timers do not need it +Util.copyBetweenDirs missing player: the create-world screen has no Data Packs step, so packs a player would pick are never copied into the new world (title/create_world.rs:289 says it has no data pack screen yet) +Util.sanitizeName skip:other builds the texture cache path of a world icon; Horizon reads icon.png straight from the save folder (title.rs) +Util.memoize skip:java memoizing function wrapper +Util.blockUntilDone skip:java waits on executor futures during startup (game test server) +Util.createIndexLookup skip:java index lookup for codecs +Util.createIndexIdentityLookup skip:java index lookup for codecs +Util.readTypedOrThrow skip:data DataFixer typed read +Util.copyAndAdd skip:java immutable list copy +Util.join skip:java immutable list concatenation +Util.copyAndPut skip:java immutable map copy +ZeroBitStorage.getAndSet port crates/horizon-core/src/paletted.rs::PalettedContainer::set single-value palette: Palette::Single returns its one value +ZeroBitStorage.set port crates/horizon-core/src/paletted.rs::PalettedContainer::set single-value palette: the stored value is the only one, nothing to write +ZeroBitStorage.get port crates/horizon-core/src/paletted.rs::PalettedContainer::get single-value palette: returns the one value +ZeroBitStorage.getAll port crates/horizon-core/src/paletted.rs::PalettedContainer::unpack single-value palette: unpack writes the one value for every entry +ContextKeySet.toString skip:debug debug text of a parameter set +ContextKeySet.Builder skip:data loot context parameter-set declarations; Horizon loot.rs takes fixed EntityLootParams +ContextMap.getOrThrow skip:java map accessor that throws +ContextMap.Builder skip:java builder for a loot context map +DebugBeeInfo.hasHive skip:debug F3 bee debug renderer data +DebugBrainDump skip:debug brain dump for the F3 debug renderer +DebugHiveInfo.pack skip:debug F3 beehive debug renderer data +DebugSubscription.Event skip:java packet codec for debug subscription values; Horizon has no debug subscriptions +DebugSubscription.Update skip:java packet codec for debug subscription updates; Horizon has no debug subscriptions +DebugSubscriptions.registerTemporaryValue skip:debug registers a debug subscription; Horizon has only the request packet id +LevelDebugSynchronizers skip:debug server push of F3 debug renderer data; Horizon has no debug subscriptions +ServerDebugSubscribers skip:debug tracks which players opted into debug renderers; Horizon has none +TrackingDebugSynchronizer skip:debug F3 debug renderer data sync; Horizon has none +TrackingDebugSynchronizer.PoiSynchronizer skip:debug F3 POI debug renderer data sync; Horizon has none +TrackingDebugSynchronizer.SourceSynchronizer skip:debug F3 debug value sync; Horizon has none +TrackingDebugSynchronizer.ValueSource skip:debug F3 debug value polling; Horizon has none +TrackingDebugSynchronizer.VillageSectionSynchronizer skip:debug F3 village section debug renderer data; Horizon has none +AbstractSampleLogger.logFullSample port crates/horizon-client/src/debug_chart.rs::SampleLogger::log_full keeps the last 240 samples, oldest dropped +AbstractSampleLogger.logPartialSample missing minor: no per-dimension partial writes (no callers in 26.3); SampleLogger::log_full takes the four values whole +AbstractSampleLogger.resetSample skip:other resets the working sample buffer; Horizon passes each full sample, nothing to reset +LocalSampleLogger.useSample port crates/horizon-client/src/debug_chart.rs::SampleLogger::log_full copies the working sample into the 240-entry ring +RemoteSampleLogger.useSample skip:debug sends chart samples to debug subscribers; Horizon has no debug subscriptions diff --git a/tools/port_map/rest-024.tsv b/tools/port_map/rest-024.tsv new file mode 100644 index 0000000000..e4dbbf3e65 --- /dev/null +++ b/tools/port_map/rest-024.tsv @@ -0,0 +1,150 @@ +EventLogDirectory.open skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.listFiles skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.parseFile skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.tryCompress skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.writeCompressed skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.createNewFile skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.CompressedFile.openReader skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.FileId.parse skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.FileList.prune skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.FileList.compressAll skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.RawFile.openChannel skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.RawFile.openReader skip:service telemetry log directory (TelemetryLogManager); not gameplay +EventLogDirectory.RawFile.compress skip:service telemetry log directory (TelemetryLogManager); not gameplay +JsonEventLog.open port crates/horizon-client/src/server_packs/download.rs::append_log gap: minor: log.json is opened per entry, not once per queue +JsonEventLog.openReader skip:other no vanilla caller; nothing reads download log.json back +JsonEventLogReader.create skip:other reader used only by JsonEventLog.openReader, which has no callers +MinecraftServerStatistics.registerJmxMonitoring skip:server JMX MBean registration +MinecraftServerStatistics.getAttribute skip:server JMX MBean attribute +MinecraftServerStatistics.getAttributes skip:server JMX MBean attributes +MinecraftServerStatistics.AttributeDescription.asMBeanAttributeInfo skip:server JMX MBean attribute info +CachedParseState.getCacheForPosition skip:java per-position memo array; Horizon memoises in a HashMap keyed by (position, rule) in State::parse +CachedParseState.acquireControl inline crates/horizon-command/src/packrat.rs::State::term per-alternative cut flag; State::term Alt arm pushes one onto controls +CachedParseState.PositionCache.findKeyIndex skip:java per-position cache lookup; State::parse uses a HashMap +CachedParseState.PositionCache.allocateNewEntry skip:java per-position cache storage; State::parse uses a HashMap +DelayedException.create port crates/horizon-command/src/packrat.rs::State::first_error Reason::Delayed keeps the key and args; first_error builds the exception at the error cursor +Dictionary.put port crates/horizon-command/src/packrat.rs::Grammar::put rejects a rule defined twice, as vanilla does +Dictionary.checkAllBound skip:debug grammar validation; State::parse panics on an unbound rule when reached +Dictionary.getOrThrow skip:other no vanilla caller; Grammar::id resolves names +Dictionary.Reference.parse port crates/horizon-command/src/packrat.rs::State::term Named arm: parses the rule and stores its value under the name +ErrorCollector.LongestOnly.discardErrorsFromShorterParse port crates/horizon-command/src/packrat.rs::State::discard_shorter +ErrorCollector.LongestOnly.store port crates/horizon-command/src/packrat.rs::State::store +ErrorCollector.LongestOnly.addErrorEntry inline crates/horizon-command/src/packrat.rs::State::store stored errors are pushed to State::errors at the last cursor +ErrorCollector.LongestOnly.entries inline crates/horizon-command/src/packrat.rs::Grammar::parse_for_suggestions_with gap: minor: the furthest-cursor entries are read here for suggestions; command errors take the first in State::first_error +ParseState.parseTopRule port crates/horizon-command/src/packrat.rs::State::parse_top gap: minor: no single-frame scope check +Rule.fromTerm port crates/horizon-command/src/packrat.rs::Grammar::put_term Rule::Term holds the term and its action +Rule.WrappedTerm.parse port crates/horizon-command/src/packrat.rs::State::parse Term arm: push frame, run term, run action, pop frame +Scope.valueIndexForAny port crates/horizon-command/src/packrat.rs::Scope::value_index +Scope.ensureCapacity skip:java growable stack array +Scope.getOrThrow port crates/horizon-command/src/packrat.rs::Scope::get returns None instead of throwing; callers unwrap +Scope.getOrDefault port crates/horizon-command/src/packrat.rs::Scope::get gap: minor: no fallback argument; callers supply unwrap_or +Scope.getAnyOrThrow port crates/horizon-command/src/packrat.rs::Scope::get_any returns None instead of throwing +Scope.toString skip:debug debug text of the scope stack +Scope.lastFrame skip:other no vanilla caller; unused helper +Scope.hasOnlySingleFrame skip:debug invariant check used only by parseTopRule +Scope.validateStructure skip:debug assert-only structure check +Term.repeated skip:java factory; Horizon builds Term::Repeated directly in snbt.rs +Term.repeatedWithTrailingSeparator skip:java factory; Horizon builds Term::RepeatedSep directly in snbt.rs +Term.repeatedWithoutTrailingSeparator skip:java factory; Horizon builds Term::RepeatedSep directly in snbt.rs +Term.Alternative.parse port crates/horizon-command/src/packrat.rs::State::term one arm of State::term +Term.LookAhead.parse port crates/horizon-command/src/packrat.rs::State::term one arm of State::term +Term.Maybe.parse port crates/horizon-command/src/packrat.rs::State::term one arm of State::term +Term.Repeated.parse port crates/horizon-command/src/packrat.rs::State::term one arm of State::term +Term.RepeatedWithSeparator.parse port crates/horizon-command/src/packrat.rs::State::term one arm of State::term +Term.Sequence.parse port crates/horizon-command/src/packrat.rs::State::term one arm of State::term +GreedyPatternParseRule.parse port crates/horizon-command/src/snbt.rs::greedy gap: minor: same character class as UNICODE_NAME; the Character.codePointOf step is missing, so \N{...} names always fail +GreedyPredicateParseRule.parse port crates/horizon-command/src/snbt.rs::greedy +NumberRunParseRule.parse port crates/horizon-command/src/snbt.rs::number_run +StringReaderTerms.TerminalCharacters.parse port crates/horizon-command/src/packrat.rs::State::term Chars arm +StringReaderTerms.TerminalWord.parse port crates/horizon-command/src/packrat.rs::State::term Word arm +TagParseRule.parse port crates/horizon-command/src/snbt.rs::parse_tag_argument gap: minor: the parser error is returned, not stored at the mark in the packrat state +UnquotedStringParseRule.parse port crates/horizon-command/src/snbt.rs::grammar custom rule 'unquoted_string': non-empty check only, no minimum-size parameter +net/minecraft/util/random/Weighted#codec skip:java codec; WeightedList::decode_inner reads data and weight per entry +WeightedList.map port crates/horizon-assets/src/blockstate/choice.rs::Choice::map keeps weights and maps values; used for variant model lists +WeightedList.entryToNonEmptyListCodec port crates/horizon-world/src/worldgen/weighted.rs::WeightedList::build rejects a list whose total weight is zero +WeightedList.contains missing minor: unsure: canSpawnMobAt checks a pack's mob type is still in the mobs list at the new position; Horizon's group loop (spawn_selection.rs ~1079-1156) carries the entry and no membership test was found +WeightedList.equals skip:java equality of a Java collection +WeightedList.hashCode skip:java hash of a Java collection +WeightedList.Builder.add skip:java builder; WeightedList::new takes (value, weight) pairs +WeightedList.Compact.get inline crates/horizon-world/src/worldgen/weighted.rs::WeightedList::sample cumulative walk; sample() draws and walks +WeightedRandom.getTotalWeight port crates/horizon-world/src/worldgen/weighted.rs::WeightedList::build checked total weight; error instead of throwing above i32 max +WeightedRandom.getRandomItem port crates/horizon-world/src/worldgen/weighted.rs::OptionalWeightedList::sample no draw and None when the total is zero, else one draw +WeightedRandom.getWeightedItem inline crates/horizon-world/src/worldgen/weighted.rs::WeightedList::sample cumulative search after the draw +AbstractConsecutiveExecutor.canBeScheduled skip:other Java executor queue plumbing; Horizon threads are Rust threads and channels +AbstractConsecutiveExecutor.pollTask skip:other Java executor queue plumbing; Horizon threads are Rust threads and channels +AbstractConsecutiveExecutor.run skip:other Java executor queue plumbing; Horizon threads are Rust threads and channels +AbstractConsecutiveExecutor.runAll skip:other Java executor queue plumbing; Horizon threads are Rust threads and channels +AbstractConsecutiveExecutor.registerForExecution skip:other Java executor queue plumbing; Horizon threads are Rust threads and channels +AbstractConsecutiveExecutor.hasWork skip:other Java executor queue plumbing; Horizon threads are Rust threads and channels +AbstractConsecutiveExecutor.toString skip:debug debug text +BlockableEventLoop.submit skip:other Java task queue of the server thread; Horizon has no BlockableEventLoop equivalent +BlockableEventLoop.executeBlocking skip:other Java task queue of the server thread; Horizon has no BlockableEventLoop equivalent +BlockableEventLoop.execute skip:other Java task queue of the server thread; Horizon has no BlockableEventLoop equivalent +BlockableEventLoop.pollTask skip:other Java task queue of the server thread; Horizon has no BlockableEventLoop equivalent +BlockableEventLoop.managedBlock skip:other Java task queue of the server thread; Horizon has no BlockableEventLoop equivalent +BlockableEventLoop.doRunTask skip:other Java task queue of the server thread; Horizon has no BlockableEventLoop equivalent +BlockableEventLoop.isNonRecoverable skip:other classifies Java Throwables (OutOfMemory, StackOverflow); Rust aborts instead +BlockableEventLoop.throwDelayedException skip:debug crash report relay between threads +BlockableEventLoop.relayDelayCrash skip:debug crash report relay between threads +ParallelMapTransform.schedule skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.BatchedTaskSplitter.scheduleBatch skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.BatchedTaskSplitter.batchSize skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.BatchedTaskSplitter.createTask skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.BatchedTaskSplitter.scheduleFinalOperation skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.Container.put skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.Container.applyOperation skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.Container.copyOut skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.SingleTaskSplitter.scheduleBatch skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.SingleTaskSplitter.scheduleFinalOperation skip:other parallel map over Java futures; Horizon uses Rust threads +ParallelMapTransform.SplitterBase.scheduleTasks skip:other parallel map over Java futures; Horizon uses Rust threads +PriorityConsecutiveExecutor.scheduleWithResult skip:other Java priority executor plumbing +ReentrantBlockableEventLoop.doRunTask skip:other reentrancy counter of the Java task loop +StrictQueue.FixedPriorityQueue.pop skip:other Java priority queue plumbing +StrictQueue.FixedPriorityQueue.push skip:other Java priority queue plumbing +TaskScheduler.scheduleWithResult skip:other Java future plumbing over schedule() +BiasedToBottomInt.sample port crates/horizon-world/src/worldgen/value_provider.rs::IntProvider::sample BiasedToBottom arm +ClampedNormalFloat.sample port crates/horizon-world/src/worldgen/value_provider.rs::FloatProvider::sample ClampedNormal arm +ClampedNormalInt.sample port crates/horizon-world/src/worldgen/value_provider.rs::IntProvider::sample ClampedNormal arm +ConstantFloat.of port crates/horizon-world/src/worldgen/value_provider.rs::FloatProvider::from_json Constant arm normalises -0.0 to 0.0 as vanilla's ZERO does +FloatProviders.codec missing minor: unsure: min/max range check is per caller in Horizon (carver.rs:80-89 checks a few bounds); no generic check +IntProviders.validateCodec missing minor: unsure: min/max range check is per caller in Horizon; no generic check +IntProviders.validate missing minor: unsure: min/max range check is per caller in Horizon; no generic check +MultipliedFloats.sample missing minor: unsure: SoundManager multiplies a wrapped sound's volume and pitch by the outer sound's; no nested product found in Horizon +TrapezoidFloat.sample port crates/horizon-world/src/worldgen/value_provider.rs::FloatProvider::sample Trapezoid arm +TrapezoidInt.sample port crates/horizon-world/src/worldgen/value_provider.rs::IntProvider::sample Trapezoid arm +UniformFloat.of port crates/horizon-world/src/worldgen/value_provider.rs::FloatProvider::from_json Uniform arm rejects max <= min +VeryBiasedToBottomInt.sample port crates/horizon-world/src/worldgen/value_provider.rs::IntProvider::sample BiasedToBottom arm with the extra draw +RegionStorageUpgrader.init inline crates/horizon-world/src/datafix/files.rs::upgrade_regions gap: minor: region folder and storage are set up per call; no progress object +RegionStorageUpgrader.upgrade port crates/horizon-world/src/datafix/files.rs::upgrade_regions gap: minor: no progress, cancel or recreate mode +RegionStorageUpgrader.createStorage inline crates/horizon-world/src/datafix/files.rs::upgrade_regions gap: minor: in-place storage only; the recreate-region-files option is not ported +RegionStorageUpgrader.getFilesToProcess inline crates/horizon-world/src/datafix/files.rs::upgrade_regions lists the .mca files; progress totals are not kept +RegionStorageUpgrader.getAllChunkPositions inline crates/horizon-world/src/datafix/files.rs::upgrade_regions walks region files and their chunks +RegionStorageUpgrader.processOnePosition inline crates/horizon-world/src/datafix/files.rs::upgrade_regions per-chunk fix through the closure; counts are not kept +RegionStorageUpgrader.tryProcessOnePosition inline crates/horizon-world/src/datafix/files.rs::upgrade_regions gap: minor: every chunk is rewritten, not only the changed ones +RegionStorageUpgrader.onFileFinished inline crates/horizon-world/src/datafix/files.rs::upgrade_regions rename of the staged file over the old one; recreate mode absent +RegionStorageUpgrader.Builder.addTagModifier skip:java builder +RegionStorageUpgrader.Builder.copy skip:java builder +RegionStorageUpgrader.Builder.build skip:java builder +UpgradeProgress.addTotalFileFixOperations skip:ui progress counters for an upgrade progress bar Horizon does not show +UpgradeProgress.incrementFinishedOperationsBy skip:ui progress counters for an upgrade progress bar Horizon does not show +UpgradeProgress.reset skip:ui progress counters for an upgrade progress bar Horizon does not show +UpgradeProgress.logProgress skip:other logs progress and sends JSON-RPC notifications; Horizon upgrades synchronously +UpgradeProgress.FileFixStats.getProgress skip:ui progress fraction for the upgrade screen +UpgradeStatusTranslator.translate skip:ui translatable status text for the upgrade screen +UpgradeStatusTranslator.Messages.forStatus skip:ui translatable status text for the upgrade screen +WorldUpgrader.getDataFixContextTag port crates/horizon-world/src/datafix/files.rs::generators gap: minor: returns (dimension, generator type) pairs, not the context compound +WorldUpgrader.verifyChunkPosAndEraseCache missing minor: Optimize World's cache erase (Heightmaps, isLightOn, BlockLight, SkyLight) is not in Horizon's datafix +WorldUpgrader.verifyChunkPos skip:other only logs a warning for a chunk with a wrong stored position +WorldUpgrader.work port crates/horizon-world/src/datafix/files.rs::upgrade_world gap: minor: no Optimize World pass and no progress; upgrade_world runs the file fixes then the data upgrade +WorldUpgrader.upgradeLevels inline crates/horizon-world/src/datafix/files.rs::upgrade_world per-dimension loop folded into upgrade_world +LongJumpUtil.isClearTransition missing minor: unsure: only the frog and goat long jumps (LongJumpToRandomPos) call it; Horizon has no such jump (frog.rs has only Croak and ShootTongue) +Cushion.isAnchorBuried inline crates/horizon-client/src/cushion.rs::can_place the resting-slab test; can_place returns false when the slab is covered +FishingHook.calculateOpenWater port crates/horizon-client/src/game/fishing.rs::Game::calculate_open_water +AbstractContainerMenu.getRedstoneSignalFromBlockEntity port crates/horizon-client/src/game/machines.rs::container_signal block entity containers for the comparator; 0 when there is none +CreativeModeTabs.validate skip:data bootstrap check of creative tab positions +BundleContents.Mutable.removeOne port crates/horizon-items/src/bundle.rs::Contents::remove_one selected stack or the first; nothing is selected afterwards +LevelAccessor.neighborShapeChanged port crates/horizon-world/src/level/update.rs::Sim::neighbor_shape_changed minor: unsure: updateLimit - 1 is applied by the callers (set_block_limit), not here; redstone.rs passes its limit straight through +LevelHeightAccessor.isInsideBuildHeight port crates/horizon-world/src/level/query.rs::Sim::is_inside_build_height takes the y; the BlockPos overload is the caller's pos.y +Block.shouldRenderFace port crates/horizon-world/src/block_render.rs::FaceVisibility::should_render_face gap: minor: uses precomputed face flags and the cutout flag, not vanilla's EmptyBlockGetter path +MultifaceBlock.availableFaces port crates/horizon-world/src/level/sculk.rs::Sim::available_faces +BeehiveBlockEntity.tickOccupants port crates/horizon-client/src/beehive.rs::Beehive::server_tick ticks each bee and releases the finished ones in order diff --git a/tools/port_map/rest-025.tsv b/tools/port_map/rest-025.tsv new file mode 100644 index 0000000000..5b32530239 --- /dev/null +++ b/tools/port_map/rest-025.tsv @@ -0,0 +1,14 @@ +PistonMovingBlockEntity.moveCollidedEntities port crates/horizon-client/src/game/piston_push.rs::Game::push_for_piston push loop; per-box distance from crates/horizon-world/src/piston_push.rs::collided_amount +SideChainPart.whenConnectedToTheRight port crates/horizon-world/src/level/shelf.rs::Part::connected_to_the_right same arms as vanilla +SideChainPart.whenConnectedToTheLeft port crates/horizon-world/src/level/shelf.rs::Part::connected_to_the_left same arms as vanilla +ProtoChunk.getFluidState port crates/horizon-world/src/worldgen/feature_region.rs::TerrainRegion::fluid_state gap: minor: read through the region's block queries; no chunk-level accessor, fluid comes from the block state +ProtoChunk.getNoiseBiome port crates/horizon-world/src/worldgen/generator.rs::ProtoChunk::biome_id_at gap: minor: takes block coordinates (quart shift inside); no check that the BIOMES status was reached, vanilla throws before it +StructureTemplate.Palette.jigsaws port crates/horizon-world/src/worldgen/structure/template.rs::StructureTemplate::jigsaws per-palette OnceLock stands in for cachedJigsaws +SkyLightEngine.updateSourcesInColumn inline crates/horizon-client/src/light.rs::relight_region column loop finds the lowest sky source and reseeds cells above and below it +FlyNodeEvaluator.findAcceptedNode port crates/horizon-client/src/mobs/path.rs::Finder::fly_accepted cost, walkable +1 and node update match vanilla +LevelStorageSource.writeGameRules port crates/horizon-client-core/src/gamerules.rs::GameRules::save gap: minor: writes in place with no temp-file swap; no runtime caller, the game saves rules through level.game_rules in level.dat +LevelStorageSource.readLevelSummary port crates/horizon-client/src/title.rs::list_worlds gap: player: a world whose level.dat cannot be read is left out of the list; vanilla lists it as corrupted; symlinked worlds are not flagged +LevelStorageSource.readLightweightData port crates/horizon-client/src/save.rs::read_dat gap: minor: reads the whole level.dat; Player and WorldGenSettings are not skipped +LevelStorageSource.validateAndCreateAccess missing minor: opening a world does not refuse a folder holding symbolic links, vanilla does; backup and delete do check links (title/world_management.rs) +SavedDataStorage.scheduleSave inline crates/horizon-client/src/game.rs::Game::save_game_metadata gap: minor: each saved-data file is written directly on every save; no dirty check and no async batching +TrackedWaypoint.ChunkWaypoint.writeContents port crates/horizon-protocol/src/packets/play.rs::TrackedWaypoint::write chunk arm writes varint 2, then x and z diff --git a/tools/port_publish.py b/tools/port_publish.py index 3541c0f978..5213a49f69 100644 --- a/tools/port_publish.py +++ b/tools/port_publish.py @@ -115,7 +115,8 @@ def body(item, report, diff, store_path, limit=25): for c, counts in sorted(details["items"].items(), key=lambda kv: -sum(kv[1].values()))[:limit]] if item["kind"] == "facts": lines += [f"- {key}: {a} → {b}" for key, (a, b) in details.items()] - where = str(store_path).replace(str(Path.home()), "~") + # A body is the same on every machine: Posix separators, `~` for the home. + where = Path(store_path).as_posix().replace(Path.home().as_posix(), "~") lines += ["", f"The full diff stays on the maintainer's machine (`{where}`), with the vanilla files it was built " "from: they are never published.", END] text = "\n".join(lines) + "\n" diff --git a/tools/pr_ready.py b/tools/pr_ready.py index d4b97023fe..fe1b1b0da5 100755 --- a/tools/pr_ready.py +++ b/tools/pr_ready.py @@ -292,7 +292,7 @@ def mod_features(crates): # What horizon-client's full content-root tests load (mod_content_tests.rs). They take minutes on prepared data, so # they are #[ignore]d and run here only when one of these changes. -CONTENT_ROOT = ("crates/horizon-client/src/mod_content", "crates/horizon-client/src/assets.rs", "crates/horizon-items/", +CONTENT_ROOT = ("crates/horizon-client/src/mod_content", "crates/horizon-client-render/src/assets.rs", "crates/horizon-items/", "crates/horizon-mods/", "crates/horizon-assets/") CONTENT_ROOT_TESTS = "cargo test --locked -p horizon-client --features horizon-client/mods --lib mod_content::tests:: -- --ignored" diff --git a/tools/roadmap.py b/tools/roadmap.py index b13511a6eb..f442c9b25d 100644 --- a/tools/roadmap.py +++ b/tools/roadmap.py @@ -245,7 +245,7 @@ def weeks(directory=JOURNAL): entries, problems = [], [] for path in sorted(directory.glob("*.md"), reverse=True) if directory.exists() else []: name = WEEK.match(path.name) - text = path.read_text() + text = path.read_text(encoding="utf-8") parts = re.match(r"# (.+)\n\n(.+?)\n\n## Technical\n\n(.+)", text.strip() + "\n", re.S) if not name or not 1 <= int(name[2]) <= 53: problems.append(f"journal/{path.name}: name it by its ISO week, like 2026-43.md") @@ -525,12 +525,12 @@ def command_build(args): return report(problems) files = outputs(data) if args.check: - stale = [path for path, text in files.items() if not path.exists() or path.read_text() != text] + stale = [path for path, text in files.items() if not path.exists() or path.read_text(encoding="utf-8") != text] return report([f"{path.relative_to(ROOT)} is out of date: run `python3 tools/roadmap.py build`" for path in stale]) for path, text in files.items(): path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(text) + path.write_text(text, encoding="utf-8") print(f"roadmap: wrote {path.relative_to(ROOT)}") return 0 @@ -555,17 +555,17 @@ def command_sync(args): if problems: return report(problems) subprocess.run(["git", "fetch", "--quiet", "--tags", "origin"], cwd=ROOT, check=False) - text, log = sync(data, SOURCE.read_text(), github_states(numbers_in(data), token())) + text, log = sync(data, SOURCE.read_text(encoding="utf-8"), github_states(numbers_in(data), token())) for line in log: print(f"roadmap: {line}") - if args.dry_run or text == SOURCE.read_text(): + if args.dry_run or text == SOURCE.read_text(encoding="utf-8"): return 0 new = tomllib.loads(text) if len(new.get("item", [])) != len(data.get("item", [])) or validate(new): return report(["sync produced an invalid file; nothing written"] + validate(new)) - SOURCE.write_text(text) + SOURCE.write_text(text, encoding="utf-8") for path, content in outputs(new).items(): - path.write_text(content) + path.write_text(content, encoding="utf-8") print("roadmap: wrote roadmap/roadmap.toml, ROADMAP.md and roadmap/roadmap.json") return 0 diff --git a/tools/test_arrow_parity.py b/tools/test_arrow_parity.py index ab6e690bec..4ee072775a 100644 --- a/tools/test_arrow_parity.py +++ b/tools/test_arrow_parity.py @@ -225,7 +225,7 @@ def main(): 'VanillaFaceSupportOracle', 'VanillaEntityAttributeOracle', 'VanillaBlockLandingOracle', 'VanillaMovementRegistryOracle', 'VanillaDamageSourceOracle']] sources = [Path(__file__), lab.ROOT / 'tools/test_native_client.py', lab.ROOT / 'tools/test_rust_parity.py', lab.ROOT / 'tools/lab.py', oracle, *helpers, lab.ROOT / 'Cargo.toml', lab.ROOT / 'Cargo.lock', lab.ROOT / 'crates/horizon-client/examples/probe/arrow_probe.rs'] - for crate in ['horizon-core', 'horizon-world', 'horizon-assets', 'horizon-client']: + for crate in ['horizon-core', 'horizon-world', 'horizon-assets', 'horizon-client', 'horizon-client-render']: sources += [lab.ROOT / 'crates' / crate / 'Cargo.toml', *sorted((lab.ROOT / 'crates' / crate / 'src').rglob('*.rs'))] tested = base.source_hashes(sources) def run(command, name): diff --git a/tools/test_block_particles_parity.py b/tools/test_block_particles_parity.py index ff50bd7456..13a10ebc68 100644 --- a/tools/test_block_particles_parity.py +++ b/tools/test_block_particles_parity.py @@ -13,7 +13,7 @@ def main(): resources = lab.RESOURCES build = lab.LOCAL / "block-particles-parity" build.mkdir(exist_ok=True) - paths = [lab.ROOT / p for p in ["tools/test_block_particles_parity.py", "tools/test_breaking_model_parity.py", "tools/oracle_harness.py", "tools/lab.py", "crates/horizon-client/src/particles.rs", "crates/horizon-client/src/game.rs", "crates/horizon-items/src/blocks.rs", "crates/horizon-client/src/mesher.rs", "crates/horizon-client/examples/probe/block_particles_fixture.rs", "mods/horizon/src/test/java/dev/horizonlab/parity/VanillaBlockParticlesOracle.java", "crates/horizon-core/src/random.rs", "Cargo.lock", "mods/horizon/src/test/java/dev/horizonlab/parity/VanillaGameplayFactsExporter.java"]] + paths = [lab.ROOT / p for p in ["tools/test_block_particles_parity.py", "tools/test_breaking_model_parity.py", "tools/oracle_harness.py", "tools/lab.py", "crates/horizon-client-render/src/particles.rs", "crates/horizon-client/src/game.rs", "crates/horizon-items/src/blocks.rs", "crates/horizon-client-render/src/mesher.rs", "crates/horizon-client/examples/probe/block_particles_fixture.rs", "mods/horizon/src/test/java/dev/horizonlab/parity/VanillaBlockParticlesOracle.java", "crates/horizon-core/src/random.rs", "Cargo.lock", "mods/horizon/src/test/java/dev/horizonlab/parity/VanillaGameplayFactsExporter.java"]] tested = base.source_hashes(paths) rows = [dict(case=i, seed=(-1 if i % 2 else 1) * i * 987654321, **row) for i, row in enumerate(breaking.cases(resources))] rows[0]["seed"] = -(2**63) diff --git a/tools/test_breaking_model_parity.py b/tools/test_breaking_model_parity.py index 272e57deae..63bebd1f5b 100644 --- a/tools/test_breaking_model_parity.py +++ b/tools/test_breaking_model_parity.py @@ -29,7 +29,7 @@ def canonical(quads): def main(): lock,jars,_=base.verify_artifacts();libraries=baking.client_libraries() resources=lab.RESOURCES;build=lab.LOCAL/"breaking-model-parity";build.mkdir(exist_ok=True) - paths=[lab.ROOT/"tools/test_breaking_model_parity.py",lab.ROOT/"crates/horizon-client/src/breaking.rs",lab.ROOT/"crates/horizon-client/src/game.rs",lab.ROOT/"crates/horizon-client/src/gfx.rs",lab.ROOT/"crates/horizon-client/src/entity.wgsl",lab.ROOT/"crates/horizon-client/src/models/catalog.rs",lab.ROOT/"crates/horizon-client/src/mesher.rs",lab.ROOT/"crates/horizon-client/src/camera.rs",lab.ROOT/"crates/horizon-client/examples/probe/breaking_fixture.rs",lab.ROOT/"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaBreakingModelOracle.java",lab.ROOT/"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaSectionMeshOracle.java",lab.ROOT/"Cargo.lock"] + paths=[lab.ROOT/"tools/test_breaking_model_parity.py",lab.ROOT/"crates/horizon-client-render/src/breaking.rs",lab.ROOT/"crates/horizon-client/src/game.rs",lab.ROOT/"crates/horizon-client-render/src/gfx.rs",lab.ROOT/"crates/horizon-client-render/src/entity.wgsl",lab.ROOT/"crates/horizon-client-render/src/models/catalog.rs",lab.ROOT/"crates/horizon-client-render/src/mesher.rs",lab.ROOT/"crates/horizon-client-render/src/camera.rs",lab.ROOT/"crates/horizon-client/examples/probe/breaking_fixture.rs",lab.ROOT/"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaBreakingModelOracle.java",lab.ROOT/"mods/horizon/src/test/java/dev/horizonlab/parity/VanillaSectionMeshOracle.java",lab.ROOT/"Cargo.lock"] tested=base.source_hashes(paths) rows=[dict(case=i,**row) for i,row in enumerate(cases(resources))] inputs=build/"inputs.jsonl";inputs.write_text("".join(json.dumps(row)+"\n" for row in rows)) diff --git a/tools/test_camera_parity.py b/tools/test_camera_parity.py index bde9ea8974..f931f1a357 100644 --- a/tools/test_camera_parity.py +++ b/tools/test_camera_parity.py @@ -209,7 +209,7 @@ def main(): 'interpolation branch is not exercised.') harness.differential( 'rust-camera-parity', 'VanillaCameraOracle', 'camera_probe', make_cases, scope, - sources=[lab.ROOT / 'crates/horizon-client/src/camera.rs']) + sources=[lab.ROOT / 'crates/horizon-client-render/src/camera.rs']) if __name__ == '__main__': diff --git a/tools/test_chest_pair_parity.py b/tools/test_chest_pair_parity.py new file mode 100644 index 0000000000..5cd1d9648e --- /dev/null +++ b/tools/test_chest_pair_parity.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Compare which chests join into a double chest against vanilla 26.3. + +The Java oracle (VanillaChestPairOracle) puts a chest and another block next +to it, each with its block entity, and runs DoubleBlockCombiner's +combineWithNeigbour as ChestBlock.combine does: the double chest's halves +(the right one first) or a single chest. That pairing decides a chest's +menu (9x6 or 9x3), what a hopper fills and the lid both halves share. The +Rust example (chest_pair_fixture) answers with horizon_client::containers' +ChestHalf, which the game's menus and hoppers use. Compared for chests, +trapped chests and copper chests in every weathering state, waxed or not, +next to each of them and a barrel, with every chest type, two facings and +the block on either side. +""" +import argparse +import json +try: + from . import interaction_lab as lib +except ImportError: + import interaction_lab as lib + +CHESTS = ['chest', 'trapped_chest', 'copper_chest', 'exposed_copper_chest', 'weathered_copper_chest', + 'oxidized_copper_chest', 'waxed_copper_chest', 'waxed_exposed_copper_chest', + 'waxed_weathered_copper_chest', 'waxed_oxidized_copper_chest'] +TYPES = ['single', 'left', 'right'] +# A chest facing north connects east (left) or west (right): ChestBlock.getConnectedDirection. +SIDES = [(1, 0), (-1, 0)] + + +def state(block, facing, kind): + return f'minecraft:{block}[facing={facing},type={kind}]' + + +def cases(): + out = [] + for block in CHESTS: + for kind in TYPES: + own = state(block, 'north', kind) + for at in SIDES: + for other in CHESTS: + for facing in ('north', 'south'): + for other_kind in TYPES: + out.append({'state': own, 'other': state(other, facing, other_kind), 'at': at}) + out.append({'state': own, 'other': 'minecraft:barrel[facing=north]', 'at': at}) + return out + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--limit', type=int, default=4) + args = parser.parse_args() + work = lib.work_dir('chest-pair-parity') + vanilla = lib.Vanilla(work) + vanilla.compile('ParityData', 'VanillaChestPairOracle') + all_cases = cases() + (work / 'cases.jsonl').write_text(''.join(json.dumps(c) + '\n' for c in all_cases), encoding='utf-8') + vanilla.run('VanillaChestPairOracle', work / 'cases.jsonl', work / 'native.txt') + lib.cargo_example('horizon-client', 'chest_pair_fixture', work / 'cases.jsonl', work / 'rust.txt') + errors = sum(line == 'error' for line in (work / 'native.txt').read_text(encoding='utf-8').split('\n')) + if errors: + print(f'{errors} cases fail in vanilla') + total = lib.compare(all_cases, work / 'native.txt', work / 'rust.txt', + lambda case, line: case['state'].split('[')[0] + ' next to ' + case['other'].split('[')[0], + json.dumps, args.limit) + raise SystemExit(1 if total or errors else 0) + + +if __name__ == '__main__': + main() diff --git a/tools/test_cloud_render_parity.py b/tools/test_cloud_render_parity.py index 91f740f914..98dd482be7 100644 --- a/tools/test_cloud_render_parity.py +++ b/tools/test_cloud_render_parity.py @@ -28,7 +28,7 @@ def make_input(build): def main(): harness.differential("cloud-render-parity", "VanillaCloudRenderOracle", "cloud_fixture", make_input, "Fancy cloud face culling, interior winding flags and mesh traversal; not full framebuffer parity.", - sources=[lab.ROOT / "crates/horizon-client/src/clouds.rs"]) + sources=[lab.ROOT / "crates/horizon-client-render/src/clouds.rs"]) if __name__ == "__main__": diff --git a/tools/test_container_title_parity.py b/tools/test_container_title_parity.py new file mode 100644 index 0000000000..df19a4b466 --- /dev/null +++ b/tools/test_container_title_parity.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +"""Compare the titles container menus open with against vanilla 26.3 (#3245). + +The Java oracle (VanillaContainerTitleOracle) loads each container block +entity with BlockEntity.loadStatic, applies the component patch of the item it +was placed from (BlockEntity.applyComponentsFromItemStack: a renamed shulker +box or copper chest gives its custom_name to CustomName), and prints the menu +provider's getDisplayName: BaseContainerBlockEntity.getName, the custom name +or getDefaultName, and for a double chest ChestBlock's MENU_PROVIDER_COMBINER +(the right half's name, else the left's, else container.chestDouble). That is +the title of the ClientboundOpenScreenPacket a server sends, which the +client's screen draws. The Rust example (container_title_fixture) places the +item with horizon_client::block_entity_item as the game does and names the +menu with horizon_client::containers, which the server's +`lan_open_container` sends. Compared for every container block: chests, +trapped and copper chests (every weathering state, waxed too), barrels, +shulker boxes of any colour, dispensers, droppers, hoppers, the three +furnaces, brewing stands and crafters, unnamed, named in their saved data, +placed from a renamed item, and double chests named on either half. +""" +import argparse +import json +try: + from . import interaction_lab as lib +except ImportError: + import interaction_lab as lib + +POS = 'x:0,y:64,z:0' + +# (block, its block entity type). +BLOCKS = [ + ('chest', 'chest'), ('trapped_chest', 'trapped_chest'), + ('copper_chest', 'chest'), ('exposed_copper_chest', 'chest'), ('weathered_copper_chest', 'chest'), + ('oxidized_copper_chest', 'chest'), ('waxed_copper_chest', 'chest'), ('waxed_oxidized_copper_chest', 'chest'), + ('barrel', 'barrel'), ('shulker_box', 'shulker_box'), ('red_shulker_box', 'shulker_box'), + ('black_shulker_box', 'shulker_box'), ('dispenser', 'dispenser'), ('dropper', 'dropper'), ('hopper', 'hopper'), + ('furnace', 'furnace'), ('smoker', 'smoker'), ('blast_furnace', 'blast_furnace'), + ('brewing_stand', 'brewing_stand'), ('crafter', 'crafter'), +] +NAME = '"minecraft:custom_name"' +# (saved NBT body, the placed item's component patch or None). +VARIANTS = [ + ('', None), + ('CustomName:"Loot"', None), + ('', '{%s:"Loot"}' % NAME), + ('', '{%s:"Box of §cred"}' % NAME), + ('CustomName:"Old"', '{%s:"New"}' % NAME), + ('CustomName:"Old"', '{"!minecraft:custom_name":{}}'), + ('', '{"minecraft:lore":["a"]}'), +] +# Double chests: (block, block entity type), then the halves' names. +DOUBLES = [('chest', 'chest'), ('trapped_chest', 'trapped_chest')] +HALVES = [(None, None), ('Right', None), (None, 'Left'), ('Right', 'Left')] + + +def be(type_id, body=''): + return '{id:"minecraft:%s",%s%s}' % (type_id, POS, ',' + body if body else '') + + +def named(type_id, name): + return be(type_id, 'CustomName:"%s"' % name if name else '') + + +def cases(): + out = [] + for block, type_id in BLOCKS: + for body, patch in VARIANTS: + case = {'block': 'minecraft:' + block, 'nbt': be(type_id, body)} + if patch is not None: + case['patch'] = patch + out.append(case) + for block, type_id in DOUBLES: + for first, second in HALVES: + out.append({'block': 'minecraft:' + block, 'first': named(type_id, first), 'second': named(type_id, second)}) + return out + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--limit', type=int, default=4) + args = parser.parse_args() + work = lib.work_dir('container-title-parity') + vanilla = lib.Vanilla(work) + vanilla.compile('ParityData', 'VanillaContainerTitleOracle') + all_cases = cases() + (work / 'cases.jsonl').write_text(''.join(json.dumps(c) + '\n' for c in all_cases), encoding='utf-8') + vanilla.run('VanillaContainerTitleOracle', work / 'cases.jsonl', work / 'native.txt') + lib.cargo_example('horizon-client', 'container_title_fixture', work / 'cases.jsonl', work / 'rust.txt') + errors = sum(line == 'error' for line in (work / 'native.txt').read_text(encoding='utf-8').split('\n')) + if errors: + print(f'{errors} cases fail in vanilla') + total = lib.compare(all_cases, work / 'native.txt', work / 'rust.txt', + lambda case, line: case['block'] + (' double' if 'first' in case else ''), + lambda case: json.dumps(case, ensure_ascii=False), args.limit) + raise SystemExit(1 if total or errors else 0) + + +if __name__ == '__main__': + main() diff --git a/tools/test_datapack_worldgen_parity.py b/tools/test_datapack_worldgen_parity.py new file mode 100644 index 0000000000..8433fbb2e6 --- /dev/null +++ b/tools/test_datapack_worldgen_parity.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +"""Compare chunks generated with a world's data packs block-for-block with vanilla. + +The packs of tools/fixtures/datapacks (Horizon's own, MIT) override and add +noises, density functions, the overworld noise settings, a placed feature and +a biome tag. Vanilla side: VanillaChunkGenerationOracle with HORIZON_DATAPACKS, +which selects every pack of the folder after vanilla, as a world does, and +loads the registries through vanilla's own pack repository. Horizon side: +chunk_dump with the same variable, reading the packs through +worldgen::overrides into GeneratorData::load_with. The same terrain sites are +compared for every block state, quart biome and block entity, first after +TERRAIN, then with decoration. Horizon without the packs must differ from +vanilla with them, so the packs are known to take effect. + +usage: test_datapack_worldgen_parity.py [--seed N] [--sites K] [--phase terrain|full] +""" +import argparse +import os +import random +import sys +from pathlib import Path + +try: + from . import lab, test_chunk_generation_parity as chunks +except ImportError: + import lab + import test_chunk_generation_parity as chunks + +# HORIZON_PARITY_DATAPACKS: another folder of packs to compare. +DATAPACKS = Path(os.environ.get('HORIZON_PARITY_DATAPACKS', lab.ROOT / 'tools/fixtures/datapacks')) +SEEDS = [12345, 4242] + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--seed', type=int, action='append') + parser.add_argument('--sites', type=int, default=3, help='terrain sites per seed besides 0,0') + parser.add_argument('--phase', choices=['terrain', 'full'], action='append') + args = parser.parse_args() + oracle = chunks.Oracle() + binary = chunks.rust_binary() + names = chunks.state_names() + build = chunks.build_dir() / 'datapacks' + build.mkdir(exist_ok=True) + differing = 0 + pack_effect = 0 + for phase in args.phase or ['terrain', 'full']: + os.environ['HORIZON_PHASE'] = phase + for seed in args.seed or SEEDS: + rng = random.Random(seed) + sites = [(0, 0)] + [(rng.randint(-300, 300), rng.randint(-300, 300)) for _ in range(args.sites)] + rects = [f'{x},{z},{x},{z}' for x, z in sites] + tag = f'{seed}-{phase}' + vanilla_path, rust_path, plain_path = (build / f'{side}-{tag}.txt' for side in ('vanilla', 'rust', 'plain')) + os.environ['HORIZON_DATAPACKS'] = str(DATAPACKS) + oracle.run(vanilla_path, ['dump', seed, 'overworld', *rects]) + stderr = chunks.run_rust(binary, rust_path, seed, 'overworld', rects) + del os.environ['HORIZON_DATAPACKS'] + for line in stderr.splitlines(): + if 'data pack' in line: + print(' horizon:', line) + vanilla, rust = chunks.parse(vanilla_path), chunks.parse(rust_path) + counts = chunks.compare(vanilla, rust, names, f'{tag} packs') + differing += counts[0] + print(f'{tag}: {len(vanilla)} chunks with the packs, differing chunks {counts[0]} ' + f'(blocks {counts[1]}, biome cells {counts[2]}, block entities {counts[3]})', flush=True) + chunks.run_rust(binary, plain_path, seed, 'overworld', rects) + plain = chunks.compare(vanilla, chunks.parse(plain_path), names, f'{tag} without packs', quiet=True) + pack_effect += plain[0] + print(f'{tag}: without the packs Horizon differs from them in {plain[0]} of {len(vanilla)} chunks ({plain[1]} blocks)') + if not pack_effect: + sys.exit('the packs changed nothing: the harness would not see them') + if differing: + sys.exit(f'{differing} chunks differ from vanilla with the packs') + print('Data pack worldgen parity passed') + + +if __name__ == '__main__': + main() diff --git a/tools/test_emitter_particles_parity.py b/tools/test_emitter_particles_parity.py index 50de9d6420..1f5e919793 100644 --- a/tools/test_emitter_particles_parity.py +++ b/tools/test_emitter_particles_parity.py @@ -46,7 +46,7 @@ def main(): 'once spawned, are covered elsewhere.') harness.differential( 'rust-emitter-particles-parity', 'VanillaEmitterParticlesOracle', 'emitter_particles_probe', make_cases, scope, - sources=[lab.ROOT / 'crates/horizon-client/src/particles.rs']) + sources=[lab.ROOT / 'crates/horizon-client-render/src/particles.rs']) if __name__ == '__main__': diff --git a/tools/test_enchanting_parity.py b/tools/test_enchanting_parity.py index 9e070c3cbf..9626b57cda 100644 --- a/tools/test_enchanting_parity.py +++ b/tools/test_enchanting_parity.py @@ -43,7 +43,8 @@ def main(): items = sorted(k for k, v in catalog.items() if 'minecraft:enchantable' in v) oracle = lab.ROOT / 'mods/horizon/src/test/java/dev/horizonlab/parity/VanillaEnchantingOracle.java' sources = [oracle, Path(__file__), lab.ROOT / 'crates/horizon-client/examples/probe/enchanting_fixture.rs', - *sorted((lab.ROOT / 'crates/horizon-client/src').glob('*.rs'))] + *sorted((lab.ROOT / 'crates/horizon-client/src').glob('*.rs')), + *sorted((lab.ROOT / 'crates/horizon-client-render/src').glob('*.rs'))] tested = base.source_hashes(sources) inputs = build / 'inputs.txt' fixtures = list(corpus(items)) diff --git a/tools/test_entity_particles_parity.py b/tools/test_entity_particles_parity.py index f34528b4c7..16d6ef7e44 100644 --- a/tools/test_entity_particles_parity.py +++ b/tools/test_entity_particles_parity.py @@ -73,7 +73,7 @@ def main(): sources = [lab.ROOT / p for p in [ 'tools/test_entity_particles_parity.py', 'tools/oracle_harness.py', 'tools/test_rust_parity.py', 'tools/lab.py', f'mods/horizon/src/test/java/dev/horizonlab/parity/{oracle}.java', 'Cargo.toml', 'Cargo.lock', - 'crates/horizon-client/Cargo.toml', 'crates/horizon-client/src/particles.rs', + 'crates/horizon-client/Cargo.toml', 'crates/horizon-client-render/src/particles.rs', f'crates/horizon-client/examples/probe/{probe}.rs', 'crates/horizon-core/src/random.rs']] tested = base.source_hashes(sources) cases_path, units = make_cases(build) diff --git a/tools/test_firework_tooltip_parity.py b/tools/test_firework_tooltip_parity.py index 5e971d8dbc..6a780d7691 100644 --- a/tools/test_firework_tooltip_parity.py +++ b/tools/test_firework_tooltip_parity.py @@ -69,5 +69,5 @@ if __name__ == '__main__': 'twinkle, defaults and tooltip_display', sources=[lab.ROOT / 'crates/horizon-items/src/enchant.rs', lab.ROOT / 'crates/horizon-client/src/item_tooltip.rs', lab.ROOT / 'crates/horizon-items/src/item_data.rs', - lab.ROOT / 'crates/horizon-client/src/particles.rs'], + lab.ROOT / 'crates/horizon-client-render/src/particles.rs'], expected_lines=COUNT) diff --git a/tools/test_first_person_parity.py b/tools/test_first_person_parity.py index fcb1206ecc..17c1612104 100644 --- a/tools/test_first_person_parity.py +++ b/tools/test_first_person_parity.py @@ -181,7 +181,7 @@ def main(): harness.differential( 'rust-first-person-parity', 'VanillaFirstPersonOracle', 'first_person_probe', make_cases, scope, sources=[lab.ROOT / 'crates/horizon-client/src/first_person.rs', lab.ROOT / 'crates/horizon-client/src/attack_strength.rs', - lab.ROOT / 'crates/horizon-client/src/camera.rs', + lab.ROOT / 'crates/horizon-client-render/src/camera.rs', lab.ROOT / 'mods/horizon/src/test/java/dev/horizonlab/parity/ParityData.java']) diff --git a/tools/test_friends.py b/tools/test_friends.py index 763b0b220d..e8f2360a59 100644 --- a/tools/test_friends.py +++ b/tools/test_friends.py @@ -16,8 +16,8 @@ is off: nothing leaves the machine. python3 tools/test_friends.py --client target/fast/horizon-client --relay target/fast/horizon-relay The host runs on its integrated server thread by default (the window is a -client of it). `--in-frame` compares the fallback; `--server-thread` is accepted -for compatibility. +client of it). `--in-frame` is DEPRECATED (never use it, it will be removed soon); +`--server-thread` is accepted for compatibility. `--role host|guest` runs one side only (two machines, e.g. over a tailnet): the host prints the code, the guest takes it with `--code` (`--relay-url` @@ -31,6 +31,7 @@ import os from pathlib import Path import socket import subprocess +import sys import tempfile import time import urllib.request @@ -377,12 +378,14 @@ def main(): mode.add_argument('--server-thread', dest='server_thread', action='store_true', default=True, help='host on its server thread (default; retained for existing callers)') mode.add_argument('--in-frame', dest='server_thread', action='store_false', - help='host the world in the window, for comparison') + help='DEPRECATED, never use: unsupported, will be removed soon') parser.add_argument('--xvfb', action='store_true', help='run the games under xvfb-run (headless Linux)') args = parser.parse_args() Game.prefix = ['xvfb-run', '-a'] if args.xvfb else [] Game.extra = ['--offscreen'] if args.offscreen else [] Game.server_thread = args.server_thread + if not args.server_thread: + print('DEPRECATED: --in-frame is unsupported and will be removed soon. Do not use it.', file=sys.stderr) args.client, args.resources = args.client.resolve(), args.resources.resolve() try: require_server_reports(args.resources) diff --git a/tools/test_item_wire_parity.py b/tools/test_item_wire_parity.py index 1be6042ec5..05fb3e3c57 100755 --- a/tools/test_item_wire_parity.py +++ b/tools/test_item_wire_parity.py @@ -70,6 +70,7 @@ FIXTURES = [ ('bees', '{bees:[{entity_data:{id:"minecraft:bee",Health:5f},ticks_in_hive:5,min_ticks_in_hive:10}]}'), ('entity_data', '{entity_data:{id:"minecraft:pig",NoAI:1b}}'), ('bucket_entity_data', '{bucket_entity_data:{Health:5f}}'), + ('instrument registry', '{instrument:"minecraft:sing_goat_horn"}'), ('instrument direct', '{instrument:{sound_event:{sound_id:"minecraft:x",range:8f},use_duration:3f,range:64f,description:"Horn"}}'), ('painting variant', '{"minecraft:painting/variant":"minecraft:kebab"}'), ('sign_text_front', '{sign_text_front:{messages:["a","b","c","d"],filtered_messages:["e","f","g","h"],color:"red",has_glowing_text:1b}}'), @@ -104,7 +105,7 @@ FIXTURES = [ # Fixtures on another item than a stick (the components need it, or read better there). ITEMS = {'profile name': 'player_head', 'profile full': 'player_head', 'trim registry': 'diamond_chestplate', 'trim direct': 'diamond_chestplate', 'banner_patterns': 'white_banner', 'pot_decorations': 'decorated_pot', - 'everything at once': 'diamond_chestplate'} + 'everything at once': 'diamond_chestplate', 'instrument registry': 'goat_horn'} def compare(vanilla, rust): diff --git a/tools/test_mipmap_parity.py b/tools/test_mipmap_parity.py index 4970313901..dd27aa7758 100644 --- a/tools/test_mipmap_parity.py +++ b/tools/test_mipmap_parity.py @@ -50,5 +50,5 @@ if __name__ == '__main__': 'MipmapGenerator.generateMipLevels (solidify, fillEmptyAreasWithDarkColor, meanLinear, ' 'darkenedAlphaBlend, alphaTestCoverage, scaleAlphaToCoverage) on static 16x16 block and item sprites ' 'with their .mcmeta texture settings', - sources=[oh.lab.ROOT / 'crates/horizon-client/src/mipmap.rs', - oh.lab.ROOT / 'crates/horizon-client/src/sprites.rs']) + sources=[oh.lab.ROOT / 'crates/horizon-client-render/src/mipmap.rs', + oh.lab.ROOT / 'crates/horizon-client-render/src/sprites.rs']) diff --git a/tools/test_mob_ai_parity.py b/tools/test_mob_ai_parity.py index 9d88ca18de..ebae91b49c 100644 --- a/tools/test_mob_ai_parity.py +++ b/tools/test_mob_ai_parity.py @@ -66,6 +66,8 @@ def scenarios(): # Night: undead do not burn, monsters seek darkness. add('zombie_night', 200, ['player 10.5 -60 0.5', 'mob minecraft:zombie 0.5 -60 0.5 0 31 persistent'], 'time=18000') add('skeleton_day_burn', 120, ['mob minecraft:skeleton 0.5 -60 0.5 0 32 persistent']) + # NoAI only skips serverAiStep: Mob.aiStep's burnUndead still sets a NoAI zombie alight (#3430). + add('zombie_noai_day_burn', 120, ['mob minecraft:zombie 0.5 -60 0.5 0 33 persistent noai']) # The wither (summoned, not built: no invulnerable spawn phase). add('wither_player', 200, ['player 10.5 -60 0.5', 'mob minecraft:wither 0.5 -58 0.5 0 41 persistent']) # A villager with a job site, a bed and a bell. @@ -233,6 +235,22 @@ def scenarios(): f'mob minecraft:{species} 0.5 -60 0.5 0 {seed} persistent{baby}', f'feed 20 0 minecraft:{food}', f'feed 40 0 minecraft:{food}', ]) + # The player uses a sword on untamed horses (#2697): Horse, AbstractChestedHorse and + # ZombieHorse.mobInteract make them mad (makeMad: rear, angry sound) instead of taking the + # rider, not while one still stands; an untamed skeleton horse ignores it (SkeletonHorse). + # The zombie horse goes at night (it burns by day), the skeleton horse stops before its + # first step sound (Horizon plays the block's). + for species, seed, babies, ticks, opts in [ + ('horse', 180, True, 80, ''), ('donkey', 181, True, 80, ''), ('mule', 182, False, 80, ''), + ('zombie_horse', 183, False, 80, 'time=18000'), ('llama', 184, True, 80, ''), + ('skeleton_horse', 185, False, 45, '')]: + for baby in ('', ' baby') if babies else ('',): + add(f'sword_{species}{baby.replace(" ", "_")}', ticks, [ + 'player 3.5 -60 0.5', + f'mob minecraft:{species} 0.5 -60 0.5 0 {seed} persistent{baby}', + 'interact 20 0 minecraft:iron_sword', 'interact 30 0 minecraft:iron_sword', + 'interact 40 0 minecraft:iron_sword', + ], opts) # Striders: standing on a lava pool (floatStrider), walking from cold grass to it # (StriderGoToLavaGoal, suffocating), and drawn by a player holding warped fungus. LAVA = 'fill -5 -63 -5 5 -61 5 minecraft:lava' @@ -243,6 +261,16 @@ def scenarios(): add('strider_cold', 200, ['mob minecraft:strider 0.5 -60 0.5 0 134 persistent']) add('strider_tempted', 200, [LAVA, 'player 9.5 -60 0.5 hand=minecraft:warped_fungus', 'mob minecraft:strider 0.5 -61 0.5 0 135 persistent']) + # Wild ocelots (Ocelot.registerGoals + reassessTrustingGoals): alone, fleeing a survival player + # inside 16 blocks (OcelotAvoidEntityGoal, sprinting inside 7), ignoring a creative one, and drawn + # by raw cod (OcelotTemptGoal). Rabbits wait for RaidGardenGoal, whose first canUse draws from + # the rabbit's random. + add('ocelot_alone', 200, ['mob minecraft:ocelot 0.5 -60 0.5 0 181 persistent']) + add('ocelot_player', 200, ['player 6.5 -60 0.5', 'mob minecraft:ocelot 0.5 -60 0.5 0 182 persistent']) + add('ocelot_player_far', 200, ['player 14.5 -60 0.5', 'mob minecraft:ocelot 0.5 -60 0.5 0 183 persistent']) + add('ocelot_creative', 200, ['player 6.5 -60 0.5 creative', 'mob minecraft:ocelot 0.5 -60 0.5 0 184 persistent']) + add('ocelot_tempted', 200, ['player 8.5 -60 0.5 hand=minecraft:cod', + 'mob minecraft:ocelot 0.5 -60 0.5 0 185 persistent']) # Bats: hanging under a stone ceiling alone, woken by a player passing beneath, flying off with # nothing above, and fluttering about a closed cave where they may hang up again. CEILING = 'fill -6 -57 -6 6 -57 6 minecraft:stone' @@ -272,6 +300,10 @@ def scenarios(): add('bee_hive_rain', 300, [HIVE, 'mob minecraft:bee 0.5 -58 0.5 0 162 persistent'], 'rain') add('bee_hive_morning', 1000, [HIVE, 'mob minecraft:bee 0.5 -58 0.5 0 163 persistent'], 'time=22900 daycycle') add('bee_hive_honey', 2600, [HIVE, 'mob minecraft:bee 0.5 -58 0.5 0 164 persistent nectar']) + # A wolf the player hits (#3185, #2820): any wolf with a target is angry (the angry texture): + # NeutralMob.updatePersistentAnger in Wolf.aiStep restarts the 20-39 s timer, one random draw, + # every tick it has one, not once when hurt. + add('wolf_hurt', 200, ['player 5.5 -60 0.5', 'mob minecraft:wolf 0.5 -60 0.5 0 181 persistent', 'hurt 20 0']) # Mobs dropped into deep water (#542): FloatGoal users float at the surface (their navigation # can float, so Mob.jumpInLiquid adds 0.04 a tick), zombies sink. for species, seed in [('pig', 171), ('cow', 172), ('wolf', 173), ('cat', 174), ('horse', 175), diff --git a/tools/test_native_client.py b/tools/test_native_client.py index 552ea8c42a..54cfa753e9 100644 --- a/tools/test_native_client.py +++ b/tools/test_native_client.py @@ -47,6 +47,8 @@ def main(): sources = [lab.ROOT / 'Cargo.toml', lab.ROOT / 'Cargo.lock', Path(__file__).resolve(), lab.ROOT / 'crates/horizon-client/Cargo.toml', lab.ROOT / 'crates/horizon-assets/Cargo.toml', *sorted((lab.ROOT / 'crates/horizon-client/src').rglob('*')), + lab.ROOT / 'crates/horizon-client-render/Cargo.toml', + *sorted((lab.ROOT / 'crates/horizon-client-render/src').rglob('*')), *sorted((lab.ROOT / 'crates/horizon-assets/src').rglob('*.rs'))] oracle = lab.ROOT / 'mods/horizon/src/test/java/dev/horizonlab/parity/VanillaGallerySelectionOracle.java' helper = oracle.with_name('VanillaCuboidModelOracle.java') diff --git a/tools/test_native_scene_collision.py b/tools/test_native_scene_collision.py index 2c292b28d9..1533a02377 100644 --- a/tools/test_native_scene_collision.py +++ b/tools/test_native_scene_collision.py @@ -71,7 +71,7 @@ def main(): lab.ROOT/'tools/lab.py', lab.ROOT/'tools/prepare_native_client.py', oracle, *helpers, lab.ROOT/'Cargo.toml', lab.ROOT/'Cargo.lock', lab.ROOT/'crates/horizon-client/examples/probe/scene_collision_probe.rs'] - for crate in ['horizon-core', 'horizon-world', 'horizon-assets', 'horizon-client']: + for crate in ['horizon-core', 'horizon-world', 'horizon-assets', 'horizon-client', 'horizon-client-render']: sources += [lab.ROOT/'crates'/crate/'Cargo.toml', *sorted((lab.ROOT/'crates'/crate/'src').rglob('*.rs'))] tested = base.source_hashes(sources) def run(command, name): diff --git a/tools/test_native_scene_motion.py b/tools/test_native_scene_motion.py index 08af5330c6..feb160a80b 100644 --- a/tools/test_native_scene_motion.py +++ b/tools/test_native_scene_motion.py @@ -790,7 +790,7 @@ def main(): sources=[Path(__file__),lab.ROOT/'tools/test_native_client.py',lab.ROOT/'tools/test_rust_parity.py',lab.ROOT/'tools/lab.py', lab.ROOT/'tools/prepare_native_client.py',oracle,*helpers,lab.ROOT/'Cargo.toml',lab.ROOT/'Cargo.lock', lab.ROOT/'crates/horizon-client/examples/probe/scene_motion_probe.rs'] - for crate in ['horizon-core','horizon-world','horizon-assets','horizon-client']: + for crate in ['horizon-core','horizon-world','horizon-assets','horizon-client','horizon-client-render']: sources += [lab.ROOT/'crates'/crate/'Cargo.toml',*sorted((lab.ROOT/'crates'/crate/'src').rglob('*.rs'))] if args.sprint_input:sources.append(lab.ROOT/'tools/test_model_baking_parity.py') tested=base.source_hashes(sources) diff --git a/tools/test_natural_spawn_parity.py b/tools/test_natural_spawn_parity.py index 8a17c937b1..b7acdb02e2 100644 --- a/tools/test_natural_spawn_parity.py +++ b/tools/test_natural_spawn_parity.py @@ -552,7 +552,7 @@ def main(): oracle = lab.ROOT / 'mods/horizon/src/test/java/dev/horizonlab/parity/VanillaNaturalSpawnOracle.java' sources = [oracle, Path(__file__), lab.ROOT / 'crates/horizon-client/examples/probe/natural_spawn_fixture.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_selection.rs', - lab.ROOT / 'crates/horizon-client/src/biome.rs', + lab.ROOT / 'crates/horizon-client-render/src/biome.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_rules.rs'] tested = base.source_hashes(sources) inputs = build / 'inputs.txt' diff --git a/tools/test_options_parity.py b/tools/test_options_parity.py index 82bc5b9ec7..4ae6c45749 100644 --- a/tools/test_options_parity.py +++ b/tools/test_options_parity.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Compare options.txt loading and saving, and the key mapping table, with the pinned client: every file is loaded through the real Options.load and written -back with Options.save. The Rust probe normalizes Horizon's intentional 33..64 +back with Options.save. The Rust probe normalizes Horizon's intentional 33..128 render-distance extension to vanilla's default; a Rust slider/persistence regression covers the extended range.""" from pathlib import Path @@ -184,12 +184,12 @@ def main(): 'fields, lenient Gson parsing and syntax-error aborts, key mappings (names, scancode-number forms, unbinding), ' 'sound volumes, model parts, line endings, the OPTIONS data fixers across version boundaries, and the ' 'KeyMapping table (order, defaults, categories, toggle mappings). Does not cover option screens or ' - 'runtime side effects of setting an option. Horizon render distances 33..64 are normalized to vanilla\'s ' + 'runtime side effects of setting an option. Horizon render distances 33..128 are normalized to vanilla\'s ' 'default by options_probe; the slider/persistence unit regression checks that intentional extension.') harness.differential( 'rust-options-parity', 'VanillaOptionsOracle', 'options_probe', make_cases, scope, - sources=[lab.ROOT / 'crates/horizon-client/src/options_file.rs', lab.ROOT / 'crates/horizon-client/src/options_fixes.rs', - lab.ROOT / 'crates/horizon-client/src/java_text.rs', lab.ROOT / 'crates/horizon-client-input/src/key_names.rs', + sources=[lab.ROOT / 'crates/horizon-client-render/src/options_file.rs', lab.ROOT / 'crates/horizon-client-render/src/options_fixes.rs', + lab.ROOT / 'crates/horizon-client-render/src/java_text.rs', lab.ROOT / 'crates/horizon-client-input/src/key_names.rs', lab.ROOT / 'crates/horizon-client-input/src/keybinds.rs']) diff --git a/tools/test_pack_screen_parity.py b/tools/test_pack_screen_parity.py index 77249ec5e6..5fe8a129f4 100644 --- a/tools/test_pack_screen_parity.py +++ b/tools/test_pack_screen_parity.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 """Compare the resource pack screen's drawing with the vanilla client's. -Each case is a pack repository (titles and descriptions as text components, +Each case is a title (the resource pack screen's, or the data pack screen's +Create World opens), a pack repository (titles and descriptions as text components, with legacy formatting codes, colours and styles; pack sources; compatible, too old, too new or unknown packs; required, fixed and positioned packs; icons), an initial selection, a GUI size, in a world or not, and what the @@ -119,7 +120,9 @@ def cases(count): (rng.randint(cx - 154, cx + 154), height - 26 + rng.randint(0, 19)), (rng.randint(cx - 160, cx + 160), rng.randint(height // 2 - 30, height // 2 + 60))]) out.append({'width': width, 'height': height, 'inWorld': rng.random() < 0.3, 'packs': packs, 'selected': selected, - 'actions': actions, 'mouse': list(mouse), 'blink': rng.choice([0, 100, 400, 650])}) + 'actions': actions, 'mouse': list(mouse), 'blink': rng.choice([0, 100, 400, 650]), + # Create World's Data Packs screen is the same screen under its own title. + 'title': 'dataPack.title' if n % 4 == 3 else 'resourcePack.title'}) return out @@ -161,7 +164,7 @@ def main(): for e in sorted(set(errors))[:5]: print('ORACLE ERROR', e[:1500]) for c, v, r in bad[:args.detail]: - brief = {k: c[k] for k in ('width', 'height', 'inWorld', 'selected', 'actions', 'mouse', 'blink')} + brief = {k: c[k] for k in ('title', 'width', 'height', 'inWorld', 'selected', 'actions', 'mouse', 'blink')} print('MISMATCH', json.dumps(brief)[:1500]) if v.get('screen') != r.get('screen'): print(f' screen: vanilla {v.get("screen")} | horizon {r.get("screen")}') diff --git a/tools/test_particle_providers_parity.py b/tools/test_particle_providers_parity.py index def7e3bfaf..15612f9bb0 100644 --- a/tools/test_particle_providers_parity.py +++ b/tools/test_particle_providers_parity.py @@ -196,7 +196,7 @@ def main(): sources = [lab.ROOT / p for p in [ 'tools/test_particle_providers_parity.py', 'tools/oracle_harness.py', 'tools/test_rust_parity.py', 'tools/lab.py', f'mods/horizon/src/test/java/dev/horizonlab/parity/{oracle}.java', 'Cargo.toml', 'Cargo.lock', - 'crates/horizon-client/Cargo.toml', 'crates/horizon-client/src/particles.rs', + 'crates/horizon-client/Cargo.toml', 'crates/horizon-client-render/src/particles.rs', f'crates/horizon-client/examples/probe/{probe}.rs', 'crates/horizon-core/src/random.rs']] tested = base.source_hashes(sources) cases_path, units = make_cases(build) diff --git a/tools/test_random_tick_sections_parity.py b/tools/test_random_tick_sections_parity.py index 0b78afadbf..5af368725b 100644 --- a/tools/test_random_tick_sections_parity.py +++ b/tools/test_random_tick_sections_parity.py @@ -66,7 +66,7 @@ if __name__ == '__main__': 'and the positions tickChunk draws in them, with each position\'s block and fluid random-tick flags. The ' 'blocks\' random-tick behaviour itself is test_random_tick_parity\'s.', sources=[lab.ROOT / 'crates/horizon-client/src/random_tick_sections.rs', - lab.ROOT / 'crates/horizon-client/src/chunk.rs', + lab.ROOT / 'crates/horizon-client-render/src/chunk.rs', lab.ROOT / 'crates/horizon-core/src/paletted.rs', lab.ROOT / 'crates/horizon-world/src/level/tick.rs', lab.ROOT / 'crates/horizon-world/src/level/ticks.rs'], diff --git a/tools/test_resource_pack_parity.py b/tools/test_resource_pack_parity.py index a935e0b340..08bc2b08ed 100644 --- a/tools/test_resource_pack_parity.py +++ b/tools/test_resource_pack_parity.py @@ -127,8 +127,8 @@ def main(): harness.differential(f"resource-pack-parity-{index}", "VanillaPackResourcesOracle", "pack_resources_fixture", make, "Pack discovery, overlays, filters and the resource stacks vanilla merges.", - sources=[lab.ROOT / "crates/horizon-client/src/packs.rs", - lab.ROOT / "crates/horizon-client/src/sound_events.rs", + sources=[lab.ROOT / "crates/horizon-client-render/src/packs.rs", + lab.ROOT / "crates/horizon-client-render/src/sound_events.rs", lab.ROOT / "crates/horizon-client-core/src/lang.rs"]) total += len(QUERIES) print(f"{total} resource pack queries match the pinned client over {len(SELECTIONS)} selections") diff --git a/tools/test_spawner_parity.py b/tools/test_spawner_parity.py index d87d4059c6..d988bca89a 100644 --- a/tools/test_spawner_parity.py +++ b/tools/test_spawner_parity.py @@ -232,10 +232,10 @@ def main(): oracle = lab.ROOT / 'mods/horizon/src/test/java/dev/horizonlab/parity/VanillaNaturalSpawnOracle.java' sources = [oracle, Path(__file__), lab.ROOT / 'crates/horizon-client/examples/probe/natural_spawn_fixture.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawner.rs', - lab.ROOT / 'crates/horizon-client/src/chunk/block_entity.rs', + lab.ROOT / 'crates/horizon-client-render/src/chunk/block_entity.rs', lab.ROOT / 'crates/horizon-client-core/src/identifier.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_selection.rs', - lab.ROOT / 'crates/horizon-client/src/biome.rs', + lab.ROOT / 'crates/horizon-client-render/src/biome.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_rules.rs'] tested = base.source_hashes(sources) inputs = build / 'inputs.txt' diff --git a/tools/test_storage_menu_parity.py b/tools/test_storage_menu_parity.py new file mode 100644 index 0000000000..5cfdc4cbd9 --- /dev/null +++ b/tools/test_storage_menu_parity.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Compare the menu a storage block opens against vanilla 26.3. + +The Java oracle (VanillaStorageMenuOracle) builds each block's block entity +and its menu with BaseContainerBlockEntity.createMenu, the menu a server +sends in ClientboundOpenScreenPacket, and prints its MenuType and whether its +first slot takes a shulker box. A shulker box opens a ShulkerBoxMenu +(minecraft:shulker_box), whose ShulkerBoxSlots refuse shulker boxes; chests +and barrels open a 9x3 ChestMenu. The Rust example (storage_menu_fixture) +answers with horizon_client::containers::block_menu_type, the menu type the +server's lan_open_container sends; the server refuses shulker boxes in the +menus of that type (lan::containers, ShulkerBoxSlot). Compared for chests, +trapped and copper chests, barrels, shulker boxes of every colour, +dispensers, droppers and hoppers. +""" +import argparse +try: + from . import interaction_lab as lib +except ImportError: + import interaction_lab as lib + +COLOURS = ['white', 'orange', 'magenta', 'light_blue', 'yellow', 'lime', 'pink', 'gray', 'light_gray', 'cyan', + 'purple', 'blue', 'brown', 'green', 'red', 'black'] +BLOCKS = (['chest', 'trapped_chest', 'barrel', 'dispenser', 'dropper', 'hopper', 'shulker_box'] + + [f'{c}_shulker_box' for c in COLOURS] + + [f'{w}copper_chest' for w in ('', 'exposed_', 'weathered_', 'oxidized_')] + + [f'waxed_{w}copper_chest' for w in ('', 'exposed_', 'weathered_', 'oxidized_')]) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--limit', type=int, default=4) + args = parser.parse_args() + work = lib.work_dir('storage-menu-parity') + vanilla = lib.Vanilla(work) + vanilla.compile('ParityData', 'VanillaStorageMenuOracle') + cases = ['minecraft:' + b for b in BLOCKS] + (work / 'cases.txt').write_text(''.join(c + '\n' for c in cases), encoding='utf-8') + vanilla.run('VanillaStorageMenuOracle', work / 'cases.txt', work / 'native.txt') + lib.cargo_example('horizon-client', 'storage_menu_fixture', work / 'cases.txt', work / 'rust.txt') + errors = sum(line == 'error' for line in (work / 'native.txt').read_text(encoding='utf-8').split('\n')) + if errors: + print(f'{errors} cases fail in vanilla') + total = lib.compare(cases, work / 'native.txt', work / 'rust.txt', lambda case, line: case, limit=args.limit) + raise SystemExit(1 if total or errors else 0) + + +if __name__ == '__main__': + main() diff --git a/tools/test_text_display_render_parity.py b/tools/test_text_display_render_parity.py new file mode 100644 index 0000000000..f738e5ea06 --- /dev/null +++ b/tools/test_text_display_render_parity.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +"""Compare a text display's text geometry with the vanilla 26.3 client's TextDisplayRenderer. + +The Java oracle (VanillaTextDisplayRenderOracle) sets a text display's synced data +(text, line width, background, opacity, flags), runs the real +TextDisplayRenderer.submitInner (line split, alignment, default background from the +options) and turns its submits into vertices as TextFeatureRenderer does, with glyphs +baked from the real font providers. `examples/probe/text_display_render_fixture` +decodes the same entity data from vanilla's wire bytes and lays the text out with +`horizon_client::text_display`. Each case's pose must agree, and its quads +[x, y, z, ARGB] in text units as multisets (Horizon draws all shadows first): the +depths keep the background behind the shadow, behind the glyph and its bold copy. + + test_text_display_render_parity.py [--count N] [--detail N] +""" +import argparse +import json +import os +import random + +try: + from . import lab, test_rust_parity as base, oracle_harness as harness, test_model_baking_parity as baking +except ImportError: + import lab + import test_rust_parity as base + import oracle_harness as harness + import test_model_baking_parity as baking + +PARITY = lab.ROOT / 'mods/horizon/src/test/java/dev/horizonlab/parity' +# Glyphs of the `default` include (ascii, accented, nonlatin_european); no Unihex. +CHARS = ('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' + ' .,:;!?\'"()[]{}<>-_+=*/\\|@#$%^&~`' 'äöüßéèêñçÅØæ' 'АБВГДабвгдΩλπ') +COLORS = ['gold', 'red', 'aqua', 'gray', 'dark_gray', 'green', 'white', 'black', '#FFAA00', '#123456', '#E0E0FF'] +LEGACY = '0123456789abcdeflmnor' + + +def words(rng, count): + return ' '.join(''.join(rng.choice(CHARS) for _ in range(rng.randint(1, 12))) for _ in range(count)) + + +def part(rng): + text = words(rng, rng.randint(1, 4)) + if rng.random() < 0.15: + text = text.replace(' ', '\n', 1) + if rng.random() < 0.2: + # Servers colour holograms with legacy codes inside the text. + text = ''.join(c if rng.random() > 0.15 else '§' + rng.choice(LEGACY) + c for c in text) + out = {'text': text} + for key in ('bold', 'italic', 'underlined', 'strikethrough'): + if rng.random() < 0.3: + out[key] = rng.random() < 0.8 + if rng.random() < 0.6: + out['color'] = rng.choice(COLORS) + return out + + +def cases(count): + rng = random.Random(0x7E57D15) + fixed = [ + {'text': {'text': 'CityBuild', 'color': 'gold', 'bold': True}, 'flags': 1}, + {'text': {'text': '', 'extra': [{'text': 'CityBuild\n', 'color': 'gold', 'bold': True}, + {'text': 'Spieler online: 42', 'color': 'gray'}]}, 'flags': 1}, + {'text': {'text': 'Underlined', 'underlined': True, 'strikethrough': True}, 'flags': 1}, + {'text': {'text': 'see through'}, 'flags': 3}, + {'text': {'text': 'default background'}, 'flags': 4}, + {'text': {'text': 'faint'}, 'flags': 1, 'opacity': 4, 'background': 0}, + ] + out = [] + for n in range(count): + c = dict(fixed[n]) if n < len(fixed) else {} + if n >= len(fixed): + parts = [part(rng) for _ in range(rng.choice([1, 1, 2, 3]))] + c['text'] = parts[0] if len(parts) == 1 else {'text': '', 'extra': parts, **({'color': rng.choice(COLORS)} if rng.random() < 0.3 else {})} + c['flags'] = rng.randrange(32) + if rng.random() < 0.6: + c['line_width'] = rng.choice([200, 100, 60, 30, 8, 400]) + if rng.random() < 0.7: + c['background'] = rng.choice([0, 0x40000000, -0x40000000, 0x7F00FF00, rng.randint(-2**31, 2**31 - 1)]) + if rng.random() < 0.5: + c['opacity'] = rng.choice([-1, 127, -128, 0, 3, 4, 25, 26, 64, rng.randint(-128, 127)]) + c['chat_only'] = rng.random() < 0.3 + c['background_opacity'] = rng.choice([0.0, 0.5, 1.0, 0.25]) + out.append(c) + return out + + +def canonical(quads): + """Each quad as its sorted corners, rounded below the 0.001 bold step; the quads sorted.""" + return sorted(tuple(sorted((round(x, 4), round(y, 4), round(z, 4), color) for x, y, z, color in q)) for q in quads) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--count', type=int, default=600) + parser.add_argument('--detail', type=int, default=3) + args = parser.parse_args() + lock, jars, _ = base.verify_artifacts() + libraries = baking.client_libraries() + build = lab.LOCAL / 'text-display-render-parity' + build.mkdir(parents=True, exist_ok=True) + (build / 'report.json').unlink(missing_ok=True) + classes = build / 'classes' + classes.mkdir(exist_ok=True) + sources = [PARITY / 'VanillaTextDisplayRenderOracle.java', lab.ROOT / 'crates/horizon-client/src/text_display.rs', + lab.ROOT / 'crates/horizon-client-render/src/hud.rs', lab.ROOT / 'crates/horizon-client-render/src/font.rs'] + tested = base.source_hashes(sources) + chosen = cases(args.count) + inputs = build / 'cases.jsonl' + inputs.write_text(''.join(json.dumps(c, ensure_ascii=False) + '\n' for c in chosen), encoding='utf-8') + dependencies = os.pathsep.join(map(str, [jars['client'], *libraries])) + harness.run_logged(['javac', '--release', lock['minecraft']['java_major'], '-proc:none', '-nowarn', '-cp', dependencies, + '-d', classes, *(PARITY / f'{n}.java' for n in ('ParityData', 'ParityFont', 'VanillaTextDisplayRenderOracle'))], + build, 'compile-java') + harness.run_logged(['java', '-Xmx1G', '-Djava.awt.headless=true', '-cp', str(classes) + os.pathsep + dependencies, + 'dev.horizonlab.parity.VanillaTextDisplayRenderOracle', inputs, build / 'vanilla.jsonl'], build, 'vanilla') + vanilla = [json.loads(l) for l in (build / 'vanilla.jsonl').read_text(encoding='utf-8').splitlines()] + errors = [(c, v['error']) for c, v in zip(chosen, vanilla) if 'error' in v] + for c, e in errors[:3]: + print('ORACLE ERROR', json.dumps(c, ensure_ascii=False), e) + wire = build / 'wire.jsonl' + wire.write_text(''.join(json.dumps({'wire': v.get('wire', ''), 'chat_only': c['chat_only'], + 'background_opacity': c['background_opacity']}) + '\n' + for c, v in zip(chosen, vanilla))) + command = harness.build_probe(build, 'text_display_render_fixture') + harness.run_logged([*command, lab.RESOURCES, wire, build / 'rust.jsonl'], build, 'rust') + rust = [json.loads(l) for l in (build / 'rust.jsonl').read_text(encoding='utf-8').splitlines()] + assert len(vanilla) == len(rust) == len(chosen), (len(vanilla), len(rust), len(chosen)) + bad, quads = [], 0 + for c, v, r in zip(chosen, vanilla, rust): + if 'error' in v: + continue + quads += len(v['quads']) + pose = all(abs(a - b) <= 2e-5 for a, b in zip(v['pose'], r['pose'])) + if not pose or canonical(v['quads']) != canonical(r['quads']): + bad.append((c, v, r, pose)) + for c, v, r, pose in bad[:args.detail]: + print('MISMATCH', json.dumps(c, ensure_ascii=False)) + if not pose: + print(' pose vanilla', v['pose'], '\n pose horizon', r['pose']) + a, b = canonical(v['quads']), canonical(r['quads']) + only_v = [q for q in a if q not in b][:3] + only_r = [q for q in b if q not in a][:3] + print(f' {len(a)} vanilla quads, {len(b)} Horizon; only vanilla {only_v}; only Horizon {only_r}') + print(f'{len(chosen) - len(bad) - len(errors)}/{len(chosen)} text display cases match ({quads} quads); ' + f'{len(errors)} oracle errors') + if bad or errors: + raise SystemExit(1) + base.ensure_sources_unchanged(sources, tested) + lab.write_json(build / 'report.json', {'minecraft': lab.VERSION, 'cases': len(chosen), 'quads': quads, 'pass': True, + 'sources_sha256': tested}) + print('text display render parity ok') + + +if __name__ == '__main__': + main() diff --git a/tools/test_trial_spawner_parity.py b/tools/test_trial_spawner_parity.py index e461e35973..27b64beb53 100644 --- a/tools/test_trial_spawner_parity.py +++ b/tools/test_trial_spawner_parity.py @@ -197,10 +197,10 @@ def main(): sources = [oracle, Path(__file__), lab.ROOT / 'crates/horizon-client/examples/probe/natural_spawn_fixture.rs', lab.ROOT / 'crates/horizon-client/src/mobs/trial_spawner.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawner.rs', - lab.ROOT / 'crates/horizon-client/src/chunk/block_entity.rs', + lab.ROOT / 'crates/horizon-client-render/src/chunk/block_entity.rs', lab.ROOT / 'crates/horizon-client-core/src/identifier.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_selection.rs', - lab.ROOT / 'crates/horizon-client/src/biome.rs', + lab.ROOT / 'crates/horizon-client-render/src/biome.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_rules.rs'] tested = base.source_hashes(sources) inputs = build / 'inputs.txt' diff --git a/tools/test_vault_parity.py b/tools/test_vault_parity.py index 1f151c3a49..5884ad464e 100644 --- a/tools/test_vault_parity.py +++ b/tools/test_vault_parity.py @@ -174,8 +174,8 @@ def main(): resources = lab.LOCAL / 'horizon-client/26.3' oracle = lab.ROOT / 'mods/horizon/src/test/java/dev/horizonlab/parity/VanillaNaturalSpawnOracle.java' sources = [oracle, Path(__file__), lab.ROOT / 'crates/horizon-client/examples/probe/natural_spawn_fixture.rs', - lab.ROOT / 'crates/horizon-client/src/vault.rs', - lab.ROOT / 'crates/horizon-client/src/chunk/block_entity.rs', + lab.ROOT / 'crates/horizon-client-render/src/vault.rs', + lab.ROOT / 'crates/horizon-client-render/src/chunk/block_entity.rs', lab.ROOT / 'crates/horizon-client-core/src/identifier.rs', lab.ROOT / 'crates/horizon-client-core/src/nbt_value.rs'] tested = base.source_hashes(sources) diff --git a/tools/test_weather_render_parity.py b/tools/test_weather_render_parity.py index cf52156e0e..b02d52dca2 100644 --- a/tools/test_weather_render_parity.py +++ b/tools/test_weather_render_parity.py @@ -32,7 +32,7 @@ def make_input(build): def main(): harness.differential("weather-render-parity", "VanillaWeatherRenderOracle", "weather_fixture", make_input, "Rain/snow column instances and quad vertices; not full framebuffer parity.", - sources=[lab.ROOT / "crates/horizon-client/src/weather.rs"]) + sources=[lab.ROOT / "crates/horizon-client-render/src/weather.rs"]) if __name__ == "__main__": diff --git a/tools/test_world_spawn_parity.py b/tools/test_world_spawn_parity.py index 33bdd4c65c..951fd92a52 100644 --- a/tools/test_world_spawn_parity.py +++ b/tools/test_world_spawn_parity.py @@ -33,9 +33,15 @@ tools/parity.py runs it in PARITY_SHARDS parts, each creating the worlds of every PARITY_SHARDS-th seed: each seed is a vanilla and a Horizon server start, ~15 s, and 60 seeds in one run were the longest harness (~25 min). +Under tools/parity.py's oracle reuse (not --fresh or --all) a seed's vanilla +spawn is kept and reused: vanilla's server is ~11 s of a seed's ~13 s, and its +spawn depends only on what the key holds (see vanilla_cache). tools/parity_shim.py +can't do it: a server's console is a pipe, and it never caches those runs. + usage: test_world_spawn_parity.py [--random N] [--jobs N] [--origins-only] """ import argparse +import hashlib import json import os import random @@ -86,13 +92,37 @@ def origins(build, classes, dependencies, all_seeds): return parse(vanilla), parse(rust) +def properties(seed): + """server.properties of a seed's world, without the port.""" + return {'online-mode': 'false', 'level-seed': str(seed), 'view-distance': '4', 'simulation-distance': '4', + 'pause-when-empty-seconds': '-1'} + + +def vanilla_cache(lock, command, classpath): + """A function giving the file that keeps a seed's vanilla spawn, or None when every vanilla world is created. + + Only under tools/parity.py's oracle reuse (PARITY_SHIM_CACHE; a harness failing on reused results is rerun + without it). The key holds what vanilla's spawn depends on: the server jar (verify_artifacts checked it and its + libraries against the lock), the JDK, the server's command line and the seed's server.properties.""" + root = os.environ.get('PARITY_SHIM_CACHE') + if not root or os.environ.get('PARITY_JAVA_CACHE') == '0': + return None + java = subprocess.run(['java', '-version'], capture_output=True, text=True, check=True).stderr + server = {'jar': lock['minecraft']['server_sha1'], 'java': java, + 'command': [part for part in command if part != classpath]} + + def record(seed): + identity = json.dumps({**server, 'properties': properties(seed)}, sort_keys=True) + return Path(root) / f'world-spawn-{hashlib.sha256(identity.encode()).hexdigest()}.json' + return record + + def world_spawn(command, directory, seed, log_name): """Creates a world with `seed` in `directory`, stops, returns its level.dat spawn position.""" if directory.exists(): shutil.rmtree(directory) directory.mkdir(parents=True) - props = {'online-mode': 'false', 'level-seed': str(seed), 'server-port': str(free_port()), - 'view-distance': '4', 'simulation-distance': '4', 'pause-when-empty-seconds': '-1'} + props = {**properties(seed), 'server-port': str(free_port())} (directory / 'server.properties').write_text(''.join(f'{k}={v}\n' for k, v in props.items()), encoding='iso-8859-1') (directory / 'eula.txt').write_text('eula=true\n') server = Server(command, directory, log_name) @@ -141,10 +171,24 @@ def main(): vanilla_server = ['java', '-Xmx2g', '-Dmax.bg.threads=1', '-cp', dependencies, 'net.minecraft.server.Main', 'nogui'] horizon_server = [binary, '--resources', RESOURCES, '--dedicated', '--nogui'] os.environ.update(lab.game_env()) + cached = vanilla_cache(lock, vanilla_server, dependencies) + reused = [] + + def vanilla_spawn(seed): + record = cached and cached(seed) + if record and record.exists(): + reused.append(seed) + return tuple(json.loads(record.read_text())) + spawn = world_spawn(vanilla_server, build / f'vanilla-{seed}', seed, f'vanilla-{seed}') + if record: + temporary = record.with_suffix(f'.{os.getpid()}.tmp') + temporary.write_text(json.dumps(spawn)) + temporary.replace(record) + return spawn def one(seed): t0 = time.time() - a = world_spawn(vanilla_server, build / f'vanilla-{seed}', seed, f'vanilla-{seed}') + a = vanilla_spawn(seed) b = world_spawn(horizon_server, build / f'horizon-{seed}', seed, f'horizon-{seed}') return seed, a, b, time.time() - t0 @@ -159,7 +203,8 @@ def main(): if ok: for path in build.glob(f'*-{seed}*'): shutil.rmtree(path, ignore_errors=True) - print(f'World spawns: {len(all_seeds) - len(spawn_diffs)}/{len(all_seeds)} seeds match in {time.time() - started:.0f}s') + print(f'World spawns: {len(all_seeds) - len(spawn_diffs)}/{len(all_seeds)} seeds match in {time.time() - started:.0f}s' + + (f' (vanilla reused for {len(reused)})' if reused else '')) if origin_diffs or spawn_diffs: sys.exit(1)