Compare commits

...
159 Commits
Author SHA1 Message Date
Ludovic Marie 8bbf748e1f Merge pull request #72 from horizon-rust/feat/smp-grief
SMP: keep water, fire, pistons, explosions, buckets and blows out of claims
2026-10-31 20:17:02 +01:00
Ludovic Marie 50c23cb7d9 Merge pull request #69 from horizon-rust/feat/smp-claims
SMP: land claims (/claim, /trust)
2026-10-31 20:16:54 +01:00
Ludovic Marie 8d5c1ed7a3 Merge pull request #66 from horizon-rust/feat/smp-places
SMP: places (spawn, homes, warps, /tpa) and /rules
2026-10-31 20:16:44 +01:00
levzzz b6c1af58cd Merge pull request #103 from horizon-rust/registry/veinminer-repo
veinminer 0.3.2 (Nightly 2026-10-31)
2026-11-01 00:06:14 +05:00
levzzzandClaude Opus 5.5 95b8f15c6b veinminer 0.3.2: Nightly 2026-10-31
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-31 23:42:38 +05:00
levzzz be6b796a20 Merge pull request #98 from horizon-rust/remove/mod-menu
Remove mod_menu (superseded by the built-in Mods menu)
2026-10-31 23:35:44 +05:00
levzzzandClaude Opus 5.5 2cd564f16b Remove mod_menu: superseded by Horizon's built-in Mods menu
The owner retires it: 0.1.1 needs an old launcher inventory feed and the game's own Mods screen now does the same job.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-31 22:39:59 +05:00
Ludovic Marie b571298b58 Merge branch 'feat/smp-claims' into feat/smp-grief 2026-10-31 16:49:55 +01:00
Ludovic Marie fefd3e8c6b Merge branch 'feat/smp-places' into feat/smp-claims 2026-10-31 16:49:53 +01:00
Ludovic Marie 4364dbfba3 Merge remote-tracking branch 'origin/main' into feat/smp-places 2026-10-31 16:49:46 +01:00
levzzz f7709827f6 Merge pull request #48 from Domanffe/blockscope-native-0.3.0
blockscope: a native-mod entry and 0.3.0
2026-10-31 09:19:01 +05:00
levzzz 602a58cfd1 Merge pull request #63 from horizon-rust/fix/smp-audit-sync
SMP: the audit log syncs only after a tick that wrote to it (-2.8 ms MSPT)
2026-10-31 09:15:55 +05:00
levzzz f8a41e6c8b Merge pull request #62 from horizon-rust/feat/smp-lottery
SMP: the lottery (/lottery), part of gambling, off by default
2026-10-31 09:15:51 +05:00
levzzzandClaude Sonnet 5.5 5495b461fd Merge main into blockscope-native-0.3.0
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-31 09:15:27 +05:00
levzzz d1e80f8485 Merge pull request #67 from horizon-rust/submit/fly_speed-0.1.0
Add fly_speed 0.1.0
2026-10-31 09:14:40 +05:00
levzzz 65aaaafe56 Merge pull request #70 from horizon-rust/submit/jei-0.1.0
Add jei 0.1.0
2026-10-31 09:14:36 +05:00
levzzz 634fc6cb7a Merge pull request #47 from horizon-rust/submit/serverify-0.1.0
Add serverify 0.1.0
2026-10-31 09:14:20 +05:00
levzzz fb9a47061c Merge pull request #65 from horizon-rust/submit/fullbright-1.0.0
Add fullbright 1.0.0
2026-10-31 09:14:16 +05:00
levzzz 0cebbbfad8 Merge pull request #73 from horizon-rust/submit/appleskin-0.1.1
Update appleskin 0.1.1
2026-10-31 09:13:43 +05:00
levzzz 52069c3e49 Merge pull request #74 from horizon-rust/submit/eatinganimation-0.1.1
Update eatinganimation 0.1.1
2026-10-31 09:13:39 +05:00
levzzzandClaude Sonnet 5.5 8479dec66f Ship the icon in the registry (icons/serverify.png): the launcher does not load raw.githubusercontent.com links
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-31 09:12:04 +05:00
levzzzandClaude Sonnet 5.5 f489d26358 Ship the icon in the registry (icons/fullbright.png): the launcher does not load raw.githubusercontent.com links
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-31 09:12:02 +05:00
levzzzandClaude Sonnet 5.5 4904b24c69 Ship the icon in the registry (icons/appleskin.png): the launcher does not load raw.githubusercontent.com links
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-31 09:11:39 +05:00
levzzzandClaude Sonnet 5.5 38421f5a8d Ship the icon in the registry (icons/eatinganimation.png): the launcher does not load raw.githubusercontent.com links
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-31 09:11:37 +05:00
Ludovic Marieandsoufied 3488a88696 Update eatinganimation 0.1.1
Submitted by @soufied through the form.

Co-authored-by: soufied <[email protected]>
2026-10-31 05:03:04 +01:00
Ludovic Marieandsoufied 2708b6a02f Update appleskin 0.1.1
Submitted by @soufied through the form.

Co-authored-by: soufied <[email protected]>
2026-10-31 04:52:23 +01:00
Ludovic MarieandClaude Opus 5.5 aaf6ccefcc SMP claims: pinned to horizon main with #4739 (API 22)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-31 00:56:05 +01:00
Ludovic MarieandClaude Opus 5.5 b0e3d98b52 SMP claims: pinned to horizon#4739's head (API 22)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 23:52:27 +01:00
Ludovic MarieandClaude Opus 5.5 803907aaa5 SMP: keep water, fire, pistons, explosions, buckets and blows out of claims (hooks 22)
On Horizon's API 22 hook points: a fluid or fire never spreads into a
claim from land of another owner (or of a player they don't trust), a
piston never pushes or pulls blocks into one, explosions spare claimed
blocks whoever lit them, buckets are guarded where they fill or empty, and
the claim's frames, armor stands, paintings, vehicles and animals
(`claims.protected_entities`) take no blows from untrusted players. The
spread and piston hooks read the open claims without allocating and never
wait on the SMP's lock. The proof adds a world_guard step on the dedicated
server, with controls outside the claim; the claims step no longer fails
when grass spreads onto its dirt. 0.8.0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 23:18:36 +01:00
Ludovic MarieandClaude Opus 5.5 ec056f9f02 SMP: land claims (/claim, /trust) on the block hooks
A player claims the chunk they stand in; only they, the players they
trust and operators break or right click blocks there
(native_destroy_block, native_right_click_block), told on the action
bar (native_action_bar, API 16). Chunks a player may hold grow with
the time played, counted by places. claims.json, written atomically.
Proved on a dedicated server: a lever, a dirt block and a placement
refused to Steve in Alex's chunk, then allowed once trusted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 22:08:36 +01:00
Ludovic MarieandClaude Opus 5.5 63f2fa9f01 SMP places: pinned to horizon main with #4610 (API 21)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 22:08:34 +01:00
Ludovic MarieandClaude Opus 5.5 1ec378ac96 SMP places: hook points API 21 (main's mod screens took 18 to 20)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 21:59:14 +01:00
Ludovic Marieandenderliker 3516095141 Add jei 0.1.0
Submitted by @enderliker through the form.

Co-authored-by: enderliker <[email protected]>
2026-10-30 21:17:57 +01:00
Ludovic MarieandClaude Opus 5.5 0eb6ca75c3 SMP places: hook points API 18 (main took 16 and 17)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 20:45:55 +01:00
Ludovic MarieandClaude Opus 5.5 26079e71ed SMP places: hook points API 16, and the proof waits for each answer
#3996's block entities took API 15 on main: horizon#4610's hooks are 16.
say() polls for the command's answer instead of a fixed 1.5 s, which a
busy machine outran.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 20:39:31 +01:00
Ludovic MarieandImmortalWhisper 7d2b14f53e Add fly_speed 0.1.0
Submitted by @ImmortalWhisper through the form.

Co-authored-by: ImmortalWhisper <[email protected]>
2026-10-30 20:27:24 +01:00
Ludovic MarieandClaude Opus 5.5 b9bd159dcf SMP: places (spawn, homes, warps, /tpa) and /rules
Teleports wait a warmup that a move or a hit calls off, then run vanilla's
tp as the server (native_run_command, API 15), so they cross levels.
Homes grow with the time played here, counted by the mod. places.json
holds the spawn, warps, homes and time played, written atomically.
Proved on a dedicated server with two clients: home from the Nether,
called off by a move and by damage, /tpa, warps, operators only.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 20:24:05 +01:00
Ludovic Marieandbezi5kt 52980b81e0 Add fullbright 1.0.0
Submitted by @bezi5kt through the form.

Co-authored-by: bezi5kt <[email protected]>
2026-10-30 20:18:00 +01:00
Ludovic MarieandClaude Opus 5.5 4fbe64e376 SMP: the lottery (/lottery), part of gambling, off by default
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 19:55:25 +01:00
Ludovic MarieandClaude Opus 5.5 5aacb47cd4 SMP: the audit log syncs only after a tick that wrote to it
A sync_data on every server tick, even with nothing appended, cost about
2.8 ms of MSPT with 55 bots on the Mac mini.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 19:55:01 +01:00
levzzz ff18bb42d5 Merge pull request #58 from horizon-rust/feat/smp-coinflip
SMP: coinflips with held stakes (/coinflip), off by default
2026-10-30 23:26:49 +05:00
levzzzandClaude Sonnet 5.5 9121109717 Merge main into feat/smp-coinflip
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-30 23:14:04 +05:00
levzzz 0faf62da1c Merge pull request #57 from horizon-rust/feat/smp-trade
SMP: player trades (/trade) with an atomic swap
2026-10-30 23:14:00 +05:00
Ludovic MarieandClaude Opus 5.5 5dad8648b3 SMP: coinflips with held stakes (/coinflip), off by default
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 19:07:38 +01:00
Ludovic MarieandClaude Opus 5.5 7f1bb6920b SMP: pinned on horizon main (f533c3a, hooks 10 merged)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 19:06:15 +01:00
Ludovic MarieandClaude Opus 5.5 7bd4f6207b SMP proof: the 26.3 keep_inventory rule
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 19:06:06 +01:00
Ludovic MarieandClaude Opus 5.5 1d4cb83ca2 SMP: player trades (/trade) on server menus
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 19:06:05 +01:00
levzzz 37f573d08f Merge pull request #56 from horizon-rust/feat/smp-market
SMP: the server market (/worth, /sell, /buy)
2026-10-30 23:04:50 +05:00
levzzz 847fd2dcbe Merge pull request #54 from horizon-rust/submit/xtoxray-2026.29.0
Add xtoxray 2026.29.0
2026-10-30 23:03:06 +05:00
levzzz 2c41bdf9fd Merge pull request #52 from horizon-rust/submit/rightharvest-0.1.0
Add rightharvest 0.1.0
2026-10-30 23:02:59 +05:00
levzzz 7704c01acf Merge pull request #51 from horizon-rust/submit/chatheads-0.1.0
Add chatheads 0.1.0
2026-10-30 23:01:45 +05:00
levzzz c2f369fb2e Merge pull request #43 from horizon-rust/submit/shulker_peek-0.1.0
Add shulker_peek 0.1.0
2026-10-30 23:01:37 +05:00
levzzzandClaude Sonnet 5.5 c13658c430 Merge main into feat/smp-market
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-30 22:57:04 +05:00
levzzz 5c1b79862e Merge pull request #55 from horizon-rust/feat/smp-economy
SMP native server mod: the economy (/balance, /pay, /baltop, /eco)
2026-10-30 22:56:59 +05:00
levzzz 8366f89925 Merge pull request #53 from horizon-rust/submit/eatinganimation-0.1.0
Add eatinganimation 0.1.0
2026-10-30 22:56:28 +05:00
levzzz 2917b77de1 Merge pull request #49 from horizon-rust/submit/appleskin-1.0.0
Add appleskin 1.0.0
2026-10-30 22:56:21 +05:00
levzzz b75251443c Merge pull request #44 from horizon-rust/submit/distant_horizons-0.1.0
Add distant_horizons 0.1.0
2026-10-30 22:55:57 +05:00
levzzzandClaude Sonnet 5.5 5d26a0bde3 rightharvest: add the icon its entry points at
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-30 22:55:33 +05:00
levzzzandClaude Sonnet 5.5 7339aa2992 chatheads: add the icon its entry points at
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-30 22:55:31 +05:00
levzzzandClaude Sonnet 5.5 0096a8a835 xtoxray: host the icon in the repo, SPDX licence id
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-30 22:55:28 +05:00
levzzzandClaude Sonnet 5.5 5728b12cbb shulker_peek: host the icon in the repo
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-30 22:55:20 +05:00
Ludovic MarieandClaude Opus 5.5 830bc7884b SMP README: players install nothing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 17:54:15 +01:00
Ludovic MarieandClaude Opus 5.5 7e795012ed SMP: the server market (/worth, /sell, /buy)
The server buys and sells vanilla items at config.json's market prices
(a starting list the owner edits): plain items only, players in
survival or adventure mode only. Items move first, then money, both in
the audit log; a sale the log refuses gives the items back, a purchase
checks money and room first. Needs hook points API 9 (the acting
player's items). config.json is written again when a version adds keys.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 17:48:10 +01:00
Ludovic MarieandClaude Opus 5.5 666479ad05 SMP native server mod: the economy (/balance, /pay, /baltop, /eco)
The official server's SMP, opt-in: not listed in the registry, built only
into the official server's engine. Balances by UUID in
world/horizon-smp/economy.json (written atomically, at most once a tick),
an audit line per change written before it, config.json with defaults.
Hooks only Horizon's hook points (API 8: server commands and players).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 17:48:08 +01:00
Ludovic MarieandRealCat1st b498e0a5fc Add xtoxray 2026.29.0
Submitted by @RealCat1st through the form.

Co-authored-by: RealCat1st <[email protected]>
2026-10-30 16:53:17 +01:00
Ludovic Marieandsoufied ebcadb8dd6 Add eatinganimation 0.1.0
Submitted by @soufied through the form.

Co-authored-by: soufied <[email protected]>
2026-10-30 15:53:17 +01:00
Ludovic MarieandDomanffe b1ad307e93 Add rightharvest 0.1.0
Submitted by @Domanffe through the form.

Co-authored-by: Domanffe <[email protected]>
2026-10-30 15:39:29 +01:00
Ludovic MarieandDomanffe 395b1422e0 Add chatheads 0.1.0
Submitted by @Domanffe through the form.

Co-authored-by: Domanffe <[email protected]>
2026-10-30 15:38:18 +01:00
Ludovic Marieandsoufied 1910ab8709 Add appleskin 1.0.0
Submitted by @soufied through the form.

Co-authored-by: soufied <[email protected]>
2026-10-30 15:00:11 +01:00
Alexander Krylov 08ca24a176 blockscope: a native-mod entry and 0.3.0
0.3.0 is the WebAssembly mod rewritten as a mixin bundle, so the entry
turns native-mod once (docs/native-mods.md). Its two .hmod versions stay
as they are. The bundle itself is not in staging yet.
2026-10-30 16:56:34 +03:00
Ludovic MarieandLouchatfroff b27aeaa93f Add serverify 0.1.0
Submitted by @Louchatfroff through the form.

Co-authored-by: Louchatfroff <[email protected]>
2026-10-30 14:36:17 +01:00
Cosmic ae217daafd Merge pull request #46 from Domanffe/icon/blockscope
blockscope: an icon the launcher can fetch
2026-10-30 09:25:42 -04:00
Alexander KrylovandHermes Agent d6501160e1 blockscope: an icon the launcher can fetch
The entry points at raw.githubusercontent.com, which the launcher refuses: it
fetches project images only from Modrinth's and CurseForge's CDNs and this
registry's Pages (launcher/src/browser/images.rs, HOSTS). So Blockscope has
shown no icon in the launcher's Mods page while every registry icon does.

icons/blockscope.png is the mod's own 256x256 RGBA icon, the size the other
icons here use, and the entry now names the Pages URL the publish workflow
serves it from (publish.yml copies icons/ to the site root).

Checked: https://horizon-rust.github.io/horizon-mods/icons/biomecompass.png
answers 200 image/png, while icons/blockscope.png and icons/zoom.png (a mod
with no file here) answer 404, so the URL is live only once the file is.

Co-Authored-By: Hermes Agent (Nous Research) <[email protected]>
2026-10-30 16:02:44 +03:00
Ludovic Marieanddenizsahin7744 c3669e5412 Add distant_horizons 0.1.0
Submitted by @denizsahin7744 through the form.

Co-authored-by: denizsahin7744 <[email protected]>
2026-10-29 22:54:51 +01:00
Ludovic Marieandvstxx 0e7ed706b6 Add shulker_peek 0.1.0
Submitted by @vstxx through the form.

Co-authored-by: vstxx <[email protected]>
2026-10-29 22:18:15 +01:00
levzzz 678eb2d1af Merge pull request #40 from SU4G3/classic-zoom-native
classic_zoom 2.0.0: a native mod
2026-10-29 23:59:32 +05:00
levzzz 55e203b1cd Merge pull request #31 from d3grando/lookat-native
LookAt 0.2.1: turn native
2026-10-29 23:59:24 +05:00
levzzz e9f14ea77f Merge pull request #41 from d3grando/mod-icons
Icons for TreeFall, SmartF3, Item New, BiomeCompass, Enchantment Hints, Mouse Tricks and Carry Block
2026-10-29 23:45:09 +05:00
d3grandoandClaude Opus 5.5 190d8f9a60 Icons for d3grando's native mods, served from this repository
The entries pointed at raw.githubusercontent.com, which playhorizon.dev's
img-src and the launcher don't load. The same 256x256 PNGs now live in
icons/, as coords_hud's does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 00:51:23 +07:00
SU4G3andClaude Opus 5.5 bbe01b52ad classic_zoom 2.0.0: a native mod
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 18:04:39 +03:00
Ludovic Marie 0d8411b352 Merge pull request #39 from horizon-rust/native-coords-hud
Coordinates HUD turns native (2.0.0), on the hook points
2026-10-29 14:32:02 +01:00
Ludovic MarieandClaude Opus 5.5 890d330600 Coordinates HUD: drop a stray Python cache file
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 14:08:25 +01:00
Ludovic MarieandClaude Opus 5.5 7fd746624a Coordinates HUD turns native (2.0.0), on the hook points
The WebAssembly SDK's example mod, ported to a horizon-mixins bundle in
native/coords-hud: the position and facing box in the top left, /where and
/coords, and a Shown setting that /coords now keeps. The time of day line is
gone: the hook points don't give the game's clock yet. Tested on Nightly
4baa571, alone and with the five other native mods; the proof drives the
built game.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 14:08:17 +01:00
Ludovic MarieandClaude Opus 5.5 7ce014f8a9 Recipes turns native (2.0.0), on hook points version 2 (#38)
Pinned to Nightly 4baa571, the first with horizon#3435's hook points v2.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-29 13:26:55 +01:00
levzzzandOpenAI Codex e62054006e Merge pull request #36 from horizon-rust/submit/classic_zoom-1.0.0
Merged by levzzz5154's AI agent (OpenAI Codex), following source and artifact review.

Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-29 15:13:16 +05:00
levzzzandOpenAI Codex b956bda63b Merge pull request #33 from horizon-rust/submit/mousetricks-0.2.0
Merged by levzzz5154's AI agent (OpenAI Codex), following source and artifact review.

Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-29 15:13:12 +05:00
levzzzandOpenAI Codex ce12d21fe3 Merge pull request #32 from horizon-rust/submit/enchantmenthints-0.1.0
Merged by levzzz5154's AI agent (OpenAI Codex), following source and artifact review.

Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-29 15:13:08 +05:00
levzzzandOpenAI Codex 34f8420003 Merge pull request #30 from horizon-rust/submit/biomecompass-0.2.2
Merged by levzzz5154's AI agent (OpenAI Codex), following source and artifact review.

Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-29 15:13:04 +05:00
levzzzandOpenAI Codex ed256db6f7 Merge pull request #29 from horizon-rust/submit/itemnew-0.1.1
Merged by levzzz5154's AI agent (OpenAI Codex), following source and artifact review.

Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-29 15:13:01 +05:00
levzzzandOpenAI Codex 4fb24a8263 Merge pull request #27 from horizon-rust/submit/treefall-0.3.0
Merged by levzzz5154's AI agent (OpenAI Codex), following source and artifact review.

Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-29 15:12:57 +05:00
levzzzandOpenAI Codex 2cc76d1dc5 Merge pull request #25 from horizon-rust/submit/blockscope-0.2.0
Merged by levzzz5154's AI agent (OpenAI Codex), following source and artifact review.

Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-29 15:12:53 +05:00
levzzzandOpenAI Codex ef5c5fe50b Merge pull request #23 from horizon-rust/submit/compass_bar-0.1.0
Merged by levzzz5154's AI agent (OpenAI Codex), following source and artifact review.

Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-29 15:12:50 +05:00
Ludovic MarieandSU4G3 c1c783574c Add classic_zoom 1.0.0
Submitted by @SU4G3 through the form.

Co-authored-by: SU4G3 <[email protected]>
2026-10-29 10:22:47 +01:00
levzzz 4b3311b800 Merge pull request #28 from horizon-rust/submit/smartf3-0.1.0
Add smartf3 0.1.0
2026-10-29 13:53:05 +05:00
levzzz b022d5b14f Merge pull request #34 from horizon-rust/submit/carryblock-0.1.2
Add carryblock 0.1.2
2026-10-29 13:40:22 +05:00
Ludovic Marieandd3grando 3a8c438ec5 Add carryblock 0.1.2
Submitted by @d3grando through the form.

Co-authored-by: d3grando <[email protected]>
2026-10-29 08:13:11 +01:00
Ludovic Marieandd3grando adea72cbdf Add mousetricks 0.2.0
Submitted by @d3grando through the form.

Co-authored-by: d3grando <[email protected]>
2026-10-29 08:13:01 +01:00
Ludovic Marieandd3grando bc79048b9a Add enchantmenthints 0.1.0
Submitted by @d3grando through the form.

Co-authored-by: d3grando <[email protected]>
2026-10-29 08:12:53 +01:00
d3grandoandClaude Opus 5.5 f6dade0df2 LookAt 0.2.1: turn native
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 13:18:27 +07:00
Ludovic Marieandd3grando fb9ab126f7 Add biomecompass 0.2.2
Submitted by @d3grando through the form.

Co-authored-by: d3grando <[email protected]>
2026-10-29 07:13:10 +01:00
Ludovic Marieandd3grando 4d975b9813 Add itemnew 0.1.1
Submitted by @d3grando through the form.

Co-authored-by: d3grando <[email protected]>
2026-10-29 07:13:01 +01:00
Ludovic Marieandd3grando e7a95dcbef Add smartf3 0.1.0
Submitted by @d3grando through the form.

Co-authored-by: d3grando <[email protected]>
2026-10-29 07:12:53 +01:00
Ludovic Marieandd3grando 266b6039d4 Add treefall 0.3.0
Submitted by @d3grando through the form.

Co-authored-by: d3grando <[email protected]>
2026-10-29 07:11:13 +01:00
Ludovic MarieandClaude Opus 5.5 22c6f14ea6 Zoom, FPS Counter, Keystrokes and Minimap turn native (2.0.0) (#26)
The four official entries switch from `mod` to `native-mod`, each with a
2.0.0 `.hnmod` built from native/<name>/ and pinned to Nightly 6f8c7a0
(nightly-source/2026-10-28-6f8c7a0), the first Nightly with the native
hook points v1 and the on/off from Options > Mods. Their 1.0.0 `.hmod`
stays published; the index lists only 2.0.0, and the launcher keeps a
player's installed `.hmod` until a native build has the mod. Settings
carry over: the same files and keys.

Recipes stays on 8f91683 (hook points v2) until a Nightly ships them.
native.py now builds mods pinned to different commits on the pin of the
mods that need the most hook points, as the launcher does, so `together`
checks the whole set without --horizon.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-29 05:32:54 +01:00
Ludovic MarieandDomanffe 0f556b3a89 Update blockscope 0.2.0
Submitted by @Domanffe through the form.

Co-authored-by: Domanffe <[email protected]>
2026-10-29 05:27:45 +01:00
Ludovic MarieandClaude Opus 5.5 d6a1ddd703 Recipes as a native mod (native/recipes, hook points v2) (#24)
* Recipes as a native mod (native/recipes, hooks 2)

Horizon's official Recipes viewer, ported from the WebAssembly mod to a
horizon-mixins bundle on the native hook points, API 2: the item panel
beside container screens, search (#tag, @namespace), recipes and uses per
station, + to fill the crafting grid, and "Click gives items" in creative.
It keeps the WebAssembly mod's setting file (give) and keys
(key.recipes.recipe, key.recipes.uses), and rests when turned off in
Options > Mods.

The viewer is runtime.rs (also the crate's library, with its tests); the
game's tables and panel events go through glue.rs, a second module.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Native mods: pin all five to Horizon 8f91683 (hook points v2)

Zoom, FPS Counter, Keystrokes, Minimap and Recipes all name the commit
they were tested on: main after horizon-rust/horizon#3435, which brought
hook points v2 (the item panel and recipe tables). One commit for all of
them, so the `together` job builds the set without --horizon. The four
older mods still need only hooks 1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-29 02:23:19 +01:00
Ludovic MarieandClaude Opus 5.5 f539b5a584 Native mods: rest when turned off in Options > Mods (#22)
Each official mod checks `native_on` (horizon-rust/horizon#3346) first in its
HUD, minimap, key, wheel and zoom hooks, so turning it off in Options > Mods
takes effect at once; the launcher's next build leaves it out. Pinned to
Horizon 01d0f45, which has the helper.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-28 23:11:49 +01:00
Ludovic Marie b07b1fdef0 Merge pull request #21 from horizon-rust/feat/native-official-mods
Horizon's native mods: one folder each in native/, with their history and CI
2026-10-28 23:03:55 +01:00
Ludovic MarieandFernandes585 b6a69cc40e Add compass_bar 0.1.0
Submitted by @Fernandes585 through the form.

Co-authored-by: Fernandes585 <[email protected]>
2026-10-28 22:11:14 +01:00
Ludovic MarieandClaude Opus 5.5 bf91022ab9 Native mods: build with horizon-mixins 0d6b103, the launcher's default since horizon#3361
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 21:14:57 +01:00
Ludovic MarieandClaude Opus 5.5 876f7d1dc7 Native mods: one folder each in native/, shared tools and CI
Horizon's own native mods (zoom, fps-overlay, keystrokes, minimap) were one
repo each; they now live here under native/<name>/ with their history.
native/tools/native.py builds, packages and proves any of them, alone or
together; native.yml checks each changed folder (fmt, clippy, tests, the
.hnmod, the game with it applied), every mod together, and every Monday all
of them on the newest Nightly. Pinned to Horizon 1149c92 (hook points v1).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 20:59:14 +01:00
Ludovic MarieandClaude Opus 5.5 415352eed1 Move horizon-rust/horizon-mod-minimap into native/minimap (history kept)
Co-Authored-By: Claude Opus 5.5 <[email protected]>

git-subtree-dir: native/minimap
git-subtree-mainline: c444610bb8
git-subtree-split: d86ecb6c4f
2026-10-28 20:54:07 +01:00
Ludovic MarieandClaude Opus 5.5 c444610bb8 Move horizon-rust/horizon-mod-keystrokes into native/keystrokes (history kept)
Co-Authored-By: Claude Opus 5.5 <[email protected]>

git-subtree-dir: native/keystrokes
git-subtree-mainline: c3c335bcca
git-subtree-split: 2a04908a1a
2026-10-28 20:54:03 +01:00
Ludovic MarieandClaude Opus 5.5 c3c335bcca Move horizon-rust/horizon-mod-fps-overlay into native/fps-overlay (history kept)
Co-Authored-By: Claude Opus 5.5 <[email protected]>

git-subtree-dir: native/fps-overlay
git-subtree-mainline: 9dbee00d1f
git-subtree-split: 1c69d4ffc9
2026-10-28 20:53:59 +01:00
Ludovic MarieandClaude Opus 5.5 9dbee00d1f Move horizon-rust/horizon-mod-zoom into native/zoom (history kept)
Co-Authored-By: Claude Opus 5.5 <[email protected]>

git-subtree-dir: native/zoom
git-subtree-mainline: e1aa98d1ed
git-subtree-split: 5cfb57fa2c
2026-10-28 20:53:55 +01:00
Ludovic MarieandClaude Opus 5.5 d86ecb6c4f Proof: check the screen once more after the last Esc
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 19:14:26 +01:00
Ludovic MarieandClaude Opus 5.5 2a04908a1a Proof: check the screen once more after the last Esc
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 19:14:23 +01:00
Ludovic MarieandClaude Opus 5.5 1c69d4ffc9 Proof: check the screen once more after the last Esc
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 19:14:20 +01:00
Ludovic MarieandClaude Opus 5.5 5cfb57fa2c Proof: check the screen once more after the last Esc
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 19:14:16 +01:00
Ludovic MarieandClaude Opus 5.5 a683d6c60f Build on any Horizon whose hook points are at least the ones it needs
Hook points are only ever added, so a later NATIVE_HOOKS_API still has
every hook this mod uses.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 19:01:30 +01:00
Ludovic MarieandClaude Opus 5.5 d4beb82c4c Build on any Horizon whose hook points are at least the ones it needs
Hook points are only ever added, so a later NATIVE_HOOKS_API still has
every hook this mod uses.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 19:01:28 +01:00
Ludovic MarieandClaude Opus 5.5 1a48aa24dc Build on any Horizon whose hook points are at least the ones it needs
Hook points are only ever added, so a later NATIVE_HOOKS_API still has
every hook this mod uses.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 19:01:26 +01:00
Ludovic MarieandClaude Opus 5.5 59ab4e8fe2 Build on any Horizon whose hook points are at least the ones it needs
Hook points are only ever added, so a later NATIVE_HOOKS_API still has
every hook this mod uses.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 19:01:23 +01:00
Ludovic MarieandClaude Opus 5.5 6ca0d02406 Native mod: minimap on Horizon's native hook points
Port of Horizon's official WebAssembly mod to a horizon-mixins bundle,
keeping its settings files. Unit tests, a .hnmod packager, a native
check against Horizon by commit and an offscreen proof (tools/).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 18:57:27 +01:00
Ludovic MarieandClaude Opus 5.5 3448e187ca Native mod: keystrokes on Horizon's native hook points
Port of Horizon's official WebAssembly mod to a horizon-mixins bundle,
keeping its settings files. Unit tests, a .hnmod packager, a native
check against Horizon by commit and an offscreen proof (tools/).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 18:57:26 +01:00
Ludovic MarieandClaude Opus 5.5 d1fed6cd56 Native mod: fps-overlay on Horizon's native hook points
Port of Horizon's official WebAssembly mod to a horizon-mixins bundle,
keeping its settings files. Unit tests, a .hnmod packager, a native
check against Horizon by commit and an offscreen proof (tools/).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 18:57:26 +01:00
Ludovic MarieandClaude Opus 5.5 647fa375e6 Native mod: zoom on Horizon's native hook points
Port of Horizon's official WebAssembly mod to a horizon-mixins bundle,
keeping its settings files. Unit tests, a .hnmod packager, a native
check against Horizon by commit and an offscreen proof (tools/).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 18:57:25 +01:00
levzzz e1aa98d1ed Merge pull request #19 from horizon-rust/feat/mod-menu
Add Mod Menu 0.1.1
2026-10-28 22:28:46 +05:00
levzzz 815bdc50f7 Merge pull request #18 from horizon-rust/submit/lookat-0.1.0
Add lookat 0.1.0
2026-10-28 22:28:30 +05:00
levzzz 28c8bf4684 Merge pull request #9 from horizon-rust/submit/blockscope-0.1.0
Add blockscope 0.1.0
2026-10-28 22:28:11 +05:00
levzzzandHermes Agent 8eadd70568 Add Veinminer 0.3.0 native source mod (depends on Horizon native support) (#20)
* feat: submit Veinminer 0.3.0 native source bundle

Document native-mod permissions and exact tested source pins, and register the reproducible source-only hnmod uploaded to staging. Depends on the upcoming Horizon native-mod registry and launcher support; do not merge before that dependency ships.

Co-Authored-By: Hermes Agent (OpenAI) <[email protected]>

* docs: describe upstream native builds and pinned compatibility

Co-Authored-By: Codex <[email protected]>

---------

Co-authored-by: Hermes Agent (OpenAI) <[email protected]>
2026-10-28 18:04:54 +01:00
OpenAI Codex 25fb233630 Add Mod Menu 0.1.1 for installed Horizon mods
Co-Authored-By: OpenAI Codex <[email protected]>
2026-10-28 14:41:59 +05:00
Ludovic Marieandd3grando 7dfe89f942 Add lookat 0.1.0
Submitted by @d3grando through the form.

Co-authored-by: d3grando <[email protected]>
2026-10-28 08:04:41 +01:00
Ludovic Marie bcc5317bac Merge pull request #17 from horizon-rust/codeowners-horizon-team
CODEOWNERS: the Horizon Team reviews mods
2026-10-28 01:01:18 +01:00
Ludovic MarieandClaude Opus 5.5 b66cfbbee9 CODEOWNERS: the Horizon Team reviews registry entries (no separate mod-reviewers team)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 01:00:55 +01:00
Ludovic Marie 9b51200853 Merge pull request #16 from horizon-rust/codeowners-team
CODEOWNERS: the mod-reviewers team
2026-10-28 00:48:49 +01:00
Ludovic MarieandClaude Opus 5.5 d8c85d1484 CODEOWNERS: the mod-reviewers team instead of names (adds levzzz5154)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 00:47:23 +01:00
Ludovic MarieandClaude Opus 5.5 d96322f963 Mod policy: authors own and answer for their mods; ports, takedowns (#14)
Third-party projects are made and owned by their authors, not by the
Horizon project; listing is not endorsement. Authors confirm their right to
publish (ports included), and rights holders get a takedown form.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-27 23:25:30 +01:00
Ludovic MarieandClaude Opus 5.5 006ca9fa3c Point links at the horizon-rust organization (#12)
The repositories moved from ludovic111 to horizon-rust. GitHub redirects the
repository URLs, but not GitHub Pages: the index now lives at
https://horizon-rust.github.io/horizon-mods/. The build passes --site so icon
URLs use the new address without waiting for a new cargo-horizon-mod.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-27 21:22:11 +01:00
Ludovic Marie 3cbaffd145 Merge pull request #11 from ludovic111/reviewers/online5277
CODEOWNERS: Online5277 instead of levzzz5154
2026-10-27 17:57:56 +01:00
Ludovic MarieandClaude Opus 5.5 38890426e1 CODEOWNERS: Online5277 instead of levzzz5154 as the third approver
Ludovic asked for his top three contributors by merged pull requests: kacpherek, rdbtCVS and Online5277.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 17:56:00 +01:00
Ludovic Marie 645ea37852 Merge pull request #10 from ludovic111/reviewers/kacpherek-levzzz
CODEOWNERS: kacpherek and levzzz5154 review registry entries
2026-10-27 15:38:50 +01:00
Ludovic MarieandClaude Opus 5.5 3d4b3488bb CODEOWNERS: kacpherek and levzzz5154 review registry entries beside rdbtCVS
Ludovic's decision (2026-10-27): the three trusted contributors approve submissions; /.github/ and revoked.toml
stay his.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 15:20:13 +01:00
Ludovic MarieandDomanffe f64dc8688a Add blockscope 0.1.0
Submitted by @Domanffe through the form.

Co-authored-by: Domanffe <[email protected]>
2026-10-27 13:58:56 +01:00
Ludovic Marie 3b73fc7a62 Merge pull request #8 from ludovic111/add-recipes
🤖 Agent: merging (as #4–#7, the official mods). The registry check passes with the checker moved to main (0c7f6b7f0), the file is byte-identical to #2446's build, and the mod's e2e passes in all five modes on main.
2026-10-26 22:42:05 +01:00
Ludovic MarieandClaude Opus 5.5 8bcff08bf2 Add recipes 1.0.0 (Horizon's official Recipes mod)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 22:35:26 +01:00
Ludovic Marie 53564d9cfe Merge pull request #7 from ludovic111/feat/download-counts
publish: each project's download count, refreshed every hour
2026-10-26 14:24:03 +01:00
Ludovic MarieandClaude Opus 5.5 2181eec3ce publish: each project's download count, refreshed every hour
publish.yml runs every hour as well as on merges. It sums GitHub's
download_count of each version's release asset into the project's
`downloads` field of index.json, which the launcher and playhorizon.dev/mods
show.

publish and check keep `files/` in the Actions cache, so CI doesn't fetch
every release asset again on each run (each fetch counts as a download).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 14:21:16 +01:00
Ludovic Marie 98f8ab5da3 Merge pull request #6 from ludovic111/official-minimap
Add minimap 1.0.0 (Horizon's official Minimap mod)
2026-10-26 10:00:03 +01:00
Ludovic MarieandClaude Opus 5.5 efe164dd3e 🤖 Agent: Add minimap 1.0.0 (Horizon's official Minimap mod)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 09:42:58 +01:00
Ludovic Marie b19da5d805 Merge pull request #5 from ludovic111/official-zoom
Add zoom 1.0.0 (Horizon's official Zoom mod)
2026-10-26 08:34:18 +01:00
Ludovic MarieandClaude Opus 5.5 5b42b91bd8 🤖 Agent: Add zoom 1.0.0 (Horizon's official Zoom mod)
The zoom extra as a mod, built from Horizon's main at c58af30ca
(mods/official/zoom).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 08:30:32 +01:00
Ludovic Marie 69290f7c42 Merge pull request #4 from ludovic111/official-hud-mods
Add fps_overlay 1.0.0 and keystrokes 1.0.0 (Horizon's official mods)
2026-10-26 07:06:21 +01:00
Ludovic MarieandClaude Opus 5.5 d05cb8cb56 🤖 Agent: Add fps_overlay 1.0.0 and keystrokes 1.0.0 (Horizon's official mods)
The FPS counter and Keystrokes extras as mods, built from Horizon's main
at 5bcc87983 (mods/official/fps-overlay, mods/official/keystrokes).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 06:48:30 +01:00
Ludovic Marie 81b7ac9bdf Merge pull request #3 from ludovic111/codeowners-reviewers
CODEOWNERS: rdbtCVS reviews beside Ludovic
2026-10-25 16:43:27 +01:00
Ludovic MarieandClaude Opus 5.5 a222e3d816 CODEOWNERS: rdbtCVS reviews beside Ludovic
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 16:43:22 +01:00
Ludovic MarieandClaude Opus 5.5 74c7d6d7bd 🤖 Agent: Remove e2e_check (end-to-end test)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 15:13:42 +01:00
Ludovic MarieandClaude Opus 5.5 0895ef6b5c 🤖 Agent: Add e2e_check 0.1.0 (registry end-to-end test, reverted next)
Test of the submission form and publishing; reverted right after.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 15:11:14 +01:00
Ludovic MarieandClaude Opus 5.5 52e1f7d0f9 The registry: coords_hud, the checks, publishing to Pages, the weekly cleanup
- mods/coords_hud.toml: the SDK's example mod, MIT, built from
  crates/horizon-mod-sdk/examples/coords-hud in Horizon.
- check.yml: a pull request's changes, each new file's checks and a trial
  run in the sandbox, the index it would publish (cargo horizon-mod from
  Horizon at HORIZON_REV).
- publish.yml: on main, new versions become releases, index.json and the
  icons go to GitHub Pages.
- cleanup.yml: staging copies nobody needs, weekly.
- CODEOWNERS, the report form, the pull request template, revoked.toml.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 15:01:10 +01:00
173 changed files with 11461 additions and 7 deletions

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
# Every change needs a review from one of these before it merges (branch protection:
# "Require review from Code Owners"). The reviewers are the Horizon Team, GitHub team horizon-rust/horizon-team
# (Ludovic decides who is in it). /.github/ and takedowns stay Ludovic's.
* @ludovic111 @horizon-rust/horizon-team
/.github/ @ludovic111
/revoked.toml @ludovic111
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Submit a project
url: https://playhorizon.dev/mods/submit
about: Mods, modpacks and resource packs for Horizon. Each submission is reviewed.
- name: Horizon itself
url: https://github.com/horizon-rust/horizon/issues
about: Bugs in the game or the launcher.
+43
View File
@@ -0,0 +1,43 @@
name: Report a project
description: Something in the registry does harm, breaks the game, isn't what it says, or isn't its author's to publish.
title: "Report: "
labels: [report]
body:
- type: markdown
attributes:
value: |
Thanks. Reviewers read every report. If it is dangerous (it deletes files, steals
accounts, spreads), say so in the first line: it can be taken down within hours, and
the launcher then turns installed copies off. Bugs in a mod go to its source repository.
- type: input
id: project
attributes:
label: Project id
description: The last part of its page's address, playhorizon.dev/mods/<id>
placeholder: coords_hud
validations:
required: true
- type: input
id: version
attributes:
label: Version
placeholder: 0.1.0, or "all"
- type: dropdown
id: why
attributes:
label: What is wrong
options:
- It does harm (files, accounts, chat it shouldn't send, anything malicious)
- It isn't what its page says
- It breaks the game or other mods
- It isn't the submitter's to publish (copyright, licence, stolen)
- Something else
validations:
required: true
- type: textarea
id: details
attributes:
label: What happened
description: Steps, what you saw, and logs if you have them (the game's log is latest.log in its folder).
validations:
required: true
+37
View File
@@ -0,0 +1,37 @@
name: Takedown request (copyright)
description: A project in the registry uses your work without your permission.
title: "Takedown: "
labels: [takedown]
body:
- type: markdown
attributes:
value: |
Thanks for telling us. A project that uses your work without permission is removed
(see [MOD-POLICY.md](https://github.com/horizon-rust/horizon-mods/blob/main/MOD-POLICY.md)).
This issue is public: to write privately instead, use [lu4ovic.com](https://lu4ovic.com/#contact).
- type: input
id: project
attributes:
label: Project id
description: The last part of its page's address, playhorizon.dev/mods/<id>
validations:
required: true
- type: textarea
id: work
attributes:
label: Your work
description: What it is, where it is published, and what the project copies from it.
validations:
required: true
- type: input
id: contact
attributes:
label: How to reach you
description: An email or profile, if not this GitHub account.
- type: checkboxes
id: rights
attributes:
label: Statement
options:
- label: I own the rights to this work, or I act for whoever does, and I believe this use is not allowed.
required: true
+14
View File
@@ -0,0 +1,14 @@
<!-- Changing mods/<id>.toml by hand? The form at https://playhorizon.dev/mods/submit does this for you. -->
**Project:** <!-- id, and new project or update -->
- [ ] I made it, or I am allowed to publish it, under the licence the entry names.
- [ ] If it ports or translates another mod, the original's licence allows it (or its authors agreed), and my page
credits and links the original.
- [ ] It has no code, assets or data from Minecraft, Mojang or Microsoft, and nothing that harms players.
- [ ] I read and accept the [mod policy](https://github.com/horizon-rust/horizon-mods/blob/main/MOD-POLICY.md): I am this
project's author or publisher and responsible for it; the Horizon project is not.
- [ ] The `source` link has the code this file was built from.
- [ ] I ran `cargo horizon-mod check <file>` and it passed.
The file goes in the `staging` release as `<sha256>.<extension>` (the form uploads it; by hand, ask a reviewer).
+89
View File
@@ -0,0 +1,89 @@
# A pull request's checks: what it changes compared with main, each new file's
# own check (its kind's, its signature, a trial run of a mod in the sandbox), and
# the index it would publish. Runs with a read-only token and no secrets; the tool
# comes from Horizon, never from the pull request, and a submission's code only
# runs inside Horizon's mod sandbox.
name: check
on:
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: check-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
HORIZON_REV: ${{ vars.HORIZON_REV || 'main' }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha }}
path: base
- uses: actions/checkout@v4
with:
path: head
persist-credentials: false
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin
~/.cargo/horizon-rev
key: cargo-horizon-mod-${{ env.HORIZON_REV }}
# main's scripts, not the pull request's.
- run: base/scripts/install-tool.sh
- name: Who submitted it
id: author
env:
LOGIN: ${{ github.event.pull_request.user.login }}
BODY: ${{ github.event.pull_request.body }}
BOT: ${{ vars.REGISTRY_BOT || 'horizon-mods-bot' }}
run: |
# The submission form opens pull requests as the bot and names the
# GitHub account that signed in to it.
author="$LOGIN"
if [ "$LOGIN" = "$BOT" ] || [ "$LOGIN" = "${BOT}[bot]" ]; then
author="$(printf '%s' "$BODY" | sed -n 's/.*<!-- submitted-by: \([A-Za-z0-9-]*\) -->.*/\1/p' | head -1)"
fi
echo "login=${author:-$LOGIN}" >> "$GITHUB_OUTPUT"
- name: What it changes
run: |
set -o pipefail
{ echo "## Changes"; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
status=0
cargo horizon-mod registry review base head --author "${{ steps.author.outputs.login }}" 2>&1 \
| tee -a "$GITHUB_STEP_SUMMARY" || status=1
echo '```' >> "$GITHUB_STEP_SUMMARY"
exit $status
# main's files, kept by publish: each download from a release counts as a
# player's.
- uses: actions/cache/restore@v4
with:
path: files
key: files-${{ hashFiles('head/mods/*.toml') }}
restore-keys: files-
- name: Download its files
run: base/scripts/fetch.sh head
- name: Check each new file
run: |
set -o pipefail
{ echo "## New files"; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
status=0
cargo horizon-mod registry check base head --files files 2>&1 | tee -a "$GITHUB_STEP_SUMMARY" || status=1
echo '```' >> "$GITHUB_STEP_SUMMARY"
exit $status
- name: The index it would publish
run: |
cargo horizon-mod registry build head --files files --out index.json --generated "$(date -u +%FT%TZ)"
python3 - >> "$GITHUB_STEP_SUMMARY" <<'PY'
import json
index = json.load(open("index.json"))
print("## index.json")
for p in index["projects"]:
print(f"- {p['kind']} `{p['id']}` {p['versions'][0]['version']}")
PY
+43
View File
@@ -0,0 +1,43 @@
# The submission form uploads files to the `staging` prerelease as
# <sha256>.<extension>. Once a version is published in its own release, or when
# no open pull request lists a file any more, its staging copy goes:
# published ones after 7 days, abandoned ones after 30.
name: cleanup
on:
schedule:
- cron: "17 4 * * 1"
workflow_dispatch:
permissions:
contents: write
pull-requests: read
jobs:
cleanup:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
steps:
- uses: actions/checkout@v4
- name: Delete staging files nobody needs
run: |
set -euo pipefail
# Hashes main lists (published), and those open pull requests add.
grep -ho 'sha256 = "[0-9a-f]\{64\}"' mods/*.toml | cut -d'"' -f2 | sort -u > published.txt || true
: > pending.txt
for pr in $(gh pr list --state open --json number --jq '.[].number'); do
gh pr diff "$pr" | grep -o '^+sha256 = "[0-9a-f]\{64\}"' | cut -d'"' -f2 >> pending.txt || true
done
now=$(date +%s)
gh api --paginate "repos/$GH_REPO/releases/tags/staging" --jq '.assets[] | [.id, .name, .created_at] | @tsv' |
while IFS=$'\t' read -r id name created; do
sha="${name%%.*}"
age=$(( (now - $(date -d "$created" +%s)) / 86400 ))
grep -qx "$sha" pending.txt && continue
if grep -qx "$sha" published.txt; then
[ "$age" -ge 7 ] || continue
else
[ "$age" -ge 30 ] || continue
fi
echo "Deleting $name ($age days)"
gh api -X DELETE "repos/$GH_REPO/releases/assets/$id"
done
+117
View File
@@ -0,0 +1,117 @@
# Horizon's own native mods (native/<name>/): each changed folder's tests and .hnmod, and the game with it applied
# on the Horizon it was tested on; every mod together, as a player's set. Every Monday, all of them on the newest
# Nightly, to catch a change of the hook points early.
name: native
on:
pull_request:
branches: [main]
paths: ["native/**", ".github/workflows/native.yml"]
push:
branches: [main]
paths: ["native/**", ".github/workflows/native.yml"]
schedule:
- cron: "17 5 * * 1"
workflow_dispatch:
inputs:
horizon:
description: "Horizon commit, tag, or `latest` (the newest Nightly); empty: the mods' tested commit"
default: latest
permissions:
contents: read
concurrency:
group: native-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
# The Rust the launcher builds native mods with (launcher/src/rust_toolchain.rs).
RUST: "1.99.1"
jobs:
changes:
runs-on: ubuntu-latest
outputs:
mods: ${{ steps.mods.outputs.mods }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: mods
env:
BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
EVENT: ${{ github.event_name }}
run: |
all=$(ls -d native/*/compatibility.json | cut -d/ -f2 | jq -R . | jq -sc .)
if [ "$EVENT" = schedule ] || [ "$EVENT" = workflow_dispatch ] || [ -z "$BASE" ] \
|| ! git cat-file -e "$BASE" 2>/dev/null; then
echo "mods=$all" >> "$GITHUB_OUTPUT"; exit 0
fi
changed=$(git diff --name-only "$BASE" HEAD)
# The shared tools or this workflow: every mod.
if grep -qE '^(native/tools/|\.github/workflows/native\.yml)' <<< "$changed"; then
echo "mods=$all" >> "$GITHUB_OUTPUT"; exit 0
fi
mods=$(grep -oE '^native/[^/]+/' <<< "$changed" | cut -d/ -f2 | sort -u \
| while read -r m; do [ -f "native/$m/compatibility.json" ] && echo "$m"; done | jq -R . | jq -sc .)
echo "mods=$mods" >> "$GITHUB_OUTPUT"
mod:
needs: changes
if: needs.changes.outputs.mods != '[]'
strategy:
fail-fast: false
matrix:
mod: ${{ fromJson(needs.changes.outputs.mods) }}
runs-on: ubuntu-latest
defaults:
run:
working-directory: native/${{ matrix.mod }}
steps:
- uses: actions/checkout@v4
- run: rustup toolchain install $RUST --profile minimal --component rustfmt --component clippy && rustup default $RUST
- run: cargo fmt --check
- run: cargo clippy --all-targets --locked -- -D warnings
- run: cargo test --locked
- name: Package the .hnmod and check what it holds
working-directory: .
run: |
python3 native/tools/native.py package ${{ matrix.mod }}
python3 - <<'PY'
import json, zipfile, glob
path, = glob.glob("native/target/*.hnmod")
names = zipfile.ZipFile(path).namelist()
manifest = json.loads(zipfile.ZipFile(path).read("horizon-native.json"))
assert manifest["hooks"] >= 1, manifest
assert all(n == "horizon-native.json" or (n.startswith(f"mixins/{manifest['id']}/") and n.endswith((".rs", ".toml"))) for n in names), names
print(path, manifest)
PY
- uses: actions/upload-artifact@v4
with:
name: hnmod-${{ matrix.mod }}
path: native/target/*.hnmod
horizon:
needs: changes
if: needs.changes.outputs.mods != '[]'
strategy:
fail-fast: false
matrix:
mod: ${{ fromJson(needs.changes.outputs.mods) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: rustup toolchain install $RUST --profile minimal && rustup default $RUST
- run: sudo apt-get update && sudo apt-get install -y libasound2-dev libudev-dev pkg-config
- name: Apply on Horizon and check the game
env:
GITHUB_TOKEN: ${{ github.token }}
HORIZON: ${{ github.event_name == 'schedule' && 'latest' || inputs.horizon || '' }}
run: python3 native/tools/native.py check ${{ matrix.mod }} ${HORIZON:+--horizon "$HORIZON"}
together:
needs: changes
if: needs.changes.outputs.mods != '[]'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: rustup toolchain install $RUST --profile minimal && rustup default $RUST
- run: sudo apt-get update && sudo apt-get install -y libasound2-dev libudev-dev pkg-config
- name: Every native mod applied together, on Horizon
env:
GITHUB_TOKEN: ${{ github.token }}
HORIZON: ${{ github.event_name == 'schedule' && 'latest' || inputs.horizon || '' }}
run: python3 native/tools/native.py check all ${HORIZON:+--horizon "$HORIZON"}
+89
View File
@@ -0,0 +1,89 @@
# Merging publishes: each new version's file becomes a release asset
# (<id>-<version>/<file>), then index.json and the icons go to GitHub Pages
# (https://horizon-rust.github.io/horizon-mods/index.json), which the launcher and
# playhorizon.dev/mods read. Nothing is published if any entry fails its checks.
# Every hour it runs again to refresh each project's `downloads`: GitHub's
# download counts of its release assets.
name: publish
on:
push:
branches: [main]
schedule:
- cron: "23 * * * *"
workflow_dispatch:
permissions:
contents: write
pages: write
id-token: write
concurrency:
group: publish
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 30
environment:
name: github-pages
url: ${{ steps.pages.outputs.page_url }}
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
HORIZON_REV: ${{ vars.HORIZON_REV || 'main' }}
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin
~/.cargo/horizon-rev
key: cargo-horizon-mod-${{ env.HORIZON_REV }}
- run: scripts/install-tool.sh
# Files kept from the last run: GitHub counts every download of a release
# asset, so fetching them all again each hour would count as players.
- uses: actions/cache@v4
with:
path: files
key: files-${{ hashFiles('mods/*.toml') }}
restore-keys: files-
- run: scripts/fetch.sh .
- name: Build the index
run: |
mkdir -p site
cargo horizon-mod registry build . --files files --out site/index.json --site https://horizon-rust.github.io/horizon-mods/ --generated "$(date -u +%FT%TZ)"
- name: Publish new files as releases
run: |
cargo horizon-mod registry list . | while read -r sha id version file; do
tag="$id-$version"
gh release view "$tag" >/dev/null 2>&1 && continue
dir="$(mktemp -d)"
cp "files/$sha" "$dir/$file"
gh release create "$tag" "$dir/$file" --title "$id $version" --latest=false \
--notes "Reviewed in the registry: mods/$id.toml. SHA-256 $sha."
done
- name: Download counts
run: |
# Each version's file is the asset at its URL: a project's downloads
# are its files' counts, every version together.
gh api --paginate "repos/$GH_REPO/releases?per_page=100" \
--jq '.[].assets[] | {key: .browser_download_url, value: .download_count}' \
| jq -s from_entries > counts.json
jq --slurpfile counts counts.json '
.projects |= map(
([.versions[].file.url | $counts[0][.] // 0] | add) as $n
| if $n > 0 then .downloads = $n else . end)' site/index.json > index.counted.json
mv index.counted.json site/index.json
jq -r '.projects[] | "\(.id) \(.downloads // 0)"' site/index.json
- name: The site
run: |
cp -R icons site/icons
cat > site/index.html <<'HTML'
<!doctype html><meta charset="utf-8"><title>Horizon mods</title>
<meta http-equiv="refresh" content="0; url=https://playhorizon.dev/mods">
<p><a href="https://playhorizon.dev/mods">playhorizon.dev/mods</a> · <a href="index.json">index.json</a></p>
HTML
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: site
- id: pages
uses: actions/deploy-pages@v4
+2
View File
@@ -1,3 +1,5 @@
/files/
/site/
/bootstrap/
/native/target/
/native/*/target/
+55
View File
@@ -0,0 +1,55 @@
# Mod policy
This registry lists mods, modpacks and packs for [Horizon](https://github.com/horizon-rust/horizon). This page says
who makes them, who is responsible for them, and how to have one removed. By submitting to the registry you accept it.
## Who makes the mods
- **Mods made by the Horizon project** are marked **Official**: their author is "Horizon" and their source is in a
[horizon-rust](https://github.com/horizon-rust) repository. They are the only mods the Horizon
project makes and is responsible for.
- **Every other mod is made, owned and maintained by its own authors**, named on its page. The Horizon project and
its maintainers are not their authors. They don't own these mods, sell them, or license them to you; each mod's
licence is the one its authors chose.
- **A listing is not an endorsement.** Reviewers check that an entry is complete, that its files match their pinned
hashes, that it builds or loads against the Horizon version it names, and that nothing obviously harmful is in it.
That review is not a legal review, a security audit or a guarantee. Mods are provided as they are, without
warranty, by their authors.
- **Native mods** (built into the game with [horizon-mixins](https://github.com/horizon-rust/horizon-mixins)) run
with the same access to your computer as the game. Install them only from authors you trust.
## What authors promise when they submit
Submitting a project, through the form or a pull request, means you confirm that:
1. You made it, or you have the right to publish it, under the licence its entry names.
2. **If it is a port or a translation of another mod** (for example from Java Edition), the original's licence allows
that, or its authors gave you permission. You keep the original's copyright and licence notices, and you credit
its authors and link the original on your page. A port of a mod whose licence reserves all rights needs its
authors' written permission.
3. It contains no code, assets or data from Minecraft, Mojang or Microsoft beyond what their
[usage guidelines](https://www.minecraft.net/usage-guidelines) and EULA allow. Horizon players bring their own
copy of the game.
4. It contains nothing that harms players: no malware, no stealing of accounts or data, no hidden network access.
5. The `source` link has the code the published files were built from.
You alone are responsible for what you publish and for any claim about it. The Horizon project may refuse, hide or
remove any project, at any time, without having to give a reason.
## Copyright and takedowns
If a project in this registry uses your work without your permission:
- [Open a takedown request](../../issues/new?template=takedown.yml) (public), or
- write privately to the maintainer through [lu4ovic.com](https://lu4ovic.com/#contact).
Say which project, which of your work it uses, and how to reach you. A claim that holds gets the project removed and
listed in [`revoked.toml`](revoked.toml): the registry stops listing it and the launcher stops offering it and turns
installed copies off. Clear claims are acted on quickly. The project's author is told, and can answer the claim in the
same issue.
Authors whose project was removed by mistake can ask for it back in that issue.
## Changes
This policy can change. The version in this repository's `main` branch is the one that applies.
+41 -7
View File
@@ -1,12 +1,25 @@
# Horizon mods
The registry of what is made for [Horizon](https://github.com/ludovic111/horizon): mods (`.hmod`), modpacks (`.hpack`)
The registry of what is made for [Horizon](https://github.com/horizon-rust/horizon): mods (`.hmod`), modpacks (`.hpack`)
and resource packs, each one reviewed before it is published. Browse them at
[playhorizon.dev/mods](https://playhorizon.dev/mods) or in the launcher (Mods, then Horizon beside Modrinth).
Players install from here through the launcher, which checks every file's SHA-256 and, when the author signs, its
signature before the game sees it. Mods run in Horizon's sandbox, with no file, network or clock access, and can only
do what their page lists.
signature before the game sees it. Sandboxed `.hmod` mods run in Horizon's sandbox,
with no file, network or clock access, and can only do what their page lists.
Native source mods (`.hnmod`, kind `native-mod`) are built into an isolated game by
a launcher with native-mod support. They run with the player’s account permissions.
Their bundles pin the exact Horizon and Horizon Mixins revisions used for the build.
Other revisions have not been validated. Registry checks inspect the source bundle without
executing native code; reviewers must also examine the source and pinned-build
validation. Publishing these requires the native-mod changes to `cargo-horizon-mod`
and the launcher to have shipped first.
**Who is responsible.** Except the projects marked Official (made by the Horizon project), everything here is made and
owned by its own authors, not by the Horizon project or its maintainers. A listing is not an endorsement. See the
[mod policy](MOD-POLICY.md): what authors promise when they submit (ports of other mods included) and how to have
a project taken down.
## Submit
@@ -24,7 +37,7 @@ What the registry takes now: mods, modpacks of registry mods, and resource packs
shader pack format is out (Iris and OptiFine packs are on Modrinth). Before you submit a mod, run what the checks run:
```sh
cargo install --git https://github.com/ludovic111/horizon cargo-horizon-mod
cargo install --git https://github.com/horizon-rust/horizon cargo-horizon-mod
cargo horizon-mod check target/horizon-mod/my_mod.hmod
```
@@ -36,7 +49,8 @@ entry, and every later version must be signed with it, so nobody else can publis
## Report
Something wrong with a project? [Report it](../../issues/new?template=report.yml). A project that does harm is taken
Something wrong with a project? [Report it](../../issues/new?template=report.yml). Your work published without your
permission? [Ask for a takedown](../../issues/new?template=takedown.yml) ([mod policy](MOD-POLICY.md)). A project that does harm is taken
down in [`revoked.toml`](revoked.toml): the launcher then refuses it and turns installed copies off, telling players
why.
@@ -50,11 +64,31 @@ why.
| `.github/workflows/check.yml` | A pull request's checks |
| `.github/workflows/publish.yml` | On merge: releases, then `index.json` on Pages |
| `.github/workflows/cleanup.yml` | Weekly: staging files nobody needs any more |
| `.github/workflows/native.yml` | Horizon's native mods: each changed one's tests, `.hnmod` and game check, all of them together, and weekly on the newest Nightly |
| `native/<name>/` | Horizon's own native mods (Zoom, FPS Counter, Keystrokes, Minimap, Recipes, Coordinates HUD): the source of their `.hnmod` files |
| `native/tools/` | Shared by them: apply on Horizon, check, build, package, prove (`native/tools/native.py`) |
| `scripts/` | What the workflows share |
The format of the entries and of `index.json` is
[docs/api/mod-index.md](https://github.com/ludovic111/horizon/blob/main/docs/api/mod-index.md) in Horizon's repository;
[docs/api/mod-index.md](https://github.com/horizon-rust/horizon/blob/main/docs/api/mod-index.md) in Horizon's repository;
the checks are `cargo horizon-mod check` and `cargo horizon-mod registry`, from Horizon at the commit in the
repository variable `HORIZON_REV` (else `main`).
The index: `https://ludovic111.github.io/horizon-mods/index.json`.
The index: `https://horizon-rust.github.io/horizon-mods/index.json`.
## Horizon's native mods
`native/<name>/` holds the source of Horizon's official mods, as
[horizon-mixins](https://github.com/horizon-rust/horizon-mixins) bundles on Horizon's native hook points
([docs/native-mods.md](https://github.com/horizon-rust/horizon/blob/main/docs/native-mods.md)). Each folder keeps the
history of the repository it came from. A mod builds on any Horizon whose hook points are at least the version its
`compatibility.json` names; `horizon_revision` is the commit it was last tested on.
```sh
python3 native/tools/native.py check zoom # apply on the tested Horizon and cargo check the game
python3 native/tools/native.py check all # every native mod together
python3 native/tools/native.py package all # native/target/<id>-<version>.hnmod, for a registry entry
python3 native/tools/native.py proof zoom --resources /path/to/.local/horizon-client/26.3
```
To make your own, start from [horizon-native-mod-template](https://github.com/horizon-rust/horizon-native-mod-template).
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

+45
View File
@@ -0,0 +1,45 @@
id = "appleskin"
kind = "native-mod"
name = "AppleSkin"
summary = "Shows how much HP, hunger, and saturation will be restored."
description = """
AppleSkin for Horizon, a native mod.
Renders saturation, exhaustion, and held-food values over the hunger bar, as the original AppleSkin does.
Based on AppleSkin by squeek502. See the LICENSE in the source for the
original copyright and licence notices."""
authors = ["soufi"]
owners = ["soufied"]
license = "MIT"
source = "https://github.com/soufied/AppleSkin-Horizon"
icon = "icons/appleskin.png"
key = "RWT6eNq4e5WfVDA2lzOOi6XCm/+jJgilqT1+HUUM33ie5MCjcRfSg760"
[[versions]]
version = "0.1.1"
published = "2026-10-31"
game = "26.3"
file = "appleskin-0.1.1.hnmod"
sha256 = "182117d2a9767bc22a7c6652efd24551990cea015193784f20aeb8a7ba35b4ac"
signature = """
untrusted comment: signature from minisign secret key
RUT6eNq4e5WfVPcAN+WeTYujFNeWw6oMKKV7nXxfIfyeqg83qrFNNA/LYfOrmxwV0DgO+tf5Sk5piJjS7hEtyKhl9rs0VpBOfgg=
trusted comment: timestamp:1793418678\tfile:appleskin-0.1.1.hnmod\thashed
gK9BQoCtM8sRPSvSiBzAT2IE8nZlCDfUa7tWCG7cG/v7H41E+4DnflE0KtAPdqi/9+MQT3ONmT4qbGFVio58BA=="""
changelog = """
dumped to 0.1.1
added icon"""
[[versions]]
version = "1.0.0"
published = "2026-10-30"
game = "26.3"
file = "appleskin-1.0.0.hnmod"
sha256 = "3c25b916f74f47faf40070a8654c546d15cb79ac3947f140e257f4cb041aeb96"
signature = """
untrusted comment: signature from minisign secret key
RUT6eNq4e5WfVEMHvcSGm73mmamWosNGGcdt7Qn05wqgRuAlDj/DyzH66lS2u8nvB43GnZJ7xCmgoM5xArHJuN9kD30iIKj0zws=
trusted comment: timestamp:1793368049\tfile:appleskin-1.0.0.hnmod\thashed
gjEf247x8XwHpOI2Ra3ngk6umC/mFZiHOCfZyzxg5IFJ7CNJVpofnw/l7ITtePPeyi4r3+0D6FvmmhtuiqVAAg=="""
changelog = "First release. AppleSkin for Horizon as a native mod: saturation, exhaustion and the food in your hand, drawn over the food bar."
+27
View File
@@ -0,0 +1,27 @@
id = "biomecompass"
kind = "native-mod"
name = "BiomeCompass"
summary = "Craft a Biome Compass, pick a biome and its needle points to the nearest one."
description = """
**A native Horizon mod**: the launcher builds it into the game (Settings → Game → Native mods, then Build). Native mods are not sandboxed and run with your account's rights, so read the source before you build it. Pinned to Horizon 142388e and horizon-mixins 0d6b103.
Craft a **Biome Compass** (a compass with a sapling on each of its four sides, any kinds), right-click it and pick a biome from the list (with a search box). The world generator is searched around you, a little every tick, and the compass's needle then points to the nearest one, with its distance and direction on the action bar.
- Works in single player, on LAN and on a server running the native build, for vanilla clients too (the picker is a vanilla dialog).
- `/biomecompass find|list|status|stop|tp` as well; a small HUD box is optional.
- Settings: search radius, sample spacing, per-tick budget, HUD.
Inspired by [Nature's Compass](https://modrinth.com/mod/natures-compass); not affiliated, no code from it."""
authors = ["d3grando"]
owners = ["d3grando"]
license = "MIT"
source = "https://github.com/d3grando/biomecompass"
icon = "icons/biomecompass.png"
[[versions]]
version = "0.2.2"
published = "2026-10-29"
game = "26.3"
file = "biomecompass-0.2.2.hnmod"
sha256 = "d9a4aedcf22318774e1ac6bed6f1e00aca13a38c8dc3426d5a7800e4d8c61bf5"
changelog = "First release."
+34
View File
@@ -0,0 +1,34 @@
id = "blockscope"
kind = "native-mod"
name = "Blockscope"
summary = "The block under your crosshair: its name, id, distance and icon, with reach, placement and opacity on its own settings page. A Jade-style tooltip for Horizon."
description = "Jade-style block info for Horizon: the block under your crosshair, its name in your language, its block id, how far it is and its icon."
authors = ["Domanffe"]
owners = ["Domanffe"]
license = "MIT"
source = "https://github.com/Domanffe/blockscope"
icon = "https://horizon-rust.github.io/horizon-mods/icons/blockscope.png"
[[versions]]
version = "0.3.0"
published = "2026-10-30"
game = "26.3"
file = "blockscope-0.3.0.hnmod"
sha256 = "ba4a1c887ab26a2a9189c671cbdbea3ce2ed212a6e3d32f1fffaa60386198358"
changelog = "Native: the WebAssembly mod rewritten as a mixin bundle for the launcher's native mods, with hardness and the tool's icon, block states, crop growth, a container's slots and a creature's health, each on its own switch. Its settings carry over from 0.2.0."
[[versions]]
version = "0.2.0"
published = "2026-10-29"
game = "26.3"
file = "blockscope-0.2.0.hmod"
sha256 = "dcb8a3d3eefe37ebc7fd1886412dc9799c5fa4dee88fd419e7f46a712df00c12"
changelog = "Reach, placement, background, coordinates and the item icon on its own settings page (Options > Mods > Blockscope), kept between games."
[[versions]]
version = "0.1.0"
published = "2026-10-27"
game = "26.3"
file = "blockscope-0.1.0.hmod"
sha256 = "8c9de32150bc845c6e1d1d28a28d6f1746cea8c124aa7e683acdd597c1b49325"
changelog = "First release: the block under the crosshair, its name, id and distance, in a box beside the crosshair."
+28
View File
@@ -0,0 +1,28 @@
id = "carryblock"
kind = "native-mod"
name = "Carry Block"
summary = "Sneak and right-click with an empty hand to pick up a chest (with its items) or a small mob, carry it and put it down."
description = """
**A native Horizon mod**: the launcher builds it into the game (Settings → Game → Native mods, then Build). Native mods are not sandboxed and run with your account's rights, so read the source before you build it. Pinned to Horizon 142388e and horizon-mixins 0d6b103.
Sneak and right-click with an empty hand to pick up a block with its contents (chests, furnaces, barrels...) or a small, friendly mob, and carry it; right-click to put it down where you want, with everything it held.
- Slowness while carrying; a small HUD box shows the load.
- Never an item: nothing to duplicate. On death the load is put down nearby, or dropped as its item with its contents.
- Follows the game's rules: spawn protection, world border, reach, adventure mode, game master blocks; no pick-up of a container someone has open, a mob being traded with, a monster, a boss or another player's pet. Spawners and similar are denied by default.
- Deny lists, survival/creative rules and slowness are settings (Options → Mods → Carry Block).
Inspired by [Carry On](https://modrinth.com/mod/carry-on); not affiliated, no code from it."""
authors = ["d3grando"]
owners = ["d3grando"]
license = "MIT"
source = "https://github.com/d3grando/carryblock"
icon = "icons/carryblock.png"
[[versions]]
version = "0.1.2"
published = "2026-10-29"
game = "26.3"
file = "carryblock-0.1.2.hnmod"
sha256 = "9a6ebd26f640a477815862eb36846fe3289046d0f4c06ef3a815f463ca9a3701"
changelog = "First release."
+23
View File
@@ -0,0 +1,23 @@
id = "chatheads"
kind = "native-mod"
name = "ChatHeads"
summary = "A player's head left of every chat line they wrote."
description = """
Each chat line from a player gets their 8x8 skin face in the strip to its left, like the Chat Heads Java mod. Lines without a player (system messages, /me, wrapped continuations) stay as they are; the face fades with its line.
Settings: Options > Mods > ChatHeads > Settings (on or off).
Known limit: the chat's hit-test does not know about the 10 px indent, so clicking a link in chat lands 10 px left of its pixels."""
authors = ["Domanffe"]
owners = ["Domanffe"]
license = "MIT"
source = "https://github.com/Domanffe/chatheads"
icon = "https://horizon-rust.github.io/horizon-mods/icons/chatheads.png"
[[versions]]
version = "0.1.0"
published = "2026-10-30"
game = "26.3"
file = "chatheads-0.1.0.hnmod"
sha256 = "c20605c24e29a762b9aea85cba1b0cc7955955a40a031efae70a5b01e4e79df4"
changelog = "First release: a native mod for Horizon 26.3. Every chat line from a player gets their 8x8 skin face in the strip to its left; system lines, /me and wrapped continuations get none. Settings in Options > Mods."
+77
View File
@@ -0,0 +1,77 @@
id = "classic_zoom"
kind = "native-mod"
name = "Classic Zoom"
summary = "Hold V to zoom 3x, scroll for more, release to snap back. Instant, no settings, inspired by WI Zoom."
description = """
## Controls
| Action | Result |
|---|---|
| Hold **V** | Zoom in 3x |
| Scroll while holding V | Zoom from 1x to 10x, x1.1 per wheel step |
| Release V | Back to normal view at once |
While V is held, the mouse wheel zooms instead of switching hotbar slots, and turning slows down in step with the zoom so aiming stays steady. Rebind the key on the mod's page in Options > Mods.
## How it differs from Horizon's own Zoom
| | Horizon Zoom | Classic Zoom |
|---|---|---|
| Default key | Z | V |
| Starting zoom | 4x (configurable) | 3x |
| Range | 1.25x to 10x | 1x to 10x |
| Wheel step | x1.25 | x1.1, finer |
| Transition | Smooth by default | Instant |
| Settings page | Yes | None |
Both can be installed at once: they use different keys.
## Notes
- Horizon caps zoom at 10x, so there is no 50x as in WI Zoom.
- Written from scratch for Horizon's mod API. Not affiliated with Wurst-Imperium.
- Some servers forbid zoom mods: check the rules before you join.
**Now a native mod (2.0.0)**: the launcher builds it into the game (Settings → Game → Native mods (experimental), then Build). Native mods are not sandboxed and run with your account's permissions, so read the source before you build it: it is one small bundle on Horizon's native hook points (key, wheel, zoom, mods list). Tested against Horizon `6f8c7a0e5d531d3834c4d0be4659a1db8e7e5c2a` with Horizon Mixins `0d6b103c70633d8fd9443d8d0acd3aadaacedb06`. A key set in 1.0.0 carries over. 1.0.0 is the WebAssembly version.
Source and issues: [github.com/SU4G3/classic-zoom](https://github.com/SU4G3/classic-zoom)"""
authors = ["Surge"]
owners = ["SU4G3"]
license = "GPL-3.0-or-later"
source = "https://github.com/SU4G3/classic-zoom"
icon = "icons/classic_zoom.png"
key = "RWTOxMcIG6MtC0QQnhWEMC+nLKJ6+u4trV/rF+4juSRuow8W4b5P+CPS"
[[versions]]
version = "2.0.0"
published = "2026-10-29"
game = "26.3"
file = "classic_zoom-2.0.0.hnmod"
sha256 = "a8bdeba615ed31b9bd10280fcf8ba04a31935bc65fff429fc0e83d1c8090dac9"
signature = """
untrusted comment: signature from minisign secret key
RUTOxMcIG6MtCyXVhFrgrbWwg3pEBeR8iQ6g3w8MlWbzunH02kAF3swud3hq59EQvuoNpQxj7eTNRy6IJ4l+++7H1EazgGLV3AY=
trusted comment: timestamp:1793286240\tfile:classic_zoom-2.0.0.hnmod\thashed
bbYFmm0wap7Mgnt+wxSMkC/FqklbuAZage02x4Kt5Mi2mzknFbhWij/6sOx5ojRAquDT1XLRpRd2UrrAy4x4Bg=="""
changelog = "Now a native mod (WebAssembly code mods are going away). Same zoom: V by default, 3x, x1.1 per wheel step within 1x-10x, instant, slower turning while zoomed; a key set in 1.0.0 carries over. Pinned to Horizon 6f8c7a0 and Horizon Mixins 0d6b103."
[[versions]]
version = "1.0.0"
published = "2026-10-29"
game = "26.3"
file = "classic_zoom-1.0.0.hmod"
sha256 = "d8bc299202d9760299a3901841adef950d46cfcc1a54e0729379d24885e9ca5d"
signature = """
untrusted comment: signature from minisign secret key
RUTOxMcIG6MtC63o8q5cdLDamPDTVjscmZ1LMdBd8xhKmsCNy6c77sBps3h65Z2/AO+apNyH6zkbnViYtwI3eAQjgrhJvR88cQs=
trusted comment: timestamp:1793265658\tfile:classic_zoom-1.0.0.hmod\thashed
QLysIbvTRiUzChl9NiUchhmO1Nn4nh4HLkJwtseTccYm3YsKz4nVieLsC6tIY6xItPGDT/43EWu89GhlnO60DA=="""
changelog = """
Initial release.
- Hold V to zoom in 3x; release to snap back to normal view.
- Scroll while holding V to zoom from 1x to 10x (x1.1 per wheel step); the hotbar doesn't move while zooming.
- Instant zoom with no transition, in the style of WI Zoom.
- Mouse turning slows down in step with the zoom for steady aiming.
- Key rebindable on the mod's page in Options > Mods.
- Permissions: render (camera zoom) and input (the zoom key and mouse wheel) only."""
+39
View File
@@ -0,0 +1,39 @@
id = "compass_bar"
kind = "mod"
name = "Compass Enhanced"
summary = "A compass bar with degrees and colored directions, plus death points that guide you back."
description = """
A compass bar at the top of your screen that shows where you are facing, in degrees, and slides smoothly as you turn.
## Features
- Compass letters (N, NE, E, SE, S, SW, W, NW) in a color you choose
- Death points: every time you die, a marker stays on the bar with the distance to where you died
- Reaching a death point turns it into an X with a countdown, or look at it and press X to dismiss it
- Arrows with the distance at the edge of the bar for death points out of view
- Optional background box and four sizes (75% to 150%)
- No commands
Settings are in Options → Mods → Compass Bar. Death points are not saved: they vanish when you close the game."""
authors = ["Isaac"]
owners = ["Fernandes585"]
license = "MIT"
source = "https://github.com/Fernandes585/Compass-Enhanced"
key = "RWScU5xtTnTnu55tXSTPbhM2BQ6KG2FLeMD9aTeX111NlQPh7cCafm1w"
[[versions]]
version = "0.1.0"
published = "2026-10-28"
game = "26.3"
file = "compass_bar-0.1.0.hmod"
sha256 = "bb8f191f89f5ea963301fd2d28315b251dfb8df9656b640975eddffaec3e6b00"
signature = """
untrusted comment: signature from minisign secret key\r
RUScU5xtTnTnuz0T7EECJHdaPC5PHAOud+04Uwen4mw75K38TKOCnK9eCKn44pX113qFMvkrP+P4CwF9FJBFX5qws5BedBPbpA0=\r
trusted comment: timestamp:1793221575\tfile:target/horizon-mod/compass_bar.hmod\thashed\r
DedLxzyaxaXaNO8y8pICM4Th3nL0N4oxamDGm0eoQb+6UDBj6pLXbtQLxqnMrbDQYmCE2gX6PGS86G1C33GGDg=="""
changelog = """
First release.
- Compass bar with degrees and compass letters (N, NE, E, SE, S, SW, W, NW)
- Death points with distance, arrival countdown and a key to dismiss them (X by default)
- Settings: bar on/off, background, scale, letter color, arrival distance, X time and death points kept"""
+39
View File
@@ -0,0 +1,39 @@
id = "coords_hud"
kind = "native-mod"
name = "Coordinates HUD"
summary = "Your coordinates and facing in a corner of the screen."
description = """
Your block position and the direction you face in the top-left corner of the screen.
- `/where` puts your coordinates and dimension in chat.
- `/coords` hides or shows the box, and the game keeps your choice (also Options > Mods > Coordinates HUD > Settings).
It reads the player's position, draws on the HUD and writes to chat: nothing else.
Now a native mod (2.0.0): built into the game by the launcher, on the Horizon you play, like any native mod
(Settings → Game → Native mods (experimental), then Build). It was the WebAssembly SDK's example mod; the time of
day line is gone, since Horizon's hook points don't give the game's clock yet. Turn it off in Options > Mods: it
rests at once, and the next build leaves it out. Its source is
[horizon-mods/native/coords-hud](https://github.com/horizon-rust/horizon-mods/tree/main/native/coords-hud).
"""
authors = ["Horizon"]
owners = ["ludovic111"]
license = "MIT"
source = "https://github.com/horizon-rust/horizon-mods/tree/main/native/coords-hud"
icon = "icons/coords_hud.png"
[[versions]]
version = "2.0.0"
published = "2026-10-29"
game = "26.3"
file = "coords_hud-2.0.0.hnmod"
sha256 = "432376fad3062b3517e274d3b8c0d0b52fc5d6de2aee20a0b340c8cab5a8cf08"
changelog = "Native: built into the game by the launcher on Horizon's native hook points; /coords is kept; turn it off in Options > Mods. The time of day line is gone for now."
[[versions]]
version = "0.1.0"
published = "2026-10-24"
game = "26.3"
file = "coords_hud-0.1.0.hmod"
sha256 = "612335e4e00d638f8b97b6c2f5c718c069c7a13811ba922557c27cb2612daf59"
changelog = "First release: the panel, `/where` and `/coords`."
+33
View File
@@ -0,0 +1,33 @@
id = "distant_horizons"
kind = "native-mod"
name = "Distant Horizons for Horizon Client"
summary = "See far past your render distance: Distant Horizons' level-of-detail terrain, ported to Rust as a native mod."
description = """
An unofficial Rust port of [Distant Horizons](https://gitlab.com/distant-horizons-team/distant-horizons) by James Seibel and contributors: the world past your render distance, drawn at lower detail the farther it is.
- Far terrain built from the chunks you visit, and in single player from the world generator, out to 64–1024 chunks.
- Detail drops with distance by Distant Horizons' own rule, with its five quality presets.
- LODs are kept on disk per world, so they load at once next time.
- Drawn by Horizon's far-terrain renderer with block textures and biome tints.
Settings: Options > Mods > Distant Horizons. In chat: `/dh` for status, `/dh distance 512`, `/dh quality high`, `/dh debug on` and more (Tab completes them).
Native source mod: enable Settings → Game → Native mods (experimental), choose Build and confirm the native-code warning. The launcher downloads its own Rust and builds a separate game (10–30 minutes the first time). It runs with your account's permissions.
Pinned to Horizon nightly `d009ebd140476b5bdc958c00f56a75e82abec691` (2026-10-29) and Horizon Mixins `0d6b103c70633d8fd9443d8d0acd3aadaacedb06`; the launcher builds these exact revisions. Other Horizon revisions have not been validated. It builds together with Horizon's official native mods; it can't be built with a native mod pinned to a different Horizon commit. Turn Video Settings > LOD Distance off: Horizon's own far terrain draws in the same place.
Known limits: heavy at 256+ chunks (about 30–40 fps on an Apple M5 MacBook Pro); water at LOD edges can show see-through walls.
LGPL-3.0, as Distant Horizons; its copyright notices are kept. Not made or endorsed by the Distant Horizons team. No Minecraft code or assets are included. Ported by Deniz Şahin with Claude (Anthropic)."""
authors = ["denizsahin7744", "Deniz7744"]
owners = ["denizsahin7744"]
license = "LGPL-3.0-only"
source = "https://github.com/denizsahin7744/distant-horizons-for-horizon-client"
[[versions]]
version = "0.1.0"
published = "2026-10-29"
game = "26.3"
file = "distant_horizons-0.1.0.hnmod"
sha256 = "c53a2501f14efb624839329aac6999ddf8c64a399d41bec42a0c878902feea81"
changelog = "First release: pinned to Horizon nightly d009ebd and Horizon Mixins 0d6b103."
+46
View File
@@ -0,0 +1,46 @@
id = "eatinganimation"
kind = "native-mod"
name = "Eating Animation"
summary = "While you eat food, or drink a potion or milk, the item in your hand shows its bitten frames."
description = """
It is a port of [Eating Animation](https://github.com/Theoness1/EatingAnimation) for Horizon.
While you eat food, or drink a potion or milk, the item in your hand shows its bitten frames.
The frames come from a resource pack you build from your own copy of the EatingAnimation download with `tools/make_frame_pack.py`, and drop into Horizon's `resourcepacks/` folder.
~~Or you can just download it from GitHub Releases...~~"""
authors = ["soufi"]
owners = ["soufied"]
license = "MIT"
source = "https://github.com/soufied/EatingAnimation-Horizon"
icon = "icons/eatinganimation.png"
key = "RWT6eNq4e5WfVDA2lzOOi6XCm/+jJgilqT1+HUUM33ie5MCjcRfSg760"
[[versions]]
version = "0.1.1"
published = "2026-10-31"
game = "26.3"
file = "eatinganimation-0.1.1.hnmod"
sha256 = "3b4d7ce3c9791facc862da7727c2c7c71025cd9bb3ee4cbeaf40f48c72bf5cb0"
signature = """
untrusted comment: signature from minisign secret key
RUT6eNq4e5WfVC2NLoRuU8m5JDlH1mh8dNmGbAZUj6bB4grd487xtihz4OLxxJRyO7jzQHrOz6nAPjcZjT/o1RIxebtX+A3tPg8=
trusted comment: timestamp:1793419092\tfile:eatinganimation-0.1.1.hnmod\thashed
OeN+0r/c6BVNO4b82/T9jp/Wh9iGpvL/B0enoxWiIB31W+7E2OmKWhceMaIZqc4w3N3gn3D8+sFrDICK55WoDQ=="""
changelog = """
dumped to v0.1.1
added icon"""
[[versions]]
version = "0.1.0"
published = "2026-10-30"
game = "26.3"
file = "eatinganimation-0.1.0.hnmod"
sha256 = "07ef4fde3ebbc4f132aab593b12187bce15e1a46689ab40bd3067c685aca85b9"
signature = """
untrusted comment: signature from minisign secret key
RUT6eNq4e5WfVJPSxJOh730W1FMzydyVRO2S58rux9xbEtEU+mDgsqWX65rscWaUH3zqOkLan6quW/u9nEZDgXrNz1n6IHnT5Aw=
trusted comment: timestamp:1793371727\tfile:eatinganimation-0.1.0.hnmod\thashed
qCCGAWAc39kDujbX9CsdXu/URjpNybAOxkomTi18GnzJhMLtsbj0ZfVJaVCSxnh0VvznGQvoH9/DEdRAxBAVBg=="""
changelog = "First release. Eating Animation for Horizon as a native mod: the food, milk and potion in your hand show bitten frames while you eat or drink them."
+26
View File
@@ -0,0 +1,26 @@
id = "enchantmenthints"
kind = "native-mod"
name = "Enchantment Hints"
summary = "A short description under every enchantment in item tooltips, in English and Russian."
description = """
**A native Horizon mod**: the launcher builds it into the game (Settings → Game → Native mods, then Build). Native mods are not sandboxed and run with your account's rights, so read the source before you build it. Pinned to Horizon 142388e and horizon-mixins 0d6b103.
Every enchantment on books and gear gets a one-line description in its tooltip: what it does, in plain words. Our own text for every 26.3 enchantment, in English and Russian.
- Always, or only while Shift is held (with a reminder line).
- Colour and line width are settings.
Settings: Options → Mods → Enchantment Hints. Inspired by [Enchantment Descriptions](https://modrinth.com/mod/enchantment-descriptions); not affiliated, no code or text from it."""
authors = ["d3grando"]
owners = ["d3grando"]
license = "MIT"
source = "https://github.com/d3grando/enchantmenthints"
icon = "icons/enchantmenthints.png"
[[versions]]
version = "0.1.0"
published = "2026-10-29"
game = "26.3"
file = "enchantmenthints-0.1.0.hnmod"
sha256 = "6f1b80a7c22e02b086d5eea2ed6f7c5481b3e40d6a974bf9e382a3df4a2de9df"
changelog = "First release."
+23
View File
@@ -0,0 +1,23 @@
id = "fly_speed"
kind = "native-mod"
name = "Fly Speed"
summary = "Allows you to change your fly speed"
description = "Fly 0.1x to 20x your normal speed: /flyspeed, /flyspeed +1 to step, /flyspeed reset for 1x. Native Horizon mod."
authors = ["ImmortalDev"]
owners = ["ImmortalWhisper"]
license = "All Rights Reserved"
source = "https://github.com/ImmortalWhisper/Fly-Speed"
key = "RWSy3RAfbzD2a0WMm0tQlVl+3GHigjwvwXKvzvnwfmoYrIAoIffbrSbP"
[[versions]]
version = "0.1.0"
published = "2026-10-30"
game = "26.3"
file = "fly_speed-0.1.0.hnmod"
sha256 = "c50a3dcfd9ff68af00560f9cd330c4d18b3d9028113b6769ded54997d5267ddf"
signature = """
untrusted comment: signature from minisign secret key\r
RUSy3RAfbzD2a/1H3F6VX9i5wllM8eP2z7FagPND9tqKj76TY2RNWvF/b4xYcqPLU7OLKAPDVY2+HglKTx9GKnAPPt0KQUYwmQQ=\r
trusted comment: fly_speed-0.1.0.hnmod\r
FcLZSTLhxoNvfQAXmKtGb96iTxdJgh2llOMq4GlGhJPgjrhCvllUHEsHWvXnk0sNKEJREKxQ3CVy8VmYcNQzBQ=="""
changelog = "Created Mod"
+38
View File
@@ -0,0 +1,38 @@
id = "fps_overlay"
kind = "native-mod"
name = "FPS Counter"
summary = "Frames per second in a corner of the screen, with the minimum and the 1% low."
description = """
Horizon's own FPS counter, formerly an extra in Options > Horizon Extras.
- Frames in the last second, in a small box in the corner you pick.
- Optionally the slowest frame and the 1% low of the last 5 seconds.
- Settings: Options > Mods > FPS Counter > Settings (corner; minimum and 1% low).
It reads the game's own frame counter and draws on the HUD: nothing else.
Now a native mod (2.0.0): built into the game by the launcher, on the Horizon you play, like any native mod
(Settings → Game → Native mods (experimental), then Build). Your settings carry over from 1.0.0. Turn it off in
Options > Mods: it rests at once, and the next build leaves it out. Its source is
[horizon-mods/native/fps-overlay](https://github.com/horizon-rust/horizon-mods/tree/main/native/fps-overlay).
"""
authors = ["Horizon"]
owners = ["ludovic111"]
license = "MIT"
source = "https://github.com/horizon-rust/horizon-mods/tree/main/native/fps-overlay"
[[versions]]
version = "2.0.0"
published = "2026-10-29"
game = "26.3"
file = "fps_overlay-2.0.0.hnmod"
sha256 = "bedb670e2490ce8239906c86eabd3a0c131ef5037b2937607231bdbdba38c171"
changelog = "Native: built into the game by the launcher on Horizon's native hook points; settings carry over; turn it off in Options > Mods."
[[versions]]
version = "1.0.0"
published = "2026-10-26"
game = "26.3"
file = "fps_overlay-1.0.0.hmod"
sha256 = "46109c3da01c058c0c15a9e43206b749627e69cd5cd023167621bb5dbdb88bcb"
changelog = "First release: the FPS counter extra as a mod, same look and options."
+29
View File
@@ -0,0 +1,29 @@
id = "fullbright"
kind = "native-mod"
name = "MoreGamma"
summary = "Brightness past 100%: set your own gamma with /gamma, or toggle full brightness with /fullbright."
description = """
A native Horizon mod: the launcher builds it into the game (Settings → Game → Native mods, then Build). Native mods are not sandboxed and run with your account's rights, so read the source before you build it. Uses only Horizon's native hook points (hooks 1); tested on Horizon 41672b1 and horizon-mixins 0d6b103.
See in the dark without torches or night vision. MoreGamma lets you set the brightness (gamma) far past the video settings' 100% "Bright" limit, with a chat command, and switch full brightness on and off.
- `/gamma <value>`: your own gamma. `1` is vanilla's "Bright", `15` is fullbright, up to `100`; `/gamma 1500%` works too.
- `/gamma` shows the gamma in effect.
- `/gamma reset` (or `off`) goes back to your video settings slider; `/gamma on` turns your last custom gamma on again.
- `/fullbright` toggles full brightness (gamma 15).
- Options → Mods → Fullbright: an on/off button, a button cycling 100/200/500/1000/1500%, and a toggle key you can bind.
Client-side: the commands are handled by your game before anything reaches a server, so it works in single player, on LAN and on servers. Your brightness slider and `options.txt` are never changed, so turning it off gives your own brightness back. Settings are remembered between sessions."""
authors = ["bezi5kt"]
owners = ["bezi5kt"]
license = "MIT"
source = "https://github.com/bezi5kt/gamma-rust"
icon = "icons/fullbright.png"
[[versions]]
version = "1.0.0"
published = "2026-10-30"
game = "26.3"
file = "fullbright-1.0.0.hnmod"
sha256 = "573c5e04188e0b1a26eb42a8aeaeeefeb6fb334a096afaa834e71b8efa353ad7"
changelog = "First release."
+26
View File
@@ -0,0 +1,26 @@
id = "itemnew"
kind = "native-mod"
name = "Item New"
summary = "Newly picked-up items glow in your hotbar and inventory until you look at them."
description = """
**A native Horizon mod**: the launcher builds it into the game (Settings → Game → Native mods, then Build). Native mods are not sandboxed and run with your account's rights, so read the source before you build it. Pinned to Horizon 142388e and horizon-mixins 0d6b103.
Items you just got (picked up, crafted, taken from a chest, given) get a soft glow in the hotbar and in inventory and container screens, until you point at them or close the screen.
- Styles: glow, glow with a star, by rarity colour.
- Which screens, and when a mark clears, are settings; `/itemnew` shows or clears the marks.
Settings: Options → Mods → Item New. Inspired by [Item Highlighter](https://modrinth.com/mod/item-highlighter); not affiliated, no code from it."""
authors = ["d3grando"]
owners = ["d3grando"]
license = "MIT"
source = "https://github.com/d3grando/itemnew"
icon = "icons/itemnew.png"
[[versions]]
version = "0.1.1"
published = "2026-10-29"
game = "26.3"
file = "itemnew-0.1.1.hnmod"
sha256 = "cdc68c9911736703ebc4b8b81895a719edd74ba9d08fe01ddb34fe09b8d490e9"
changelog = "First release."
+96
View File
@@ -0,0 +1,96 @@
id = "jei"
kind = "native-mod"
name = "JEI for Horizon"
summary = "An item and recipe viewer for Horizon: every item beside container screens, with search, recipes and uses"
description = """
An item and recipe viewer for [Horizon](https://playhorizon.dev), the Rust reimplementation of Minecraft: Java Edition.
Horizon JEI is inspired by [JustEnoughItems (JEI)](https://github.com/mezz/JustEnoughItems). JEI serves as the reference specification, while this project is a completely new Rust implementation built around Horizon's native hook points.
**Version 0.1.0 — Core Viewer**
### Features
- **Item Browser:** Browse every item registered by the game through a paginated panel alongside container screens, with a search bar supporting JEI's search grammar: words, `"quoted phrases"`, `-exclusions`, `|` alternatives, `@namespace`, and `#tag`.
- **Recipe Viewer:** Press **R** to view an item's recipes or **U** to see its uses. Both work over the item panel and container slots.
- **Recipe Categories:** Dedicated tabs for crafting, furnace, blast furnace, smoker, campfire, stonecutter, smithing, and brewing, supporting all five of the game's recipe layouts, including cooking time and XP.
- **Recipe Navigation:** Browse recipe history with **Back** and click recipe slots to open the corresponding item.
- **Recipe Placement:** Use the **+** button to place a recipe into the open crafting grid, provided it is available in the recipe book.
- **Item Giving:** Give items through Options → Mods → JEI or `/jei give`. In creative mode, left-click gives one item and right-click gives a full stack. In survival mode, clicking opens the recipe viewer instead.
- **Localization:** English and Spanish interface translations. Item names are retrieved directly from the game and respect the player's language.
- **Diagnostics:** `/jei` reports the game's recipe table sizes.
### Current Limitations
The 0.1.0 release focuses on the core viewer. Bookmarks, container slot click integration, tooltips, creative inventory integration, non-recipe categories (anvil, grindstone, composter, and fuel), and shift-click recipe placement are not yet supported.
Some of these features require native hook points that Horizon does not currently expose. See the [JEI parity matrix](https://github.com/enderliker/horizon-jei/blob/main/docs/jei-parity.md) for implementation details.
### Requirements & Security
Requires Horizon **26.3**, hook points version **2**, and the current Nightly build.
Horizon native mods are compiled directly into the game and are **not sandboxed**. Only build source code you trust.
Horizon JEI reads the game's tables, container interface, chat, and its own settings directory (`horizon-mods/.horizon-mod-data/jei/`). It does not write to any other location.
*Unofficial project. Not affiliated with mezz, Mojang, Microsoft, or the Horizon project.*"""
authors = ["enderliker"]
owners = ["enderliker"]
license = "MIT"
source = "https://github.com/enderliker/horizon-jei"
key = "RWRqzx718YYz/OHRM+pAnFWlDv8Rrgar5C4NszQg3EKJWQZWNel5Sslf"
[[versions]]
version = "0.1.0"
published = "2026-10-30"
game = "26.3"
file = "jei-0.1.0.hnmod"
sha256 = "c5590e0d4618d435fc0c56abcc9cdabf253be694710068a3dd4643ac25466b5b"
signature = """
untrusted comment: signature from minisign secret key
RURqzx718YYz/AZEFNcRh0lQFAgg8JsladNTOULcWqBnZFoFoApH0UvoQQkTk8wC/LfZpfJkrn7XRzSUOPUOfudUebBSHHdVYQ0=
trusted comment: jei 0.1.0 for Horizon 26.3
OqC3JuFFGuGNdxATsGIvM20nrld1ME2ks3tI2hfaMX2s5bCgwYjieUDk7FbDQD8zxd3KHqOYaUTz6T+njgUNCw=="""
changelog = """
The first playable Horizon JEI viewer, built on Horizon's native hook points (version 2).
### Item Browser & Search
- Added an item panel alongside container screens, displaying every item known to the game with pagination.
- Implemented JEI's search grammar:
- Words and `"quoted phrases"`.
- `-exclusions` and `|` alternatives.
- `@namespace` and `#tag` filters.
- `$tooltip` and `%creative tab` are not supported yet, pending access to the required data.
### Recipe & Usage Viewer
- Press **R** to view an item's recipes and **U** to view its uses.
- Works over the item panel and any slot in a container screen.
- Clicking an item in the panel opens the same views.
- Added recipe pages with dedicated tabs for:
- Crafting
- Furnace
- Blast Furnace
- Smoker
- Campfire
- Stonecutter
- Smithing
- Brewing
- Supports all five of the game's recipe layouts, including cooking time and XP.
- Added a **Back** button to navigate through viewing history.
- Clicking a recipe slot opens that item's recipes.
### Recipe Placement
- Added the **+** button to place recipes into the open crafting grid when the recipe book contains them.
- The button remains disabled until the recipe is available in the recipe book.
- Shift-click is not supported yet because the panel's action event does not expose modifier keys.
### Item Giving
- Added item giving through **Options → Mods → JEI** or `/jei give`.
- In creative mode, left-click gives one item and right-click gives a full stack.
- In survival mode, clicking an item opens its recipes instead.
### Localization & Diagnostics
- Added English and Spanish UI translations, configurable through the mod's **Language** setting.
- Item names are retrieved directly from the game and always respect the player's language.
- Added `/jei` to report the game's table sizes."""
+38
View File
@@ -0,0 +1,38 @@
id = "keystrokes"
kind = "native-mod"
name = "Keystrokes"
summary = "Your movement keys, mouse buttons and space bar in a corner, lit while held, with clicks per second."
description = """
Horizon's own keystrokes overlay, formerly an extra in Options > Horizon Extras.
- Forward, left, back and right as you bound them, the two mouse buttons and the space bar, lit while held.
- Clicks per second under each mouse button (can be turned off).
- Settings: Options > Mods > Keystrokes > Settings (corner; clicks per second).
It reads which keys you hold and your clicks, and draws on the HUD: nothing else.
Now a native mod (2.0.0): built into the game by the launcher, on the Horizon you play, like any native mod
(Settings → Game → Native mods (experimental), then Build). Your settings carry over from 1.0.0. Turn it off in
Options > Mods: it rests at once, and the next build leaves it out. Its source is
[horizon-mods/native/keystrokes](https://github.com/horizon-rust/horizon-mods/tree/main/native/keystrokes).
"""
authors = ["Horizon"]
owners = ["ludovic111"]
license = "MIT"
source = "https://github.com/horizon-rust/horizon-mods/tree/main/native/keystrokes"
[[versions]]
version = "2.0.0"
published = "2026-10-29"
game = "26.3"
file = "keystrokes-2.0.0.hnmod"
sha256 = "bed34d2f8049e92c52cd7c91c0c4c8c9d165faaf0183500cf4021d9e04783a7d"
changelog = "Native: built into the game by the launcher on Horizon's native hook points; settings carry over; turn it off in Options > Mods."
[[versions]]
version = "1.0.0"
published = "2026-10-26"
game = "26.3"
file = "keystrokes-1.0.0.hmod"
sha256 = "fc7ab8fffba4d9df9da2b5aa84dd9ed33be4712fe65a0b518094f5feb64966d4"
changelog = "First release: the keystrokes extra as a mod, same look and options."
+35
View File
@@ -0,0 +1,35 @@
id = "lookat"
kind = "native-mod"
name = "LookAt"
summary = "The block or mob under your crosshair as a vanilla tooltip: icon, name in your language, health, the tool it needs and where it comes from"
description = """
The block or entity under your crosshair at the top of the screen, drawn as a vanilla tooltip: its item's icon, its name in your language and the namespace it comes from.
- **Blocks**: up to 1024 blocks away, across distant terrain.
- **Mobs and players**: name and health as hearts or numbers; boats, minecarts, armor stands, paintings and item frames (with what they hold).
- **Harvest tool**: ✔ or ✘ with the tool a block needs.
**Settings** (Options → Mods → LookAt): range, position (top, top left, top right), size, background opacity, item icon, where it comes from, position and distance, entities, health, harvest tool. Boxes keep clear of F3, boss bars, the minimap and effect icons.
**Now a native mod (0.2.1)**: the launcher builds it into the game (Settings → Game → Native mods (experimental), then Build). Native mods are not sandboxed and run with your account's permissions, so read the source before you build it. Tested against Horizon `142388e1c136e622652ec7505af15d8ad195e041` with Horizon Mixins `0d6b103c70633d8fd9443d8d0acd3aadaacedb06`; the launcher builds these exact pinned revisions. Settings from 0.1.0 carry over. 0.1.0 is the WebAssembly version."""
authors = ["d3grando"]
owners = ["d3grando"]
license = "MIT"
source = "https://github.com/d3grando/lookat"
icon = "icons/lookat.png"
[[versions]]
version = "0.2.1"
published = "2026-10-29"
game = "26.3"
file = "lookat-0.2.1.hnmod"
sha256 = "6c15726116825b5955984223d0e1bccd5d18375005af243a061b2ba2b9139f33"
changelog = "Now a native mod (WebAssembly code mods are going away). New: mobs, players and other entities under the crosshair with their health, the harvest tool a block needs, a size setting, and boxes that keep clear of F3, boss bars, the minimap and effect icons. Pinned to Horizon 142388e and Horizon Mixins 0d6b103."
[[versions]]
version = "0.1.0"
published = "2026-10-28"
game = "26.3"
file = "lookat-0.1.0.hmod"
sha256 = "f3da841f96ffc4d43a636c4aa6503907281ed36cd8711c498447ef5402943970"
changelog = "First release."
+38
View File
@@ -0,0 +1,38 @@
id = "minimap"
kind = "native-mod"
name = "Minimap"
summary = "A map of the world around you in a corner of the screen."
description = """
Horizon's own minimap, formerly an extra in Options > Horizon Extras.
- A map of the land around you, drawn like a map item: vanilla's map colours with their height shading and water depth, and your arrow.
- Underground, under a roof and in the Nether it maps the floor around your height.
- Settings: Options > Mods > Minimap > Settings (corner; size; square or round; zoom, 0.5 to 4 blocks per pixel; turn with the player).
It shows a map in a corner of the screen: nothing else.
Now a native mod (2.0.0): built into the game by the launcher, on the Horizon you play, like any native mod
(Settings → Game → Native mods (experimental), then Build). Your settings carry over from 1.0.0. Turn it off in
Options > Mods: it rests at once, and the next build leaves it out. Its source is
[horizon-mods/native/minimap](https://github.com/horizon-rust/horizon-mods/tree/main/native/minimap).
"""
authors = ["Horizon"]
owners = ["ludovic111"]
license = "MIT"
source = "https://github.com/horizon-rust/horizon-mods/tree/main/native/minimap"
[[versions]]
version = "2.0.0"
published = "2026-10-29"
game = "26.3"
file = "minimap-2.0.0.hnmod"
sha256 = "6f5dbca0699562b088a04b3f5abdc1b7faf8da51257a96da0a4ed639c6644ed9"
changelog = "Native: built into the game by the launcher on Horizon's native hook points; settings carry over; turn it off in Options > Mods."
[[versions]]
version = "1.0.0"
published = "2026-10-26"
game = "26.3"
file = "minimap-1.0.0.hmod"
sha256 = "2906eb2832f4e64075be2aa55da0829f40b2eccf26727951dd19d7607e9fe704"
changelog = "First release: the minimap extra as a mod, same look and options."
+26
View File
@@ -0,0 +1,26 @@
id = "mousetricks"
kind = "native-mod"
name = "Mouse Tricks"
summary = "Inventory mouse tricks: drag to spread or gather, the wheel moves items one by one, and R sorts."
description = """
**A native Horizon mod**: the launcher builds it into the game (Settings → Game → Native mods, then Build). Native mods are not sandboxed and run with your account's rights, so read the source before you build it. Pinned to Horizon 142388e and horizon-mixins 0d6b103.
- **Right-drag**: one item into each slot you pass.
- **Left-drag**: gather the same items; with Shift, quick-move many stacks.
- **Mouse wheel** over a slot: move items one at a time between your inventory and a container.
- **Sort** (R, or middle click on an empty spot): merges partial stacks and sorts the part under the mouse (a chest, or your main inventory; the hotbar only if you ask). Creative-tab order, by name or by count. Items with other names, enchantments or damage never merge; bundles stay put.
Everything goes through the game's own container clicks, so servers accept it. Each trick is a setting (Options → Mods → Mouse Tricks). Inspired by [Mouse Tweaks](https://modrinth.com/mod/mouse-tweaks); not affiliated, no code from it."""
authors = ["d3grando"]
owners = ["d3grando"]
license = "MIT"
source = "https://github.com/d3grando/mousetricks"
icon = "icons/mousetricks.png"
[[versions]]
version = "0.2.0"
published = "2026-10-29"
game = "26.3"
file = "mousetricks-0.2.0.hnmod"
sha256 = "48c60955371963f8e320a9efbe8285bb276d255c29178a8a021e7e2b47535cbe"
changelog = "First release."
+42
View File
@@ -0,0 +1,42 @@
id = "recipes"
kind = "native-mod"
name = "Recipes"
summary = "Every item beside your inventory, with its recipes and uses."
description = """
Horizon's own recipe viewer.
- Every item sits in a panel beside your inventory and container screens, with a search box: words in the name or the id, `@minecraft` for a namespace, `#logs` for a tag.
- R over an item (in the panel, in any slot, or in a recipe) shows its recipes, and U shows its uses. A click does the same: a left click for recipes, a right click for uses. Both keys can be rebound in Controls.
- Recipes have a tab for each station: crafting table, furnace, blast furnace, smoker, campfire, stonecutter, smithing table and brewing stand. Back returns through the pages you opened.
- + beside a crafting recipe you know fills the crafting grid, as the recipe book does.
- In creative mode, turn on "Click gives items" in its settings: a click on an item in the panel then gives you one, and a right click gives a stack.
- On servers that are not Horizon, it shows this game's own 26.3 recipes. Which recipes you know, and what + can fill, follow the server's recipe book.
It reads the game's items and recipes, hears its own keys and your clicks on the panel, and places recipes or gives items only when you ask: nothing else.
Now a native mod (2.0.0): built into the game by the launcher, on the Horizon you play, like any native mod
(Settings → Game → Native mods (experimental), then Build). It needs Horizon's native hook points version 2, in the
Nightlies from 4baa571 on. Your settings and keys carry over from 1.0.0. Turn it off in Options > Mods: it rests at
once, and the next build leaves it out. Its source is
[horizon-mods/native/recipes](https://github.com/horizon-rust/horizon-mods/tree/main/native/recipes).
"""
authors = ["Horizon"]
owners = ["ludovic111"]
license = "MIT"
source = "https://github.com/horizon-rust/horizon-mods/tree/main/native/recipes"
[[versions]]
version = "2.0.0"
published = "2026-10-29"
game = "26.3"
file = "recipes-2.0.0.hnmod"
sha256 = "51fddf300709404170061355afdf0b407f28eb928bcc0a9133c241cd63f4a0ed"
changelog = "Native: built into the game by the launcher on Horizon's native hook points (version 2); settings and keys carry over; turn it off in Options > Mods."
[[versions]]
version = "1.0.0"
published = "2026-10-26"
game = "26.3"
file = "recipes-1.0.0.hmod"
sha256 = "d393b6f0061377113eeb3bfd710dbf68f564de1c2949da694c8e4de1b6b983c6"
changelog = "First release: search, recipes and uses (R/U), station tabs, filling the grid, and giving items in creative mode."
+21
View File
@@ -0,0 +1,21 @@
id = "rightharvest"
kind = "native-mod"
name = "RightClickHarvest"
summary = "Right-click a ripe crop to pick it and sow it again."
description = """
With an empty hand, right-click a ripe wheat, carrot, potato, beetroot or nether wart: the crop is harvested with the game's own drops, sound and statistics, and sown again from a seed in your inventory.
No settings: turn it off in Options > Mods."""
authors = ["Domanffe"]
owners = ["Domanffe"]
license = "MIT"
source = "https://github.com/Domanffe/rightharvest"
icon = "https://horizon-rust.github.io/horizon-mods/icons/rightharvest.png"
[[versions]]
version = "0.1.0"
published = "2026-10-30"
game = "26.3"
file = "rightharvest-0.1.0.hnmod"
sha256 = "a43870a89827c1d0fa861a663aa1781819b236f994191abf971c98125a3a4653"
changelog = "First release: a native mod for Horizon 26.3. With an empty hand, right-click a ripe crop to harvest it with the game's own drops and sow it again from a seed you carry."
+56
View File
@@ -0,0 +1,56 @@
id = "serverify"
kind = "native-mod"
name = "Serverify"
summary = "Find Minecraft servers and add them to your Multiplayer list, from a page in your web browser."
description = """
Serverify is a server browser. Press **F8** (or type `/serverify web`, or press the button on the mod's settings page) and your web browser opens a page with a search box, a category menu and cards showing each server's picture, address, player count and country. Press **Add** and the server goes to the top of your Multiplayer list. Then join it from Horizon's own Multiplayer screen.
It is the Horizon version of [Serverify](https://github.com/Louchatfroff/serverify), the server browser for Fabric, Quilt, Forge and NeoForge, by the same author.
## What it does
- Opens a server browser page in your web browser: search, 22 categories, an offline-mode filter and an **Add** button.
- Adds servers to `servers.dat` in the game folder, with the name cleaned of colour codes.
- Adds chat commands: `/serverify web`, `list [category]`, `search <words>`, `next`, `prev`, `add <number>` and `categories`.
- Adds a row in Options > Mods with a rebindable key (F8 by default) and a settings page with two buttons: open the browser, and **Offline-mode servers only** (on by default).
Horizon cannot join servers that check Minecraft accounts yet, which is most public servers, so by default the page shows servers that accept offline-mode players. Turn the filter off to see all of them.
## What it contacts
- **Modrinth.** The game asks `api.modrinth.com` for the server list and `cdn.modrinth.com` for server pictures, only when you look for servers.
- **Three listing sites.** With the offline-mode filter off (and for top-mc-servers.net's list of cracked servers when it is on), the game fetches public pages of `top-mc-servers.net`, `minecraftservers.org` and `minecraft-mp.com`, and their server pictures. The game builds those addresses itself. The page reads the fetched pages with the browser's own HTML parser.
- **A page on your computer.** The first time you open the page, the mod starts a small web server on `127.0.0.1` (this computer only, never the network) with a random secret in its address, made for each session. The page talks only to that server. A request with another host name, another origin or no secret is refused. The server can show the list, send a picture and add one server to your Multiplayer list.
- It sends no data about you, and makes no request at any other time.
## What it does not do
- It does not run any code from the sites it reads, and it does not send your data anywhere.
- It does not change anything but `servers.dat` (one entry at a time) and its own settings in `horizon-mods/.horizon-mod-data/serverify/`.
- It does not draw screens or buttons in the game: Horizon's hook points have none, so the screen is your web browser.
- It cannot make Horizon join servers that check Minecraft accounts.
- Adding a server rewrites `servers.dat`, which drops the hidden direct-join entries Horizon keeps. Your saved servers stay.
## Install
Install it from the launcher's Mods page, then Settings > Native mods (experimental) > Build, and play the Native mods instance. Guide: https://github.com/Louchatfroff/serverify/tree/main/horizon/serverify#readme
Native mods are not sandboxed. The source is public and licensed GPL-3.0-only: https://github.com/Louchatfroff/serverify/tree/main/horizon/serverify"""
authors = ["Louchatfroff"]
owners = ["Louchatfroff"]
license = "GPL-3.0-only"
source = "https://github.com/Louchatfroff/serverify"
icon = "icons/serverify.png"
[[versions]]
version = "0.1.0"
published = "2026-10-30"
game = "26.3"
file = "serverify-0.1.0.hnmod"
sha256 = "f9ad0777f8fc7ea532f799da8a9eb72df2348c8d6bb5dc104f81b6ab47209501"
changelog = """
First release for Horizon, ported from the Java Edition mod of the same name.
- A server browser page in your web browser (F8, `/serverify web`, or the settings button).
- Chat commands to list, search and add servers.
- An offline-mode filter, on by default, because Horizon cannot join servers that check Minecraft accounts yet."""
+31
View File
@@ -0,0 +1,31 @@
id = "shulker_peek"
kind = "native-mod"
name = "ShulkerPeek"
summary = "A shulker box's contents as a 9 x 3 grid in its tooltip."
description = """
Hover a shulker box to see what's inside: its 27 slots appear as a 9 x 3 grid right in the tooltip, laid out exactly like the box's own screen, with item icons and stack counts.
- Works with the plain shulker box and all 16 dyed ones, whether empty, partly filled or full.
- Works on every screen with item tooltips: your inventory, chests and other containers, the creative inventory, the item panel, chat item links and trading.
- Keeps the tooltip's usual lines (name, lore, "64 x Diamond"...); the grid goes underneath.
- Respects hidden tooltips: a box whose `minecraft:tooltip_display` hides its contents stays hidden.
- Settings in Options > Mods > ShulkerPeek > Settings: show the grid always or only while holding Shift, show a grid for empty boxes, and tint the grid with the box's dye colour.
- Uses your resource pack's own shulker box texture.
Works in single player and on LAN worlds you host. Horizon currently leaves a box's contents out of what its built-in server sends to the game window, which is why even the "64 x Diamond" lines are missing there. ShulkerPeek sends the contents the way a vanilla server does. On vanilla servers the grid works too; on other Horizon servers without the mod, boxes look empty.
It never changes items, inventories or worlds. Turn it off any time in Options > Mods.
This is a native mod built for Horizon Nightly 2026-10-29 (`5e391eb`). It builds together with Horizon's official native mods (FPS Counter, Zoom, Minimap, Recipes and others)."""
authors = ["vstxx"]
owners = ["vstxx"]
license = "MIT"
source = "https://github.com/vstxx/shulkerpeek"
icon = "icons/shulker_peek.png"
[[versions]]
version = "0.1.0"
published = "2026-10-29"
game = "26.3"
file = "shulker_peek-0.1.0.hnmod"
sha256 = "e4e33cb698d91042b2ed2f93a1c935f279d16d83988115d53c0f33530cabeab5"
+27
View File
@@ -0,0 +1,27 @@
id = "smartf3"
kind = "native-mod"
name = "SmartF3"
summary = "A tidier, configurable F3 debug screen: the same information in modules you can colour, reorder and turn off."
description = """
**A native Horizon mod**: the launcher builds it into the game (Settings → Game → Native mods, then Build). Native mods are not sandboxed and run with your account's rights, so read the source before you build it. Pinned to Horizon 142388e and horizon-mixins 0d6b103.
The F3 debug screen, grouped into 14 modules: FPS, server, coordinates, chunk, facing, world, biome, light, chart keys, system, rendering, and the targeted block, fluid and entity.
- Each module: on/off, label and value colours, left or right column, order.
- Text shadow, background (vanilla, dark, none), gap between modules, compact layout.
- The vanilla F3 is one click (or `/smartf3 off`) away; the F3 charts and keys work as in vanilla.
Settings: Options → Mods → SmartF3. Inspired by [BetterF3](https://modrinth.com/mod/betterf3); not affiliated, no code from it."""
authors = ["d3grando"]
owners = ["d3grando"]
license = "MIT"
source = "https://github.com/d3grando/smartf3"
icon = "icons/smartf3.png"
[[versions]]
version = "0.1.0"
published = "2026-10-29"
game = "26.3"
file = "smartf3-0.1.0.hnmod"
sha256 = "666fc77a12411c8b525dca3d3d478595e760b73c48b62b936fb6026419547929"
changelog = "First release."
+27
View File
@@ -0,0 +1,27 @@
id = "treefall"
kind = "native-mod"
name = "TreeFall"
summary = "Cut one log of a natural tree with an axe and the whole tree comes down."
description = """
**A native Horizon mod** (built into the game by the launcher; native mods are not sandboxed, so read the source before you build it).
Cut one log of a natural tree with an axe and the whole tree comes down. Every log is broken through the game's ordinary mining path, so drops, statistics, hunger and axe wear are exactly as if you had chopped each log yourself.
- Sneak to cut a single log; log cabins (logs without natural leaves) are never felled.
- **Settings page** (Options > Mods > TreeFall): which tools fell (axe / any tool / anything), sneaking, largest tree, leaves needed, cut time (as one log, or longer for bigger trees), tool wear (every log / once / a factor / none), keep durability, hunger, drops at the stump, replant a sapling, break the leaves, huge mushrooms.
- `/treefall` on/off per player; LAN guests too.
In the spirit of FallingTree; not affiliated, no code from it. Pinned to Horizon 142388e and horizon-mixins 0d6b103."""
authors = ["d3grando"]
owners = ["d3grando"]
license = "MIT"
source = "https://github.com/d3grando/treefall"
icon = "icons/treefall.png"
[[versions]]
version = "0.3.0"
published = "2026-10-29"
game = "26.3"
file = "treefall-0.3.0.hnmod"
sha256 = "6f7feea8d0f55291953476b272f00cfc4be42d64972e7efefeb3df47c9dae8ce"
changelog = "First registry release. A settings page in Options > Mods; the cut can take longer for bigger trees (and letting go cancels it, in single player too); trees come down from the top and leave a stump when the axe would wear out; replanting, drops at the stump, leaves and huge mushrooms are options."
+46
View File
@@ -0,0 +1,46 @@
id = "veinminer"
kind = "native-mod"
name = "Veinminer"
summary = "Mine connected ores and matching trees using Horizon's native player-mining behavior."
description = """
An unofficial Rust port by levzzz5154. Mines connected veins, including diagonal neighbours,
through Horizon's player-mining code, preserving loot, XP, tool wear and server block events.
Includes per-player toggles, configurable block/tool groups, a block limit and cooldown.
Native source mod: enable Settings → Game → Native mods (experimental), choose Build,
and confirm the native-code warning. A supported launcher downloads its own Rust toolchain
and builds a separate game using Horizon Mixins. Requires Git and Horizon's platform build
dependencies, and runs with your account's permissions.
Each version is pinned to one Horizon Nightly and Horizon Mixins revision, named in its
changelog; the launcher builds those exact revisions. Other Horizon revisions have not
been validated. Ordinary Play does not compile mods; rebuild after changing the enabled
native-mod combination.
Use `/veinminer toggle`, `/veinminer on`, `/veinminer off` or `/veinminer status`.
`/veinminer reload` requires operator permission. Survival mining only; install on the
world host/server. Delayed mining, drop merging and Java client addons are not supported.
Original project: [MiraculixxT's Veinminer](https://github.com/MiraculixxT/Veinminer).
Independently written Rust code; no upstream mod code or artwork is bundled. MIT licensed.
"""
authors = ["levzzz5154"]
owners = ["levzzz5154"]
license = "MIT"
source = "https://github.com/levzzz5154/horizon-veinminer"
[[versions]]
version = "0.3.0"
published = "2026-10-28"
game = "26.3"
file = "veinminer-0.3.0.hnmod"
sha256 = "ec308c02c65bad64043828a19ef6254d1eb9d422bb1123cd065cb9e6e7b126c8"
changelog = "Native launcher bundle, pinned to nightly 83f15fa27 and Horizon Mixins 0.5.0."
[[versions]]
version = "0.3.2"
published = "2026-10-31"
game = "26.3"
file = "veinminer-0.3.2.hnmod"
sha256 = "31a4249da1b35972ae8b34201373c86352f4adc2f5cb3e015749d1fbfecf00a0"
changelog = "Pinned to Nightly 2026-10-31 (d91cab6) and Horizon Mixins 4342a0b. Reads each level's height from the loaded world, so data-pack dimension heights are respected."
+33
View File
@@ -0,0 +1,33 @@
id = "xtoxray"
kind = "native-mod"
name = "X To Xray"
summary = "Opensource Xraymod"
description = """
Press X To Xray!
Does Not Work On Servers.
Instalation:
1. Download Horizon
2. Get Horizon Working
3. Add The Mod Trough The Horizon Launcher
4. Start Xraying
"""
authors = ["1_st_Cat"]
owners = ["RealCat1st"]
license = "Apache-2.0"
source = "https://github.com/RealCat1st/XToXrayRust"
icon = "icons/xtoxray.png"
key = "RWQVF+EFGGnX6eUNms+cP5AceLvx9b0rFYYXCty7so4MVbJ1SNpi9zNK"
[[versions]]
version = "2026.29.0"
published = "2026-10-30"
game = "26.3"
file = "xtoxray-2026.29.0.hnmod"
sha256 = "3446a4a70a2b87ef6e9de00ce8a255aa83c2e44ccd5acc9f14887662ed55f27c"
signature = """
untrusted comment: signature from rsign secret key
RUQVF+EFGGnX6cZkn/iGZ9FUHXC45MU+8uuj7l7eRkZ+bR/x7S8s2U34F7nv/f6jKXdzeLZi3QoSA5g5oXqP99/KlGgd/19ygwY=
trusted comment: timestamp:1793375231\tfile:xtoxray-2026.29.0.hnmod\tprehashed
2h6QOc/dnek1KMCGL2jerhCObc9pECmShr0kKYJhEEFz52Hhb4C3vmjc9vuwE3CftUHs+StHkalHQVLVQy8eCg=="""
changelog = "Release"
+39
View File
@@ -0,0 +1,39 @@
id = "zoom"
kind = "native-mod"
name = "Zoom"
summary = "Hold a key to zoom in; the mouse wheel zooms further while you hold it."
description = """
Horizon's own zoom, formerly an extra in Options > Horizon Extras.
- Hold the key (Z by default) to zoom in at the starting zoom; let go and the view comes back.
- While you hold it, the mouse wheel zooms further in or out (up to 10x) and the hotbar stays put.
- Smooth zoom eases in and out like the spyglass; slower turning keeps aiming steady.
- Settings: Options > Mods > Zoom > Settings (the key; starting zoom; smooth zoom; slower turning).
It hears its own key and the mouse wheel while you hold it, and zooms the camera: nothing else.
Now a native mod (2.0.0): built into the game by the launcher, on the Horizon you play, like any native mod
(Settings → Game → Native mods (experimental), then Build). Your settings carry over from 1.0.0. Turn it off in
Options > Mods: it rests at once, and the next build leaves it out. Its source is
[horizon-mods/native/zoom](https://github.com/horizon-rust/horizon-mods/tree/main/native/zoom).
"""
authors = ["Horizon"]
owners = ["ludovic111"]
license = "MIT"
source = "https://github.com/horizon-rust/horizon-mods/tree/main/native/zoom"
[[versions]]
version = "2.0.0"
published = "2026-10-29"
game = "26.3"
file = "zoom-2.0.0.hnmod"
sha256 = "49fa2d7d635225c53304fbf1477606c0677eb23d94dac75b730f6c3ba065f127"
changelog = "Native: built into the game by the launcher on Horizon's native hook points; settings carry over; turn it off in Options > Mods."
[[versions]]
version = "1.0.0"
published = "2026-10-26"
game = "26.3"
file = "zoom-1.0.0.hmod"
sha256 = "447f7cc49b4e8535c8252a871ac6fbdafcc7bfcf881297429dc93f185b2f9340"
changelog = "First release: the zoom extra as a mod, same look and options."
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "horizon-mod-coords-hud"
version = "2.0.0"
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "horizon-mod-coords-hud"
version = "2.0.0"
edition = "2024"
license = "MIT"
publish = false
description = "Horizon's Coordinates HUD, a native mod: your position and facing in a corner of the screen."
# The mod's logic and its tests: the same file the bundle adds to the game.
[lib]
path = "mixins/coords_hud/runtime.rs"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Ludovic Marie and the Horizon contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+46
View File
@@ -0,0 +1,46 @@
# Coordinates HUD (Horizon native mod)
Your block position and the direction you face in a box in the top-left corner of the screen.
- `/where` puts your coordinates and dimension in chat.
- `/coords` hides or shows the box, and the game keeps your choice (also Options > Mods > Coordinates HUD > Settings).
It is a [horizon-mixins](https://github.com/horizon-rust/horizon-mixins) bundle (`mixins/coords_hud/`) that the Horizon
launcher builds into the game. It replaces the WebAssembly Coordinates HUD 0.1.0, the WebAssembly SDK's example mod.
The time of day line is gone: Horizon's hook points don't give the game's clock yet.
## Install
In the Horizon launcher: Mods → Coordinates HUD → Install, then Settings → Game → Native mods (experimental) → Build. The
launcher downloads its own Rust and builds the game with your native mods; see Horizon's
[native mods](https://github.com/horizon-rust/horizon/blob/main/docs/native-mods.md).
## Made on the hook points
The bundle hooks only Horizon's native hook points (`crates/horizon-client/src/game/native_hooks.rs`), which keep
their names and signatures across releases. `compatibility.json` names the version it needs (`"hooks": 1`), so the
launcher builds it on any Horizon with that version or a later one, together with any other mod. `horizon_revision` is
only the commit it was last tested on.
| File | What it is |
|---|---|
| `mixins/coords_hud/horizon-mixin.toml` | The bundle: one module and the HEAD hooks |
| `mixins/coords_hud/runtime.rs` | The mod's logic and state, added to the game as `game::official_coords_hud`; also this crate's library, for its tests |
| `mixins/coords_hud/*.rs` | One small helper per hook |
| `proof.py` (with `../tools/game.py`) | The proof: the built game driven with `horizon-ctl --offscreen` on fresh, temporary data |
## Build and prove
```sh
(cd native/coords-hud && cargo test) # the logic
python3 native/tools/native.py check coords-hud # apply on the tested Horizon and cargo check the game
python3 native/tools/native.py check all # with the other native mods, as a player's set
python3 native/tools/native.py package coords-hud # native/target/coords_hud-<version>.hnmod for the registry
python3 native/tools/native.py proof coords-hud --resources /path/to/.local/horizon-client/26.3
```
The proof needs resources prepared from your own Minecraft 26.3 (`horizon-prepare`); none are in this repository.
It checks the row and the settings page in Options > Mods, that the box shows in the top left, that `/coords` hides it
and writes `horizon-mods/.horizon-mod-data/coords_hud/shown`, and takes a screenshot of `/where`'s line in chat.
License: MIT.
+8
View File
@@ -0,0 +1,8 @@
{
"game": "26.3",
"hooks": 1,
"horizon_repository": "https://github.com/horizon-rust/horizon",
"horizon_revision": "4baa571566e39bfd6594988d3bb2bc85440c30e7",
"horizon_mixins_repository": "https://github.com/horizon-rust/horizon-mixins",
"horizon_mixins_revision": "0d6b103c70633d8fd9443d8d0acd3aadaacedb06"
}
@@ -0,0 +1,10 @@
// A click on the mod's settings page: show or hide the box, and keep it.
fn official_coords_hud_click(game: &mut crate::game::Game<'_>, mod_id: &str, button: usize) {
use crate::game::official_coords_hud as m;
if mod_id != m::ID || button != 0 {
return;
}
let shown = !m::shown(|| game.native_setting(m::ID, "shown"));
m::set_shown(shown);
game.native_set_setting(m::ID, "shown", if shown { "true" } else { "false" });
}
@@ -0,0 +1,20 @@
// /where puts the position in chat; /coords hides or shows the box.
fn coords_hud_command(game: &mut crate::game::Game<'_>, command: &str, ran: &mut bool) {
use crate::game::official_coords_hud as m;
let name = command.split(' ').next().unwrap_or("");
if !game.native_on(m::ID) || !matches!(name, "where" | "coords") {
return;
}
*ran = true;
if name == "coords" {
let shown = !m::shown(|| game.native_setting(m::ID, "shown"));
m::set_shown(shown);
game.native_set_setting(m::ID, "shown", if shown { "true" } else { "false" });
game.native_chat(if shown { "Coordinates panel shown" } else { "Coordinates panel hidden" });
return;
}
match game.native_player() {
Some(p) => game.native_chat(&m::where_line(p.x, p.y, p.z, p.dimension)),
None => game.native_chat("Not in a world"),
}
}
@@ -0,0 +1,68 @@
[mod]
id = "coords_hud"
version = "2.0.0"
priority = 100
# The commit it was tested on. It uses only the native hook points (hooks 1 in
# compatibility.json): the launcher builds it on any Horizon that has them.
[compatibility]
horizon_revision = "4baa571566e39bfd6594988d3bb2bc85440c30e7"
[[module]]
id = "runtime"
file = "crates/horizon-client/src/game.rs"
name = "official_coords_hud"
source = "runtime.rs"
[[injection]]
id = "hud"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_hud"
helper = "hud.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "batch", ty = "&mut horizon_client::hud::HudBatch", value = "&mut *batch" },
{ name = "corners", ty = "&mut horizon_client::extras_hud::Corners", value = "&mut *corners" },
]
[[injection]]
id = "command"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_command"
helper = "command.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "command", ty = "&str", value = "command" },
{ name = "ran", ty = "&mut bool", value = "&mut *ran" },
]
[[injection]]
id = "list"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_mods"
helper = "list.rs"
bindings = [
{ name = "mods", ty = "&mut Vec<crate::game::native_hooks::NativeMod>", value = "&mut *mods" },
]
[[injection]]
id = "settings"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_settings"
helper = "settings.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "mod_id", ty = "&str", value = "mod_id" },
{ name = "buttons", ty = "&mut Vec<String>", value = "&mut *buttons" },
]
[[injection]]
id = "click"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_settings_click"
helper = "click.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "mod_id", ty = "&str", value = "mod_id" },
{ name = "button", ty = "usize", value = "button" },
]
@@ -0,0 +1,28 @@
// The box in the top-left corner, drawn as the WebAssembly mods' panels were;
// the debug screen takes that corner.
fn coords_hud(
game: &mut crate::game::Game<'_>,
batch: &mut horizon_client::hud::HudBatch,
corners: &mut horizon_client::extras_hud::Corners,
) {
use crate::game::official_coords_hud as m;
use horizon_client::extras::Corner;
// Turned off in Options > Mods: nothing until the next build leaves it out.
if !game.native_on(m::ID) || !game.native_corner_shown(Corner::TopLeft) {
return;
}
if !m::shown(|| game.native_setting(m::ID, "shown")) {
return;
}
let Some(p) = game.native_player() else {
return;
};
let lines = m::lines(p.x, p.y, p.z, p.yaw);
let w = lines.iter().map(|l| game.native_text_width(l)).fold(0.0, f32::max) + 4.0;
let h = 10.0 * lines.len() as f32 + 2.0;
let (x, y) = corners.place(Corner::TopLeft, w, h);
game.native_fill(batch, [x, y, w, h], m::BACKGROUND);
for (i, line) in lines.iter().enumerate() {
game.native_text(batch, line, [x + 2.0, y + 2.0 + 10.0 * i as f32], m::INK, true);
}
}
@@ -0,0 +1,10 @@
// The mod's row in Options > Mods.
fn official_coords_hud_listed(mods: &mut Vec<crate::game::native_hooks::NativeMod>) {
use crate::game::official_coords_hud as m;
mods.push(crate::game::native_hooks::NativeMod {
id: m::ID.into(),
title: format!("{} {}", m::NAME, m::VERSION),
description: m::DESCRIPTION.into(),
keys: Vec::new(),
});
}
@@ -0,0 +1,100 @@
// Horizon's Coordinates HUD, formerly the WebAssembly SDK's example mod: the
// block position and the direction the player faces in a box in the top-left
// corner, `/where` to put the position in chat and `/coords` to hide or show
// the box. This file is both the mod's Cargo library (its tests) and the
// module the bundle adds to the game.
/// The mod's id (its settings folder), name and version.
pub const ID: &str = "coords_hud";
pub const NAME: &str = "Coordinates HUD";
pub const VERSION: &str = "2.0.0";
pub const DESCRIPTION: &str = "Your coordinates and facing in a corner of the screen. /where puts them in chat, /coords hides or shows the box.";
/// The WebAssembly mods' panel: translucent black, white text with a shadow.
pub const BACKGROUND: u32 = 0x7800_0000;
pub const INK: u32 = 0xFF_FFFF;
/// Direction.fromYRot: yaw 0 faces south, 90 west.
pub fn facing(yaw: f32) -> &'static str {
let i = (yaw / 90.0 + 0.5).floor() as i32 & 3;
["South (+Z)", "West (-X)", "North (-Z)", "East (+X)"][i as usize]
}
/// The box's lines, as the WebAssembly mod wrote them.
pub fn lines(x: f64, y: f64, z: f64, yaw: f32) -> [String; 2] {
[
format!("XYZ {x:.1} / {y:.1} / {z:.1}"),
format!("Facing {}", facing(yaw)),
]
}
/// `/where`'s line: `12, 70, -4 in minecraft:the_nether`.
pub fn where_line(x: f64, y: f64, z: f64, dimension: &str) -> String {
format!(
"{:.0}, {:.0}, {:.0} in {dimension}",
x.floor(),
y.floor(),
z.floor()
)
}
/// The stored `shown` value: shown unless it says `false`.
pub fn read_shown(stored: Option<String>) -> bool {
stored.is_none_or(|v| v.trim() != "false")
}
pub fn shown_label(shown: bool) -> String {
format!("Shown: {}", if shown { "ON" } else { "OFF" })
}
thread_local! {
/// Whether the box shows, read once from its file, then kept.
static SHOWN: std::cell::Cell<Option<bool>> = const { std::cell::Cell::new(None) };
}
/// Whether the box shows, read once through `get` (the `shown` file).
pub fn shown(get: impl FnOnce() -> Option<String>) -> bool {
SHOWN.with(|s| {
let found = s.get().unwrap_or_else(|| read_shown(get()));
s.set(Some(found));
found
})
}
/// Replaces it (`/coords` or the settings page changed it).
pub fn set_shown(shown: bool) {
SHOWN.with(|s| s.set(Some(shown)));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn facing_follows_vanilla_yaw() {
assert_eq!(facing(0.0), "South (+Z)");
assert_eq!(facing(90.0), "West (-X)");
assert_eq!(facing(-180.0), "North (-Z)");
assert_eq!(facing(-90.0), "East (+X)");
assert_eq!(facing(314.0), "East (+X)");
}
#[test]
fn lines_read_as_the_webassembly_mods() {
assert_eq!(
lines(12.5, 64.0, -3.25, 180.0),
["XYZ 12.5 / 64.0 / -3.2", "Facing North (-Z)"]
);
assert_eq!(
where_line(12.5, 70.2, -3.25, "minecraft:the_nether"),
"12, 70, -4 in minecraft:the_nether"
);
}
#[test]
fn shown_unless_stored_false() {
assert!(read_shown(None));
assert!(read_shown(Some("true".into())));
assert!(!read_shown(Some("false\n".into())));
assert_eq!(shown_label(false), "Shown: OFF");
}
}
@@ -0,0 +1,8 @@
// The mod's settings page: one button, the box shown or hidden.
fn official_coords_hud_page(game: &mut crate::game::Game<'_>, mod_id: &str, buttons: &mut Vec<String>) {
use crate::game::official_coords_hud as m;
if mod_id != m::ID {
return;
}
buttons.push(m::shown_label(m::shown(|| game.native_setting(m::ID, "shown"))));
}
+56
View File
@@ -0,0 +1,56 @@
"""Coordinates HUD in a running modded game: its row and settings page in Options > Mods, the box in the top
left, /coords hiding and showing it (and keeping that), and /where's line in chat (screenshots)."""
import json
import time
from game import Game, difference
DATA = "horizon-mods/.horizon-mod-data/coords_hud"
TOP_LEFT, CHAT = (0.0, 0.0, 0.25, 0.1), (0.0, 0.7, 0.5, 0.95)
def typed(g, line):
"""As the player types it: T opens the chat box, then the line and Enter."""
g.run("key", "t", "tap")
time.sleep(0.5)
g.run("type", line)
g.run("key", "enter", "tap")
time.sleep(1)
def run(ctl, resources, out):
with Game(ctl, resources, out) as g:
g.run("options", "mods")
assert "Coordinates HUD 2.0.0: ON" in g.labels(), g.labels()
g.run("click", g.ids()["Settings"])
page = g.labels()
assert "Shown: ON" in page, page
g.run("click", g.ids()["Shown: ON"])
clicked = (g.home / DATA / "shown").read_text()
g.run("click", g.ids()["Shown: OFF"])
g.play()
g.run("command", "tp @s ~ ~ ~ 0 10")
time.sleep(2)
shown = g.shot("1-shown")
typed(g, "/coords")
hidden_file = (g.home / DATA / "shown").read_text()
time.sleep(4)
hidden = g.shot("2-hidden")
typed(g, "/coords")
typed(g, "/where")
where = g.shot("3-where")
results = {
"page": page,
"shown_file_after_click": clicked,
"shown_file_after_coords": hidden_file,
"shown_file_at_end": (g.home / DATA / "shown").read_text(),
"diff_top_left_hidden": round(difference(shown, hidden, TOP_LEFT), 1),
"diff_top_left_shown_again": round(difference(shown, where, TOP_LEFT), 1),
"diff_chat_where": round(difference(hidden, where, CHAT), 1),
}
print(json.dumps(results, indent=2))
assert clicked == "false" and hidden_file == "false", results
assert results["shown_file_at_end"] == "true", results
assert results["diff_top_left_hidden"] > 4, results
assert results["diff_top_left_shown_again"] < results["diff_top_left_hidden"], results
# native_chat lines are the client's; chat-log reads the server's chat here: see 3-where.png.
print(json.dumps({"proof": "coords_hud", "ok": True, **results}, indent=2))
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "horizon-mod-fps-overlay"
version = "2.0.0"
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "horizon-mod-fps-overlay"
version = "2.0.0"
edition = "2024"
license = "MIT"
publish = false
description = "Horizon's official FPS counter, a native mod: frames per second in a corner of the screen."
# The mod's logic and its tests: the same file the bundle adds to the game.
[lib]
path = "mixins/fps_overlay/runtime.rs"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Ludovic Marie and the Horizon contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+43
View File
@@ -0,0 +1,43 @@
# FPS Counter (Horizon native mod)
Horizon's official FPS counter: frames per second in a box in a corner of the screen, and, if you like, the slowest frame and the 1% low of the last 5 seconds. Its settings page (Options > Mods > FPS Counter > Settings) picks the corner and the lows.
It is a [horizon-mixins](https://github.com/horizon-rust/horizon-mixins) bundle (`mixins/fps_overlay/`) that the Horizon
launcher builds into the game. It replaces the WebAssembly FPS Counter 1.x and keeps its settings: the same files (`horizon-mods/.horizon-mod-data/fps_overlay/corner`, `lows`).
## Install
In the Horizon launcher: Mods → FPS Counter → Install, then Settings → Game → Native mods (experimental) → Build. The
launcher downloads its own Rust and builds the game with your native mods; see Horizon's
[native mods](https://github.com/horizon-rust/horizon/blob/main/docs/native-mods.md).
## Made on the hook points
The bundle hooks only Horizon's native hook points (`crates/horizon-client/src/game/native_hooks.rs`), which keep
their names and signatures across releases. `compatibility.json` names the version it needs (`"hooks": 1`), so the
launcher builds FPS Counter on any Horizon with that version or a later one, the player's own game by default, together with any other
mod. `horizon_revision` is only the commit it was last tested on.
| File | What it is |
|---|---|
| `mixins/fps_overlay/horizon-mixin.toml` | The bundle: one module and the HEAD hooks |
| `mixins/fps_overlay/runtime.rs` | The mod's logic and state, added to the game as `game::official_fps_overlay`; also this crate's library, for its tests |
| `mixins/fps_overlay/*.rs` | One small helper per hook |
| `../tools/native.py` | Shared by the native mods: apply on Horizon (from GitHub, by commit), check, build, package the `.hnmod`, run the proof |
| `proof.py` (with `../tools/game.py`) | The proof: the built game driven with `horizon-ctl --offscreen` on fresh, temporary data |
## Build and prove
```sh
(cd native/fps-overlay && cargo test) # the logic
python3 native/tools/native.py check fps-overlay # apply on the tested Horizon and cargo check the game
python3 native/tools/native.py check fps-overlay --horizon latest # ... on the newest Nightly
python3 native/tools/native.py check all # with the other native mods, as a player's set
python3 native/tools/native.py package fps-overlay # native/target/fps_overlay-<version>.hnmod for the registry
python3 native/tools/native.py proof fps-overlay --resources /path/to/.local/horizon-client/26.3
```
The proof needs resources prepared from your own Minecraft 26.3 (`horizon-prepare`); none are in this repository.
It seeds the WebAssembly mod's settings (bottom right, lows on) in a temporary game folder, then checks that Options > Mods shows them, and that the Corner button moves the box to the top left and writes the same file. CI runs `cargo test`, packages the `.hnmod` and checks the game on the tested commit, and every Monday on the newest Nightly.
License: MIT.
+8
View File
@@ -0,0 +1,8 @@
{
"game": "26.3",
"hooks": 1,
"horizon_repository": "https://github.com/horizon-rust/horizon",
"horizon_revision": "6f8c7a0e5d531d3834c4d0be4659a1db8e7e5c2a",
"horizon_mixins_repository": "https://github.com/horizon-rust/horizon-mixins",
"horizon_mixins_revision": "0d6b103c70633d8fd9443d8d0acd3aadaacedb06"
}
@@ -0,0 +1,13 @@
// A click on the mod's settings page: change the setting and keep it.
fn official_fps_overlay_click(game: &mut crate::game::Game<'_>, mod_id: &str, button: usize) {
use crate::game::official_fps_overlay as m;
if mod_id != m::ID {
return;
}
let mut settings = m::settings(|key| game.native_setting(m::ID, key));
settings.click(button);
m::set_settings(settings);
for (key, value) in settings.stored() {
game.native_set_setting(m::ID, key, &value);
}
}
@@ -0,0 +1,57 @@
[mod]
id = "fps_overlay"
version = "2.0.0"
priority = 100
# The commit it was tested on. It uses only the native hook points (hooks 1 in
# compatibility.json): the launcher builds it on any Horizon that has them.
[compatibility]
horizon_revision = "6f8c7a0e5d531d3834c4d0be4659a1db8e7e5c2a"
[[module]]
id = "runtime"
file = "crates/horizon-client/src/game.rs"
name = "official_fps_overlay"
source = "runtime.rs"
[[injection]]
id = "list"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_mods"
helper = "list.rs"
bindings = [
{ name = "mods", ty = "&mut Vec<crate::game::native_hooks::NativeMod>", value = "&mut *mods" },
]
[[injection]]
id = "settings"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_settings"
helper = "settings.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "mod_id", ty = "&str", value = "mod_id" },
{ name = "buttons", ty = "&mut Vec<String>", value = "&mut *buttons" },
]
[[injection]]
id = "click"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_settings_click"
helper = "click.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "mod_id", ty = "&str", value = "mod_id" },
{ name = "button", ty = "usize", value = "button" },
]
[[injection]]
id = "hud"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_hud"
helper = "hud.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "batch", ty = "&mut horizon_client::hud::HudBatch", value = "&mut *batch" },
{ name = "corners", ty = "&mut horizon_client::extras_hud::Corners", value = "&mut *corners" },
]
@@ -0,0 +1,27 @@
// The box in its corner, as the extra drew it; the debug screen takes the top
// left.
fn fps_hud(
game: &mut crate::game::Game<'_>,
batch: &mut horizon_client::hud::HudBatch,
corners: &mut horizon_client::extras_hud::Corners,
) {
use crate::game::official_fps_overlay as m;
// Turned off in Options > Mods: nothing until the next build leaves it out.
if !game.native_on(m::ID) {
return;
}
use horizon_client::extras::Corner;
let settings = {
let game = &*game;
m::settings(|key| game.native_setting(m::ID, key))
};
let corner = [Corner::TopLeft, Corner::TopRight, Corner::BottomLeft, Corner::BottomRight][settings.corner];
if !game.native_corner_shown(corner) {
return;
}
let text = settings.text(game.native_frame_rate());
let w = game.native_text_width(&text) + 4.0;
let (x, y) = corners.place(corner, w, 12.0);
game.native_fill(batch, [x, y, w, 12.0], m::BACKGROUND);
game.native_text(batch, &text, [x + 2.0, y + 2.0], m::INK, true);
}
@@ -0,0 +1,10 @@
// The mod's row in Options > Mods.
fn official_fps_overlay_listed(mods: &mut Vec<crate::game::native_hooks::NativeMod>) {
use crate::game::official_fps_overlay as m;
mods.push(crate::game::native_hooks::NativeMod {
id: m::ID.into(),
title: format!("{} {}", m::NAME, m::VERSION),
description: m::DESCRIPTION.into(),
keys: Vec::new(),
});
}
@@ -0,0 +1,133 @@
// Horizon's official FPS counter, formerly the WebAssembly mod and before
// that a built-in extra: the game's frame counter (frames in the last second,
// the slowest frame and the 1% low of the last 5 seconds) in a box in a
// corner, drawn as the extra drew it. Its settings are the WebAssembly mod's
// files (`horizon-mods/.horizon-mod-data/fps_overlay/corner`, `lows`), so a
// player keeps them. This file is both the mod's Cargo library (its tests)
// and the module the bundle adds to the game.
/// The mod's id (its settings folder), name and version.
pub const ID: &str = "fps_overlay";
pub const NAME: &str = "FPS Counter";
pub const VERSION: &str = "2.0.0";
pub const DESCRIPTION: &str = "Frames per second in a corner of the screen, with the slowest frame and the 1% low if you like. Horizon's own.";
/// The extra's box: translucent black, white text with a shadow.
pub const BACKGROUND: u32 = 0x7800_0000;
pub const INK: u32 = 0xFF_FFFF;
/// The corners, as the settings files name them and the page labels them.
pub const CORNERS: [(&str, &str); 4] = [
("top_left", "Top left"),
("top_right", "Top right"),
("bottom_left", "Bottom left"),
("bottom_right", "Bottom right"),
];
/// The settings, as the WebAssembly FPS Counter kept them.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Settings {
/// Index in `CORNERS`.
pub corner: usize,
/// Show the slowest frame and the 1% low too.
pub lows: bool,
}
impl Settings {
/// From the stored values; the extra's defaults (top left, no lows) for
/// the ones missing.
pub fn read(get: impl Fn(&str) -> Option<String>) -> Self {
let corner = get("corner")
.and_then(|c| CORNERS.iter().position(|k| k.0 == c.trim()))
.unwrap_or(0);
let lows = get("lows").is_some_and(|v| v.trim() == "true");
Self { corner, lows }
}
pub fn labels(&self) -> Vec<String> {
let on = |b: bool| if b { "ON" } else { "OFF" };
vec![
format!("Corner: {}", CORNERS[self.corner].1),
format!("Minimum and 1% low: {}", on(self.lows)),
]
}
pub fn click(&mut self, i: usize) {
match i {
0 => self.corner = (self.corner + 1) % CORNERS.len(),
1 => self.lows = !self.lows,
_ => {}
}
}
pub fn stored(&self) -> [(&'static str, String); 2] {
let lows = if self.lows { "true" } else { "false" };
[
("corner", CORNERS[self.corner].0.to_owned()),
("lows", lows.to_owned()),
]
}
/// The box's text for a reading (fps, min, 1% low).
pub fn text(&self, (fps, min, low): (u32, u32, u32)) -> String {
if self.lows {
format!("{fps} fps min {min} 1% {low}")
} else {
format!("{fps} fps")
}
}
}
thread_local! {
/// The settings, read once from the files, then kept.
static SETTINGS: std::cell::Cell<Option<Settings>> = const { std::cell::Cell::new(None) };
}
/// The settings, read once through `get` (one file per key).
pub fn settings(get: impl Fn(&str) -> Option<String>) -> Settings {
SETTINGS.with(|s| {
let found = s.get().unwrap_or_else(|| Settings::read(get));
s.set(Some(found));
found
})
}
/// Replaces the settings (the settings page changed them).
pub fn set_settings(settings: Settings) {
SETTINGS.with(|s| s.set(Some(settings)));
}
#[cfg(test)]
mod tests {
use super::*;
fn stored<'a>(pairs: &'a [(&'a str, &'a str)]) -> impl Fn(&str) -> Option<String> + 'a {
|key| pairs.iter().find(|p| p.0 == key).map(|p| p.1.to_owned())
}
#[test]
fn reads_and_writes_the_webassembly_mods_settings() {
let s = Settings::read(stored(&[]));
assert_eq!(
s,
Settings {
corner: 0,
lows: false
}
);
assert_eq!(s.text((60, 40, 30)), "60 fps");
let mut s = Settings::read(stored(&[("corner", "bottom_right\n"), ("lows", "true")]));
assert_eq!(
s,
Settings {
corner: 3,
lows: true
}
);
assert_eq!(s.text((60, 40, 30)), "60 fps min 40 1% 30");
assert_eq!(
s.labels(),
["Corner: Bottom right", "Minimum and 1% low: ON"]
);
s.click(0);
s.click(1);
assert_eq!(
s.stored(),
[("corner", "top_left".into()), ("lows", "false".into())]
);
}
}
@@ -0,0 +1,9 @@
// The mod's settings page: the WebAssembly mod's buttons.
fn official_fps_overlay_page(game: &mut crate::game::Game<'_>, mod_id: &str, buttons: &mut Vec<String>) {
use crate::game::official_fps_overlay as m;
if mod_id != m::ID {
return;
}
let game = &*game;
buttons.extend(m::settings(|key| game.native_setting(m::ID, key)).labels());
}
+39
View File
@@ -0,0 +1,39 @@
"""FPS Counter in a running modded game: the WebAssembly mod's settings files are read and written as they
were, and the frame counter shows in its corner and moves with the Corner setting."""
import json
import time
from game import Game, difference
DATA = "horizon-mods/.horizon-mod-data/fps_overlay"
# A player's settings from the WebAssembly mod.
FILES = {f"{DATA}/corner": "bottom_right", f"{DATA}/lows": "true"}
TOP_LEFT, OTHER = (0.0, 0.0, 0.25, 0.1), (0.75, 0.9, 1.0, 1.0)
def run(ctl, resources, out):
with Game(ctl, resources, out, FILES) as g:
g.run("options", "mods")
assert "FPS Counter 2.0.0: ON" in g.labels(), g.labels()
g.run("click", g.ids()["Settings"])
page = g.labels()
assert page[:2] == ["Corner: Bottom right", "Minimum and 1% low: ON"], page
g.shot("settings-page")
g.play()
g.run("command", "tp @s ~ ~ ~ 0 10")
time.sleep(2)
first = g.shot("1-bottom-right")
g.run("options", "mods")
g.run("click", g.ids()["Settings"])
g.run("click", g.ids()["Corner: Bottom right"])
moved = (g.home / DATA / "corner").read_text()
g.play()
time.sleep(2)
second = g.shot("2-top-left")
results = {
"corner_file_after_click": moved,
"diff_top_left": round(difference(first, second, TOP_LEFT), 1),
"diff_old_corner": round(difference(first, second, OTHER), 1),
}
assert moved == "top_left", results
assert results["diff_top_left"] > 4 and results["diff_old_corner"] > 4, results
print(json.dumps({"proof": "fps_overlay", "ok": True, **results}, indent=2))
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "horizon-mod-keystrokes"
version = "2.0.0"
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "horizon-mod-keystrokes"
version = "2.0.0"
edition = "2024"
license = "MIT"
publish = false
description = "Horizon's official Keystrokes, a native mod: movement keys, mouse buttons and space bar in a corner, lit while held."
# The mod's logic and its tests: the same file the bundle adds to the game.
[lib]
path = "mixins/keystrokes/runtime.rs"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Ludovic Marie and the Horizon contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+43
View File
@@ -0,0 +1,43 @@
# Keystrokes (Horizon native mod)
Horizon's official Keystrokes: your movement keys, mouse buttons and space bar in a corner, lit while held, with clicks per second. Its settings page (Options > Mods > Keystrokes > Settings) picks the corner and the clicks per second.
It is a [horizon-mixins](https://github.com/horizon-rust/horizon-mixins) bundle (`mixins/keystrokes/`) that the Horizon
launcher builds into the game. It replaces the WebAssembly Keystrokes 1.x and keeps its settings: the same files (`horizon-mods/.horizon-mod-data/keystrokes/corner`, `cps`).
## Install
In the Horizon launcher: Mods → Keystrokes → Install, then Settings → Game → Native mods (experimental) → Build. The
launcher downloads its own Rust and builds the game with your native mods; see Horizon's
[native mods](https://github.com/horizon-rust/horizon/blob/main/docs/native-mods.md).
## Made on the hook points
The bundle hooks only Horizon's native hook points (`crates/horizon-client/src/game/native_hooks.rs`), which keep
their names and signatures across releases. `compatibility.json` names the version it needs (`"hooks": 1`), so the
launcher builds Keystrokes on any Horizon with that version or a later one, the player's own game by default, together with any other
mod. `horizon_revision` is only the commit it was last tested on.
| File | What it is |
|---|---|
| `mixins/keystrokes/horizon-mixin.toml` | The bundle: one module and the HEAD hooks |
| `mixins/keystrokes/runtime.rs` | The mod's logic and state, added to the game as `game::official_keystrokes`; also this crate's library, for its tests |
| `mixins/keystrokes/*.rs` | One small helper per hook |
| `../tools/native.py` | Shared by the native mods: apply on Horizon (from GitHub, by commit), check, build, package the `.hnmod`, run the proof |
| `proof.py` (with `../tools/game.py`) | The proof: the built game driven with `horizon-ctl --offscreen` on fresh, temporary data |
## Build and prove
```sh
(cd native/keystrokes && cargo test) # the logic
python3 native/tools/native.py check keystrokes # apply on the tested Horizon and cargo check the game
python3 native/tools/native.py check keystrokes --horizon latest # ... on the newest Nightly
python3 native/tools/native.py check all # with the other native mods, as a player's set
python3 native/tools/native.py package keystrokes # native/target/keystrokes-<version>.hnmod for the registry
python3 native/tools/native.py proof keystrokes --resources /path/to/.local/horizon-client/26.3
```
The proof needs resources prepared from your own Minecraft 26.3 (`horizon-prepare`); none are in this repository.
It seeds the WebAssembly mod's settings (bottom right, no clicks per second) in a temporary game folder, then checks that Options > Mods shows them, and that the Corner button moves the keys to the top left and writes the same file. CI runs `cargo test`, packages the `.hnmod` and checks the game on the tested commit, and every Monday on the newest Nightly.
License: MIT.
+8
View File
@@ -0,0 +1,8 @@
{
"game": "26.3",
"hooks": 1,
"horizon_repository": "https://github.com/horizon-rust/horizon",
"horizon_revision": "6f8c7a0e5d531d3834c4d0be4659a1db8e7e5c2a",
"horizon_mixins_repository": "https://github.com/horizon-rust/horizon-mixins",
"horizon_mixins_revision": "0d6b103c70633d8fd9443d8d0acd3aadaacedb06"
}
@@ -0,0 +1,13 @@
// A click on the mod's settings page: change the setting and keep it.
fn official_keystrokes_click(game: &mut crate::game::Game<'_>, mod_id: &str, button: usize) {
use crate::game::official_keystrokes as m;
if mod_id != m::ID {
return;
}
let mut settings = m::settings(|key| game.native_setting(m::ID, key));
settings.click(button);
m::set_settings(settings);
for (key, value) in settings.stored() {
game.native_set_setting(m::ID, key, &value);
}
}
@@ -0,0 +1,57 @@
[mod]
id = "keystrokes"
version = "2.0.0"
priority = 100
# The commit it was tested on. It uses only the native hook points (hooks 1 in
# compatibility.json): the launcher builds it on any Horizon that has them.
[compatibility]
horizon_revision = "6f8c7a0e5d531d3834c4d0be4659a1db8e7e5c2a"
[[module]]
id = "runtime"
file = "crates/horizon-client/src/game.rs"
name = "official_keystrokes"
source = "runtime.rs"
[[injection]]
id = "list"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_mods"
helper = "list.rs"
bindings = [
{ name = "mods", ty = "&mut Vec<crate::game::native_hooks::NativeMod>", value = "&mut *mods" },
]
[[injection]]
id = "settings"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_settings"
helper = "settings.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "mod_id", ty = "&str", value = "mod_id" },
{ name = "buttons", ty = "&mut Vec<String>", value = "&mut *buttons" },
]
[[injection]]
id = "click"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_settings_click"
helper = "click.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "mod_id", ty = "&str", value = "mod_id" },
{ name = "button", ty = "usize", value = "button" },
]
[[injection]]
id = "hud"
kind = "HEAD"
target = "horizon_client::game::native_hooks::Game<'_>::native_hud"
helper = "hud.rs"
bindings = [
{ name = "game", ty = "&mut crate::game::Game<'_>", value = "&mut *self" },
{ name = "batch", ty = "&mut horizon_client::hud::HudBatch", value = "&mut *batch" },
{ name = "corners", ty = "&mut horizon_client::extras_hud::Corners", value = "&mut *corners" },
]
@@ -0,0 +1,43 @@
// The keys in their corner, as the extra drew them; the debug screen takes the
// top left.
fn keystrokes_hud(
game: &mut crate::game::Game<'_>,
batch: &mut horizon_client::hud::HudBatch,
corners: &mut horizon_client::extras_hud::Corners,
) {
use crate::game::official_keystrokes as m;
// Turned off in Options > Mods: nothing until the next build leaves it out.
if !game.native_on(m::ID) {
return;
}
use horizon_client::extras::Corner;
let settings = {
let game = &*game;
m::settings(|key| game.native_setting(m::ID, key))
};
let corner = [Corner::TopLeft, Corner::TopRight, Corner::BottomLeft, Corner::BottomRight][settings.corner];
if !game.native_corner_shown(corner) {
return;
}
let mut down = |name: &str| game.native_mapping(name).unwrap_or_default();
let view = m::View {
moves: m::MOVES.map(&mut down),
jump: down("key.jump").0,
attack: down("key.attack").0,
use_: down("key.use").0,
cps: None,
};
let view = m::View {
cps: settings.cps.then(|| game.native_clicks()),
..view
};
let (w, h) = m::SIZE;
let (x, y) = corners.place(corner, w, h);
let game = &*game;
for shape in m::shapes(&view, &|text| game.native_text_width(text)) {
match shape {
m::Shape::Fill([dx, dy, w, h], argb) => game.native_fill(batch, [x + dx, y + dy, w, h], argb),
m::Shape::Text(text, [dx, dy], rgb, shadow) => game.native_text(batch, &text, [x + dx, y + dy], rgb, shadow),
}
}
}
@@ -0,0 +1,10 @@
// The mod's row in Options > Mods.
fn official_keystrokes_listed(mods: &mut Vec<crate::game::native_hooks::NativeMod>) {
use crate::game::official_keystrokes as m;
mods.push(crate::game::native_hooks::NativeMod {
id: m::ID.into(),
title: format!("{} {}", m::NAME, m::VERSION),
description: m::DESCRIPTION.into(),
keys: Vec::new(),
});
}
@@ -0,0 +1,229 @@
// Horizon's official Keystrokes, formerly the WebAssembly mod and before that
// a built-in extra: the movement keys, the mouse buttons and the space bar in
// a corner, lit while held, with clicks per second, drawn as the extra drew
// them. Its settings are the WebAssembly mod's files
// (`horizon-mods/.horizon-mod-data/keystrokes/corner`, `cps`), so a player
// keeps them. This file is both the mod's Cargo library (its tests) and the
// module the bundle adds to the game.
/// The mod's id (its settings folder), name and version.
pub const ID: &str = "keystrokes";
pub const NAME: &str = "Keystrokes";
pub const VERSION: &str = "2.0.0";
pub const DESCRIPTION: &str = "Your movement keys, mouse buttons and space bar in a corner, lit while held, with clicks per second. Horizon's own.";
/// The extra's colours: a key up is translucent black with white text, a key
/// held is light with dark text.
pub const BACKGROUND: u32 = 0x7800_0000;
pub const HELD: u32 = 0xB0FF_FFFF;
pub const INK_HELD: u32 = 0x14_1414;
pub const INK: u32 = 0xFF_FFFF;
/// A key's side and the gap between keys, in GUI pixels.
const KEY: f32 = 22.0;
const GAP: f32 = 2.0;
/// The mappings of the top two rows, in the order they are laid out.
pub const MOVES: [&str; 4] = ["key.forward", "key.left", "key.back", "key.right"];
/// The corners, as the settings files name them and the page labels them.
pub const CORNERS: [(&str, &str); 4] = [
("top_left", "Top left"),
("top_right", "Top right"),
("bottom_left", "Bottom left"),
("bottom_right", "Bottom right"),
];
/// The settings, as the WebAssembly Keystrokes kept them.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Settings {
/// Index in `CORNERS`.
pub corner: usize,
/// Clicks per second under the mouse buttons.
pub cps: bool,
}
impl Settings {
/// From the stored values; the extra's defaults (top left, clicks per
/// second shown) for the ones missing.
pub fn read(get: impl Fn(&str) -> Option<String>) -> Self {
let corner = get("corner")
.and_then(|c| CORNERS.iter().position(|k| k.0 == c.trim()))
.unwrap_or(0);
let cps = get("cps").is_none_or(|v| v.trim() != "false");
Self { corner, cps }
}
pub fn labels(&self) -> Vec<String> {
let on = |b: bool| if b { "ON" } else { "OFF" };
vec![
format!("Corner: {}", CORNERS[self.corner].1),
format!("Clicks per second: {}", on(self.cps)),
]
}
pub fn click(&mut self, i: usize) {
match i {
0 => self.corner = (self.corner + 1) % CORNERS.len(),
1 => self.cps = !self.cps,
_ => {}
}
}
pub fn stored(&self) -> [(&'static str, String); 2] {
let cps = if self.cps { "true" } else { "false" };
[
("corner", CORNERS[self.corner].0.to_owned()),
("cps", cps.to_owned()),
]
}
}
/// What the box shows: each movement key (held, label), the space bar, the
/// mouse buttons, and the clicks per second when shown.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct View {
pub moves: [(bool, String); 4],
pub jump: bool,
pub attack: bool,
pub use_: bool,
pub cps: Option<(u32, u32)>,
}
/// One thing to draw, relative to the box's top left corner.
#[derive(Clone, Debug, PartialEq)]
pub enum Shape {
/// x, y, w, h and argb.
Fill([f32; 4], u32),
/// The text, its x and y, rgb, and a shadow.
Text(String, [f32; 2], u32, bool),
}
/// The box's size.
pub const SIZE: (f32, f32) = (3.0 * KEY + 2.0 * GAP, 3.0 * KEY + 3.0 * GAP + 10.0);
/// A key's box with its lines centred; `width` measures text.
fn key_box(
out: &mut Vec<Shape>,
[x, y, w, h]: [f32; 4],
held: bool,
lines: &[&str],
width: &dyn Fn(&str) -> f32,
) {
let (fill, ink) = if held {
(HELD, INK_HELD)
} else {
(BACKGROUND, INK)
};
out.push(Shape::Fill([x, y, w, h], fill));
let total = lines.len() as f32 * 9.0 - 1.0;
for (i, line) in lines.iter().enumerate() {
let at = [
(x + (w - width(line)) / 2.0).round(),
(y + (h - total) / 2.0).round() + i as f32 * 9.0,
];
out.push(Shape::Text((*line).to_owned(), at, ink, !held));
}
}
/// The box, as the extra laid it out: W, then A S D, then the mouse buttons
/// with their clicks per second, then the space bar.
pub fn shapes(v: &View, width: &dyn Fn(&str) -> f32) -> Vec<Shape> {
let (w, _) = SIZE;
let step = KEY + GAP;
let mut out = Vec::with_capacity(24);
let [forward, left, back, right] = &v.moves;
key_box(
&mut out,
[step, 0.0, KEY, KEY],
forward.0,
&[&forward.1],
width,
);
for (i, key) in [left, back, right].into_iter().enumerate() {
key_box(
&mut out,
[i as f32 * step, step, KEY, KEY],
key.0,
&[&key.1],
width,
);
}
let mw = (w - GAP) / 2.0;
let my = 2.0 * step;
let buttons = [
(v.attack, "LMB", v.cps.map(|c| c.0)),
(v.use_, "RMB", v.cps.map(|c| c.1)),
];
for (i, (held, label, cps)) in buttons.into_iter().enumerate() {
let rate = cps.map(|n| format!("{n} CPS"));
let lines: Vec<&str> = std::iter::once(label).chain(rate.as_deref()).collect();
key_box(
&mut out,
[i as f32 * (mw + GAP), my, mw, KEY],
held,
&lines,
width,
);
}
let sy = my + step;
key_box(&mut out, [0.0, sy, w, 10.0], v.jump, &[], width);
let bar = 0xFF00_0000 | if v.jump { INK_HELD } else { INK };
out.push(Shape::Fill([w / 2.0 - 12.0, sy + 4.0, 24.0, 2.0], bar));
out
}
thread_local! {
/// The settings, read once from the files, then kept.
static SETTINGS: std::cell::Cell<Option<Settings>> = const { std::cell::Cell::new(None) };
}
/// The settings, read once through `get` (one file per key).
pub fn settings(get: impl Fn(&str) -> Option<String>) -> Settings {
SETTINGS.with(|s| {
let found = s.get().unwrap_or_else(|| Settings::read(get));
s.set(Some(found));
found
})
}
/// Replaces the settings (the settings page changed them).
pub fn set_settings(settings: Settings) {
SETTINGS.with(|s| s.set(Some(settings)));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads_the_webassembly_mods_settings() {
let none = |_: &str| None;
assert_eq!(
Settings::read(none),
Settings {
corner: 0,
cps: true
}
);
let get = |k: &str| (k == "cps").then(|| "false".to_owned());
let mut s = Settings::read(get);
assert!(!s.cps);
s.click(0);
assert_eq!(s.labels(), ["Corner: Top right", "Clicks per second: OFF"]);
assert_eq!(s.stored()[0], ("corner", "top_right".to_owned()));
}
#[test]
fn lays_the_keys_out_as_the_extra_did() {
let mut v = View {
moves: ["W", "A", "S", "D"].map(|k| (false, k.to_owned())),
cps: Some((3, 0)),
..View::default()
};
v.moves[0].0 = true;
let width = |t: &str| t.len() as f32 * 6.0;
let out = shapes(&v, &width);
// W lit: light fill, dark text with no shadow, centred in its key.
assert_eq!(out[0], Shape::Fill([24.0, 0.0, 22.0, 22.0], HELD));
assert_eq!(
out[1],
Shape::Text("W".into(), [32.0, 7.0], INK_HELD, false)
);
assert!(out.contains(&Shape::Text("3 CPS".into(), [2.0, 60.0], INK, true)));
assert_eq!(SIZE, (70.0, 82.0));
}
}
@@ -0,0 +1,9 @@
// The mod's settings page: the WebAssembly mod's buttons.
fn official_keystrokes_page(game: &mut crate::game::Game<'_>, mod_id: &str, buttons: &mut Vec<String>) {
use crate::game::official_keystrokes as m;
if mod_id != m::ID {
return;
}
let game = &*game;
buttons.extend(m::settings(|key| game.native_setting(m::ID, key)).labels());
}
+39
View File
@@ -0,0 +1,39 @@
"""Keystrokes in a running modded game: the WebAssembly mod's settings files are read and written as they
were, and the key box shows in its corner and moves with the Corner setting."""
import json
import time
from game import Game, difference
DATA = "horizon-mods/.horizon-mod-data/keystrokes"
# A player's settings from the WebAssembly mod.
FILES = {f"{DATA}/corner": "bottom_right", f"{DATA}/cps": "false"}
TOP_LEFT, OTHER = (0.0, 0.0, 0.25, 0.3), (0.8, 0.6, 1.0, 1.0)
def run(ctl, resources, out):
with Game(ctl, resources, out, FILES) as g:
g.run("options", "mods")
assert "Keystrokes 2.0.0: ON" in g.labels(), g.labels()
g.run("click", g.ids()["Settings"])
page = g.labels()
assert page[:2] == ["Corner: Bottom right", "Clicks per second: OFF"], page
g.shot("settings-page")
g.play()
g.run("command", "tp @s ~ ~ ~ 0 10")
time.sleep(2)
first = g.shot("1-bottom-right")
g.run("options", "mods")
g.run("click", g.ids()["Settings"])
g.run("click", g.ids()["Corner: Bottom right"])
moved = (g.home / DATA / "corner").read_text()
g.play()
time.sleep(2)
second = g.shot("2-top-left")
results = {
"corner_file_after_click": moved,
"diff_top_left": round(difference(first, second, TOP_LEFT), 1),
"diff_old_corner": round(difference(first, second, OTHER), 1),
}
assert moved == "top_left", results
assert results["diff_top_left"] > 4 and results["diff_old_corner"] > 4, results
print(json.dumps({"proof": "keystrokes", "ok": True, **results}, indent=2))
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "horizon-mod-minimap"
version = "2.0.0"
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "horizon-mod-minimap"
version = "2.0.0"
edition = "2024"
license = "MIT"
publish = false
description = "Horizon's official Minimap, a native mod: the terrain around you in a corner, in map colours."
# The mod's logic and its tests: the same file the bundle adds to the game.
[lib]
path = "mixins/minimap/runtime.rs"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Ludovic Marie and the Horizon contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loaded 100 of 173 files, more files were not shown because too many files have changed in this diff. Show more