From 3de52c252cda1558ca88faefdff9ec7317c95a07 Mon Sep 17 00:00:00 2001 From: chexit Date: Sat, 31 Oct 2026 03:20:06 +0300 Subject: [PATCH] game.world-seed (API 25) Co-Authored-By: Claude Opus 5.5 --- crates/horizon-client/src/game/mods.rs | 3 +++ crates/horizon-mod-sdk/wit/horizon.wit | 4 ++++ crates/horizon-mods/src/api_level.rs | 6 ++++-- crates/horizon-mods/src/runtime.rs | 5 +++++ docs/api/mods-reference.md | 1 + 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/horizon-client/src/game/mods.rs b/crates/horizon-client/src/game/mods.rs index 7f9714a18..e96744775 100644 --- a/crates/horizon-client/src/game/mods.rs +++ b/crates/horizon-client/src/game/mods.rs @@ -599,6 +599,9 @@ impl Game<'_> { .is_some() .then(|| self.mods.world_id.clone()) .flatten(), + world_seed: (self.player.is_some() + && self.mods.world_id.as_deref().is_some_and(|w| w.starts_with("world:"))) + .then_some(self.level.seed), } } /// The first block along the player's view within diff --git a/crates/horizon-mod-sdk/wit/horizon.wit b/crates/horizon-mod-sdk/wit/horizon.wit index 805c2a515..774cc5851 100644 --- a/crates/horizon-mod-sdk/wit/horizon.wit +++ b/crates/horizon-mod-sdk/wit/horizon.wit @@ -51,6 +51,10 @@ interface game { /// world, `server:
` on a server; none on the title screen. /// Mods keep their state apart by it. world-id: func() -> option; + /// The open saved world's seed (AirDef fork, API level 25): with the + /// folder name it tells a new world from an old one that had the same + /// name. None on the title screen and on a server. + world-seed: func() -> option; /// A block along the player's view. record hit { /// The block hit. diff --git a/crates/horizon-mods/src/api_level.rs b/crates/horizon-mods/src/api_level.rs index 579af9e25..3af36e7c1 100644 --- a/crates/horizon-mods/src/api_level.rs +++ b/crates/horizon-mods/src/api_level.rs @@ -7,13 +7,13 @@ //! version's level so the launcher can tell before it installs. /// The newest level this Horizon serves. -pub const LEVEL: u32 = 24; +pub const LEVEL: u32 = 25; /// Every function the game offers mods, as `interface.function`, with its /// level. Level 0 is the contract of Horizon 0.2.67 to 0.2.69. A new /// function takes the next level and raises [`LEVEL`] (the `api_reference` /// test fails until it is listed here). -pub const FUNCTIONS: [(&str, u32); 90] = [ +pub const FUNCTIONS: [(&str, u32); 91] = [ ("log.write", 0), ("game.get-player", 0), ("game.get-clock", 0), @@ -118,6 +118,8 @@ pub const FUNCTIONS: [(&str, u32); 90] = [ ("render.set-canvas", 24), ("render.canvas-events", 24), ("render.canvas-open", 24), + // AirDef fork: the open world's seed, so a new world is not an old one of the same name. + ("game.world-seed", 25), ]; /// What the game's program carries so the launcher can read its level diff --git a/crates/horizon-mods/src/runtime.rs b/crates/horizon-mods/src/runtime.rs index 81286975f..910986549 100644 --- a/crates/horizon-mods/src/runtime.rs +++ b/crates/horizon-mods/src/runtime.rs @@ -189,6 +189,8 @@ pub struct Snapshot { pub creative: bool, /// The open world (`game.world-id`, AirDef fork). pub world: Option, + /// The open saved world's seed (`game.world-seed`, AirDef fork). + pub world_seed: Option, } /// Capability `recipes`'s tables: `items` in the registry's order, which @@ -1930,6 +1932,9 @@ impl game::Host for State { fn world_id(&mut self) -> Option { self.snapshot.world.clone() } + fn world_seed(&mut self) -> Option { + self.snapshot.world_seed + } fn raycast(&mut self, distance: f32) -> Option { let (at, hit) = self.snapshot.view_hit.as_ref()?; // NaN compares false: no hit. diff --git a/docs/api/mods-reference.md b/docs/api/mods-reference.md index cf6e61440..3ab23b490 100644 --- a/docs/api/mods-reference.md +++ b/docs/api/mods-reference.md @@ -126,6 +126,7 @@ A block along the player's view. | `get-player` | `func() -> option` | None on the title screen. | | `get-clock` | `func() -> option` | None on the title screen. | | `world-id` | `func() -> option` | Which world is open (AirDef fork): `world:` for a saved world, `server:
` on a server; none on the title screen. Mods keep their state apart by it. | +| `world-seed` | `func() -> option` | The open saved world's seed (AirDef fork, API level 25): with the folder name it tells a new world from an old one that had the same name. None on the title screen and on a server. | | `raycast` | `func(distance: f32) -> option` | The first block along the player's view (fluids pass), up to `distance` blocks (at most 1024; past the loaded chunks, across the distant terrain when LOD Distance is on), as of this tick. None when nothing is hit, and on the title screen. | ## `chat`