mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Compare commits
191
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e322f591a | ||
|
|
95ace2feff | ||
|
|
409ac72702 | ||
|
|
0548953031 | ||
|
|
0235d33aef | ||
|
|
78616f1f94 | ||
|
|
9ec105172f | ||
|
|
a3c239b849 | ||
|
|
13d7d9995d | ||
|
|
92c462a9b1 | ||
|
|
77bfff8eb5 | ||
|
|
8ea9a2c849 | ||
|
|
04cd6ece23 | ||
|
|
9218dc3e7d | ||
|
|
5b66bd0ec0 | ||
|
|
f75896ec45 | ||
|
|
2b294bbe3a | ||
|
|
f220157a0f | ||
|
|
3f28d6dbd1 | ||
|
|
a80973335c | ||
|
|
c91ca07115 | ||
|
|
d203ca04be | ||
|
|
11494e17f9 | ||
|
|
ff0e16e3b9 | ||
|
|
70b51333d3 | ||
|
|
8f0c178957 | ||
|
|
5a30084c1d | ||
|
|
9e49037875 | ||
|
|
9169aec896 | ||
|
|
cddd325067 | ||
|
|
b19b4e92d3 | ||
|
|
1727cea578 | ||
|
|
5c0f6aa56c | ||
|
|
364922fa5e | ||
|
|
1d8f853946 | ||
|
|
33f3347d84 | ||
|
|
8f69fb313f | ||
|
|
2d966fb3ad | ||
|
|
62a01db8f0 | ||
|
|
8871ba0b28 | ||
|
|
299c0b495d | ||
|
|
c29d37de1d | ||
|
|
cbacadae22 | ||
|
|
caa1be7abe | ||
|
|
1970ab1a7b | ||
|
|
0e57b1f6cf | ||
|
|
c9d61dcab7 | ||
|
|
8dfce86201 | ||
|
|
7a8c5a2642 | ||
|
|
dd9c9433b6 | ||
|
|
e037d39314 | ||
|
|
9b12c89191 | ||
|
|
ac17fd2b0f | ||
|
|
e9027c77c2 | ||
|
|
95bd72abaf | ||
|
|
0fc7603da8 | ||
|
|
68b626fc9d | ||
|
|
5edfd95da6 | ||
|
|
ae4ffbea8f | ||
|
|
c2c364f195 | ||
|
|
b1532a179a | ||
|
|
ff85f100ec | ||
|
|
c6af857433 | ||
|
|
1a71386829 | ||
|
|
e12c5d4f23 | ||
|
|
8f91683323 | ||
|
|
c5221cc92d | ||
|
|
471d815aeb | ||
|
|
955e7fe48d | ||
|
|
40073ab9c0 | ||
|
|
5c2bc0af4a | ||
|
|
11f22e26fa | ||
|
|
e6bb2f74c7 | ||
|
|
08c67f3d2f | ||
|
|
db8c6d6331 | ||
|
|
e665b6bf00 | ||
|
|
0048f04f1b | ||
|
|
791d8b8950 | ||
|
|
b9a040dc21 | ||
|
|
fe3333f3f0 | ||
|
|
1dce2ab885 | ||
|
|
b56048d996 | ||
|
|
ee90b3b34b | ||
|
|
78e449714a | ||
|
|
ab0f591673 | ||
|
|
8740d3897c | ||
|
|
142388e1c1 | ||
|
|
a72f06cfab | ||
|
|
fab9c37413 | ||
|
|
93b7ddd09e | ||
|
|
6a221890bd | ||
|
|
57a6f4e9aa | ||
|
|
95ecc9d771 | ||
|
|
99207f16b1 | ||
|
|
4a094bee90 | ||
|
|
66e1fc5079 | ||
|
|
b276d65b5f | ||
|
|
c404ef5e2a | ||
|
|
5a2d71d2ea | ||
|
|
60773a0e92 | ||
|
|
0291a5864f | ||
|
|
d2e7a54082 | ||
|
|
a7efe597ea | ||
|
|
f70501aa42 | ||
|
|
d0a29a0f66 | ||
|
|
380d71d8a8 | ||
|
|
0543cf0345 | ||
|
|
e3fda9ce67 | ||
|
|
abdff5c89d | ||
|
|
b78ff5f55e | ||
|
|
9d9e1a97a8 | ||
|
|
1743bbc525 | ||
|
|
cd2fea1f02 | ||
|
|
f1212a0e5a | ||
|
|
df4877a10b | ||
|
|
cc575e2c6a | ||
|
|
e14d6f79b1 | ||
|
|
827bd7f609 | ||
|
|
06fe3e8e69 | ||
|
|
158d99a528 | ||
|
|
98da8f82b8 | ||
|
|
465832b0e4 | ||
|
|
0318c132eb | ||
|
|
632179bda6 | ||
|
|
09043cd199 | ||
|
|
69bb386a6d | ||
|
|
11b36e3867 | ||
|
|
382cc39713 | ||
|
|
f351bcb06b | ||
|
|
bbe656e4ce | ||
|
|
1861c82434 | ||
|
|
8ba30576d1 | ||
|
|
8f26771053 | ||
|
|
33921460cc | ||
|
|
e1d825312f | ||
|
|
5ea61ed3d8 | ||
|
|
2466ccec98 | ||
|
|
d3121cea67 | ||
|
|
5dd3bf02d3 | ||
|
|
7bfc737d81 | ||
|
|
2e8c8e1dc7 | ||
|
|
727bd7f2dc | ||
|
|
e54cf96271 | ||
|
|
47bb5c668f | ||
|
|
a90e3bc36d | ||
|
|
305ffc4e39 | ||
|
|
cac7fa95fd | ||
|
|
1db835fa0c | ||
|
|
60c5d9fefd | ||
|
|
222dc28be9 | ||
|
|
98d0c6d1a3 | ||
|
|
81be26d686 | ||
|
|
1a8d0ef420 | ||
|
|
afd40fdbfa | ||
|
|
511f2c42d8 | ||
|
|
2607d06f94 | ||
|
|
732be56485 | ||
|
|
ab955d66c4 | ||
|
|
8fdcb37ddd | ||
|
|
e9b71c288a | ||
|
|
db27568929 | ||
|
|
9c9ce0b435 | ||
|
|
af41d0325c | ||
|
|
e21957efd7 | ||
|
|
6790913e0b | ||
|
|
deea1a5b83 | ||
|
|
f6807fe7cd | ||
|
|
783c25e9bc | ||
|
|
8e36842891 | ||
|
|
7b841af095 | ||
|
|
033c89a1b2 | ||
|
|
2dc154ff7f | ||
|
|
f6e63d252d | ||
|
|
4f13db42b0 | ||
|
|
84d204327f | ||
|
|
574f3587ed | ||
|
|
ee6b5bf585 | ||
|
|
1232745064 | ||
|
|
52c667f423 | ||
|
|
3382b666ce | ||
|
|
49ce3e6d5a | ||
|
|
3e0f4cf653 | ||
|
|
92c073de61 | ||
|
|
139de824aa | ||
|
|
ed01a93df6 | ||
|
|
1a56e74324 | ||
|
|
25b84dfc2e | ||
|
|
a2098c2ffa | ||
|
|
d98dc8edc9 | ||
|
|
bf05176692 | ||
|
|
b042426180 |
No files matched your search
@@ -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/<file>.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
|
||||
```
|
||||
@@ -35,11 +35,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` |
|
||||
@@ -99,6 +100,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 +114,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::<module>` paths), `G:` = bin (`crates/horizon-client/src/game/`). The lib can't see
|
||||
`Game`; bin code reaches the lib as `horizon_client::<module>`.
|
||||
|
||||
| Subsystem | Files |
|
||||
|
||||
Generated
+43
-2
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<img src="docs/media/banner.webp" alt="Horizon: Minecraft: Java Edition 26.3, rewritten in Rust and checked against vanilla by 281 differential harnesses" width="100%">
|
||||
<img src="docs/media/banner.webp" alt="Horizon: Minecraft: Java Edition 26.3, rewritten in Rust and checked against vanilla by more than 300 differential harnesses" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -23,13 +23,13 @@
|
||||
|
||||
# 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).
|
||||
|
||||
@@ -48,7 +48,7 @@ The [documentation](https://playhorizon.dev/docs/) covers each of these.
|
||||
- **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.
|
||||
- **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 |
|
||||
|
||||
+111
-30
@@ -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)
|
||||
|
||||
</details>
|
||||
|
||||
#### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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)
|
||||
|
||||
</details>
|
||||
|
||||
#### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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)
|
||||
|
||||
</details>
|
||||
|
||||
#### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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.
|
||||
|
||||
</details>
|
||||
|
||||
#### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
`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)
|
||||
|
||||
</details>
|
||||
|
||||
@@ -244,6 +256,20 @@ Horizon's own way to make shader packs, with tools to write and test them. Every
|
||||
|
||||
</details>
|
||||
|
||||
### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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)
|
||||
|
||||
</details>
|
||||
|
||||
### Horizon extras
|
||||
|
||||
#### Discord Rich Presence
|
||||
@@ -268,6 +294,16 @@ A review of the whole tree: dead code and stubs removed ([#1432](https://github.
|
||||
|
||||
</details>
|
||||
|
||||
#### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
[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).
|
||||
|
||||
</details>
|
||||
|
||||
## Next
|
||||
|
||||
### Performance
|
||||
@@ -444,6 +480,16 @@ A player's idea (#horizon-ideas). `horizon-server` already runs headless; left:
|
||||
|
||||
</details>
|
||||
|
||||
#### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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)).
|
||||
|
||||
</details>
|
||||
|
||||
### 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
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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)
|
||||
|
||||
</details>
|
||||
|
||||
@@ -468,16 +514,6 @@ Block entities and menus: ticking machines with slots, server menu logic and the
|
||||
|
||||
</details>
|
||||
|
||||
#### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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').
|
||||
|
||||
</details>
|
||||
|
||||
### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -708,6 +742,18 @@ Issue [#1270](https://github.com/horizon-rust/horizon/issues/1270) asks for Ely.
|
||||
|
||||
</details>
|
||||
|
||||
#### Join servers on older versions
|
||||
|
||||
Join servers that run older Minecraft versions, such as 1.21 or 1.8 PvP servers, from Horizon.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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)
|
||||
|
||||
</details>
|
||||
|
||||
#### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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)
|
||||
|
||||
</details>
|
||||
|
||||
#### 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.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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)
|
||||
|
||||
</details>
|
||||
|
||||
@@ -784,6 +842,16 @@ The game side is merged ([docs/controllers.md](docs/controllers.md)): pads read
|
||||
|
||||
</details>
|
||||
|
||||
#### Splitscreen
|
||||
|
||||
Play with a friend on the same screen, each with a controller.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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.
|
||||
|
||||
</details>
|
||||
|
||||
#### 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
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -862,6 +930,18 @@ Issue [#1709](https://github.com/horizon-rust/horizon/issues/1709): aarch64 buil
|
||||
|
||||
</details>
|
||||
|
||||
#### Horizon on Windows ARM64
|
||||
|
||||
A native Horizon for Windows on ARM computers, such as Snapdragon laptops.
|
||||
|
||||
<details><summary>Technical</summary>
|
||||
|
||||
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)
|
||||
|
||||
</details>
|
||||
|
||||
#### 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)
|
||||
|
||||
@@ -494,7 +494,11 @@ pub fn canonical(tag: &Tag) -> Option<Tag> {
|
||||
_ => string("minecraft:air"),
|
||||
},
|
||||
);
|
||||
f.put("facing", Tag::Int(int(tag.get("facing")).unwrap_or(0)));
|
||||
// Direction.LEGACY_ID_CODEC: the 3D data value as a byte.
|
||||
f.put(
|
||||
"facing",
|
||||
Tag::Byte(int(tag.get("facing")).unwrap_or(0) as i8),
|
||||
);
|
||||
f.float("progress", 0.0);
|
||||
f.flag("extending", false);
|
||||
f.flag("source", false);
|
||||
@@ -873,6 +877,23 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// `PistonMovingBlockEntity.saveAdditional`: `facing` through
|
||||
/// `Direction.LEGACY_ID_CODEC` (a byte), `progress` a float.
|
||||
#[test]
|
||||
fn moving_piston_fields_match_vanilla() {
|
||||
let out = canon(
|
||||
"{id:\"minecraft:piston\",x:1,y:2,z:3,blockState:{Name:\"minecraft:stone\"},\
|
||||
facing:5,progress:0.5f,extending:1b,source:0b}",
|
||||
);
|
||||
assert_eq!(out.get("facing"), Some(&Tag::Byte(5)));
|
||||
assert_eq!(out.get("progress"), Some(&f32_tag(0.5)));
|
||||
assert_eq!(out.get("extending"), Some(&Tag::Byte(1)));
|
||||
assert_eq!(out.get("source"), Some(&Tag::Byte(0)));
|
||||
let fresh = canon("{id:\"minecraft:piston\",x:1,y:2,z:3}");
|
||||
assert_eq!(fresh.get("facing"), Some(&Tag::Byte(0)));
|
||||
assert_eq!(fresh.get("blockState"), Some(&string("minecraft:air")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loot_table_replaces_items_and_zero_seed_is_omitted() {
|
||||
let out = canon(
|
||||
|
||||
@@ -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<Vec<u8>>;
|
||||
/// 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<String> {
|
||||
vec!["minecraft".into()]
|
||||
@@ -68,6 +72,9 @@ impl ResourceSource for std::path::PathBuf {
|
||||
fn read(&self, path: &str) -> Option<Vec<u8>> {
|
||||
std::fs::read(self.join(path)).ok()
|
||||
}
|
||||
fn exists(&self, path: &str) -> bool {
|
||||
self.join(path).is_file()
|
||||
}
|
||||
fn namespaces(&self) -> Vec<String> {
|
||||
std::fs::read_dir(self.join("assets"))
|
||||
.into_iter()
|
||||
@@ -190,7 +197,7 @@ pub fn available(sources: &[&dyn ResourceSource]) -> Vec<LanguageInfo> {
|
||||
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(
|
||||
@@ -284,4 +291,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<Vec<u8>> {
|
||||
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::<Vec<_>>(),
|
||||
["en_us", "fr_fr"]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,9 @@ pub struct Registries {
|
||||
pub function_tags: Vec<String>,
|
||||
/// 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<String>,
|
||||
/// Every registry's element and tag ids (`minecraft:item` -> ...), as
|
||||
/// the server's registry access and reloadable registries hold them.
|
||||
pub by_registry: BTreeMap<String, RegistryIds>,
|
||||
|
||||
@@ -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"] }
|
||||
@@ -42,11 +42,11 @@ impl HeightmapState for StateFacts {
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Properties(Arc<Vec<StateFacts>>);
|
||||
#[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<Self> {
|
||||
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();
|
||||
+8
-1
@@ -187,7 +187,14 @@ pub fn list_textures(root: &Path) -> Vec<String> {
|
||||
};
|
||||
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)
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+63
-3
@@ -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<Next>,
|
||||
pub next: Option<Next>,
|
||||
pub min_spawn_delay: i32,
|
||||
pub max_spawn_delay: i32,
|
||||
pub spawn_count: i32,
|
||||
@@ -301,6 +301,24 @@ pub struct Spawner {
|
||||
pub spin: f64,
|
||||
pub o_spin: f64,
|
||||
}
|
||||
impl Default for Spawner {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
spawn_delay: 20,
|
||||
potentials: Vec::new(),
|
||||
next: None,
|
||||
min_spawn_delay: 200,
|
||||
max_spawn_delay: 800,
|
||||
spawn_count: 4,
|
||||
max_nearby_entities: 6,
|
||||
required_player_range: 16,
|
||||
spawn_range: 4,
|
||||
client_delay: 20,
|
||||
spin: 0.0,
|
||||
o_spin: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// `TrialSpawnerConfig`.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TrialConfig {
|
||||
@@ -314,6 +332,26 @@ pub struct TrialConfig {
|
||||
pub loot_tables_to_eject: Vec<(String, i32)>,
|
||||
pub items_to_drop_when_ominous: String,
|
||||
}
|
||||
impl Default for TrialConfig {
|
||||
/// `TrialSpawnerConfig.Builder`'s defaults.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
spawn_range: 4,
|
||||
total_mobs: 6.0,
|
||||
simultaneous_mobs: 2.0,
|
||||
total_mobs_added_per_player: 2.0,
|
||||
simultaneous_mobs_added_per_player: 1.0,
|
||||
ticks_between_spawn: 40,
|
||||
spawn_potentials: Vec::new(),
|
||||
loot_tables_to_eject: vec![
|
||||
("minecraft:spawners/trial_chamber/consumables".into(), 1),
|
||||
("minecraft:spawners/trial_chamber/key".into(), 1),
|
||||
],
|
||||
items_to_drop_when_ominous:
|
||||
"minecraft:spawners/trial_chamber/items_to_drop_when_ominous".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// A `Holder<TrialSpawnerConfig>` as saved: a registry id
|
||||
/// (`minecraft:trial_chamber/breeze/normal`) or an inline config.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
@@ -347,6 +385,28 @@ pub struct TrialSpawner {
|
||||
/// and their weights (the rolled counts).
|
||||
pub dispensing: Option<Vec<ItemStack>>,
|
||||
}
|
||||
impl Default for TrialSpawner {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
normal: ConfigSlot::default(),
|
||||
ominous: ConfigSlot::default(),
|
||||
target_cooldown_length: 36000,
|
||||
required_player_range: 14,
|
||||
detected_players: Vec::new(),
|
||||
current_mobs: Vec::new(),
|
||||
cooldown_ends_at: 0,
|
||||
next_mob_spawns_at: 0,
|
||||
total_mobs_spawned: 0,
|
||||
next_spawn_data: None,
|
||||
ejecting_loot_table: None,
|
||||
is_ominous: false,
|
||||
has_display: false,
|
||||
spin: 0.0,
|
||||
o_spin: 0.0,
|
||||
dispensing: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// `VaultConfig`.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct VaultConfig {
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -330,7 +330,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<String>)> = Vec::new();
|
||||
for line in backtrace.lines() {
|
||||
@@ -605,7 +605,7 @@ 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"
|
||||
);
|
||||
}
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 467 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user