From 5b13af2f7592ef9a2dfd0d368064f57789e88fb8 Mon Sep 17 00:00:00 2001 From: Kacpherek Date: Wed, 28 Oct 2026 06:54:33 +0100 Subject: [PATCH] Count a minecart's comparator signal only when a detector rail reads it Every with_level built every chest and hopper cart's signal through item lookups. tick_scheduled calls it once per scheduled tick, so freshly generated forests (thousands of leaf ticks) next to mineshaft chest carts spent a large share of the server tick there. detector_rail_signal now counts the cart it reads. --- crates/horizon-client/src/game.rs | 25 +++++++++++++++------ crates/horizon-client/src/game/minecarts.rs | 18 +-------------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/crates/horizon-client/src/game.rs b/crates/horizon-client/src/game.rs index c97af3a47..7f5f8f62a 100644 --- a/crates/horizon-client/src/game.rs +++ b/crates/horizon-client/src/game.rs @@ -585,8 +585,8 @@ struct GameLevel<'w> { cart_boxes: Vec<([f64; 3], [f64; 3])>, /// The item frames a comparator reads through a conductor. item_frames: &'w [horizon_client::entities::ItemFrame], - /// Each minecart's kind and comparator signal, in `cart_boxes` order. - cart_signals: Vec<(minecarts::CartKind, i32)>, + /// The minecarts, in `cart_boxes` order, for a detector rail's comparator. + carts: &'w [minecarts::Minecart], /// Arrow and thrown trident boxes (wooden buttons). arrow_boxes: Vec<([f64; 3], [f64; 3])>, /// The other entities that keep blocks from being placed into them. @@ -737,7 +737,7 @@ impl GameLevel<'_> { Vec3::new(x + 0.8, y + 0.8, z + 0.8), ); let mut container = None; - for ((lo, hi), &(kind, signal)) in self.cart_boxes.iter().zip(&self.cart_signals) { + for ((lo, hi), cart) in self.cart_boxes.iter().zip(self.carts) { let cart = Aabb::new( Vec3::new(lo[0], lo[1], lo[2]), Vec3::new(hi[0], hi[1], hi[2]), @@ -745,16 +745,28 @@ impl GameLevel<'_> { if !area.intersects(cart) { continue; } - match kind { + match cart.kind { minecarts::CartKind::CommandBlock => return 0, minecarts::CartKind::Chest | minecarts::CartKind::Hopper => { - container = container.or(Some(signal)); + // Counted here, for the cart a comparator reads, not for + // every cart on every block tick. + container = container.or_else(|| Some(self.cart_signal(cart))); } _ => {} } } container.unwrap_or(0) } + /// A container minecart's comparator signal: its open menu's slots while + /// someone has it open, else its own. + fn cart_signal(&self, cart: &minecarts::Minecart) -> i32 { + let key = (i32::MAX - 15, minecarts::CART_KEY_Y, cart.id as i32); + let slots = match self.world.block_entities.get(&key) { + Some(BlockEntity::Chest(open)) => open, + _ => &cart.items, + }; + minecarts::Minecart::container_signal(slots, self.items) + } /// Every entity box that blocks building: the players who aren't /// spectators, the minecarts and the `Blockers` (a removed mob or a marker /// armor stand, whose box is empty, doesn't). @@ -2854,7 +2866,6 @@ impl Game<'_> { ) }) .collect(); - let cart_signals = self.cart_signals(); let arrow_boxes = self .creatures .projectiles @@ -2898,7 +2909,7 @@ impl Game<'_> { let mut level = GameLevel { cart_boxes, item_frames: &self.item_frames, - cart_signals, + carts: &self.carts, arrow_boxes, blockers, dropped: &self.dropped, diff --git a/crates/horizon-client/src/game/minecarts.rs b/crates/horizon-client/src/game/minecarts.rs index 9dead141c..a56f107a2 100644 --- a/crates/horizon-client/src/game/minecarts.rs +++ b/crates/horizon-client/src/game/minecarts.rs @@ -202,7 +202,7 @@ impl Minecart { self.velocity.x.hypot(self.velocity.z) } /// `AbstractContainerMenu.getRedstoneSignalFromContainer` of `slots`. - fn container_signal(slots: &[Option], items: &Items) -> i32 { + pub(super) fn container_signal(slots: &[Option], items: &Items) -> i32 { let mut total = 0.0f32; for s in slots.iter().flatten() { let max = items.get(&s.item).map_or(64, |i| i.max_stack).min(64); @@ -1029,22 +1029,6 @@ impl Game<'_> { } best } - /// Each cart's kind and comparator signal, for the detector rail - /// (`DetectorRailBlock.getAnalogOutputSignal`). A cart whose menu is open - /// counts the items in that menu. - pub(super) fn cart_signals(&self) -> Vec<(CartKind, i32)> { - self.carts - .iter() - .map(|c| { - let key = (i32::MAX - 15, CART_KEY_Y, c.id as i32); - let slots = match self.world.block_entities.get(&key) { - Some(BlockEntity::Chest(open)) => open, - _ => &c.items, - }; - (c.kind, Minecart::container_signal(slots, self.res.items())) - }) - .collect() - } /// `interact`: ride, open, fuel or ignite the cart under the crosshair. pub(super) fn interact_cart(&mut self) -> bool { let Some((i, d)) = self.cart_hit() else {