Merge pull request #11978 from horizon-rust/split/using-item

🤖 Agent: pure move of 237 lines (an item in use, tick by tick) to game/using_item.rs; apply_consume_effect and drop_bundle_content stay in game.rs because an open draft changes them.
This commit is contained in:
Ludovic Marie authored and GitHub committed 2026-11-07 20:03:38 +01:00
commit fdc87aecdb
7 files changed
+255 -245

No files matched your search

+1 -1
View File
@@ -159,7 +159,7 @@ state stays separate from the library tests. This compiles gameplay twice for ex
| Block behaviour | `crates/horizon-world/src/level/` (`mod.rs`, `tick.rs`, `hooks.rs`, `redstone.rs`, `place.rs`, `interact.rs`); game.rs `fn tick_blocks`, `fn process_level_events` |
| Mobs | L: `mobs.rs` (types), `mobs/` (`ai.rs`, `goals.rs`, `brain.rs`, `*_brain.rs`, `nav.rs`, `path.rs`, `species.rs`, `spawning.rs`, `render.rs`, `anim.rs`); G: `creatures.rs` (ticking, spawning, mob events), one file per mob family (`dragon.rs`, `wither.rs`, `villagers.rs`, `piglins.rs`, `golems.rs`...), `natural_spawning.rs` |
| Other entities | L: `entities.rs`, `projectiles.rs`, `arrows.rs`; G: `boats.rs`, `minecarts.rs`, `mounts.rs`, `armor_stands.rs`, `paintings.rs`, `frames.rs`, `fireworks.rs`, `tnt.rs`, `explosion.rs` |
| Player actions | game.rs (`fn use_hand`, `fn use_item`, `fn attack`, `fn tick_mining`, `fn break_block`); G: `hand.rs`, `combat.rs`, `consume.rs`, `potions.rs`, `shield.rs`, `crossbow.rs`, `trident.rs`, `fishing.rs`; L: `motion.rs` + `motion/` (movement), `collisions.rs`; crate `horizon-items`: `items.rs`, `item_data.rs`, `enchant.rs`, `blocks.rs` |
| Player actions | game.rs (`fn use_hand`, `fn use_item`, `fn attack`, `fn tick_mining`, `fn break_block`); G: `hand.rs`, `combat.rs`, `consume.rs`, `using_item.rs` (an item in use, tick by tick), `potions.rs`, `shield.rs`, `crossbow.rs`, `trident.rs`, `fishing.rs`; L: `motion.rs` + `motion/` (movement), `collisions.rs`; crate `horizon-items`: `items.rs`, `item_data.rs`, `enchant.rs`, `blocks.rs` |
| Inventory and containers | L: `menu.rs` (`AbstractContainerMenu.clicked`), `container_screen.rs`, `containers.rs`, `creative.rs`, `crafting.rs`, `recipe_book*.rs`, `stations.rs`, `merchant.rs`; game.rs `fn menu_ops`, `fn click_inventory`; G: `stations.rs`, `trading.rs`, `recipe_book.rs`, `machines.rs` |
| Commands | parse: crate `horizon-command` (`args.rs`, `selector.rs`, `tree_*.rs`, `vanilla.rs`); run: G: `commands.rs` (dispatch) + `commands/*.rs` (`admin`, `execute`, `items`, `players`, `world_cmds`...); L: `scoreboard_cmd.rs`, `data_cmd.rs`; `horizon-client-core`: `nbt_path.rs`, `scoreboard.rs`; G: `functions.rs`, `command_blocks.rs` |
| UI screens | `Screen` enum in game.rs; L: `title.rs` + `title/`, `options_ui.rs`, `multiplayer.rs`, `pack_screen.rs`, `graphics_ui.rs`, `screen_draw.rs`, `ui.rs` (widgets), `hud.rs`, `hud_layout.rs`, `hud_overlays.rs`, `chat.rs`, `tab_list.rs`; G: `startup_menu.rs`, `screens.rs` (pause, death), `options.rs`, `lan_screen.rs`, `chat.rs`, `sign_edit.rs`, `books.rs`, `creative_screen.rs` |
+1 -237
View File
@@ -421,6 +421,7 @@ mod trident;
mod tutorial;
mod tweaks;
mod ui_control;
mod using_item;
mod vault;
mod vehicle_teleport;
mod villagers;
@@ -7537,55 +7538,6 @@ impl Game<'_> {
game_master: self.can_use_game_master_blocks(),
}
}
/// `Consumable.startConsuming` for a held consumable: food needs
/// `Player.canEat` (`Consumable.canConsume`), anything else is always
/// consumed. The right click is held for `consume_seconds`.
fn start_eating(&mut self, info: &horizon_client::items::ItemInfo) -> bool {
let Some(stack) = self.held().cloned() else {
return false;
};
if info.consumable.is_none()
|| (info.food.is_some()
&& (self.player.is_none() || !self.can_eat(&stack, info.can_always_eat)))
{
return false;
}
// as Consumable.startConsuming does, no time is eaten at once, without a use
// (the server's eating reaches a client through the inventory).
if let Some(consumable) = info.consumable.as_ref()
&& item_use::consumes_at_once(consumable.ticks())
{
if self.remote.is_none() {
// the cooldown of ItemStack.use's applyAfterUseComponentSideEffects.
self.apply_use_cooldown(&stack);
self.finish_consuming(info, consumable, &stack);
}
return true;
}
self.start_using_item();
true
}
/// `InstrumentItem.use` for a goat horn: the player holds it up for the
/// instrument's `use_duration`, its sound plays at the player
/// (`SoundSource.RECORDS`, volume `range / 16`), `INSTRUMENT_PLAY` is
/// posted, the used statistic counts and the horn is on cooldown as long.
/// A horn whose instrument is unknown fails, which still ends the click.
fn use_instrument(&mut self, stack: &ItemStack) {
let Some(instrument) = self.res.items().instrument(stack) else {
return;
};
self.start_using_item();
self.play_player_bound_sound(&instrument.sound, 2, instrument.range / 16.0, 1.0, true);
self.post_entity_event(
"minecraft:instrument_play",
horizon_client::mobs::Who::Player,
);
if instrument.use_duration > 0.0 {
let group = horizon_client::cooldowns::group(stack, None);
self.add_cooldown(group, instrument.use_ticks());
}
self.award_stat("used", &stack.item, 1);
}
fn apply_consume_effect(&mut self, effect: &horizon_client::items::ConsumeEffect) {
use horizon_client::items::ConsumeEffect;
match effect {
@@ -7646,152 +7598,6 @@ impl Game<'_> {
}
}
}
fn consume_held(&mut self) {
if self.creative {
return;
}
let cell = self.held_mut();
if let Some(s) = cell.as_mut() {
s.count -= 1;
if s.count == 0 {
*cell = None;
}
}
}
fn tick_using(&mut self) {
let Some(hand) = self.used_hand() else {
return;
};
self.with_hand(hand, Self::tick_used_item);
}
/// `LivingEntity.updatingUsingItem` for the item in use, its hand acting.
fn tick_used_item(&mut self) {
let Some((slot, ticks)) = self.using else {
return;
};
let Some(stack) = self.stack_in(slot).cloned() else {
self.using = None;
return;
};
// Switching hotbar slots only ends a main-hand use.
let hand_changed = slot != self.selected && slot != shield::OFFHAND_SLOT;
if !self.right || hand_changed || self.screen != Screen::Playing {
self.using = None;
self.release_used_item(&stack, ticks, hand_changed);
return;
}
// ServerPlayer.updateUsingItem: USING_ITEM every tick of the use.
self.trigger_using_item(&stack);
let Some(info) = self.items().get(&stack.item).cloned() else {
self.using = None;
return;
};
// a mod's tick ran the use out: finished now, or frozen until let go
// for an item used on release (CrossbowItem.useOnRelease).
if !self.use_item_ticked(&stack, ticks) {
if &*stack.item != "minecraft:crossbow" {
self.complete_using_item(slot, &stack, &info);
}
return;
}
if self.blocks_attacks(&stack).is_some() {
// ShieldItem: used for 72000 ticks, i.e. until released.
self.item_on_use_tick(&stack, ticks);
self.using = Some((slot, ticks + 1));
return;
}
if self.tick_drinking(slot, ticks, &stack) {
if self.using.is_none() {
self.use_item_finished(&stack);
}
return;
}
if &*stack.item == "minecraft:bow" {
self.using = Some((slot, ticks + 1));
return;
}
if &*stack.item == "minecraft:crossbow" {
self.tick_crossbow(slot, ticks, &stack);
return;
}
// TridentItem and EnderEyeItem: used until released (an eye's
// getUseDuration 0 never counts down to finishUsingItem).
if matches!(&*stack.item, "minecraft:trident" | "minecraft:ender_eye") {
self.using = Some((slot, ticks + 1));
return;
}
// ItemStack.onUseTick: a spear stabs what its speed allows, until released.
if horizon_client::spear::Kinetic::of(&stack.item).is_some() {
self.spear_tick(&stack, ticks);
self.using = Some((slot, ticks + 1));
return;
}
if &*stack.item == "minecraft:spyglass" {
// SpyglassItem.getUseDuration (1200, as a mod left it), then
// completeUsingItem: finishUsingItem lowers it, and the finish hook.
match item_use::count_down(ticks, self.use_duration) {
Some(ticks) => self.using = Some((slot, ticks)),
None => self.complete_using_item(slot, &stack, &info),
}
return;
}
if let Some(duration) = self
.res
.items()
.instrument(&stack)
.map(|i| i.use_ticks().max(0) as u32)
{
// InstrumentItem.getUseDuration: held up that long. When it runs
// out, LivingEntity.stopUsingItem ends the use and causes the
// interaction vibration; letting go of the button already does.
if ticks + 1 < duration {
self.using = Some((slot, ticks + 1));
} else {
self.stop_using_item(&stack);
}
return;
}
if &*stack.item == "minecraft:brush" {
self.tick_brush(&stack, slot, ticks);
return;
}
if info.bundle {
self.tick_bundle(slot, ticks);
return;
}
if self.mod_use(&stack) {
self.tick_mod_use(slot, ticks, &stack);
return;
}
let Some(consumable) = info.consumable.as_ref() else {
self.using = None;
return;
};
// ItemStack.onUseTick: Consumable.shouldEmitParticlesAndSounds with
// the remaining ticks before this tick counts them down.
// the duration is getUseDuration's when the use started, as a mod's
// Start and Tick left it.
let remaining = self.use_duration - ticks as i32;
if consumable.should_emit(remaining) {
self.emit_consume(consumable, &stack, 5);
}
self.item_on_use_tick(&stack, ticks);
match item_use::count_down(ticks, self.use_duration) {
Some(ticks) => self.using = Some((slot, ticks)),
None => self.complete_using_item(slot, &stack, &info),
}
}
/// `BundleItem.onUseTick` and `getUseDuration` (200): the first tick and
/// then every second tick from the 11th on drop one stack out of the
/// bundle in `slot`; the use ends after 200 ticks.
fn tick_bundle(&mut self, slot: usize, ticks: u32) {
const USE_DURATION: u32 = 200;
let remaining = USE_DURATION - ticks.min(USE_DURATION);
if ticks == 0 || (remaining < USE_DURATION - 10 && remaining.is_multiple_of(2)) {
self.drop_bundle_content(slot);
}
self.using = (ticks + 1 < USE_DURATION).then_some((slot, ticks + 1));
}
/// `BundleItem.dropContent`: `removeOneItemFromBundle` and `Player.drop`,
/// then the drop sound and the used statistic.
fn drop_bundle_content(&mut self, slot: usize) {
@@ -7828,48 +7634,6 @@ impl Game<'_> {
);
self.award_stat("used", &item, 1);
}
/// `LivingEntity.releaseUsingItem` (`Item.releaseUsing`) for `stack`,
/// used for `ticks`, its hand acting: a bow or a trident lets go (not
/// when its hand changed), a spyglass stops scoping, and a mod's own
/// release follows. A mod's `Stop` may skip the release. `stopUsingItem`
/// ends the use after it.
pub(super) fn release_used_item(&mut self, stack: &ItemStack, ticks: u32, hand_changed: bool) {
let canceled = !hand_changed && !self.use_item_stopped(stack, ticks);
let (release, spyglass) = item_use::release_parts(canceled, hand_changed);
match &*stack.item {
"minecraft:bow" if release => self.release_bow(stack, self.ticks_held(stack, ticks)),
"minecraft:trident" if release => self.release_trident(self.ticks_held(stack, ticks)),
"minecraft:spyglass" if spyglass => self.stop_spyglass(),
_ => {}
}
if release {
self.mod_release_using(stack, ticks);
}
self.stop_using_item(stack);
}
/// `LivingEntity.stopUsingItem`: the use of `stack` is over (`useItem` and
/// its remaining ticks are cleared), and when the use effects of the stack
/// ask for it, `ItemStack.causeUseVibration` sends an `ITEM_INTERACT_FINISH`
/// game event from the player, at its feet.
pub(super) fn stop_using_item(&mut self, stack: &ItemStack) {
self.using = None;
self.use_vibration(stack, "minecraft:item_interact_finish");
}
/// `ItemStack.causeUseVibration` for `stack` used by the player: the
/// `event` game event (`ITEM_INTERACT_START` when the use begins,
/// `ITEM_INTERACT_FINISH` when it ends) from the player, at its feet, when
/// the stack's use effects ask for interaction vibrations (the default).
pub(super) fn use_vibration(&mut self, stack: &ItemStack, event: &'static str) {
let default = self.items().get(&stack.item).map(|i| i.interact_vibrations);
if !item_use::interact_vibrations(stack, default) {
return;
}
let Some(p) = self.player.as_ref().map(|p| p.position) else {
return;
};
let ctx = self.player_context(None);
self.post_game_event(event, [p.x, p.y, p.z], ctx);
}
/// `ServerPlayerGameMode.tick` while a player digs: level event 2020 every
/// fourth tick since the START (the hit sound), else 2019 (the crack only),
/// for everyone around, the digger included (`levelEvent(null, ...)`).
@@ -0,0 +1,246 @@
//! An item in use, tick by tick (`LivingEntity.startUsingItem`, `updatingUsingItem`,
//! `releaseUsingItem`, `stopUsingItem`): eating and drinking, goat horns, bundles, and the
//! vibrations a use makes.
use super::*;
impl Game<'_> {
/// `Consumable.startConsuming` for a held consumable: food needs
/// `Player.canEat` (`Consumable.canConsume`), anything else is always
/// consumed. The right click is held for `consume_seconds`.
pub(super) fn start_eating(&mut self, info: &horizon_client::items::ItemInfo) -> bool {
let Some(stack) = self.held().cloned() else {
return false;
};
if info.consumable.is_none()
|| (info.food.is_some()
&& (self.player.is_none() || !self.can_eat(&stack, info.can_always_eat)))
{
return false;
}
// as Consumable.startConsuming does, no time is eaten at once, without a use
// (the server's eating reaches a client through the inventory).
if let Some(consumable) = info.consumable.as_ref()
&& item_use::consumes_at_once(consumable.ticks())
{
if self.remote.is_none() {
// the cooldown of ItemStack.use's applyAfterUseComponentSideEffects.
self.apply_use_cooldown(&stack);
self.finish_consuming(info, consumable, &stack);
}
return true;
}
self.start_using_item();
true
}
/// `InstrumentItem.use` for a goat horn: the player holds it up for the
/// instrument's `use_duration`, its sound plays at the player
/// (`SoundSource.RECORDS`, volume `range / 16`), `INSTRUMENT_PLAY` is
/// posted, the used statistic counts and the horn is on cooldown as long.
/// A horn whose instrument is unknown fails, which still ends the click.
pub(super) fn use_instrument(&mut self, stack: &ItemStack) {
let Some(instrument) = self.res.items().instrument(stack) else {
return;
};
self.start_using_item();
self.play_player_bound_sound(&instrument.sound, 2, instrument.range / 16.0, 1.0, true);
self.post_entity_event(
"minecraft:instrument_play",
horizon_client::mobs::Who::Player,
);
if instrument.use_duration > 0.0 {
let group = horizon_client::cooldowns::group(stack, None);
self.add_cooldown(group, instrument.use_ticks());
}
self.award_stat("used", &stack.item, 1);
}
pub(super) fn consume_held(&mut self) {
if self.creative {
return;
}
let cell = self.held_mut();
if let Some(s) = cell.as_mut() {
s.count -= 1;
if s.count == 0 {
*cell = None;
}
}
}
pub(super) fn tick_using(&mut self) {
let Some(hand) = self.used_hand() else {
return;
};
self.with_hand(hand, Self::tick_used_item);
}
/// `LivingEntity.updatingUsingItem` for the item in use, its hand acting.
pub(super) fn tick_used_item(&mut self) {
let Some((slot, ticks)) = self.using else {
return;
};
let Some(stack) = self.stack_in(slot).cloned() else {
self.using = None;
return;
};
// Switching hotbar slots only ends a main-hand use.
let hand_changed = slot != self.selected && slot != shield::OFFHAND_SLOT;
if !self.right || hand_changed || self.screen != Screen::Playing {
self.using = None;
self.release_used_item(&stack, ticks, hand_changed);
return;
}
// ServerPlayer.updateUsingItem: USING_ITEM every tick of the use.
self.trigger_using_item(&stack);
let Some(info) = self.items().get(&stack.item).cloned() else {
self.using = None;
return;
};
// a mod's tick ran the use out: finished now, or frozen until let go
// for an item used on release (CrossbowItem.useOnRelease).
if !self.use_item_ticked(&stack, ticks) {
if &*stack.item != "minecraft:crossbow" {
self.complete_using_item(slot, &stack, &info);
}
return;
}
if self.blocks_attacks(&stack).is_some() {
// ShieldItem: used for 72000 ticks, i.e. until released.
self.item_on_use_tick(&stack, ticks);
self.using = Some((slot, ticks + 1));
return;
}
if self.tick_drinking(slot, ticks, &stack) {
if self.using.is_none() {
self.use_item_finished(&stack);
}
return;
}
if &*stack.item == "minecraft:bow" {
self.using = Some((slot, ticks + 1));
return;
}
if &*stack.item == "minecraft:crossbow" {
self.tick_crossbow(slot, ticks, &stack);
return;
}
// TridentItem and EnderEyeItem: used until released (an eye's
// getUseDuration 0 never counts down to finishUsingItem).
if matches!(&*stack.item, "minecraft:trident" | "minecraft:ender_eye") {
self.using = Some((slot, ticks + 1));
return;
}
// ItemStack.onUseTick: a spear stabs what its speed allows, until released.
if horizon_client::spear::Kinetic::of(&stack.item).is_some() {
self.spear_tick(&stack, ticks);
self.using = Some((slot, ticks + 1));
return;
}
if &*stack.item == "minecraft:spyglass" {
// SpyglassItem.getUseDuration (1200, as a mod left it), then
// completeUsingItem: finishUsingItem lowers it, and the finish hook.
match item_use::count_down(ticks, self.use_duration) {
Some(ticks) => self.using = Some((slot, ticks)),
None => self.complete_using_item(slot, &stack, &info),
}
return;
}
if let Some(duration) = self
.res
.items()
.instrument(&stack)
.map(|i| i.use_ticks().max(0) as u32)
{
// InstrumentItem.getUseDuration: held up that long. When it runs
// out, LivingEntity.stopUsingItem ends the use and causes the
// interaction vibration; letting go of the button already does.
if ticks + 1 < duration {
self.using = Some((slot, ticks + 1));
} else {
self.stop_using_item(&stack);
}
return;
}
if &*stack.item == "minecraft:brush" {
self.tick_brush(&stack, slot, ticks);
return;
}
if info.bundle {
self.tick_bundle(slot, ticks);
return;
}
if self.mod_use(&stack) {
self.tick_mod_use(slot, ticks, &stack);
return;
}
let Some(consumable) = info.consumable.as_ref() else {
self.using = None;
return;
};
// ItemStack.onUseTick: Consumable.shouldEmitParticlesAndSounds with
// the remaining ticks before this tick counts them down.
// the duration is getUseDuration's when the use started, as a mod's
// Start and Tick left it.
let remaining = self.use_duration - ticks as i32;
if consumable.should_emit(remaining) {
self.emit_consume(consumable, &stack, 5);
}
self.item_on_use_tick(&stack, ticks);
match item_use::count_down(ticks, self.use_duration) {
Some(ticks) => self.using = Some((slot, ticks)),
None => self.complete_using_item(slot, &stack, &info),
}
}
/// `BundleItem.onUseTick` and `getUseDuration` (200): the first tick and
/// then every second tick from the 11th on drop one stack out of the
/// bundle in `slot`; the use ends after 200 ticks.
pub(super) fn tick_bundle(&mut self, slot: usize, ticks: u32) {
const USE_DURATION: u32 = 200;
let remaining = USE_DURATION - ticks.min(USE_DURATION);
if ticks == 0 || (remaining < USE_DURATION - 10 && remaining.is_multiple_of(2)) {
self.drop_bundle_content(slot);
}
self.using = (ticks + 1 < USE_DURATION).then_some((slot, ticks + 1));
}
/// `LivingEntity.releaseUsingItem` (`Item.releaseUsing`) for `stack`,
/// used for `ticks`, its hand acting: a bow or a trident lets go (not
/// when its hand changed), a spyglass stops scoping, and a mod's own
/// release follows. A mod's `Stop` may skip the release. `stopUsingItem`
/// ends the use after it.
pub(super) fn release_used_item(&mut self, stack: &ItemStack, ticks: u32, hand_changed: bool) {
let canceled = !hand_changed && !self.use_item_stopped(stack, ticks);
let (release, spyglass) = item_use::release_parts(canceled, hand_changed);
match &*stack.item {
"minecraft:bow" if release => self.release_bow(stack, self.ticks_held(stack, ticks)),
"minecraft:trident" if release => self.release_trident(self.ticks_held(stack, ticks)),
"minecraft:spyglass" if spyglass => self.stop_spyglass(),
_ => {}
}
if release {
self.mod_release_using(stack, ticks);
}
self.stop_using_item(stack);
}
/// `LivingEntity.stopUsingItem`: the use of `stack` is over (`useItem` and
/// its remaining ticks are cleared), and when the use effects of the stack
/// ask for it, `ItemStack.causeUseVibration` sends an `ITEM_INTERACT_FINISH`
/// game event from the player, at its feet.
pub(super) fn stop_using_item(&mut self, stack: &ItemStack) {
self.using = None;
self.use_vibration(stack, "minecraft:item_interact_finish");
}
/// `ItemStack.causeUseVibration` for `stack` used by the player: the
/// `event` game event (`ITEM_INTERACT_START` when the use begins,
/// `ITEM_INTERACT_FINISH` when it ends) from the player, at its feet, when
/// the stack's use effects ask for interaction vibrations (the default).
pub(super) fn use_vibration(&mut self, stack: &ItemStack, event: &'static str) {
let default = self.items().get(&stack.item).map(|i| i.interact_vibrations);
if !item_use::interact_vibrations(stack, default) {
return;
}
let Some(p) = self.player.as_ref().map(|p| p.position) else {
return;
};
let ctx = self.player_context(None);
self.post_game_event(event, [p.x, p.y, p.z], ctx);
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ command above to inspect the selection rather than relying on historical counts.
| 12 | Guests' actions on a server (98) | `G/lan_actions.rs` (`fn as_guest`, `fn tick_guests`), `G/lan_{attributes,pvp,chat,crafting,stations,trading,mount_menu,waypoints}.rs` | server_player_combat, server_player_damage, server_player_targets, lan_e2e (all) |
| 13 | Modpack porting, mod content (97) | `crates/horizon-port-pack/`, `L/mod_content.rs`, `tools/port_*.py` | `cargo test -p horizon-port-pack`; `python3 tools/mod_port_harness.py` |
| 14 | Mob AI (96) | `L/mobs/ai.rs`, `goals.rs`, `brain.rs`, `*_brain.rs`, `nav.rs`, `path.rs`, `control.rs` | mob_ai (probe `examples/mob_ai_probe.rs`; known failure on main: drowned_*, #290) |
| 15 | Items and item use (96) | `I/items.rs`, `I/item_data.rs`, `G/hand.rs`, `G/consume.rs`, `G/potions.rs`, `R/effects.rs`; game.rs `fn use_hand`, `fn use_item` | use_item_on, item_use, item_use_lifecycle, consumable, consume_effect, status_effect |
| 15 | Items and item use (96) | `I/items.rs`, `I/item_data.rs`, `G/hand.rs`, `G/consume.rs`, `G/using_item.rs`, `G/potions.rs`, `R/effects.rs`; game.rs `fn use_hand`, `fn use_item` | use_item_on, item_use, item_use_lifecycle, consumable, consume_effect, status_effect |
| 16 | Options and settings screens (93) | `R/options_ui.rs`, `G/options.rs`, `R/settings.rs`, `R/options_file.rs`, `R/graphics_ui.rs`, `R/graphics.rs` | options, toggle_keys |
| 17 | HUD, chat, tab list (93) | `R/hud.rs`, `R/hud_layout.rs`, `R/hud_overlays.rs`, `R/chat.rs`, `G/chat.rs`, `R/tab_list.rs`, `L/locator_bar.rs`, `G/locator_bar.rs` | hud, tab_list, locator_bar, chat_screen, secure_chat |
| 18 | Command execution (88) | `G/commands.rs`, `G/commands/*.rs`, `L/scoreboard_cmd.rs`, `L/data_cmd.rs`, `K/scoreboard.rs`, `G/functions.rs` | command_exec (corpus `tools/command_exec_corpus.py`), scoreboard, server_ops_feedback, lan_e2e commands |
+2 -2
View File
@@ -210,8 +210,8 @@ InkSacItem.tryApplyToSign inline crates/horizon-client/src/game/sign_edit.rs::Ga
InstrumentItem.create skip:java builds the horn stack
InstrumentItem.getInstrument skip:data instrument read from the item data
InstrumentItem.getUseDuration port crates/horizon-client/src/game/item_draw.rs::use_duration goat horn use duration 72000
InstrumentItem.play inline crates/horizon-client/src/game.rs::Game::use_instrument the instrument's sound at the player (SoundSource.RECORDS, volume `range / 16`), `INSTRUMENT_PLAY` and the use-duration cooldown
InstrumentItem.use port crates/horizon-client/src/game.rs::Game::use_instrument holds the horn up for the instrument's use duration, plays it, counts the used statistic; an unknown instrument fails
InstrumentItem.play inline crates/horizon-client/src/game/using_item.rs::Game::use_instrument the instrument's sound at the player (SoundSource.RECORDS, volume `range / 16`), `INSTRUMENT_PLAY` and the use-duration cooldown
InstrumentItem.use port crates/horizon-client/src/game/using_item.rs::Game::use_instrument holds the horn up for the instrument's use duration, plays it, counts the used statistic; an unknown instrument fails
Instruments.bootstrap skip:data registry bootstrap of the instrument list
Instruments.register skip:data registry bootstrap of the instrument list
ItemFrameItem.mayPlace port crates/horizon-client/src/game/frames.rs::Game::place_item_frame
1 Squid.hurtServer inline crates/horizon-client/src/mobs.rs::Mob::damage_with_player_context after shared parent damage, require last living attacker for true; retain health loss and existing ink guard
210 InstrumentItem.create skip:java builds the horn stack
211 InstrumentItem.getInstrument skip:data instrument read from the item data
212 InstrumentItem.getUseDuration port crates/horizon-client/src/game/item_draw.rs::use_duration goat horn use duration 72000
213 InstrumentItem.play inline crates/horizon-client/src/game.rs::Game::use_instrument crates/horizon-client/src/game/using_item.rs::Game::use_instrument the instrument's sound at the player (SoundSource.RECORDS, volume `range / 16`), `INSTRUMENT_PLAY` and the use-duration cooldown
214 InstrumentItem.use port crates/horizon-client/src/game.rs::Game::use_instrument crates/horizon-client/src/game/using_item.rs::Game::use_instrument holds the horn up for the instrument's use duration, plays it, counts the used statistic; an unknown instrument fails
215 Instruments.bootstrap skip:data registry bootstrap of the instrument list
216 Instruments.register skip:data registry bootstrap of the instrument list
217 ItemFrameItem.mayPlace port crates/horizon-client/src/game/frames.rs::Game::place_item_frame
+3 -3
View File
@@ -92,8 +92,8 @@ LivingEntity.canGlide inline crates/horizon-client/src/motion.rs::MotionWorld::p
LivingEntity.onItemPickup port crates/horizon-client/src/game/sulfur_cubes.rs::Game::sulfur_cube_pickups gap: player: only a sulfur cube's pickup fires thrown_item_picked_up_by_entity; a piglin picking up gold you threw does not, so Oh Shiny never unlocks
LivingEntity.isUsingItem inline crates/horizon-client/src/game/remote.rs::RemoteInputWorld::using_item Game::using is Some; a mob's is extra.using_item above 0
LivingEntity.setLivingEntityFlag skip:java bit accessor; the flags are built when synced in game/lan.rs::mob_meta
LivingEntity.getTicksUsingItem inline crates/horizon-client/src/game.rs::Game::tick_used_item the tick count kept in Game::using
LivingEntity.stopUsingItem port crates/horizon-client/src/game.rs::Game::stop_using_item using is dropped when the use ends, and ItemStack.causeUseVibration sends ITEM_INTERACT_FINISH at the player when the stack's use effects ask for it
LivingEntity.getTicksUsingItem inline crates/horizon-client/src/game/using_item.rs::Game::tick_used_item the tick count kept in Game::using
LivingEntity.stopUsingItem port crates/horizon-client/src/game/using_item.rs::Game::stop_using_item using is dropped when the use ends, and ItemStack.causeUseVibration sends ITEM_INTERACT_FINISH at the player when the stack's use effects ask for it
LivingEntity.getLocalBoundsForPose inline crates/horizon-client/src/game/mounts.rs::rider_poses the rider's box in each dismount pose, standing, crouching then swimming, from the pose's dimensions at its scale; the strider dismount tests it (strider_dismount_location), the other vehicles use their own side checks
LivingEntity.wouldNotSuffocateAtTargetPose port crates/horizon-client/src/pose.rs::MotionWorld::can_fit_pose gap: minor: the player's poses only; a camel changing pose is not checked
LivingEntity.canUsePortal port crates/horizon-client/src/game/nether_portal.rs::Game::mob_can_use_portal gap: minor: a sleeping mob is not excluded
@@ -130,7 +130,7 @@ Mob.hasValidEquippableItemForSlot skip:other Horizon stores only items that fit
Mob.createEquipmentParams inline crates/horizon-client/src/game/mob_gear.rs::Game::equip_from_table EntityLootParams with the mob as `this`
Mob.getEquipmentForSlot inline crates/horizon-client/src/mobs/finalize.rs::Ctx::armor the material table leather, copper, golden, chainmail, iron, diamond by piece
Mob.enchantSpawnedEquipment port crates/horizon-client/src/game/mob_gear.rs::Game::enchant_slot chance times the local difficulty's special multiplier
Mob.usePlayerItem port crates/horizon-client/src/game.rs::Game::consume_held gap: minor: no use_remainder (a bottle or bowl a food item leaves behind)
Mob.usePlayerItem port crates/horizon-client/src/game/using_item.rs::Game::consume_held gap: minor: no use_remainder (a bottle or bowl a food item leaves behind)
Mob.setNoAi skip:java plain field Mob::no_ai, synced as a flag in game/lan.rs::mob_meta
Mob.setLeftHanded skip:java plain field Extra::left_handed, synced in game/lan.rs::mob_meta
Mob.setAggressive skip:java plain field Ai::aggressive, synced in game/lan.rs::mob_meta
1 Interaction.defineSynchedData port crates/horizon-client/src/game/interactions.rs::RemoteInteraction::new width 1.0, height 1.0, response false
92 LivingEntity.onItemPickup port crates/horizon-client/src/game/sulfur_cubes.rs::Game::sulfur_cube_pickups gap: player: only a sulfur cube's pickup fires thrown_item_picked_up_by_entity; a piglin picking up gold you threw does not, so Oh Shiny never unlocks
93 LivingEntity.isUsingItem inline crates/horizon-client/src/game/remote.rs::RemoteInputWorld::using_item Game::using is Some; a mob's is extra.using_item above 0
94 LivingEntity.setLivingEntityFlag skip:java bit accessor; the flags are built when synced in game/lan.rs::mob_meta
95 LivingEntity.getTicksUsingItem inline crates/horizon-client/src/game.rs::Game::tick_used_item crates/horizon-client/src/game/using_item.rs::Game::tick_used_item the tick count kept in Game::using
96 LivingEntity.stopUsingItem port crates/horizon-client/src/game.rs::Game::stop_using_item crates/horizon-client/src/game/using_item.rs::Game::stop_using_item using is dropped when the use ends, and ItemStack.causeUseVibration sends ITEM_INTERACT_FINISH at the player when the stack's use effects ask for it
97 LivingEntity.getLocalBoundsForPose inline crates/horizon-client/src/game/mounts.rs::rider_poses the rider's box in each dismount pose, standing, crouching then swimming, from the pose's dimensions at its scale; the strider dismount tests it (strider_dismount_location), the other vehicles use their own side checks
98 LivingEntity.wouldNotSuffocateAtTargetPose port crates/horizon-client/src/pose.rs::MotionWorld::can_fit_pose gap: minor: the player's poses only; a camel changing pose is not checked
99 LivingEntity.canUsePortal port crates/horizon-client/src/game/nether_portal.rs::Game::mob_can_use_portal gap: minor: a sleeping mob is not excluded
130 Mob.createEquipmentParams inline crates/horizon-client/src/game/mob_gear.rs::Game::equip_from_table EntityLootParams with the mob as `this`
131 Mob.getEquipmentForSlot inline crates/horizon-client/src/mobs/finalize.rs::Ctx::armor the material table leather, copper, golden, chainmail, iron, diamond by piece
132 Mob.enchantSpawnedEquipment port crates/horizon-client/src/game/mob_gear.rs::Game::enchant_slot chance times the local difficulty's special multiplier
133 Mob.usePlayerItem port crates/horizon-client/src/game.rs::Game::consume_held crates/horizon-client/src/game/using_item.rs::Game::consume_held gap: minor: no use_remainder (a bottle or bowl a food item leaves behind)
134 Mob.setNoAi skip:java plain field Mob::no_ai, synced as a flag in game/lan.rs::mob_meta
135 Mob.setLeftHanded skip:java plain field Extra::left_handed, synced in game/lan.rs::mob_meta
136 Mob.setAggressive skip:java plain field Ai::aggressive, synced in game/lan.rs::mob_meta
+1 -1
View File
@@ -66,7 +66,7 @@ FireworkRocketItem.getEntityJustOutsideOfBlockPos port crates/horizon-client/src
HangingEntityItem.mayPlace port crates/horizon-client/src/game/paintings.rs::Game::place_painting paintings take no vertical face; an item frame (ItemFrameItem.mayPlace) may go on one and only needs the build height, which place_item_frame checks
HangingEntityItem.appendHoverText port crates/horizon-client/src/game/stations.rs::Game::stack_tooltip_lines
Item.getDestroySpeed port crates/horizon-items/src/items.rs::Items::mining_speed
Item.finishUsingItem port crates/horizon-client/src/game.rs::Game::consume_held the consumable effects are applied by apply_consume_effect
Item.finishUsingItem port crates/horizon-client/src/game/using_item.rs::Game::consume_held the consumable effects are applied by apply_consume_effect
Item.getBarColor port crates/horizon-client-render/src/hud_overlays.rs::damage_bar width and hue from the damage fraction
Item.mineBlock port crates/horizon-client/src/game.rs::Game::mine_block_with_held
Item.isCorrectToolForDrops port crates/horizon-items/src/items.rs::Items::correct_for_drops
1 LoomMenu.<init> port crates/horizon-client/src/menu.rs::Menu::new the loom's banner, dye, pattern and result slots are laid out for Kind::Loom in the constructor
66 HangingEntityItem.mayPlace port crates/horizon-client/src/game/paintings.rs::Game::place_painting paintings take no vertical face; an item frame (ItemFrameItem.mayPlace) may go on one and only needs the build height, which place_item_frame checks
67 HangingEntityItem.appendHoverText port crates/horizon-client/src/game/stations.rs::Game::stack_tooltip_lines
68 Item.getDestroySpeed port crates/horizon-items/src/items.rs::Items::mining_speed
69 Item.finishUsingItem port crates/horizon-client/src/game.rs::Game::consume_held crates/horizon-client/src/game/using_item.rs::Game::consume_held the consumable effects are applied by apply_consume_effect
70 Item.getBarColor port crates/horizon-client-render/src/hud_overlays.rs::damage_bar width and hue from the damage fraction
71 Item.mineBlock port crates/horizon-client/src/game.rs::Game::mine_block_with_held
72 Item.isCorrectToolForDrops port crates/horizon-items/src/items.rs::Items::correct_for_drops