mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 02:56:17 -10:00
Merge pull request #7564 from levzzz5154/modapi/level-world
Native mods: a level's seed, difficulty, game rules and weather through LevelRef (#7387)
This commit is contained in:
6 files changed
+114
-1
No files matched your search
@@ -252,6 +252,7 @@ mod native_item_container;
|
||||
mod native_item_energy;
|
||||
mod native_item_fluids;
|
||||
mod native_level_refs;
|
||||
mod native_level_world;
|
||||
mod native_lifecycle;
|
||||
mod native_living_refs;
|
||||
mod native_machines;
|
||||
|
||||
@@ -9,7 +9,7 @@ use horizon_client::world::World;
|
||||
|
||||
impl Game<'_> {
|
||||
/// The world a handle names now, or None for a stale or foreign one.
|
||||
fn native_level_world(&self, level: LevelRef) -> Option<&World> {
|
||||
pub(super) fn native_level_world(&self, level: LevelRef) -> Option<&World> {
|
||||
if self.remote.is_some() || self.native_lifecycle.epoch() != Some(level.epoch()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
//! A level's world settings for native server mods, through a `LevelRef`
|
||||
//! (docs/native-mods.md, "Entity and level handles", #7387): the seed, the
|
||||
//! difficulty, game rules and weather. They return `None` on a client or for
|
||||
//! a handle of an earlier open.
|
||||
use super::Game;
|
||||
use horizon_client::command::vanilla::DIFFICULTIES;
|
||||
use horizon_client::mod_api::{GameRuleValue, LevelRef};
|
||||
|
||||
/// `Level.isRaining` and `isThundering` from the rain and thunder levels.
|
||||
fn weather_flags((rain, thunder): (f32, f32)) -> (bool, bool) {
|
||||
(rain > 0.2, thunder > 0.9)
|
||||
}
|
||||
|
||||
impl Game<'_> {
|
||||
/// The world's seed (`WorldOptions.seed`), as `/seed` shows it. API
|
||||
/// @API:level_world@.
|
||||
pub(super) fn native_level_seed(&self, level: LevelRef) -> Option<i64> {
|
||||
self.native_level_world(level)?;
|
||||
Some(self.level.seed)
|
||||
}
|
||||
/// The difficulty (`Level.getDifficulty`): `peaceful`, `easy`, `normal`
|
||||
/// or `hard`, as `/difficulty` names it. API @API:level_world@.
|
||||
pub(super) fn native_level_difficulty(&self, level: LevelRef) -> Option<&'static str> {
|
||||
self.native_level_world(level)?;
|
||||
DIFFICULTIES.get(usize::from(self.ui.difficulty)).copied()
|
||||
}
|
||||
/// Game rule `rule` (`minecraft:keep_inventory` or `keep_inventory`), as
|
||||
/// `/gamerule` queries it: None for an unknown rule. API @API:level_world@.
|
||||
pub(super) fn native_level_game_rule(
|
||||
&self,
|
||||
level: LevelRef,
|
||||
rule: &str,
|
||||
) -> Option<GameRuleValue> {
|
||||
self.native_level_world(level)?;
|
||||
let r = horizon_client::gamerules::rule(rule)?;
|
||||
self.ui.rules.get(r.name)
|
||||
}
|
||||
/// The rain and thunder levels, each 0.0 to 1.0 (`Level.getRainLevel`,
|
||||
/// `getThunderLevel`): both 0 in a level with no weather, as the Nether
|
||||
/// and the End. API @API:level_world@.
|
||||
pub(super) fn native_level_weather(&self, level: LevelRef) -> Option<(f32, f32)> {
|
||||
let world = self.native_level_world(level)?;
|
||||
if level.dimension() != super::Dim::Overworld.id() {
|
||||
return Some((0.0, 0.0));
|
||||
}
|
||||
Some(world.weather.levels(1.0))
|
||||
}
|
||||
/// Whether it rains (`Level.isRaining`: a rain level above 0.2). API
|
||||
/// @API:level_world@.
|
||||
pub(super) fn native_level_is_raining(&self, level: LevelRef) -> Option<bool> {
|
||||
Some(weather_flags(self.native_level_weather(level)?).0)
|
||||
}
|
||||
/// Whether it thunders (`Level.isThundering`: a thunder level above 0.9).
|
||||
/// API @API:level_world@.
|
||||
pub(super) fn native_level_is_thundering(&self, level: LevelRef) -> Option<bool> {
|
||||
Some(weather_flags(self.native_level_weather(level)?).1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn rain_and_thunder_start_above_vanillas_thresholds() {
|
||||
assert_eq!(super::weather_flags((0.2, 0.0)), (false, false));
|
||||
assert_eq!(super::weather_flags((0.21, 0.9)), (true, false));
|
||||
assert_eq!(super::weather_flags((1.0, 0.95)), (true, true));
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ pub use crate::game::{
|
||||
NativePlayer, NativePlayerRender, NativeReloadListener, NativeShake, NativeStructureBlock,
|
||||
NativeStructureTemplate, NativeWorldDraw,
|
||||
};
|
||||
pub use crate::gamerules::Value as GameRuleValue;
|
||||
pub use crate::gfx::native_render::{
|
||||
BlockSpriteArrays, RenderHooks, RenderNeeds, RenderNeedsEvent, WorldDrawn, WorldDrawnEvent,
|
||||
wgpu,
|
||||
|
||||
@@ -3711,6 +3711,9 @@ a dimension.
|
||||
- `level.block_state(host, pos)` (`minecraft:oak_stairs[facing=east,…]`, as `/setblock` takes it), `block` (the name
|
||||
alone), `block_property(host, pos, key)`, `is_loaded`, `height` (lowest `y` and height), `game_time` and `day_time`
|
||||
read any level the server has loaded, not only the one in place. A block in a chunk that isn't loaded reads as `None`.
|
||||
- `level.seed(host)`, `difficulty` (`"normal"`, as `/difficulty` names it), `game_rule(host, "keep_inventory")` (a
|
||||
`GameRuleValue`, `Bool` or `Int`; None for an unknown rule), `weather` (rain and thunder levels, 0.0 to 1.0; both 0
|
||||
where the level has no weather), `is_raining` and `is_thundering` read its world settings.
|
||||
- `host.living(entity)` gives a `LivingRef` for a mob, a player, an armor stand or the dragon (vanilla's
|
||||
`LivingEntity`); `living.entity()` goes back. `living.equipment(host, EquipmentSlot::Head)` and `hand_item(host,
|
||||
Hand::Main)` read its equipment (an empty slot reads as `None`); `set_equipment(host, slot, stack)` puts a stack in a
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"hooks": [],
|
||||
"helpers": [
|
||||
{
|
||||
"name": "level_seed",
|
||||
"target": "horizon_client::game::native_level_world::Game<'_>::native_level_seed",
|
||||
"receiver": "level"
|
||||
},
|
||||
{
|
||||
"name": "level_difficulty",
|
||||
"target": "horizon_client::game::native_level_world::Game<'_>::native_level_difficulty",
|
||||
"receiver": "level"
|
||||
},
|
||||
{
|
||||
"name": "level_game_rule",
|
||||
"target": "horizon_client::game::native_level_world::Game<'_>::native_level_game_rule",
|
||||
"receiver": "level"
|
||||
},
|
||||
{
|
||||
"name": "level_weather",
|
||||
"target": "horizon_client::game::native_level_world::Game<'_>::native_level_weather",
|
||||
"receiver": "level"
|
||||
},
|
||||
{
|
||||
"name": "level_is_raining",
|
||||
"target": "horizon_client::game::native_level_world::Game<'_>::native_level_is_raining",
|
||||
"receiver": "level"
|
||||
},
|
||||
{
|
||||
"name": "level_is_thundering",
|
||||
"target": "horizon_client::game::native_level_world::Game<'_>::native_level_is_thundering",
|
||||
"receiver": "level"
|
||||
}
|
||||
],
|
||||
"rust_summary": "a level's world settings through a `LevelRef`: the seed, the difficulty, game rules and weather.",
|
||||
"doc_summary": "`level.seed(host)`, `difficulty`, `game_rule(host, name)` (`GameRuleValue`), `weather`, `is_raining` and `is_thundering`. Bundles need \"hooks\": @API:level_world@.",
|
||||
"comments": [
|
||||
"crates/horizon-client/src/game/native_level_world.rs"
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user