Native mods
Behaviour that needs code (a new key, a HUD, a tool that changes how mining works) is a native mod. It is one flat Rust project with horizon-mod.toml, Cargo.toml and src/, beside its assets/ and data/. Start with cargo horizon-mod new my_mod --template native and implement the typed hooks; raw injections are a rare escape hatch (Injections). How the launcher builds one, what it pins and why it is not sandboxed: native mods. The typed hook API is generated automatically at build time, in debug and release builds alike; its generated Rust files are build output, not files to regenerate or commit. Engine contributors adding a hook only run cargo run -p xtask -- mod-api to fill its hand-written placeholder body (Adding a hook).
From the modding book of arismoko/horizonbfbda6a
A native mod's config files go in the game's shared config/ folder, beside options.txt (on a dedicated server, beside server.properties), as Fabric and NeoForge mods' do: the mod picks its own file names, such as config/<mod id>.json or a folder config/<mod id>/. It reads and writes them with Host::config_files(), which is bound to the calling mod and refuses any other mod's files. A mod that lists its files with native_register_configs gets them read for it, written from their defaults when missing, and handed over again when edited (native_config_loading, native_config_reloading, as NeoForge's ModConfigEvent), and a world can keep its own server config in serverconfig/ (native mods). Settings kept with set_setting stay in horizon-mods/.horizon-mod-data/<id>/. setting and set_setting are bound to the calling mod and its registered keys: naming another mod's ID or an unregistered key never reads or changes a file. Typed reads cache values by data folder and key; a successful set_setting invalidates that key, while a failed write leaves the cached value unchanged. Raw setting reads access the file. Register typed settings with Registrar::register_setting to put them on Options > Mods; edits persist before setting_changed runs, and only the owning mod receives that event. 8c838a6b381b93150deaeed2057acd1
For a client HUD's session information, use the read-only ClientHost::client_session() service and the local PlayerRef's block_breaking and ambient_mood reads rather than raw injections. Missing sessions, external display names and memory addresses remain explicit optional values; all counters stay local. See client session and local-player status for their exact availability and frame-time semantics.
native_compute_fov_modifier(player, base, modifier) runs on the client each tick for the local player's FOV scale, after vanilla effects and before zoom and camera easing. player contains the calculation inputs and base is the original scale. Non-finite modifiers or values outside 0.01..=10 fall back to base. It does not affect a spectator camera on a mob.
The client hook native_render_name_tag(entity_id, kind, vanilla_visible, visible, text) runs at player and mob, armor stand, and item frame name decisions before shared geometry. Set visible to override vanilla and text to Some literal replacement text (NeoForge RenderNameTagEvent). Distance and entity availability still apply; the local player's tag is considered only when its own camera and settings gates allow it.
Native mods are Rust projects that implement the game's typed hook API (impl Hooks). Start with cargo horizon-mod new my_mod --template native (Trait mods). Each project has one horizon-mod.toml, with exact build pins in [native], and ships as one .hmod. For the rare case the API does not cover, internals injections use compiled raw slots only; unlisted targets are refused. Use mixins for supported native source edits. See Injections. An injection is an escape hatch; request a typed hook for the behavior it needs.
Native mods are native code with the player's rights, not sandboxed. The first install shows a trust notice, and every build waits behind a red warning; the registry lists one only after a review (its later releases are checked by machine). They play in a game of their own, built for the instance they are installed in.
If starting a managed native client directly reports unregistered cache directories, use Play from the launcher with the same data directory. Select the intended Horizon instance and installed build, or run horizon-launcher play --instance <instance> --version <tag>; horizon-launcher versions --installed lists installed tags. Without --version, an instance's pin or the launcher's selection may choose another build. A Cargo target/ executable is not itself an installed version: use the published native build for that instance instead. Ordinary Play requires a signed-in account that owns Minecraft: Java Edition. For an unsigned developer installation with persisted developer provenance, HORIZON_LAUNCHER_DEVELOPER_OFFLINE=1 enables account-free, single-player-only Play if the installed game advertises --developer-single-player through --launch-api; rebuild and reinstall older developer games and their native mods. Signed builds and their native descendants still require the account. See launcher commands for the offline restrictions. Starting the launcher or asking for --help alone does not register the directories. Standalone developer builds with no launcher registry among the executable's ancestors can still be run directly.
The game's Mods screen only lists code mods (it has no switch). A code mod's settings page, when it has one, opens from the Config button. The launcher decides which mods are on. A browser install that finishes after a world opens waits until the world closes before activating its content.
Villager handles preserve the full signed integer level (at least 1), including values above the five vanilla trade tiers. Higher levels use the master badge and do not gain further vanilla trades.
Measuring native mod calls
An administrator with permission level 3 can run /modprofile start, exercise the mod, then run /modprofile stop. Profiling is off by default. Starting twice or stopping without an active session reports an error. /modprofile report prints the last completed summary without writing a file or stopping an active session. Feedback goes only to the sender. The same controls work through horizon-ctl command modprofile start (and stop or report).
Stop saves one local, atomically written profiles/modprofile-<timestamp>-<counter>.json under the game's folder. The export is bounded to 8 MiB; a save failure is reported and the stopped summary remains available. Load the file locally in Firefox Profiler's Marker Table; Horizon never uploads it. The raw Gecko version 37 markers contain mod/hook/Part identities, counts, exact nanosecond timings and log2 histograms, never arguments or player/world data. Timings are inclusive: an outer call includes nested calls. These are process-wide aggregates, not invocation traces or sampling shares. This first command release measures native guarded calls only; wasm timing and a dedicated profiling guide follow separately.
Chapters
- Server services and player handles
- Typed server commands
- Observing advancement progress
- Filtering toast notifications
- Native mod source revisions
- Runtime packages and identity
- A bundle
- Dimension shape
- Raid handles
- Tool transformation previews
- Named hook points
- State flags on the living handles
- Client effect interpolation
- The build's warnings
- A mod's own ingredient kinds, matching at craft time
- The mod's crate
- Conditional data
- Reading recipe rules
- Recipe filters
- A mod's own enchantment effect kinds
- A mod's own consume effects
- A mod's own loot functions and conditions
- Recipe book category declarations (first slice of #5501)
- A mod's own ingredient kinds
- Structure types
- Fluid ingredients
- Data files
- What the launcher does
- Build log
- Rebuilds stay on the instance's Horizon
- Install from GitHub
- Cache and disk
- Trust
- Multiplayer
- Standalone server export
- Developer acceptance workflow
- Level sounds
- Client particles and movement queries
- Extending block entity validity
- Typed bulk block edits
- Refusing dimension travel
- A local unsigned developer base
- Reading data maps
- Machine-readable SDK catalog
- Server item particles
- Registering cauldron interactions
- Dropped items during block-entity ticks
- Level block notifications and cracks
- Measuring styled text outside drawing
Edit this page on GitHub · Spotted something wrong? Open an issue .