Merge upstream main into w1-bow

# Conflicts:
#	crates/horizon-client/src/mod_api/generated.rs
This commit is contained in:
arismoko committed 2026-11-02 15:01:27 +00:00
commit a09da57204
58 files changed
+1411 -470

No files matched your search

+10 -2
View File
@@ -7011,7 +7011,11 @@ impl Renderer {
let additive_quads = (frame.additive.len() / 4) as u32 - self.entity_batch.moved_additive;
let see_through_quads = (frame.see_through.len() / 4) as u32;
let shadow_only_quads = (frame.shadow_only.len() / 4) as u32;
let entity_vertices = u64::from(posed) + all_entities.len() as u64;
// Glowing entities' silhouettes posed on the GPU go last, past every
// material (the outline pass draws them from there).
let outline_base = posed + all_entities.len() as u32;
let outlined = skin.map_or(0, |s| s.outline_vertices);
let entity_vertices = u64::from(outline_base) + u64::from(outlined);
if entity_vertices > self.entity_capacity {
self.entity_capacity = entity_vertices.next_power_of_two();
self.entity_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
@@ -7037,6 +7041,7 @@ impl Renderer {
skin,
&self.entity_buffer,
0,
outline_base,
);
}
let static_sections = frame.static_block_entities;
@@ -7163,6 +7168,7 @@ impl Renderer {
&mut self.uploads,
&mut encoder,
frame.glowing,
(outline_base, outlined / 4),
[self.config.width, self.config.height],
);
if let Some((rotation, brightness)) = frame.stars {
@@ -7194,7 +7200,8 @@ impl Renderer {
lightning::QUADS
})
.max(weather_quads)
.max(frame.glowing.len() as u32 / 4);
.max(frame.glowing.len() as u32 / 4)
.max(outlined / 4);
if max_quads > self.index_quads {
self.index_quads = max_quads.next_power_of_two();
self.indices = Self::quad_indices(&self.device, self.index_quads);
@@ -9083,6 +9090,7 @@ impl Renderer {
view_target,
&self.entity_group,
&self.indices,
&self.entity_buffer,
timing.as_mut(),
);
// The world icon grabs here: after the level and the hand, before
@@ -29,6 +29,8 @@ pub(super) struct EntityOutline {
vertices: wgpu::Buffer,
capacity: u64,
quads: u32,
/// The silhouettes posed on the GPU: first vertex in the entity buffer, quads.
posed: (u32, u32),
targets: Option<Targets>,
}
@@ -159,6 +161,7 @@ impl EntityOutline {
vertices: Self::vertex_buffer(device, 256),
capacity: 256,
quads: 0,
posed: (0, 0),
targets: None,
}
}
@@ -175,16 +178,20 @@ impl EntityOutline {
/// Uploads this frame's silhouettes (`LevelRenderer.currentFrameRendersEntityOutline`
/// when there are any) and sizes the targets like the window. A frame with
/// none gives the targets' video memory back until an entity glows again.
/// `posed`: where the silhouettes posed on the GPU start in the entity
/// buffer (`entity_skin`'s outline jobs), and how many quads they are.
pub(super) fn prepare(
&mut self,
device: &wgpu::Device,
uploads: &mut super::upload::Uploads,
encoder: &mut wgpu::CommandEncoder,
silhouettes: &[Vertex],
posed: (u32, u32),
size: [u32; 2],
) {
self.quads = (silhouettes.len() / 4) as u32;
if self.quads == 0 {
self.posed = posed;
if self.quads == 0 && posed.1 == 0 {
self.targets = None;
return;
}
@@ -193,12 +200,14 @@ impl EntityOutline {
self.capacity = vertices.next_power_of_two();
self.vertices = Self::vertex_buffer(device, self.capacity);
}
uploads.buffer(
encoder,
&self.vertices,
0,
bytemuck::cast_slice(silhouettes),
);
if !silhouettes.is_empty() {
uploads.buffer(
encoder,
&self.vertices,
0,
bytemuck::cast_slice(silhouettes),
);
}
if self.targets.as_ref().is_some_and(|t| t.size == size) {
return;
}
@@ -260,9 +269,14 @@ impl EntityOutline {
frame: &wgpu::TextureView,
entity_group: &wgpu::BindGroup,
indices: &wgpu::Buffer,
entities: &wgpu::Buffer,
mut timing: Option<&mut super::pack_timing::PassTiming>,
) {
let Some(t) = self.targets.as_ref().filter(|_| self.quads > 0) else {
let Some(t) = self
.targets
.as_ref()
.filter(|_| self.quads > 0 || self.posed.1 > 0)
else {
return;
};
let clear = wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT);
@@ -277,8 +291,15 @@ impl EntityOutline {
pass.set_pipeline(&self.silhouette);
pass.set_bind_group(0, entity_group, &[]);
pass.set_index_buffer(indices.slice(..), wgpu::IndexFormat::Uint32);
pass.set_vertex_buffer(0, self.vertices.slice(..));
pass.draw_indexed(0..self.quads * 6, 0, 0..1);
if self.quads > 0 {
pass.set_vertex_buffer(0, self.vertices.slice(..));
pass.draw_indexed(0..self.quads * 6, 0, 0..1);
}
let (first, quads) = self.posed;
if quads > 0 {
pass.set_vertex_buffer(0, entities.slice(..));
pass.draw_indexed(0..quads * 6, first as i32, 0..1);
}
}
// post_effect/entity_outline.json; its last pass only copies swap back to the
// outline target, so the blit reads swap.
@@ -107,6 +107,11 @@ pub struct SkinFrame {
pub jobs: Vec<SkinJob>,
/// Vertices the jobs write: a whole number of quads.
pub vertices: u32,
/// Glowing entities' silhouettes posed here (`OutlineBufferSource`'s copy of
/// their draws, [`SkinFrame::outline_since`]): `dst` counts from the first.
pub outline_jobs: Vec<SkinJob>,
/// Vertices the outline jobs write.
pub outline_vertices: u32,
}
impl SkinFrame {
@@ -115,6 +120,24 @@ impl SkinFrame {
self.parts.clear();
self.jobs.clear();
self.vertices = 0;
self.outline_jobs.clear();
self.outline_vertices = 0;
}
/// Copies the draws since job `first` into the glowing entities' outline
/// (`Frame::glowing`'s, on the GPU), as `glowing.rs`'s `mark_outline` copies
/// vertices: the same ones, in `color` (`Vertex::color`) with no normal.
pub fn outline_since(&mut self, first: usize, color: u32) {
for i in first..self.jobs.len() {
let job = self.jobs[i];
self.outline_jobs.push(SkinJob {
dst: self.outline_vertices,
color,
mode: SkinJob::FLAT,
..job
});
self.outline_vertices += job.count;
}
}
/// The renderer took the new models.
@@ -138,7 +161,10 @@ impl SkinFrame {
/// Whether the renderer has anything to take.
pub fn pending(&self) -> bool {
self.reset || !self.new_model.is_empty() || !self.jobs.is_empty()
self.reset
|| !self.new_model.is_empty()
|| !self.jobs.is_empty()
|| !self.outline_jobs.is_empty()
}
}
@@ -293,7 +319,8 @@ impl EntitySkin {
}
/// Records the frame's jobs: they write `frame.vertices` vertices into
/// `out` from vertex `base` on. Call after the CPU vertices' upload.
/// `out` from vertex `base` on, and the outline jobs `frame.outline_vertices`
/// from vertex `outline_base` on. Call after the CPU vertices' upload.
#[allow(clippy::too_many_arguments)]
pub(super) fn dispatch(
&mut self,
@@ -303,26 +330,32 @@ impl EntitySkin {
frame: &SkinFrame,
out: &wgpu::Buffer,
base: u32,
outline_base: u32,
) {
if frame.jobs.is_empty() {
if frame.jobs.is_empty() && frame.outline_jobs.is_empty() {
return;
}
let Some(model) = &self.model_buffer else {
return;
};
// Whole rows of workgroups past `ROW` jobs: the padding jobs write nothing.
let count = frame.jobs.len() as u32;
let count = (frame.jobs.len() + frame.outline_jobs.len()) as u32;
let rows = count.div_ceil(ROW);
let padded = if rows > 1 {
(rows * ROW) as usize
} else {
frame.jobs.len()
count as usize
};
self.staged.clear();
self.staged.extend(frame.jobs.iter().map(|job| SkinJob {
dst: job.dst + base,
..*job
}));
let moved = |at: u32| {
move |job: &SkinJob| SkinJob {
dst: job.dst + at,
..*job
}
};
self.staged.extend(frame.jobs.iter().map(moved(base)));
self.staged
.extend(frame.outline_jobs.iter().map(moved(outline_base)));
self.staged.resize(padded, SkinJob::default());
self.parts.ensure(
device,
@@ -369,8 +402,22 @@ impl EntitySkin {
/// the same way: the reference its tests hold the shader and the game's walk
/// (`mobs::skin`) to. `model` is the whole store.
pub fn expand(model: &[SkinVertex], frame: &SkinFrame) -> Vec<Vertex> {
let mut out = vec![bytemuck::Zeroable::zeroed(); frame.vertices as usize];
for job in &frame.jobs {
expand_jobs(model, frame, &frame.jobs, frame.vertices)
}
/// [`expand`] of the frame's outline jobs: the silhouettes they write.
pub fn expand_outline(model: &[SkinVertex], frame: &SkinFrame) -> Vec<Vertex> {
expand_jobs(model, frame, &frame.outline_jobs, frame.outline_vertices)
}
fn expand_jobs(
model: &[SkinVertex],
frame: &SkinFrame,
jobs: &[SkinJob],
vertices: u32,
) -> Vec<Vertex> {
let mut out = vec![bytemuck::Zeroable::zeroed(); vertices as usize];
for job in jobs {
for k in 0..job.count {
let v = model[(job.first + k) as usize];
let part = &frame.parts[(job.part + v.part) as usize];
@@ -557,13 +604,30 @@ mod tests {
},
],
vertices: 120,
outline_jobs: Vec::new(),
outline_vertices: 0,
};
// A glowing entity's silhouette: the item job and the first job again,
// in its team colour with no normal.
let mut frame = frame;
frame.jobs.push(frame.jobs[2]);
frame.jobs.last_mut().expect("pushed").dst = 120;
frame.jobs.push(SkinJob {
dst: 144,
..frame.jobs[0]
});
frame.vertices = 192;
frame.outline_since(4, 0xff20_c0e0);
assert_eq!(frame.outline_vertices, 72);
let expected = expand(&model, &frame);
// The posed vertices land after 5 others, which they leave alone.
let outline = expand_outline(&model, &frame);
// The posed vertices land after 5 others, which they leave alone, and the
// silhouettes 3 past them.
let before = 5u32;
let outline_base = before + frame.vertices + 3;
let out = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: u64::from(before + frame.vertices) * 44,
size: u64::from(outline_base + frame.outline_vertices) * 44,
usage: EntitySkin::usage(true) | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
@@ -572,7 +636,15 @@ mod tests {
let mut uploads = Uploads::new(&device);
let mut encoder = device.create_command_encoder(&Default::default());
assert!(skin.register(&device, &mut uploads, &mut encoder, &frame));
skin.dispatch(&device, &mut uploads, &mut encoder, &frame, &out, before);
skin.dispatch(
&device,
&mut uploads,
&mut encoder,
&frame,
&out,
before,
outline_base,
);
let read = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: out.size(),
@@ -595,5 +667,24 @@ mod tests {
"vertex {i}: {got:?} != {want:?}"
);
}
let silhouettes = &got[(outline_base - before) as usize..];
assert_eq!(silhouettes.len(), outline.len());
for (i, (got, want)) in silhouettes.iter().zip(&outline).enumerate() {
assert_eq!(
bytemuck::bytes_of(got),
bytemuck::bytes_of(want),
"silhouette {i}: {got:?} != {want:?}"
);
}
// A silhouette is its draw's vertex with the colour and no normal
// (`mark_outline`).
for (copy, of) in outline.iter().zip(&expected[120..]) {
let marked = Vertex {
color: 0xff20_c0e0,
normal: [0; 4],
..*of
};
assert_eq!(bytemuck::bytes_of(copy), bytemuck::bytes_of(&marked));
}
}
}
@@ -753,6 +753,9 @@ fn print(out: &mut String, t: i32, i: usize, m: &Mob, player_down: bool, now: i6
if let Some((acts, mem)) = brain {
let _ = write!(out, "\tact {acts}\tmem {mem}");
}
if horizon_client::mobs::finalize::ageable(m.species()) {
let _ = write!(out, "\tage {}", m.extra.age);
}
if m.species() == Species::Villager {
let _ = write!(out, "\tvx {}", villager(m));
}
@@ -1026,6 +1029,7 @@ pub fn main() -> horizon_client::Result<()> {
m.rider = Some(horizon_client::mobs::equine::Rider::default());
}
"noai" => m.no_ai = true,
"agelocked" => m.extra.age_locked = true,
// Warden.finalizeSpawn's own part.
"digcooldown" | "emerge" => horizon_client::mobs::warden::finalize(
&mut m,
+3
View File
@@ -170,6 +170,8 @@ pub struct ArrowState {
/// The block it stuck in (`lastState`).
pub last_state: Option<BlockStateId>,
pub pickup: Pickup,
/// Its entity type is `SpectralArrow` (the type is its own, not the pickup item's).
pub spectral: bool,
/// `pickupItemStack`: what picking the arrow up gives.
pub pickup_item: crate::items::ItemStack,
/// `AbstractArrow.firedFromWeapon`: the crossbow or bow it was shot from.
@@ -227,6 +229,7 @@ impl ArrowState {
shake: 0,
last_state: None,
pickup: Pickup::Disallowed,
spectral: false,
pickup_item: crate::items::ItemStack::new("minecraft:arrow", 1),
fired_from_weapon: None,
no_physics: false,
+62 -2
View File
@@ -1483,6 +1483,7 @@ impl ArmorStand {
facts: &BlockFacts,
in_water: bool,
push: &mut dyn FnMut(Aabb, &mut Vec3),
border: Option<&BorderGeometry>,
) {
self.previous = self.position;
if self.marker {
@@ -1519,7 +1520,20 @@ impl ArmorStand {
1.0
};
let mut velocity = wanted;
if let Ok(moved) = scene.resolve(self.bounds(), wanted, 0.0, self.on_ground) {
// Entity.collide: the world border's walls stop the stand too.
let moved = SceneCollisions {
world: scene.world,
binding: scene.binding,
subject: scene.subject,
}
.resolve_in_border(
self.bounds(),
wanted,
0.0,
self.on_ground,
border.map(|b| (b, [self.position.x, self.position.z])),
);
if let Ok(moved) = moved {
self.position = Vec3::new(
self.position.x + moved.x,
self.position.y + moved.y,
@@ -2699,7 +2713,53 @@ mod armor_stand_motion_tests {
binding: collision,
subject: None,
};
stand.tick(&mut scene, facts, false, &mut |_, _| {});
stand.tick(&mut scene, facts, false, &mut |_, _| {}, None);
}
/// `Entity.collectColliders` (#7080): the world border's wall stops an
/// armor stand as it stops the player. A border of size 10 around
/// (20, 20) has its east wall at x = 25; the stand starts at x = 24 on the
/// test scene's flat ground, moving 1.5 east.
#[test]
fn the_world_border_stops_an_armor_stand() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../.local/horizon-client")
.join(horizon_core::VANILLA_VERSION);
if !root.join("movement-properties.nbt").exists() {
return;
}
let assets = crate::assets::Assets::load(&root).unwrap();
let render = assets.reload(&root).unwrap();
let facts = BlockFacts::load(&root, assets.registry).unwrap();
let collision = crate::collisions::load(&root, assets.registry).unwrap();
let world = World::test_scene(&assets, &render, 3).unwrap();
let border = BorderGeometry::new([20.0, 20.0], 10.0, 29_999_984).unwrap();
for bordered in [false, true] {
let mut stand = ArmorStand::new(1, Vec3::new(24.0, 4.0, 20.5), 0.0);
stand.on_ground = true;
stand.velocity = Vec3::new(1.5, 0.0, 0.0);
let mut scene = SceneCollisions {
world: &world,
binding: &collision,
subject: None,
};
stand.tick(
&mut scene,
&facts,
false,
&mut |_, _| {},
bordered.then_some(&border),
);
let east = stand.bounds().max_position().x;
if bordered {
assert!(
(east - 25.0).abs() < 1.0e-7,
"stops on the wall, got {east}"
);
} else {
assert!(east > 25.0, "no border: goes on, got {east}");
}
}
}
#[test]
+4 -4
View File
@@ -380,7 +380,7 @@ mod tests {
binding: &collision,
subject: None,
};
still.tick(&mut scene, &facts, in_water, &mut |_, _| {});
still.tick(&mut scene, &facts, in_water, &mut |_, _| {}, None);
assert_eq!(still.position.x, at.x);
assert_eq!(still.velocity.x, 0.0);
@@ -394,7 +394,7 @@ mod tests {
binding: &collision,
subject: None,
};
marker.tick(&mut scene, &facts, in_water, &mut |_, _| {});
marker.tick(&mut scene, &facts, in_water, &mut |_, _| {}, None);
assert_eq!(marker.position, at);
assert_eq!(marker.velocity, Vec3::ZERO);
@@ -406,7 +406,7 @@ mod tests {
binding: &collision,
subject: None,
};
carried.tick(&mut scene, &facts, in_water, &mut |_, _| {});
carried.tick(&mut scene, &facts, in_water, &mut |_, _| {}, None);
assert!((carried.position.x - (at.x + 0.014)).abs() < 1e-12);
assert!((carried.velocity.x - 0.014 * f64::from(0.8f32)).abs() < 1e-12);
for _ in 0..20 {
@@ -416,7 +416,7 @@ mod tests {
binding: &collision,
subject: None,
};
carried.tick(&mut scene, &facts, in_water, &mut |_, _| {});
carried.tick(&mut scene, &facts, in_water, &mut |_, _| {}, None);
}
assert!(carried.position.x > at.x + 0.5);
}
+171 -7
View File
@@ -27,7 +27,7 @@ fn string(s: &str) -> Tag {
}
/// Entity type ID (`id`).
pub fn kind(tag: &Tag) -> Option<String> {
text(tag.get("id"))
horizon_client_core::identifier::normalize_id(&text(tag.get("id"))?)
}
pub fn position(tag: &Tag) -> Option<Vec3> {
read_doubles::<3>(tag.get("Pos")).map(|p| Vec3::new(p[0], p[1], p[2]))
@@ -2238,7 +2238,12 @@ pub fn arrow_to_nbt(
return None;
};
let base = Base {
id: "minecraft:arrow",
// A spectral arrow is its own entity type.
id: if a.spectral || &*a.pickup_item.item == "minecraft:spectral_arrow" {
"minecraft:spectral_arrow"
} else {
"minecraft:arrow"
},
uuid,
position: p.position,
motion: p.velocity,
@@ -2298,10 +2303,17 @@ pub fn arrow_from_nbt(
let position = position(tag)?;
let owner = uuid_from_tag(tag.get("Owner"));
let from_player = player.is_some() && owner == player;
// `getDefaultPickupItem`: a `SpectralArrow` is picked up as its own item.
let spectral = kind(tag).as_deref() == Some("minecraft:spectral_arrow");
let default_item = if spectral {
"minecraft:spectral_arrow"
} else {
"minecraft:arrow"
};
let item = tag
.get("item")
.and_then(item_from_nbt)
.unwrap_or_else(|| crate::items::ItemStack::new("minecraft:arrow", 1));
.unwrap_or_else(|| crate::items::ItemStack::new(default_item, 1));
let kind = ProjectileKind::Arrow {
base_damage: float(tag.get("damage")).unwrap_or(2.0),
critical: flag(tag, "crit"),
@@ -2322,6 +2334,7 @@ pub fn arrow_from_nbt(
a.has_been_shot = flag(tag, "HasBeenShot");
a.fire_ticks = number(tag.get("Fire")).unwrap_or(0) as i16 as i32;
a.fall_distance = float(tag.get("fall_distance")).unwrap_or(0.0);
a.spectral = spectral;
a.pickup_item = item;
a.fired_from_weapon = tag.get("weapon").and_then(item_from_nbt);
p.stuck = a.in_ground;
@@ -2403,12 +2416,13 @@ fn put_projectile(tag: &mut Tag, p: &Projectile, owner: Option<[i32; 4]>) {
/// The thrown item projectiles Horizon saves: each entity type's id is also
/// the item it saves when none was loaded (`getDefaultItem`).
const THROWABLE_ITEMS: [&str; 5] = [
const THROWABLE_ITEMS: [&str; 6] = [
"minecraft:snowball",
"minecraft:egg",
"minecraft:ender_pearl",
"minecraft:splash_potion",
"minecraft:lingering_potion",
"minecraft:experience_bottle",
];
/// The item a throwable flies as: its stack (a potion's), the mod type's
/// item, or the item its entity type is named after.
@@ -2432,6 +2446,7 @@ pub fn throwable_kind(id: &str) -> Option<ProjectileKind> {
"minecraft:egg" => Some(ProjectileKind::Egg),
"minecraft:ender_pearl" => Some(ProjectileKind::EnderPearl),
// The entity type is the id; the `Item` read later may be another potion.
"minecraft:experience_bottle" => Some(ProjectileKind::ExperienceBottle),
_ => Some(ProjectileKind::Potion(
Box::new(crate::items::ItemStack::new(id, 1)),
id == "minecraft:lingering_potion",
@@ -2454,12 +2469,17 @@ pub fn saved_with_chunk(p: &Projectile) -> bool {
| ProjectileKind::Egg
| ProjectileKind::EnderPearl
| ProjectileKind::Potion(..)
| ProjectileKind::ExperienceBottle
| ProjectileKind::LlamaSpit
| ProjectileKind::ShulkerBullet { target: None, .. }
| ProjectileKind::Thrown(_) => true,
ProjectileKind::Arrow { .. } => p.arrow.is_some(),
// Not written yet: tridents save from their own list.
// Not written yet: tridents save from their own list, and a bullet
// with a target would lose it.
ProjectileKind::Trident
| ProjectileKind::LlamaSpit
| ProjectileKind::ShulkerBullet { .. }
| ProjectileKind::ShulkerBullet {
target: Some(_), ..
}
| ProjectileKind::WitherSkull { .. } => false,
}
}
@@ -2522,6 +2542,59 @@ pub fn throwable_from_nbt(tag: &Tag, player: Option<[i32; 4]>) -> Option<Project
Some(p)
}
/// `LlamaSpit` and `ShulkerBullet` as `/summon` or a chunk loads them:
/// `Projectile`'s `Owner`, `LeftOwner`, `NoGravity` and the motion. A bullet
/// loaded without a `Target` has none, so it falls (`ShulkerBullet.tick`).
pub fn spit_or_bullet_from_nbt(tag: &Tag, player: Option<[i32; 4]>) -> Option<Projectile> {
let kind = match kind(tag)?.as_str() {
"minecraft:llama_spit" => ProjectileKind::LlamaSpit,
"minecraft:shulker_bullet" => ProjectileKind::ShulkerBullet {
target: None,
dir: None,
steps: 0,
delta: [0.0; 3],
seed: 0,
},
_ => return None,
};
let owner = owner_uuid(tag);
let from_player = player.is_some() && owner == player;
let mut p = Projectile::new(kind, position(tag)?, from_player.then_some(Who::Player));
p.velocity = motion(tag);
[p.yaw, p.pitch] = rotation(tag);
p.left_owner = flag(tag, "LeftOwner");
p.owner_uuid = owner;
p.no_gravity = flag(tag, "NoGravity");
Some(p)
}
/// The save of [`spit_or_bullet_from_nbt`]'s kinds: `Projectile`'s keys.
pub fn spit_or_bullet_to_nbt(
p: &Projectile,
uuid: [i32; 4],
owner: Option<[i32; 4]>,
raw: Option<&Tag>,
) -> Option<Tag> {
if !matches!(
p.kind,
ProjectileKind::LlamaSpit | ProjectileKind::ShulkerBullet { target: None, .. }
) {
return None;
}
let base = Base {
id: p.kind.id(),
uuid,
position: p.position,
motion: p.velocity,
rotation: [p.yaw, p.pitch],
on_ground: false,
fall_distance: 0.0,
};
let mut tag = base.tag(raw);
put_projectile(&mut tag, p, owner);
Some(tag)
}
/// `Projectile`'s `Owner`.
pub fn owner_uuid(tag: &Tag) -> Option<[i32; 4]> {
uuid_from_tag(tag.get("Owner"))
@@ -3311,6 +3384,31 @@ mod tests {
use super::*;
use crate::items::ItemStack;
#[test]
fn mob_loading_accepts_default_namespace_and_preserves_custom_namespaces() {
let cow = crate::mobs::sensor_scan_tests::kind();
let types = [
("minecraft:cow".into(), cow.clone()),
("test:cow".into(), cow),
]
.into();
let pos = || {
Tag::List(
[4.5_f64, 220.0, 6.5]
.map(|v| Tag::Double(v.to_bits()))
.to_vec(),
)
};
for id in ["cow", ":cow", "minecraft:cow", "test:cow"] {
let tag = Tag::compound([("id", string(id)), ("Pos", pos()), ("NoAI", Tag::Byte(1))]);
let mob = mob_from_nbt(&tag, &types, 7, None).expect(id);
assert_eq!(mob.position, Vec3::new(4.5, 220.0, 6.5));
assert!(mob.no_ai);
}
let unknown = Tag::compound([("id", string("other:cow")), ("Pos", pos())]);
assert!(mob_from_nbt(&unknown, &types, 8, None).is_none());
}
/// A saved rocket and trident come back with their own Life, LifeTime,
/// flags and position (the keys vanilla's addAdditionalSaveData writes).
#[test]
@@ -5287,6 +5385,72 @@ mod mod_thrown_nbt_tests {
// a type no mod declares now is not a thrown kind.
assert!(throwable_kind("gone:bolt").is_none());
}
fn summoned(id: &str, extra: &[(&str, Tag)]) -> Tag {
let mut c = vec![
("id", Tag::String(id.into())),
("Pos", doubles(&[0.5, 110.0, 0.5])),
("Motion", doubles(&[0.0, 0.0, 0.0])),
];
c.extend(extra.iter().cloned());
Tag::compound(c)
}
/// Four summoned kinds had no loader and stayed inert (a spectral arrow,
/// a shulker bullet, a llama's spit and an experience bottle never fell).
#[test]
fn summoned_projectiles_of_every_kind_load_and_honour_no_gravity() {
use crate::entity_rules::EntityRules;
let flag = ("NoGravity", Tag::Byte(1));
let arrow = arrow_from_nbt(&summoned("minecraft:spectral_arrow", &[]), None, |_| None)
.unwrap()
.0;
assert!(arrow.is_spectral_arrow() && !arrow.no_gravity);
let held = arrow_from_nbt(
&summoned("minecraft:spectral_arrow", std::slice::from_ref(&flag)),
None,
|_| None,
)
.unwrap()
.0;
assert!(held.no_gravity);
// ...and it is saved as the entity type it is.
let saved = arrow_to_nbt(&arrow, [1; 4], None, None, None).unwrap();
assert_eq!(kind(&saved).as_deref(), Some("minecraft:spectral_arrow"));
let plain = arrow_from_nbt(&summoned("minecraft:arrow", &[]), None, |_| None)
.unwrap()
.0;
let saved = arrow_to_nbt(&plain, [1; 4], None, None, None).unwrap();
assert_eq!(kind(&saved).as_deref(), Some("minecraft:arrow"));
for id in ["minecraft:llama_spit", "minecraft:shulker_bullet"] {
let p = spit_or_bullet_from_nbt(&summoned(id, &[]), None).unwrap();
assert_eq!(p.kind.id(), id);
assert!(!p.no_gravity && p.position == Vec3::new(0.5, 110.0, 0.5));
let held =
spit_or_bullet_from_nbt(&summoned(id, std::slice::from_ref(&flag)), None).unwrap();
assert!(held.no_gravity);
}
assert!(spit_or_bullet_from_nbt(&summoned("minecraft:arrow", &[]), None).is_none());
// a spit and a target-less bullet are saved and selectable (`/data`, `@e`).
let spit = spit_or_bullet_from_nbt(&summoned("minecraft:llama_spit", &[]), None).unwrap();
assert!(saved_with_chunk(&spit));
let saved = spit_or_bullet_to_nbt(&spit, [1; 4], None, None).unwrap();
assert_eq!(kind(&saved).as_deref(), Some("minecraft:llama_spit"));
// A summoned experience bottle flies as the bottle item, with a bottle's gravity.
let bottle =
throwable_from_nbt(&summoned("minecraft:experience_bottle", &[]), None).unwrap();
assert_eq!(bottle.kind.id(), "minecraft:experience_bottle");
assert!(!bottle.no_gravity && (bottle.get_gravity() - 0.07).abs() < 1e-12);
let held =
throwable_from_nbt(&summoned("minecraft:experience_bottle", &[flag]), None).unwrap();
assert!(held.no_gravity && held.get_gravity() == 0.0);
let saved = throwable_to_nbt(&bottle, [1; 4], None, None).unwrap();
assert_eq!(kind(&saved).as_deref(), Some("minecraft:experience_bottle"));
let item = saved.get("Item").and_then(item_from_nbt).unwrap();
assert_eq!(&*item.item, "minecraft:experience_bottle");
}
#[test]
fn no_gravity_survives_save_and_load_for_orbs() {
// Entity.saveWithoutId / load: NoGravity round-trips on a kind that
+12 -8
View File
@@ -3782,13 +3782,13 @@ impl Game<'_> {
/// Item entity `i` leaves the level (`Entity.discard`); mods hear it.
pub(super) fn remove_dropped(&mut self, i: usize) -> ItemEntity {
let e = self.dropped.remove(i);
self.native_entities_left("minecraft:item", &[e.id]);
self.native_entity_left(e.id);
e
}
/// Experience orb `i` leaves the level (`Entity.discard`); mods hear it.
pub(super) fn remove_orb(&mut self, i: usize) -> ExperienceOrb {
let o = self.orbs.remove(i);
self.native_entities_left("minecraft:experience_orb", &[o.id]);
self.native_entity_left(o.id);
o
}
/// The item entity `spawn_item` just added, unless a native mod kept it
@@ -7332,6 +7332,9 @@ impl Game<'_> {
if running {
horizon_client::spans::time("tick_lan_packets", || self.tick_lan_packets());
horizon_client::spans::time("tick_blocks", || self.tick_blocks())?;
// What the blocks spawned (a dispensed minecart, a lit TNT, a
// placed boat) joins before the entities tick.
self.native_joins_flush(false, 0);
}
if running {
horizon_client::spans::time("tick_paintings", || self.tick_paintings());
@@ -7363,34 +7366,35 @@ impl Game<'_> {
self.handle_entity_portals();
self.handle_end_gateway_portals();
});
let before = self.dropped_ids();
horizon_client::spans::time("tick_items", || self.tick_items());
if self.remote.is_none() {
self.hoppers_catch_items();
}
// Merged, burnt, despawned or picked up.
self.native_items_left_since(&before);
self.native_leaves_flush();
self.native_joins_flush(false, 0);
horizon_client::spans::time("touch_arrows", || self.touch_arrows());
}
horizon_client::spans::time("spawn_loaded_entities", || self.spawn_loaded_entities());
// The ride the player's file names, once the vehicle is in the world.
self.restore_root_vehicle();
if running {
let mobs_before = self.mob_ids();
horizon_client::spans::time("tick_mobs", || self.tick_mobs())?;
// Dead, despawned or gone into a hive.
self.native_mobs_left_since(&mobs_before);
self.native_leaves_flush();
horizon_client::spans::time("recheck_golem_openers", || self.recheck_golem_openers());
self.tick_mod_container_viewers();
let before = self.orb_ids();
horizon_client::spans::time("tick_experience", || self.tick_experience());
// Merged, despawned or picked up.
self.native_orbs_left_since(&before);
self.native_leaves_flush();
horizon_client::spans::time("entity_portals", || {
self.find_entity_portals();
self.resolve_arrivals();
});
}
// The rest of the tick's spawns join, and what left is told of.
self.native_joins_flush(false, 0);
self.native_leaves_flush();
horizon_client::spans::time("tick_lan", || self.tick_lan(false));
self.tick_friends();
horizon_client::spans::time("tick_hand", || self.tick_hand());
+17 -11
View File
@@ -810,17 +810,23 @@ impl Game<'_> {
subject: None,
};
let mut tops: Vec<BlockPos> = Vec::new();
a.tick(&mut scene, facts, in_water, &mut |bounds, velocity| {
horizon_client::motion::bubble_column_push_over(
world,
registry,
facts,
collision,
bounds,
velocity,
|pos| tops.push(pos),
);
});
a.tick(
&mut scene,
facts,
in_water,
&mut |bounds, velocity| {
horizon_client::motion::bubble_column_push_over(
world,
registry,
facts,
collision,
bounds,
velocity,
|pos| tops.push(pos),
);
},
Some(self.border.geometry()),
);
for pos in tops {
self.bubble_column_particles(pos);
}
+1 -2
View File
@@ -2105,9 +2105,8 @@ impl Game<'_> {
Target::Spawner(id) => self.ominous_spawners.retain(|s| s.id != id),
}
// The orbs marked for removal are gone (Entity.kill removes them).
let before = self.orb_ids();
self.orbs.retain(|o| o.age < u32::MAX / 2);
self.native_orbs_left_since(&before);
self.native_leaves_flush();
}
fn cmd_kill(&mut self, ctx: &Context) -> R<i32> {
let targets = if ctx.get("targets").is_some() {
@@ -173,7 +173,8 @@ impl Game<'_> {
let raw = self.persist.raw.get(&id);
entity_nbt::arrow_to_nbt(p, random_uuid(), owner, in_block, raw)
.or_else(|| entity_nbt::fireball_to_nbt(p, random_uuid(), owner, raw))
.or_else(|| entity_nbt::throwable_to_nbt(p, random_uuid(), owner, raw))?
.or_else(|| entity_nbt::throwable_to_nbt(p, random_uuid(), owner, raw))
.or_else(|| entity_nbt::spit_or_bullet_to_nbt(p, random_uuid(), owner, raw))?
}
Target::Crystal(id) => {
let c = self.crystals.iter().find(|c| c.id == id)?;
@@ -879,7 +880,9 @@ impl Game<'_> {
let target = self.spawn_entity_from_nbt(tag);
self.native_mobs_joined(from, false);
// A mod kept the entity out: tryAddFreshEntityWithPassengers is false.
if target.is_none() && self.persist.joins_canceled != canceled {
if self.persist.joins_canceled != canceled
&& target.is_none_or(|t| self.target_position(t).is_none())
{
return Err(err("commands.summon.failed.uuid", &[]));
}
Ok(target)
+24 -8
View File
@@ -1053,9 +1053,9 @@ impl Game<'_> {
for e in &mut events[from..] {
if let MobEvent::Projectile(p) = e
&& let ProjectileKind::ShulkerBullet { target, .. } = &mut p.kind
&& *target == Who::Player
&& *target == Some(Who::Player)
{
*target = Who::Mob(id);
*target = Some(Who::Mob(id));
}
}
}
@@ -4813,7 +4813,16 @@ impl Game<'_> {
// flies as: the splash happens where the hit was, a lingering
// potion's cloud sits at the mob it hit (ThrownLingeringPotion),
// and the potion that lands names the hurt it deals.
(ProjectileKind::Potion(stack, _), hit) => {
(kind @ (ProjectileKind::Potion(..) | ProjectileKind::ExperienceBottle), hit) => {
// A bottle flies as the bottle item (`getDefaultItem`).
let bottle;
let stack = match kind {
ProjectileKind::Potion(stack, _) => stack,
_ => {
bottle = Box::new(ItemStack::new("minecraft:experience_bottle", 1));
&bottle
}
};
let block = match hit {
Hit::Block { pos, face, .. } => Some((
pos,
@@ -6528,12 +6537,15 @@ impl Game<'_> {
Vec::new()
};
let body = out.len();
// The GPU path: an ordinary mob, no outline, no layers the CPU path changes after
// drawing them (a player's, the HDR tiers below, the sulfur cube's shell).
// The GPU path: an ordinary mob, no layers the CPU path changes after drawing them
// (a player's, the HDR tiers below, the sulfur cube's shell). A glowing mob's
// silhouette is posed too (`SkinFrame::outline_since`), unless it is invisible:
// then only the outline draws.
let silhouette_on_gpu = outline.is_none() || !mob.invisible;
let posable = skinning
&& !is_player
&& !layers_only
&& outline.is_none()
&& silhouette_on_gpu
&& armor.is_empty()
&& !matches!(
mob.species(),
@@ -6612,14 +6624,18 @@ impl Game<'_> {
}
// CustomHeadLayer (a pumpkin, skull or head item) included.
if layers {
// Held items join the GPU path's draws unless an outline reads their vertices.
let skin = (skinning && outline.is_none()).then_some(&mut skinned);
// Held items join the GPU path's draws, their silhouette too.
let skin = (skinning && silhouette_on_gpu).then_some(&mut skinned);
self.mob_block_geometry(mob, camera, alpha, packed, skin, out);
// `native_mob_layer`: a native mod's layer, over the vanilla ones.
self.mod_mob_layer_geometry(mob, who, alpha as f32, camera, packed, out);
}
if let Some(rgb) = outline {
self.mark_outline(&out[entity_start..], rgb);
if let Some(((first, _, _), _)) = skinned {
let color = horizon_client::mobs::render::abgr(rgb);
self.skin_frame.borrow_mut().outline_since(first, color);
}
if mob.invisible {
out.truncate(entity_start);
continue;
@@ -1100,6 +1100,7 @@ impl Game<'_> {
| K::EnderPearl
| K::LlamaSpit
| K::Potion { .. }
| K::ExperienceBottle
| K::Thrown(_) => (0.25, 0.2125),
K::SmallFireball | K::ShulkerBullet { .. } | K::WitherSkull { .. } => {
(0.3125, 0.265_625)
+1 -1
View File
@@ -241,7 +241,7 @@ mod tests {
// A shulker bullet takes a blow, with no pick radius round its box.
let bullet = Projectile::new(
ProjectileKind::ShulkerBullet {
target: Who::Player,
target: Some(Who::Player),
dir: None,
steps: 0,
delta: [0.0; 3],
+3
View File
@@ -1456,6 +1456,7 @@ impl Game<'_> {
// A gateway keeps the item in its level (teleportSameDimension
// moves it): it doesn't leave, so no native_entity_leave_level.
self.dropped.remove(i);
self.native_leave_skip(id);
self.detach_entity(id);
tag
} else if let Some(i) = self.mobs.iter().position(|m| m.id == id) {
@@ -1479,11 +1480,13 @@ impl Game<'_> {
}
self.mobs.retain(|m| !gone.contains(&m.id));
for g in gone {
self.native_leave_skip(g);
self.detach_entity(g);
}
tag
} else if let Some(i) = self.creatures.projectiles.iter().position(|p| p.id == id) {
let pearl = Box::new(self.creatures.projectiles.remove(i));
self.native_leave_skip(id);
let (job, target) = self.gateway_job([gateway.x, gateway.y, gateway.z]);
let arrival = GatewayArrival {
traveller: Arriving::Pearl(pearl),
+1 -3
View File
@@ -623,7 +623,6 @@ impl Game<'_> {
items_ignore: items_ignore_explosion(self.ui.rules.bool("mob_griefing"), interaction),
spared: exploding.filter(|_| self.combat.explosion_source_named),
};
let (items_before, orbs_before) = (self.dropped_ids(), self.orb_ids());
let (items_len, orbs_len) = (self.dropped.len(), self.orbs.len());
let (mut items_kept, mut orbs_kept) = (0, 0);
let (mut boats_gone, mut carts_gone) = (0, 0);
@@ -723,8 +722,7 @@ impl Game<'_> {
}
self.dropped.drain(items_kept..items_len);
self.orbs.drain(orbs_kept..orbs_len);
self.native_items_left_since(&items_before);
self.native_orbs_left_since(&orbs_before);
self.native_leaves_flush();
// Frames, paintings, cushions and armour stands last: vanilla lists
// the entities in reach before hurting any, so the items they drop
// here are neither destroyed nor pushed by the same blast.
+6 -2
View File
@@ -618,8 +618,8 @@ impl Game<'_> {
seed,
);
let glowing = self.entity_glowing(item.id);
// Posed on the GPU when every layer can be (the outline reads the CPU's vertices).
let skinned = (skinning && !glowing)
// Posed on the GPU when every layer can be, its silhouette too when it glows.
let skinned = skinning
.then(|| self.submit_item_skinned(&state, &copies, vertex_light(packed), lights))
.flatten();
if skinned.is_none() {
@@ -630,6 +630,10 @@ impl Game<'_> {
if glowing {
let rgb = super::glowing::team_color(self.mob_team(item.id));
self.mark_outline(&out[entity_start..], rgb);
if let Some(((first, _, _), _)) = skinned {
let color = horizon_client::mobs::render::abgr(rgb);
self.skin_frame.borrow_mut().outline_since(first, color);
}
} else {
self.cull_skinned(out, entity_start, skinned);
}
+2 -4
View File
@@ -635,7 +635,6 @@ impl Game<'_> {
// over the 40 a vehicle takes) destroys them. Lightning is no fire
// source, so a TNT cart does not prime. `ExperienceOrb.hurtServer`:
// 5 damage off 5 health. Last first, since each removes its entry.
let orbs_before = self.orb_ids();
let mut left = self.thunder_count(strike, ThunderHit::Vehicle)
+ self.thunder_count(strike, ThunderHit::Orb);
for h in self.entity_handles_rev() {
@@ -659,7 +658,7 @@ impl Game<'_> {
_ => {}
}
}
self.native_orbs_left_since(&orbs_before);
self.native_leaves_flush();
// Every player in the box, the server's players too (0.6 x 1.8).
let strike_players = move |g: &mut Self| {
if let Some(p) = g.player.as_ref().map(|p| p.position)
@@ -679,7 +678,6 @@ impl Game<'_> {
strike_players(self);
self.each_guest_hit(strike_players);
// Dropped items burn up (ItemEntity health 5), save fire-resistant ones.
let before = self.dropped_ids();
let mut left = self.thunder_count(strike, ThunderHit::Item);
for h in self.entity_handles_rev() {
if left == 0 {
@@ -696,7 +694,7 @@ impl Game<'_> {
self.dropped.remove(i);
}
}
self.native_items_left_since(&before);
self.native_leaves_flush();
struck
}
/// `LightningBolt.shouldRenderAtSqrDistance`: a bolt is drawn within 64
+177 -21
View File
@@ -258,9 +258,38 @@ impl Game<'_> {
}
}
/// Entities joining the level (`native_entity_join_level`), for every kind in
/// the entity walk. Kept out of `native_hooks.rs`, whose lines may not hold macros.
/// The entities the walk does not hold: Horizon keeps these in lists of their own.
#[derive(Clone, Copy)]
enum Apart {
Crystal(usize),
Cushion(usize),
Painting(usize),
Frame(usize),
Dragon,
}
/// One entity a mod is told of: in the walk, or apart from it.
#[derive(Clone, Copy)]
enum Slot {
Walk(super::entity::EntityHandle),
Apart(Apart),
}
/// Entities joining and leaving the level (`native_entity_join_level`,
/// `native_entity_leave_level`), for every kind in the entity walk and the
/// five kept apart from it. Kept out of `native_hooks.rs`, whose lines may not hold macros.
impl Game<'_> {
/// Whether an entity may be waiting to be told of: one with an id above
/// the last announced, or one of the kinds that take their id late and
/// have none. Reads a counter and five lists' ids; no walk.
fn joins_pending(&self) -> bool {
self.next_entity > self.persist.joined_mark
|| self.tnts.iter().any(|t| t.id == 0)
|| self.falling.iter().any(|f| f.id == 0)
|| self.creatures.projectiles.iter().any(|p| p.id == 0)
|| self.tridents.iter().any(|t| t.id == 0)
|| self.rockets.iter().any(|r| r.id == 0)
}
/// Tells `native_entity_join_level` once for every entity added since the
/// last call, of any kind, in the order they were created (ids count up
/// from one counter). A mod that cancels takes the entity back out here,
@@ -268,11 +297,10 @@ impl Game<'_> {
/// `loaded_from_disk` is for the entities that came with a chunk. Returns
/// whether `watch` (an id, or 0) is still in the level. Walks the lists
/// with static dispatch and allocates nothing but the kind name of an
/// entity it announces; nothing runs when no mod is loaded or on a client
/// of a server (which tells nothing).
/// entity it announces; nothing runs when no mod is loaded, on a client
/// of a server (which tells nothing), or when nothing is new.
pub(super) fn native_joins_flush(&mut self, loaded_from_disk: bool, watch: u32) -> bool {
use super::entity::Entity;
if self.remote.is_some() || crate::mod_api::mods().is_empty() {
if self.remote.is_some() || crate::mod_api::mods().is_empty() || !self.joins_pending() {
return true;
}
for t in &mut self.tnts {
@@ -291,11 +319,9 @@ impl Game<'_> {
super::lan_entities::assign_entity_id(&mut self.next_entity, &mut r.id);
}
let mut kept = true;
while let Some((handle, id)) = self.next_unannounced() {
while let Some((slot, id)) = self.next_unannounced() {
self.persist.joined_mark = id;
let (kind, at) = super::entity::with_entity!(self, handle, e => {
(e.native_type(), e.position())
});
let (kind, at) = self.slot_kind_and_position(slot);
let mut canceled = false;
let dimension = self.dim.id();
self.native_entity_join_level(
@@ -311,32 +337,162 @@ impl Game<'_> {
self.forget_entity(id);
// Handles are indexes: the entity is taken out before any
// other change to its list.
self.discard_entity(handle);
self.discard_slot(slot);
kept &= id != watch;
} else {
self.persist.live.push((id, kind.into_owned(), true));
}
}
kept
}
/// The entity with the lowest id above the last one announced, by handle.
fn next_unannounced(&self) -> Option<(super::entity::EntityHandle, u32)> {
/// The entity with the lowest id above the last one announced.
fn next_unannounced(&self) -> Option<(Slot, u32)> {
use super::entity::Entity;
let mark = self.persist.joined_mark;
let mut best: Option<(super::entity::EntityHandle, u32)> = None;
for handle in self.entity_handles() {
let id = {
use super::entity::Entity;
super::entity::with_entity!(self, handle, e => e.id())
};
let mut best: Option<(Slot, u32)> = None;
let mut offer = |slot: Slot, id: u32| {
if id > mark && best.is_none_or(|(_, b)| id < b) {
best = Some((handle, id));
best = Some((slot, id));
}
};
for handle in self.entity_handles() {
offer(
Slot::Walk(handle),
super::entity::with_entity!(self, handle, e => e.id()),
);
}
for (i, c) in self.crystals.iter().enumerate() {
offer(Slot::Apart(Apart::Crystal(i)), c.id);
}
for (i, c) in self.cushions.iter().enumerate() {
offer(Slot::Apart(Apart::Cushion(i)), c.id);
}
for (i, p) in self.paintings.iter().enumerate() {
offer(Slot::Apart(Apart::Painting(i)), p.id);
}
for (i, f) in self.item_frames.iter().enumerate() {
offer(Slot::Apart(Apart::Frame(i)), f.id);
}
if let Some(d) = &self.dragon {
offer(Slot::Apart(Apart::Dragon), d.id);
}
best
}
/// The type id and position a mod is told of.
fn slot_kind_and_position(&self, slot: Slot) -> (std::borrow::Cow<'static, str>, Vec3) {
use super::entity::Entity;
let block = |p: [i32; 3]| {
Vec3::new(
f64::from(p[0]) + 0.5,
f64::from(p[1]) + 0.5,
f64::from(p[2]) + 0.5,
)
};
match slot {
Slot::Walk(h) => {
super::entity::with_entity!(self, h, e => (e.native_type(), e.position()))
}
Slot::Apart(Apart::Crystal(i)) => {
("minecraft:end_crystal".into(), self.crystals[i].position)
}
Slot::Apart(Apart::Cushion(i)) => {
("minecraft:cushion".into(), self.cushions[i].position)
}
Slot::Apart(Apart::Painting(i)) => {
("minecraft:painting".into(), block(self.paintings[i].pos))
}
Slot::Apart(Apart::Frame(i)) => {
let f = &self.item_frames[i];
let kind = if f.glow {
"minecraft:glow_item_frame"
} else {
"minecraft:item_frame"
};
(kind.into(), block(f.pos))
}
Slot::Apart(Apart::Dragon) => (
"minecraft:ender_dragon".into(),
self.dragon.as_ref().map_or(Vec3::ZERO, |d| d.position),
),
}
}
/// Takes the entity out of the level, before it ever ticked.
fn discard_slot(&mut self, slot: Slot) {
match slot {
Slot::Walk(h) => self.discard_entity(h),
Slot::Apart(Apart::Crystal(i)) => drop(self.crystals.remove(i)),
Slot::Apart(Apart::Cushion(i)) => drop(self.cushions.remove(i)),
Slot::Apart(Apart::Painting(i)) => drop(self.paintings.remove(i)),
Slot::Apart(Apart::Frame(i)) => drop(self.item_frames.remove(i)),
Slot::Apart(Apart::Dragon) => self.dragon = None,
}
}
/// Entities that were moved, not added (a portal's arrival keeps being
/// silent): everything now in the level counts as announced.
/// silent): everything now in the level counts as announced, and will
/// be told when it leaves.
pub(super) fn native_joins_skip(&mut self) {
if self.remote.is_none() && !crate::mod_api::mods().is_empty() {
while let Some((slot, id)) = self.next_unannounced() {
self.persist.joined_mark = id;
let kind = self.slot_kind_and_position(slot).0.into_owned();
self.persist.live.push((id, kind, true));
}
}
self.persist.joined_mark = self.persist.joined_mark.max(self.next_entity);
}
/// An entity taken out of the level without leaving it (an item through
/// an End gateway): forgotten, not told of.
pub(super) fn native_leave_skip(&mut self, id: u32) {
if let Ok(i) = self.persist.live.binary_search_by_key(&id, |l| l.0) {
self.persist.live.remove(i);
}
}
/// Entity `id` has left the level: told once, if mods were told it joined.
pub(super) fn native_entity_left(&mut self, id: u32) {
if self.remote.is_some() {
return;
}
if let Ok(i) = self.persist.live.binary_search_by_key(&id, |l| l.0) {
let (id, kind, _) = self.persist.live.remove(i);
let dimension = self.dim.id();
self.native_entity_leave_level(dimension, id, &kind);
}
}
/// Tells `native_entity_leave_level` for every announced entity that is
/// no longer in any list (died, despawned, picked up, burnt, blown up, gone
/// through a portal, unloaded), lowest id first. Marks the entities still
/// there in the registry and tells the rest: no allocation, and nothing
/// runs when no mod is loaded or none was announced.
pub(super) fn native_leaves_flush(&mut self) {
use super::entity::Entity;
if self.persist.live.is_empty() || self.remote.is_some() {
return;
}
for l in &mut self.persist.live {
l.2 = false;
}
let live = &mut self.persist.live;
let mut seen = |id: u32| {
if let Ok(i) = live.binary_search_by_key(&id, |l| l.0) {
live[i].2 = true;
}
};
super::entity::for_each_entity!(super::entity::entity_lists_of!(self), e => {
seen(e.id());
});
self.crystals.iter().for_each(|c| seen(c.id));
self.cushions.iter().for_each(|c| seen(c.id));
self.paintings.iter().for_each(|p| seen(p.id));
self.item_frames.iter().for_each(|f| seen(f.id));
if let Some(d) = &self.dragon {
seen(d.id);
}
while let Some(i) = self.persist.live.iter().position(|l| !l.2) {
let (id, kind, _) = self.persist.live.remove(i);
let dimension = self.dim.id();
self.native_entity_leave_level(dimension, id, &kind);
}
}
}
#[cfg(test)]
+24 -120
View File
@@ -697,7 +697,7 @@
//! change a cauldron.
//!
//! API 53 adds entities leaving a level (`native_entity_leave_level`), for
//! item entities, experience orbs and mobs so far.
//! every kind the join hook tells (see API 47).
//!
//! API 52 adds `native_block_entity_sync`: a mod block entity's block and update tag
//! sent to clients again (`Level.sendBlockUpdated`).
@@ -718,7 +718,7 @@
//! API 47 adds entities joining a level (`native_entity_join_level`); it tells
//! every kind the game keeps in its entity lists (items, orbs, mobs, boats,
//! minecarts, armor stands, TNT, falling blocks, projectiles, rockets,
//! tridents).
//! tridents, end crystals, the dragon, cushions, paintings, item frames).
//!
//! API 46 adds bounded world-space rim and colored-light requests
//! (`native_visuals`) and opt-in texture-matched emissive material
@@ -2309,6 +2309,16 @@ impl Game<'_> {
pub(super) fn native_commands(&mut self, commands: &mut Vec<&'static str>) {
mod_api::dispatch_commands(self, mod_api::mods(), commands);
}
/// Local player movement input, after reading it and before applying it.
pub(super) fn native_movement_input(
&self,
forward: &mut f32,
strafe: &mut f32,
jump: &mut bool,
sneak: &mut bool,
) {
mod_api::dispatch_movement_input(self, mod_api::mods(), forward, strafe, jump, sneak);
}
/// World field of view, after normal calculation and zoom and before projection/Nausea.
/// Called for world rendering and near-plane overlays; hand/HUD FOV uses its own path.
/// Only finite values in 1..=179 degrees replace the computed FOV.
@@ -6369,8 +6379,10 @@ impl Game<'_> {
/// a level closing with nobody in it included. Not for the entities still
/// loaded when the server stops, as in vanilla. `entity` is its ID and
/// `kind` its type (`minecraft:item`); it is no longer in the level's lists.
/// It only hears. 53 tells item entities, experience orbs and mobs (`kind`
/// the mob type); later APIs add other kinds, so check `kind`. Server-side.
/// It only hears. 53 told item entities, experience orbs and mobs; it now
/// tells every kind `native_entity_join_level` does (`kind` is the type), each
/// once, when the game next notices it gone (the tick or action that removed
/// it). Check `kind`. Server-side.
pub(super) fn native_entity_leave_level(&mut self, dimension: &str, entity: u32, kind: &str) {
mod_api::dispatch_entity_leave_level(self, mod_api::mods(), dimension, entity, kind);
}
@@ -6384,12 +6396,14 @@ impl Game<'_> {
/// back out as if never added, and a `/summon` of it fails. Tells every
/// kind in the game's entity lists (items, orbs, mobs, boats, minecarts,
/// armor stands, primed TNT, falling blocks, projectiles, rockets,
/// tridents), once each, in the order they were created; check `kind`. A
/// gameplay spawn is told when the next tick starts (a drop, an orb, a mob, a
/// `/summon` and a chunk's entities are told at once). A mod that
/// cancels one of the others sees it removed before it ticks again.
/// Not told: end crystals, the dragon, cushions, paintings, item frames and
/// the entities Horizon keeps without simulating. Server-side.
/// tridents, end crystals, the dragon, cushions, paintings, item frames),
/// once each, in the order they were created; check `kind`. A drop, an
/// orb, a mob, a `/summon` and a chunk's entities are told at once, any
/// other spawn between the tick's phases (it is checked after the blocks,
/// the vehicles, the projectiles, the items and the mobs, and at the
/// tick's end). A mod that cancels one of the others sees it removed
/// before it ticks again. Not told: the entities Horizon keeps without
/// simulating (displays, markers, interactions). Server-side.
pub(super) fn native_entity_join_level(
&mut self,
dimension: &str,
@@ -8310,22 +8324,6 @@ pub(super) fn mob_kind(mob: &horizon_client::mobs::Mob) -> String {
["minecraft:", mob.kind.id.trim_start_matches("minecraft:")].concat()
}
/// The ids of `before` (id, kind) that are not in `now`, grouped by kind in
/// first-seen order: the entities that left the level.
fn vanished_by_kind(
before: &[(u32, String)],
now: &std::collections::HashSet<u32>,
) -> Vec<(String, Vec<u32>)> {
let mut out: Vec<(String, Vec<u32>)> = Vec::new();
for (id, kind) in before.iter().filter(|(id, _)| !now.contains(id)) {
match out.iter_mut().find(|(k, _)| k == kind) {
Some((_, ids)) => ids.push(*id),
None => out.push((kind.clone(), Vec::from([*id]))),
}
}
out
}
/// Where the game calls the hook points.
impl Game<'_> {
/// The acting player as the hook points name one: the guest whose turn
@@ -8447,80 +8445,6 @@ impl Game<'_> {
self.native_mob_spawned(self.dim.id(), &kind, pos, natural);
}
}
/// `native_entity_leave_level` for each of `entities`, of `kind`, gone
/// from the level in place. A client of a server tells nothing.
pub(super) fn native_entities_left(&mut self, kind: &str, entities: &[u32]) {
if self.remote.is_some() {
return;
}
let dimension = self.dim.id();
for &entity in entities {
self.native_entity_leave_level(dimension, entity, kind);
}
}
/// The IDs of the level's item entities, for `native_items_left_since`
/// (none on a client of a server, which tells nothing).
pub(super) fn dropped_ids(&self) -> Vec<u32> {
if self.remote.is_some() || mod_api::mods().is_empty() {
return Vec::new();
}
self.dropped.iter().map(|e| e.id).collect()
}
/// The IDs of the level's experience orbs, for `native_orbs_left_since`.
pub(super) fn orb_ids(&self) -> Vec<u32> {
if self.remote.is_some() || mod_api::mods().is_empty() {
return Vec::new();
}
self.orbs.iter().map(|o| o.id).collect()
}
/// The (id, kind) of the level's mobs, for `native_mobs_left_since` (none
/// on a client of a server, which tells nothing).
pub(super) fn mob_ids(&self) -> Vec<(u32, String)> {
if self.remote.is_some() || mod_api::mods().is_empty() {
return Vec::new();
}
self.mobs.iter().map(|m| (m.id, mob_kind(m))).collect()
}
/// The mobs of `before` gone since have left the level (died, despawned,
/// entered a hive, unloaded). Mobs spawned meanwhile are not counted.
pub(super) fn native_mobs_left_since(&mut self, before: &[(u32, String)]) {
// Nothing was taken: no mod listens, or a client of a server.
if before.is_empty() {
return;
}
let now: std::collections::HashSet<u32> = self.mobs.iter().map(|m| m.id).collect();
for (kind, ids) in vanished_by_kind(before, &now) {
self.native_entities_left(&kind, &ids);
}
}
/// The item entities of `before` gone since have left the level.
pub(super) fn native_items_left_since(&mut self, before: &[u32]) {
// Nothing was taken: no mod listens, or a client of a server.
if before.is_empty() {
return;
}
let now: std::collections::HashSet<u32> = self.dropped.iter().map(|e| e.id).collect();
let gone: Vec<u32> = before
.iter()
.copied()
.filter(|id| !now.contains(id))
.collect();
self.native_entities_left("minecraft:item", &gone);
}
/// The experience orbs of `before` gone since have left the level.
pub(super) fn native_orbs_left_since(&mut self, before: &[u32]) {
// Nothing was taken: no mod listens, or a client of a server.
if before.is_empty() {
return;
}
let now: std::collections::HashSet<u32> = self.orbs.iter().map(|o| o.id).collect();
let gone: Vec<u32> = before
.iter()
.copied()
.filter(|id| !now.contains(id))
.collect();
self.native_entities_left("minecraft:experience_orb", &gone);
}
/// The acting player's blow killed mob `index`.
pub(super) fn native_kill_event(&mut self, index: usize) {
let (entity, kind) = (self.mobs[index].id, mob_kind(&self.mobs[index]));
@@ -9371,26 +9295,6 @@ fn mod_screen_opens(
#[cfg(test)]
mod tests {
#[test]
fn vanished_mobs_are_reported_by_kind() {
use std::collections::HashSet;
let z = || "minecraft:zombie".to_string();
let b = || "minecraft:bee".to_string();
let before = vec![(1, z()), (2, b()), (3, z()), (4, b())];
let now: HashSet<u32> = [1, 4, 99].into_iter().collect(); // 99 spawned meanwhile
assert_eq!(
super::vanished_by_kind(&before, &now),
vec![(b(), vec![2]), (z(), vec![3])]
);
let all: HashSet<u32> = [1, 2, 3, 4, 5].into_iter().collect();
assert!(super::vanished_by_kind(&before, &all).is_empty());
assert_eq!(
super::vanished_by_kind(&before, &HashSet::new()),
vec![(z(), vec![1, 3]), (b(), vec![2, 4])]
);
assert!(super::vanished_by_kind(&[], &now).is_empty());
}
/// Exercise the gameplay constructor, where name-based entities compete
/// with a mod block's sign template.
#[test]
+23 -7
View File
@@ -45,6 +45,10 @@ pub struct EntityMemory {
pub(super) joins_canceled: u32,
/// The highest entity id `native_entity_join_level` has been told of.
pub(super) joined_mark: u32,
/// The entities mods were told of and that have not left since: id (in
/// increasing order), type, and whether the last leave sweep saw it.
/// Only filled while a mod is loaded.
pub(super) live: Vec<(u32, String, bool)>,
}
/// The key that marks a compound queued with the chunk loads as a fresh add
@@ -355,6 +359,10 @@ impl Game<'_> {
kind.as_str(),
"minecraft:experience_orb"
| "minecraft:arrow"
| "minecraft:spectral_arrow"
| "minecraft:llama_spit"
| "minecraft:shulker_bullet"
| "minecraft:experience_bottle"
| "minecraft:fireball"
| "minecraft:small_fireball"
| "minecraft:wind_charge"
@@ -398,7 +406,7 @@ impl Game<'_> {
self.remember_tags(id, &tag);
}
}
"minecraft:arrow" => {
"minecraft:arrow" | "minecraft:spectral_arrow" => {
if let Some((mut p, in_block)) = entity_nbt::arrow_from_nbt(
&tag,
player_uuid,
@@ -431,6 +439,17 @@ impl Game<'_> {
self.remember_last_projectile_tags(&tag);
}
}
"minecraft:llama_spit" | "minecraft:shulker_bullet" => {
if let Some(mut p) = entity_nbt::spit_or_bullet_from_nbt(&tag, player_uuid)
{
self.claim_owner(&mut p);
self.next_entity += 1;
p.id = self.next_entity;
self.restore_portal_cooldown(p.id, &tag);
self.add_projectile(p);
self.remember_last_projectile_tags(&tag);
}
}
id if entity_nbt::throwable_kind(id).is_some() => {
if let Some(mut p) = entity_nbt::throwable_from_nbt(&tag, player_uuid) {
self.claim_owner(&mut p);
@@ -1326,7 +1345,6 @@ impl Game<'_> {
})
.map(|i| self.mobs[i].id)
.collect();
let mobs_before = self.mob_ids();
self.mobs.retain(|m| {
let keep = !leaving.contains(&m.id);
if !keep {
@@ -1334,7 +1352,6 @@ impl Game<'_> {
}
keep
});
let (items_before, orbs_before) = (self.dropped_ids(), self.orb_ids());
self.dropped.retain(|e| {
let keep = !gone(e.position);
if !keep {
@@ -1344,9 +1361,7 @@ impl Game<'_> {
});
self.orbs.retain(|o| !gone(o.position));
// Unloaded with their chunk: PersistentEntitySectionManager.stopTracking.
self.native_mobs_left_since(&mobs_before);
self.native_items_left_since(&items_before);
self.native_orbs_left_since(&orbs_before);
self.native_leaves_flush();
// the ones saved with the chunk go with it (a trident too, unsaved).
self.creatures.projectiles.retain(|p| {
let leaves = entity_nbt::saved_with_chunk(p) || p.arrow.is_some();
@@ -1512,7 +1527,8 @@ impl Game<'_> {
.map(|s| self.state_to_nbt(s));
let tag = entity_nbt::arrow_to_nbt(p, uuid, owner, in_block, raw.as_ref())
.or_else(|| entity_nbt::fireball_to_nbt(p, uuid, owner, raw.as_ref()))
.or_else(|| entity_nbt::throwable_to_nbt(p, uuid, owner, raw.as_ref()))?;
.or_else(|| entity_nbt::throwable_to_nbt(p, uuid, owner, raw.as_ref()))
.or_else(|| entity_nbt::spit_or_bullet_to_nbt(p, uuid, owner, raw.as_ref()))?;
Some((
chunk_of(p.position),
with_raw(tag, self.persist.raw.get(&p.id)),
+14
View File
@@ -5781,6 +5781,20 @@ impl Game<'_> {
if self.integrated.is_none() && self.server_address().is_some() {
keys.analog = None;
}
let [mut strafe, mut forward] = keys.direction();
let original = [strafe, forward];
self.native_movement_input(&mut forward, &mut strafe, &mut keys.jump, &mut keys.shift);
if [strafe, forward] != original {
if forward != original[1] {
keys.forward = forward > 0.0;
keys.backward = forward < 0.0;
}
if strafe != original[0] {
keys.left = strafe > 0.0;
keys.right = strafe < 0.0;
}
keys.analog = Some([strafe, forward]);
}
let input = u8::from(keys.forward)
| u8::from(keys.backward) << 1
| u8::from(keys.left) << 2
@@ -98,7 +98,7 @@ fn projectile_kind(kind: &str) -> Option<ProjectileKind> {
"minecraft:breeze_wind_charge" => ProjectileKind::BreezeWindCharge,
// Homing state stays on the server; the client interpolates its positions.
"minecraft:shulker_bullet" => ProjectileKind::ShulkerBullet {
target: horizon_client::mobs::Who::Player,
target: Some(horizon_client::mobs::Who::Player),
dir: None,
steps: 0,
delta: [0.0; 3],
+4 -1
View File
@@ -222,7 +222,10 @@ impl Game<'_> {
if r.trial {
tag.insert("PersistenceRequired".into(), Tag::Byte(1));
}
let tag = Tag::Compound(tag);
let mut tag = Tag::Compound(tag);
// EntityType.loadEntityRecursive applies the spawner's placement
// callback to every passenger as well as the root.
super::commands::place_summoned(&mut tag, r.position);
let mob = horizon_client::entity_nbt::mob_from_nbt(
&tag,
self.res.mob_types(),
+133 -53
View File
@@ -636,6 +636,7 @@ pub fn tick(
if m.species() == Species::Endermite {
endermite_lifetime(m);
}
ageable_step(m, w, level_random, events);
// AbstractHorse.tick runs the stand counter down with or without AI.
if super::equine::is_equine(m.species())
&& !super::camel::is_camel(m.species())
@@ -1263,6 +1264,68 @@ fn play_ambient(m: &mut Mob, w: &MobWorld, events: &mut Vec<MobEvent>) {
fn turtle_ambient_silent(m: &Mob) -> bool {
m.fluids.in_water || !m.on_ground || m.is_baby()
}
/// `AgeableMob.aiStep` runs even when `Mob.serverAiStep` is disabled.
fn ageable_step(
m: &mut Mob,
w: &MobWorld,
level_random: &mut LegacyRandom,
events: &mut Vec<MobEvent>,
) {
// AgeableMob.aiStep: a forced age-up's particles (`sendParticles`, so the
// players are sent them), then the age.
if m.extra.forced_age_timer > 0 {
if m.extra.forced_age_timer % 4 == 0 {
let (w, h) = (f64::from(m.width()), f64::from(m.height()));
let r = &mut m.random;
let x = m.position.x + w * (2.0 * r.next_double() - 1.0);
let y = m.position.y + h * r.next_double() + 0.5;
let z = m.position.z + w * (2.0 * r.next_double() - 1.0);
// sendParticles(HAPPY_VILLAGER, x, y, z, 1, 0, 0, 0, 0): exactly there, at rest.
events.push(MobEvent::LevelParticles {
kind: "happy_villager",
at: [x, y, z],
spread: [0.0; 3],
speed: 0.0,
count: 1,
});
}
m.extra.forced_age_timer -= 1;
}
// AgeableMob.aiStep: an age counts only for its subclasses (a tadpole
// counts its own age up, a zombie's baby age never moves). `canAgeUp`:
// a baby grows unless an age lock holds it; a breeding pause counts down.
if m.alive() && super::finalize::ageable(m.species()) {
if can_age_up(m) {
let (bw, bh, _) = m.dims();
// AgeableMob.setAge(getAge() + 1): the crossing is where a species'
// ageBoundaryReached runs (a goat's and a hoglin's attack damage, a
// sulfur cube's size), which the age write used to skip.
let next = m.extra.age + 1;
super::finalize::set_age(m, next);
// setAge at the age boundary: refreshDimensions.
if m.extra.age >= 0 {
m.fudge_position_after_size_change(w, bw, bh);
if m.species() == Species::Villager {
super::villager_brain::age_boundary_reached(m, w, level_random, events);
}
// Turtle.ageBoundaryReached: a grown turtle drops a scute.
if m.species() == Species::Turtle {
events.push(MobEvent::GiftLoot {
mob: m.id,
table: "minecraft:gameplay/turtle_grow",
at: [m.position.x, m.position.y, m.position.z],
});
}
}
} else if m.extra.age > 0 {
m.extra.age -= 1;
}
}
if super::finalize::ageable(m.species()) {
make_age_locked_particle(m, events);
}
}
/// LivingEntity.aiStep (and Mob/AgeableMob/Animal additions).
fn ai_step(m: &mut Mob, w: &MobWorld, level_random: &mut LegacyRandom, events: &mut Vec<MobEvent>) {
if m.species() == Species::Parrot {
@@ -1337,59 +1400,7 @@ fn ai_step(m: &mut Mob, w: &MobWorld, level_random: &mut LegacyRandom, events: &
if m.kind.flags.climber {
m.climbing = m.horizontal_collision;
}
// AgeableMob.aiStep: a forced age-up's particles (`sendParticles`, so the
// players are sent them), then the age.
if m.extra.forced_age_timer > 0 {
if m.extra.forced_age_timer % 4 == 0 {
let (w, h) = (f64::from(m.width()), f64::from(m.height()));
let r = &mut m.random;
let x = m.position.x + w * (2.0 * r.next_double() - 1.0);
let y = m.position.y + h * r.next_double() + 0.5;
let z = m.position.z + w * (2.0 * r.next_double() - 1.0);
// sendParticles(HAPPY_VILLAGER, x, y, z, 1, 0, 0, 0, 0): exactly there, at rest.
events.push(MobEvent::LevelParticles {
kind: "happy_villager",
at: [x, y, z],
spread: [0.0; 3],
speed: 0.0,
count: 1,
});
}
m.extra.forced_age_timer -= 1;
}
// AgeableMob.aiStep: an age counts only for its subclasses (a tadpole
// counts its own age up, a zombie's baby age never moves). `canAgeUp`:
// a baby grows unless an age lock holds it; a breeding pause counts down.
if m.alive() && super::finalize::ageable(m.species()) {
if can_age_up(m) {
let (bw, bh, _) = m.dims();
// AgeableMob.setAge(getAge() + 1): the crossing is where a species'
// ageBoundaryReached runs (a goat's and a hoglin's attack damage, a
// sulfur cube's size), which the age write used to skip.
let next = m.extra.age + 1;
super::finalize::set_age(m, next);
// setAge at the age boundary: refreshDimensions.
if m.extra.age >= 0 {
m.fudge_position_after_size_change(w, bw, bh);
if m.species() == Species::Villager {
super::villager_brain::age_boundary_reached(m, w, level_random, events);
}
// Turtle.ageBoundaryReached: a grown turtle drops a scute.
if m.species() == Species::Turtle {
events.push(MobEvent::GiftLoot {
mob: m.id,
table: "minecraft:gameplay/turtle_grow",
at: [m.position.x, m.position.y, m.position.z],
});
}
}
} else if m.extra.age > 0 {
m.extra.age -= 1;
}
}
if super::finalize::ageable(m.species()) {
make_age_locked_particle(m, events);
}
ageable_step(m, w, level_random, events);
if m.alive() {
// Animal.aiStep
if m.extra.age != 0 {
@@ -3431,6 +3442,75 @@ mod age_up_tests {
use super::*;
use std::sync::Arc;
/// Official Java 26.3: NoAI leaves both age clocks running. AgeLocked
/// holds a baby's age, but a positive breeding cooldown still expires.
#[test]
fn age_clocks_tick_with_no_ai_and_respect_baby_age_lock() {
let ran = crate::mobs::world::with_test_world(&[], |w, types| {
for species in ["pig", "cow", "horse", "turtle"] {
for no_ai in [true, false] {
for locked in [false, true] {
for age in [-2, 2] {
let kind =
Arc::new(types.types[&format!("minecraft:{species}")].clone());
let mut m = Mob::new(1, kind, Vec3::new(4.0, 220.0, 4.0), 0.0);
m.no_ai = no_ai;
m.no_gravity = true;
m.persistent = true;
m.extra.age_locked = locked;
super::super::finalize::set_age(&mut m, age);
let (mut random, mut events) = (LegacyRandom::new(0), Vec::new());
let expected = if age > 0 {
[1, 0, 0, 0]
} else if locked {
[-2, -2, -2, -2]
} else {
[-1, 0, 0, 0]
};
for (elapsed, expected_age) in expected.into_iter().enumerate() {
let previous_age = m.extra.age;
let previous_position = m.position;
tick(&mut m, w, &mut random, &mut events);
assert_eq!(
m.extra.age,
expected_age,
"{species}: NoAI={no_ai}, AgeLocked={locked}, initial Age={age}, tick={}",
elapsed + 1
);
if no_ai {
assert_eq!(m.velocity, Vec3::ZERO);
// Growing dimensions can reposition the mob at adulthood.
if previous_age != -1 || expected_age != 0 {
assert_eq!(m.position, previous_position);
}
}
}
if species == "turtle" && age < 0 && !locked {
assert_eq!(
events
.iter()
.filter(|event| matches!(
event,
MobEvent::GiftLoot {
table: "minecraft:gameplay/turtle_grow",
..
}
))
.count(),
1,
"growing up drops one scute"
);
}
}
}
}
}
});
if !ran {
eprintln!("age clock regression needs prepared vanilla resources");
}
}
/// A baby goat `age` ticks from adulthood, from no vanilla data.
fn baby_goat(age: i32) -> Mob {
let mut kind = crate::mobs::sensor_scan_tests::kind();
+47 -8
View File
@@ -714,12 +714,14 @@ pub trait Hooks: Send + Sync {
/// back out as if never added, and a `/summon` of it fails. Tells every
/// kind in the game's entity lists (items, orbs, mobs, boats, minecarts,
/// armor stands, primed TNT, falling blocks, projectiles, rockets,
/// tridents), once each, in the order they were created; check `kind`. A
/// gameplay spawn is told when the next tick starts (a drop, an orb, a mob, a
/// `/summon` and a chunk's entities are told at once). A mod that
/// cancels one of the others sees it removed before it ticks again.
/// Not told: end crystals, the dragon, cushions, paintings, item frames and
/// the entities Horizon keeps without simulating. Server-side.
/// tridents, end crystals, the dragon, cushions, paintings, item frames),
/// once each, in the order they were created; check `kind`. A drop, an
/// orb, a mob, a `/summon` and a chunk's entities are told at once, any
/// other spawn between the tick's phases (it is checked after the blocks,
/// the vehicles, the projectiles, the items and the mobs, and at the
/// tick's end). A mod that cancels one of the others sees it removed
/// before it ticks again. Not told: the entities Horizon keeps without
/// simulating (displays, markers, interactions). Server-side.
///
/// Return `Flow::Cancel` to take it: this sets `canceled`, and later mods are skipped.
fn entity_join_level(&self, host: &mut dyn Host, event: &mut EntityJoinLevelEvent<'_>) -> Flow {
@@ -763,8 +765,10 @@ pub trait Hooks: Send + Sync {
/// a level closing with nobody in it included. Not for the entities still
/// loaded when the server stops, as in vanilla. `entity` is its ID and
/// `kind` its type (`minecraft:item`); it is no longer in the level's lists.
/// It only hears. 53 tells item entities, experience orbs and mobs (`kind`
/// the mob type); later APIs add other kinds, so check `kind`. Server-side.
/// It only hears. 53 told item entities, experience orbs and mobs; it now
/// tells every kind `native_entity_join_level` does (`kind` is the type), each
/// once, when the game next notices it gone (the tick or action that removed
/// it). Check `kind`. Server-side.
fn entity_leave_level(&self, host: &mut dyn Host, event: &mut EntityLeaveLevelEvent<'_>) {
let _ = host;
let _ = event;
@@ -4531,6 +4535,11 @@ pub trait Hooks: Send + Sync {
let _ = host;
let _ = event;
}
/// Local player movement input, after reading it and before applying it.
fn movement_input(&self, host: &dyn Host, event: &mut MovementInputEvent) {
let _ = host;
let _ = event;
}
}
/// What [`Hooks::frame`] is told. Fields may be added: the struct is `#[non_exhaustive]`.
#[non_exhaustive]
@@ -13886,3 +13895,33 @@ pub fn dispatch_living_get_projectile(
mod_hook.living_get_projectile(host, &mut event);
}
}
/// What [`Hooks::movement_input`] is told. Fields may be added: the struct is `#[non_exhaustive]`.
#[non_exhaustive]
pub struct MovementInputEvent {
pub forward: f32,
pub strafe: f32,
pub jump: bool,
pub sneak: bool,
}
pub fn dispatch_movement_input(
host: &dyn Host,
mods: &[Box<dyn Hooks>],
forward: &mut f32,
strafe: &mut f32,
jump: &mut bool,
sneak: &mut bool,
) {
let mut event = MovementInputEvent {
forward: *forward,
strafe: *strafe,
jump: *jump,
sneak: *sneak,
};
for mod_hook in super::entered(mods, "movement_input") {
mod_hook.movement_input(host, &mut event);
}
*forward = event.forward;
*strafe = event.strafe;
*jump = event.jump;
*sneak = event.sneak;
}
+18 -1
View File
@@ -2033,9 +2033,26 @@ fn link(target: &Path, at: &Path) -> std::io::Result<()> {
}
Ok(())
} else {
fs::copy(target, at).map(|_| ())
link_file(target, at, |target, at| fs::hard_link(target, at))
}
}
/// A file of the overlay where symlinks need a privilege: a hard link to the base's file, which costs a name where
/// a copy costs the file's bytes (the base is ~28,000 files, 0.6 GiB), or a copy when no link can be made (another
/// volume, a filesystem without links, the NTFS limit on links to one file). A link shares the base file's data,
/// which is safe because the overlay is only ever replaced name by name (`Overlay::write` removes the name before it
/// writes), never written in place. A symlink is copied through, as before: linked, it would stay a symlink.
#[cfg(any(not(unix), test))]
fn link_file(
target: &Path,
at: &Path,
hard_link: impl FnOnce(&Path, &Path) -> std::io::Result<()>,
) -> std::io::Result<()> {
let symlink = fs::symlink_metadata(target).is_ok_and(|m| m.file_type().is_symlink());
if !symlink && hard_link(target, at).is_ok() {
return Ok(());
}
fs::copy(target, at).map(|_| ())
}
/// A tag file's JSON text with `more` appended to its values (no repeats).
fn merge_tag(text: Option<&str>, more: &[J], what: &str) -> Result<String> {
@@ -3774,3 +3774,168 @@ fn a_feature_gives_a_mods_sign_its_block_entity() {
assert_eq!(generate(true), (placed, placed));
let _ = fs::remove_dir_all(&scratch);
}
/// A small stand-in for the base root: nested folders, an empty file, an empty folder.
fn small_base(base: &Path) {
for (name, bytes) in [
("items.json", &b"{}"[..]),
("empty.log", &b""[..]),
("assets/minecraft/lang/en_us.json", &b"{\"a\": 1}"[..]),
(
"data/minecraft/tags/block/logs.json",
&b"{\"values\": []}"[..],
),
] {
let path = base.join(name);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path, bytes).unwrap();
}
fs::create_dir_all(base.join("worldgen/empty-folder")).unwrap();
}
/// Every folder (`None`) and file (`Some(bytes)`) under `root`, by relative path.
fn tree(root: &Path) -> BTreeMap<String, Option<Vec<u8>>> {
fn walk(root: &Path, dir: &Path, out: &mut BTreeMap<String, Option<Vec<u8>>>) {
for entry in fs::read_dir(dir).unwrap().flatten() {
let path = entry.path();
let rel = path
.strip_prefix(root)
.unwrap()
.to_string_lossy()
.replace('\\', "/");
if path.is_dir() {
out.insert(rel, None);
walk(root, &path, out);
} else {
out.insert(rel, Some(fs::read(&path).unwrap()));
}
}
}
let mut out = BTreeMap::new();
walk(root, root, &mut out);
out
}
/// Writes into a file where it is (same data), not by replacing it: what a link shares and a copy does not.
fn edit_in_place(path: &Path, bytes: &[u8]) {
use std::io::Write;
let mut file = fs::File::options()
.write(true)
.truncate(true)
.open(path)
.unwrap();
file.write_all(bytes).unwrap();
}
/// The overlay looks like the base, and shares its files (a hard link on Windows, a symlink elsewhere) rather than
/// holding copies of them.
#[test]
fn the_overlay_shares_the_base_files() {
let scratch = std::env::temp_dir().join(format!("horizon-mod-share-{}", std::process::id()));
let _ = fs::remove_dir_all(&scratch);
let (base, out) = (scratch.join("base"), scratch.join("root"));
small_base(&base);
Overlay::new(&base, &out).unwrap();
assert_eq!(tree(&out), tree(&base));
edit_in_place(
&base.join("assets/minecraft/lang/en_us.json"),
b"{\"a\": 2}",
);
assert_eq!(
fs::read(out.join("assets/minecraft/lang/en_us.json")).unwrap(),
b"{\"a\": 2}",
"the overlay copied the base file instead of linking it"
);
let _ = fs::remove_dir_all(&scratch);
}
/// Writing a file of the overlay replaces the name, so the base file it was linked to keeps its bytes and the other
/// files of the overlay still follow the base.
#[test]
fn writing_into_the_overlay_leaves_the_base_alone() {
let scratch = std::env::temp_dir().join(format!("horizon-mod-write-{}", std::process::id()));
let _ = fs::remove_dir_all(&scratch);
let (base, out) = (scratch.join("base"), scratch.join("root"));
small_base(&base);
let overlay = Overlay::new(&base, &out).unwrap();
let before = tree(&base);
overlay
.write(
"data/minecraft/tags/block/logs.json",
b"{\"values\": [\"mod:log\"]}",
)
.unwrap();
overlay.write("items.json", b"{\"mod\": 1}").unwrap();
overlay.write("assets/mod/new.json", b"{}").unwrap();
assert_eq!(tree(&base), before, "a write reached the base");
assert_eq!(
fs::read(out.join("data/minecraft/tags/block/logs.json")).unwrap(),
b"{\"values\": [\"mod:log\"]}"
);
assert_eq!(fs::read(out.join("items.json")).unwrap(), b"{\"mod\": 1}");
edit_in_place(
&base.join("assets/minecraft/lang/en_us.json"),
b"{\"a\": 3}",
);
assert_eq!(
fs::read(out.join("assets/minecraft/lang/en_us.json")).unwrap(),
b"{\"a\": 3}"
);
// A rebuild starts by removing the overlay: that removes names, not the base's data.
fs::remove_dir_all(&out).unwrap();
assert_eq!(fs::read(base.join("items.json")).unwrap(), b"{}");
let _ = fs::remove_dir_all(&scratch);
}
/// Where a hard link cannot be made (another volume, a filesystem without links) a file is copied, as it always was
/// on Windows; where it can, the file is linked.
#[test]
fn a_file_is_linked_or_else_copied() {
let scratch = std::env::temp_dir().join(format!("horizon-mod-linkfile-{}", std::process::id()));
let _ = fs::remove_dir_all(&scratch);
fs::create_dir_all(&scratch).unwrap();
let target = scratch.join("base.json");
fs::write(&target, b"original").unwrap();
let linked = scratch.join("linked.json");
link_file(&target, &linked, |from, to| fs::hard_link(from, to)).unwrap();
edit_in_place(&target, b"edited");
assert_eq!(fs::read(&linked).unwrap(), b"edited", "not a link");
let copied = scratch.join("copied.json");
fs::write(&target, b"original").unwrap();
link_file(&target, &copied, |_, _| {
Err(std::io::Error::other("no links here"))
})
.unwrap();
edit_in_place(&target, b"edited");
assert_eq!(fs::read(&copied).unwrap(), b"original", "not a copy");
// A target that is not there is an error whichever way the file is made.
let missing = link_file(
&scratch.join("missing.json"),
&scratch.join("x.json"),
|from, to| fs::hard_link(from, to),
);
assert!(missing.is_err());
let _ = fs::remove_dir_all(&scratch);
}
/// A symlink in the base is copied through, as before; linked, the overlay's file would be a symlink too.
#[cfg(unix)]
#[test]
fn a_symlink_file_is_copied_through() {
let scratch = std::env::temp_dir().join(format!("horizon-mod-symfile-{}", std::process::id()));
let _ = fs::remove_dir_all(&scratch);
fs::create_dir_all(&scratch).unwrap();
fs::write(scratch.join("real.json"), b"real").unwrap();
std::os::unix::fs::symlink("real.json", scratch.join("alias.json")).unwrap();
let at = scratch.join("copy.json");
link_file(&scratch.join("alias.json"), &at, |from, to| {
fs::hard_link(from, to)
})
.unwrap();
assert!(!fs::symlink_metadata(&at).unwrap().file_type().is_symlink());
assert_eq!(fs::read(&at).unwrap(), b"real");
let _ = fs::remove_dir_all(&scratch);
}
+57 -7
View File
@@ -94,9 +94,10 @@ pub enum ProjectileKind {
/// flight that bursts on whatever it meets.
WindCharge,
BreezeWindCharge,
/// `ShulkerBullet`: homing on `target` in axis-aligned steps.
/// `ShulkerBullet`: homing on `target` in axis-aligned steps; one with
/// no target (a summoned one) falls like any projectile.
ShulkerBullet {
target: Who,
target: Option<Who>,
/// The current step direction (3D data value), if any.
dir: Option<u8>,
steps: i32,
@@ -116,6 +117,8 @@ pub enum ProjectileKind {
/// type is the entity's own and `/summon` can pair either with any `Item`,
/// so it is kept next to the stack (a thrown potion's follows its item).
Potion(Box<crate::items::ItemStack>, bool),
/// `ThrownExperienceBottle`: its own entity type, gravity 0.07.
ExperienceBottle,
/// a content mod's type.
Thrown(&'static ThrownType),
}
@@ -148,6 +151,7 @@ impl ProjectileKind {
| Self::EnderPearl
| Self::LlamaSpit
| Self::Potion(..)
| Self::ExperienceBottle
| Self::Thrown(_) => 0.25,
Self::SmallFireball
| Self::WindCharge
@@ -175,6 +179,7 @@ impl ProjectileKind {
// `ThrownSplashPotion`/`ThrownLingeringPotion`: the item names it.
Self::Potion(_, true) => "minecraft:lingering_potion",
Self::Potion(_, false) => "minecraft:splash_potion",
Self::ExperienceBottle => "minecraft:experience_bottle",
Self::Thrown(t) => t.id,
}
}
@@ -314,13 +319,16 @@ impl crate::entity_rules::EntityRules for Projectile {
}
/// Each projectile class's `getDefaultGravity`.
fn get_default_gravity(&self) -> f64 {
match self.kind {
match &self.kind {
ProjectileKind::Arrow { .. } | ProjectileKind::Trident => 0.05,
ProjectileKind::SmallFireball
| ProjectileKind::LargeFireball { .. }
| ProjectileKind::WitherSkull { .. } => 0.0,
ProjectileKind::LlamaSpit => 0.06,
// `ThrownExperienceBottle.getDefaultGravity` is 0.07.
ProjectileKind::ExperienceBottle => 0.07,
ProjectileKind::Potion { .. } => 0.05,
ProjectileKind::ShulkerBullet { .. } => 0.04,
_ => 0.03,
}
}
@@ -465,7 +473,7 @@ impl Projectile {
|| self
.arrow
.as_ref()
.is_some_and(|a| &*a.pickup_item.item == "minecraft:spectral_arrow")
.is_some_and(|a| a.spectral || &*a.pickup_item.item == "minecraft:spectral_arrow")
}
/// `Arrow.ID_EFFECT_COLOR` (`updateColor`): the opaque colour of the
/// potion the arrow carries, from its tipped pickup item or the effect a
@@ -598,7 +606,7 @@ impl Projectile {
);
let mut p = Self::new(
ProjectileKind::ShulkerBullet {
target,
target: Some(target),
dir: Some(1),
steps: 0,
delta: [0.0; 3],
@@ -629,7 +637,11 @@ impl Projectile {
]
}
fn target_box(&self, w: &impl ProjectileWorld) -> Option<Aabb> {
let ProjectileKind::ShulkerBullet { target, .. } = self.kind else {
let ProjectileKind::ShulkerBullet {
target: Some(target),
..
} = self.kind
else {
return None;
};
w.targets()
@@ -743,7 +755,8 @@ impl Projectile {
v.z + (dl[2] - v.z) * 0.2,
);
} else {
self.velocity.y -= 0.04;
// `Entity.applyGravity`: `getGravity` is 0 under NoGravity.
self.velocity.y -= crate::entity_rules::EntityRules::get_gravity(self);
}
let from = self.position;
let to = from.add_vector(self.velocity);
@@ -1004,6 +1017,10 @@ impl Projectile {
pub fn drawn_item(&self) -> Option<crate::items::ItemStack> {
match &self.kind {
ProjectileKind::Potion(stack, _) => Some((**stack).clone()),
ProjectileKind::ExperienceBottle => Some(crate::items::ItemStack::new(
"minecraft:experience_bottle",
1,
)),
ProjectileKind::Thrown(t) => Some(crate::items::ItemStack::new(t.item, 1)),
_ => None,
}
@@ -1018,6 +1035,7 @@ impl Projectile {
"item/fire_charge"
}
ProjectileKind::Potion(stack, _) => potion_sprite(&stack.item),
ProjectileKind::ExperienceBottle => "item/experience_bottle",
ProjectileKind::LlamaSpit => "entity/llama/llama_spit",
// Drawn as `WindChargeModel` and `ShulkerBulletModel` by the game.
ProjectileKind::WindCharge
@@ -1298,6 +1316,38 @@ mod tests {
assert_eq!(settings("ruby:late"), (5, 3, true));
}
/// `ShulkerBullet.tick` with no target: `applyGravity` (0.04, none under
/// NoGravity), then the move, no drag. 20 ticks from y 110 fall 8.4.
#[test]
fn a_shulker_bullet_without_a_target_falls_unless_no_gravity() {
let bullet = || {
Projectile::new(
ProjectileKind::ShulkerBullet {
target: None,
dir: None,
steps: 0,
delta: [0.0; 3],
seed: 0,
},
Vec3::new(0.5, 110.0, 0.5),
None,
)
};
let w = Empty(vec![]);
let mut falling = bullet();
let mut held = bullet();
held.no_gravity = true;
for _ in 0..20 {
falling.tick(&w);
held.tick(&w);
}
assert!(
(falling.position.y - 101.6).abs() < 1e-6,
"{}",
falling.position.y
);
assert_eq!(held.position, Vec3::new(0.5, 110.0, 0.5));
}
#[test]
fn llama_spit_moves_first_then_drags_and_falls() {
let mut p = Projectile::new(ProjectileKind::LlamaSpit, Vec3::new(0.0, 100.0, 0.0), None);
+29 -1
View File
@@ -1,6 +1,8 @@
//! A mod crate's view of the mod API: `Hooks` is implemented with the names `mod_api` exports.
//! The test is that this file builds; #7044's mod could not name `key`'s event.
use horizon_client::mod_api::{Hooks, Host, InputKeyEvent, KeyEvent, winit};
use horizon_client::mod_api::{
Hooks, Host, InputKeyEvent, KeyEvent, MovementInputEvent, dispatch_movement_input, winit,
};
struct Keys;
@@ -18,3 +20,29 @@ impl Hooks for Keys {
fn a_mod_implements_the_key_hooks() {
let _: Box<dyn Hooks> = Box::new(Keys);
}
#[test]
fn movement_input_writes_back_all_fields() {
struct TestHost;
impl Host for TestHost {}
struct Movement;
impl Hooks for Movement {
fn movement_input(&self, _host: &dyn Host, event: &mut MovementInputEvent) {
event.forward = -0.5;
event.strafe = 0.25;
event.jump = true;
event.sneak = true;
}
}
let mods: Vec<Box<dyn Hooks>> = vec![Box::new(Movement)];
let (mut forward, mut strafe, mut jump, mut sneak) = (1.0, 0.0, false, false);
dispatch_movement_input(
&TestHost,
&mods,
&mut forward,
&mut strafe,
&mut jump,
&mut sneak,
);
assert_eq!((forward, strafe, jump, sneak), (-0.5, 0.25, true, true));
}
+4 -4
View File
@@ -14,7 +14,7 @@ use std::{
path::PathBuf,
sync::{
Mutex, OnceLock,
atomic::{AtomicU64, Ordering},
atomic::{AtomicU32, Ordering},
},
time::{SystemTime, UNIX_EPOCH},
};
@@ -24,7 +24,7 @@ const VERSION: u32 = 2;
const FILE_NAME: &str = "behaviours-v2.bin";
const MAX_FILE_BYTES: usize = 128 * 1024 * 1024;
const MAX_DECODED_BYTES: usize = 256 * 1024 * 1024;
static NEXT_TEMP: AtomicU64 = AtomicU64::new(0);
static NEXT_TEMP: AtomicU32 = AtomicU32::new(0);
static DIR: OnceLock<PathBuf> = OnceLock::new();
/// Keep snapshots in `dir` (the client passes `<game folder>/.horizon-cache`).
@@ -776,9 +776,9 @@ fn write(
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::AtomicU32;
static NEXT_FIXTURE: AtomicU64 = AtomicU64::new(0);
static NEXT_FIXTURE: AtomicU32 = AtomicU32::new(0);
struct FixtureDir(PathBuf);
impl Drop for FixtureDir {
+2 -2
View File
@@ -11,7 +11,7 @@ use std::{
fs::{self, File, OpenOptions},
io::{self, Read, Seek, SeekFrom, Write},
path::{Path, PathBuf},
sync::atomic::{AtomicU64, Ordering},
sync::atomic::{AtomicU32, Ordering},
};
const SECTOR: usize = 4096;
@@ -387,7 +387,7 @@ impl RegionFile {
}
type BTreeChanges<'a> = std::collections::BTreeMap<usize, Option<&'a ChunkRecord>>;
static NEXT_TEMP: AtomicU64 = AtomicU64::new(0);
static NEXT_TEMP: AtomicU32 = AtomicU32::new(0);
/// Writes a complete new region to a temporary sibling, then publishes it with
/// an atomic, non-overwriting hard link. Drop rolls back unpublished files.
+2 -2
View File
@@ -10,10 +10,10 @@ use std::{
fs,
io::{Seek, SeekFrom, Write},
path::{Path, PathBuf},
sync::atomic::{AtomicU64, Ordering},
sync::atomic::{AtomicU32, Ordering},
};
static NEXT: AtomicU64 = AtomicU64::new(0);
static NEXT: AtomicU32 = AtomicU32::new(0);
struct Directory(PathBuf);
impl Directory {
fn new() -> Self {
+2
View File
@@ -841,6 +841,8 @@ API 108 adds `native_server_data_loaded(reload)` fires once the server's data ta
API 109 adds `native_compute_fov(partial, camera, degrees)` receives the computed world FOV after settings, ordinary FOV effects and zoom, before projection/Nausea. `camera` is its world position and `partial` the interpolation fraction. Mods can narrow or widen the angle; only finite values in 1..=179 degrees are accepted, otherwise the original computed angle is used. It runs for world rendering and near-plane overlays, not just once per frame. Hand/HUD FOV and the title panorama keep their existing paths. Bundles need "hooks": 109.
`native_movement_input(forward, strafe, jump, sneak)` runs on the client each local player tick after input is read and before it is applied. Mods may change all four values; positive strafe is left. The server receives direction flags, not scalar magnitudes, so remote servers may correct modified movement. Bundles need "hooks": @API:movement_input@.
API 110 adds mobs drawn by client mods: `native_mob_submit(mob, draw, drawn)` is asked each frame for every mob in view but players,
with its id, kind, custom name and the frame's body and head turns and walk cycle; a mod draws a model of its own with the
block entity drawing helpers under `draw`'s pose (at the feet, turned with the body) and sets `drawn` to leave out the vanilla
@@ -585,6 +585,7 @@ public final class VanillaMobAiOracle {
new net.minecraft.world.item.ItemStack(BuiltInRegistries.ITEM.getValue(
net.minecraft.resources.Identifier.parse("minecraft:red_harness"))));
case "noai" -> mob.setNoAi(true);
case "agelocked" -> method(AgeableMob.class, "setAgeLocked", boolean.class).invoke(mob, true);
case "profession" -> {
Villager villager = (Villager) mob;
villager.setVillagerData(villager.getVillagerData().withProfession(level.registryAccess(),
@@ -1109,6 +1110,7 @@ public final class VanillaMobAiOracle {
mem.sort(null);
sb.append("\tmem ").append(String.join(" ", mem));
}
if (m instanceof AgeableMob ageable) sb.append("\tage ").append(ageable.getAge());
if (m instanceof Villager villager) sb.append("\tvx ").append(villager(villager));
if (m instanceof net.minecraft.world.entity.animal.golem.CopperGolem golem) {
Object opened = get(golem, "openedChestPos");
+1
View File
@@ -350,6 +350,7 @@ class StressSceneTests(unittest.TestCase):
self.assertEqual(summons("stress_items", "item"), 2000)
self.assertEqual(sum(summons("stress_held", k) for k in stress_scenes.HOLDERS), 1000)
self.assertEqual(sum(summons("stress_eyes", k) for k in stress_scenes.EYED), 1000)
self.assertEqual(sum(summons("stress_glowing", k) for k in stress_scenes.MOBS), 1000)
self.assertEqual(summons("stress_frames", "item_frame"), 512)
self.assertEqual(summons("stress_armor_stands", "armor_stand"), 500)
self.assertEqual(stress_scenes.setup("stress_empty"), stress_scenes.SETTINGS + stress_scenes.floor(
+33
View File
@@ -1686,6 +1686,38 @@ def summoned_potions():
return ops
def trial_spawner_passengers():
"""Configured passengers must exist as entities with a vehicle (#7254)."""
ops = [*setup('/fill 1 219 1 13 219 8 stone',
'/fill 1 220 1 13 224 8 air', '/gamerule spawn_mobs true')]
# Exercise namespace and position independently, plus the explicit control.
for i, (kind, pos) in enumerate([
('chicken', ''), ('minecraft:chicken', ''),
('chicken', ',Pos:[4.5d,220.0d,6.5d]'),
('minecraft:chicken', ',Pos:[4.5d,220.0d,6.5d]'),
]):
parent, rider = f'trial_parent_{i}', f'trial_rider_{i}'
ops += setup(
'/setblock 4 220 4 trial_spawner[trial_spawner_state=active]'
'{normal_config:{spawn_range:3,total_mobs:1.0f,simultaneous_mobs:1.0f,'
'total_mobs_added_per_player:0.0f,simultaneous_mobs_added_per_player:0.0f,'
'ticks_between_spawn:1000},spawn_data:{entity:{id:"zombie",'
f'Tags:["{parent}"],Pos:[4.5d,220.0d,6.5d],NoAI:1b,'
f'Passengers:[{{id:"{kind}",NoAI:1b,Tags:["{rider}"]{pos}}}]'
'}},next_mob_spawns_at:0L}',
'/data modify block 4 220 4 registered_players append from entity @s UUID')
ops += [
{'op': 'tick', 'n': 5},
cmd(f'/execute if entity @e[tag={parent}]'),
cmd(f'/execute if entity @e[tag={rider}]'),
cmd(f'/execute as @e[tag={rider}] on vehicle if entity @s[tag={parent}]'),
cmd(f'/data get entity @e[tag={parent},limit=1] Passengers[0].Tags'),
*setup(f'/kill @e[tag={parent}]', f'/kill @e[tag={rider}]',
'/setblock 4 220 4 air'),
]
return ops + setup('/fill 1 219 1 13 224 8 air', '/gamerule spawn_mobs false')
def groups():
return [
('setup', group_setup()),
@@ -1698,6 +1730,7 @@ def groups():
('wither spawn', wither_spawn()),
('riding teleport', riding_teleport()),
('mob riding', mob_riding()),
('trial spawner passengers', trial_spawner_passengers()),
('camel riders', camel_riders()),
('scores, teams and bossbars', scores()),
('execute', execute()),
@@ -0,0 +1,16 @@
{
"hooks": [
{
"name": "movement_input",
"target": "horizon_client::game::native_hooks::Game<'_>::native_movement_input",
"neoforge": "MovementInputUpdateEvent",
"side": "client",
"cancellable": false,
"mod_api": {
"contract": "listen"
}
}
],
"rust_summary": "Local player movement input before application (`native_movement_input`).",
"doc_summary": "`native_movement_input(forward, strafe, jump, sneak)` lets client mods change local movement after input is read and before it is applied. Positive strafe is left; servers receive direction flags rather than scalar magnitudes. Bundles need hooks API @API:movement_input@."
}
+10 -10
View File
@@ -61,7 +61,7 @@ StandingSignBlock.getStateForPlacement port crates/horizon-world/src/level/place
StandingSignBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror
StandingSignBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate
StandingSignBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class
StemBlock.getCloneItemStack missing unsure: minor: middle-click on a stem may not return its seed item; no stem case found in the pick code
StemBlock.getCloneItemStack port crates/horizon-client/src/game/block_items.rs::clone_item a pumpkin or melon stem, attached or not, picks the seed it grew from; any other block picks its own item
StemBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid
StemBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal
StemBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::stem_random_tick
@@ -75,8 +75,8 @@ SuspiciousEffectHolder.tryGet port crates/horizon-items/src/flowers.rs::stew_eff
TallSeagrassBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class
TallSeagrassBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class
TallSeagrassBlock.mayPlaceOn port crates/horizon-world/src/level/survive.rs::Sim::may_place_on
TestBlock missing minor: game-test block for operators; Horizon keeps only its NBT
TestInstanceBlock missing minor: game-test instance block; Horizon keeps only its NBT
TestBlock skip:debug game-test tooling: an operator's test block runs a structure when triggered and Horizon keeps only its block entity NBT
TestInstanceBlock skip:debug game-test tooling: the instance block runs and exports a test structure; Horizon keeps its NBT
TintedParticleLeavesBlock.spawnFallingLeavesParticle port crates/horizon-client/src/animate_tick.rs::block_animate_tick falling tinted leaf particle
TorchBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick torch flame
TorchflowerCropBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block growth arm
@@ -100,9 +100,9 @@ WallHangingSignBlock.canAttachTo inline crates/horizon-world/src/level/place.rs:
WallHangingSignBlock.canPlace port crates/horizon-world/src/level/place.rs::Sim::wall_hanging_sign_can_place
WallHangingSignBlock.createBlockStateDefinition skip:data states come from the generated block state table
WallHangingSignBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class
WallHangingSignBlock.isHittingEditableSide missing unsure: player: a hanging sign clicked on its side may not chain onto a wall hanging sign
WallHangingSignBlock.isHittingEditableSide inline crates/horizon-world/src/level/interact.rs::Behaviours::block_use_item_on the faces of the sign it faces and its opposite are the editable ones; a hanging sign aimed at any other face passes
WallHangingSignBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate
WallHangingSignBlock.shouldTryToChainAnotherHangingSign missing unsure: player: a second hanging sign used on a wall sign may not chain on
WallHangingSignBlock.shouldTryToChainAnotherHangingSign inline crates/horizon-world/src/level/interact.rs::Behaviours::block_use_item_on passing on a non-editable face lets the held hanging sign place its own block, which chains onto the wall sign
WallHangingSignBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class
WallHangingSignBlock.useItemOn port crates/horizon-world/src/level/interact.rs::Behaviours::use_item_on gap: player: chaining a hanging sign onto a wall hanging sign is not ported here
WallSignBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class
@@ -132,7 +132,7 @@ BannerPatternLayers.removeLast inline crates/horizon-client/src/cauldron.rs::int
BannerPatternLayers.Builder skip:java builder for banner pattern layers
BannerPatterns skip:data registry bootstrap
BarrelBlockEntity.loadAdditional port crates/horizon-client-core/src/block_entity_nbt.rs::canonical gap: minor: canonical NBT form only, no load step of its own
BarrelBlockEntity.recheckOpen missing unsure: player: a barrel lid may stay open after a player walks away; only golem openers are rechecked on a timer
BarrelBlockEntity.recheckOpen missing the lid's periodic recheck of whether its viewers are still there; Horizon draws no barrel lid at all (containers.rs holds its 27 slots), so nothing rechecks one, where a golem's openers are rechecked on a timer
BarrelBlockEntity.saveAdditional port crates/horizon-client-core/src/block_entity_nbt.rs::canonical gap: minor: canonical NBT form only
BarrelBlockEntity.startOpen port crates/horizon-client/src/game/block_entity_anims.rs::Game::barrel_openers_changed
BarrelBlockEntity.stopOpen port crates/horizon-client/src/game/block_entity_anims.rs::Game::barrel_openers_changed
@@ -162,15 +162,15 @@ PotentSulfurBlockEntity.setLevel inline crates/horizon-client/src/game/potent_su
PotentSulfurBlockEntity.spawnGeyserParticle port crates/horizon-client/src/game/potent_sulfur.rs::Game::spawn_geyser_particle ticked in #3516; `PotentSulfurBlockEntity.spawnGeyserParticle` is cited at crates/horizon-client/src/game/potent_sulfur.rs:380
PotentSulfurBlockEntity.spawnNoxiousGasCloudParticle port crates/horizon-client/src/game/potent_sulfur.rs::Game::spawn_noxious_gas_cloud_particle ticked in #3516; `PotentSulfurBlockEntity.spawnNoxiousGasCloudParticle` is cited at crates/horizon-client/src/game/potent_sulfur.rs:395
RandomizableContainerBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place the item's minecraft:container_loot hands the block its LootTable and LootTableSeed
RandomizableContainerBlockEntity.canOpen missing unsure: minor: the spectator and loot-table open checks were not found
RandomizableContainerBlockEntity.canOpen inline crates/horizon-client/src/game/advancements.rs::Game::container_locked_for the negative of it: a spectator opens any container, and otherwise the lock decides (LockCode.canUnlock)
RandomizableContainerBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect the block's LootTable and LootTableSeed leave on the item as minecraft:container_loot
RandomizableContainerBlockEntity.createMenu port crates/horizon-client/src/game.rs::Game::open_menu gap: minor: the spectator and loot-table checks are not ported here
SculkSensorBlockEntity.loadAdditional port crates/horizon-client-render/src/sculk.rs::SculkEntity::load
SculkSensorBlockEntity.saveAdditional port crates/horizon-client-render/src/sculk.rs::SculkEntity::to_nbt
SculkSensorBlockEntity.VibrationUser.canReceiveVibration port crates/horizon-client-render/src/sculk.rs::valid_vibration
SculkSensorBlockEntity.VibrationUser.onReceiveVibration port crates/horizon-client-render/src/sculk.rs::Data::schedule
TestBlockEntity missing minor: game-test block entity; reset, trigger and logging are not ported, NBT only
TestInstanceBlockEntity missing minor: game-test structure tooling; export and chunk forcing are not ported
TestBlockEntity skip:debug game-test tooling: reset, trigger and logging belong to the test runner, which Horizon does not have; its NBT is kept
TestInstanceBlockEntity skip:debug game-test tooling: export and chunk forcing belong to the test runner, which Horizon does not have
TrialSpawnerConfig.calculateTargetSimultaneousMobs port crates/horizon-client/src/mobs/trial_spawner.rs::TrialConfig::target_simultaneous_mobs
TrialSpawnerConfig.calculateTargetTotalMobs port crates/horizon-client/src/mobs/trial_spawner.rs::TrialConfig::target_total_mobs
TrialSpawnerConfig.withSpawning skip:java builder-style setter on the config record
@@ -204,7 +204,7 @@ ImposterProtoChunk skip:other wrapper so the vanilla status pipeline can read a
LinearPalette skip:java palette storage; Horizon's chunk.rs keeps block ids in plain arrays
PalettedContainerFactory skip:java factory for palette storage; Horizon's chunk.rs keeps block ids in plain arrays
SingleValuePalette skip:java palette storage; Horizon's chunk.rs keeps block ids in plain arrays
UpgradeData missing unsure: minor: block upgrades of old chunks on load are not ported
UpgradeData missing the block upgrades an old chunk takes when it loads (the Sides bits a save carries); the datafix writes and keeps UpgradeData (datafix/chunk_leaves.rs) but no load path applies the upgrades
ChunkDependencies.getRadiusOf inline crates/horizon-client-render/src/streaming.rs::Ranges::margins fixed margins instead of a per-status radius
ChunkPyramid.Builder skip:java builder for the status pyramid; Horizon uses fixed margins
ChunkStatusTasks.buildTerrain inline crates/horizon-world/src/worldgen/generator.rs::Pipeline::generate_with_context terrain pass
1 RodBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror rod facing mirrored through the state table
61 StandingSignBlock.mirror port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::mirror
62 StandingSignBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate
63 StandingSignBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class
64 StemBlock.getCloneItemStack missing port crates/horizon-client/src/game/block_items.rs::clone_item unsure: minor: middle-click on a stem may not return its seed item; no stem case found in the pick code a pumpkin or melon stem, attached or not, picks the seed it grew from; any other block picks its own item
65 StemBlock.isValidBonemealTarget port crates/horizon-world/src/level/bonemeal.rs::Sim::bonemeal_valid
66 StemBlock.performBonemeal port crates/horizon-world/src/level/bonemeal.rs::Sim::perform_bonemeal
67 StemBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::stem_random_tick
75 TallSeagrassBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class
76 TallSeagrassBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class
77 TallSeagrassBlock.mayPlaceOn port crates/horizon-world/src/level/survive.rs::Sim::may_place_on
78 TestBlock missing skip:debug minor: game-test block for operators; Horizon keeps only its NBT game-test tooling: an operator's test block runs a structure when triggered and Horizon keeps only its block entity NBT
79 TestInstanceBlock missing skip:debug minor: game-test instance block; Horizon keeps only its NBT game-test tooling: the instance block runs and exports a test structure; Horizon keeps its NBT
80 TintedParticleLeavesBlock.spawnFallingLeavesParticle port crates/horizon-client/src/animate_tick.rs::block_animate_tick falling tinted leaf particle
81 TorchBlock.animateTick port crates/horizon-client/src/animate_tick.rs::block_animate_tick torch flame
82 TorchflowerCropBlock.randomTick port crates/horizon-world/src/level/tick.rs::Sim::random_tick_block growth arm
100 WallHangingSignBlock.canPlace port crates/horizon-world/src/level/place.rs::Sim::wall_hanging_sign_can_place
101 WallHangingSignBlock.createBlockStateDefinition skip:data states come from the generated block state table
102 WallHangingSignBlock.getStateForPlacement port crates/horizon-world/src/level/place.rs::Sim::placement_class
103 WallHangingSignBlock.isHittingEditableSide missing inline crates/horizon-world/src/level/interact.rs::Behaviours::block_use_item_on unsure: player: a hanging sign clicked on its side may not chain onto a wall hanging sign the faces of the sign it faces and its opposite are the editable ones; a hanging sign aimed at any other face passes
104 WallHangingSignBlock.rotate port crates/horizon-world/src/worldgen/structure.rs::StateTransforms::rotate
105 WallHangingSignBlock.shouldTryToChainAnotherHangingSign missing inline crates/horizon-world/src/level/interact.rs::Behaviours::block_use_item_on unsure: player: a second hanging sign used on a wall sign may not chain on passing on a non-editable face lets the held hanging sign place its own block, which chains onto the wall sign
106 WallHangingSignBlock.updateShape port crates/horizon-world/src/level/shapes.rs::Sim::update_shape_class
107 WallHangingSignBlock.useItemOn port crates/horizon-world/src/level/interact.rs::Behaviours::use_item_on gap: player: chaining a hanging sign onto a wall hanging sign is not ported here
108 WallSignBlock.canSurvive port crates/horizon-world/src/level/survive.rs::Sim::can_survive_class
132 BannerPatternLayers.Builder skip:java builder for banner pattern layers
133 BannerPatterns skip:data registry bootstrap
134 BarrelBlockEntity.loadAdditional port crates/horizon-client-core/src/block_entity_nbt.rs::canonical gap: minor: canonical NBT form only, no load step of its own
135 BarrelBlockEntity.recheckOpen missing unsure: player: a barrel lid may stay open after a player walks away; only golem openers are rechecked on a timer the lid's periodic recheck of whether its viewers are still there; Horizon draws no barrel lid at all (containers.rs holds its 27 slots), so nothing rechecks one, where a golem's openers are rechecked on a timer
136 BarrelBlockEntity.saveAdditional port crates/horizon-client-core/src/block_entity_nbt.rs::canonical gap: minor: canonical NBT form only
137 BarrelBlockEntity.startOpen port crates/horizon-client/src/game/block_entity_anims.rs::Game::barrel_openers_changed
138 BarrelBlockEntity.stopOpen port crates/horizon-client/src/game/block_entity_anims.rs::Game::barrel_openers_changed
162 PotentSulfurBlockEntity.spawnGeyserParticle port crates/horizon-client/src/game/potent_sulfur.rs::Game::spawn_geyser_particle ticked in #3516; `PotentSulfurBlockEntity.spawnGeyserParticle` is cited at crates/horizon-client/src/game/potent_sulfur.rs:380
163 PotentSulfurBlockEntity.spawnNoxiousGasCloudParticle port crates/horizon-client/src/game/potent_sulfur.rs::Game::spawn_noxious_gas_cloud_particle ticked in #3516; `PotentSulfurBlockEntity.spawnNoxiousGasCloudParticle` is cited at crates/horizon-client/src/game/potent_sulfur.rs:395
164 RandomizableContainerBlockEntity.applyImplicitComponents port crates/horizon-client/src/block_entity_item.rs::place the item's minecraft:container_loot hands the block its LootTable and LootTableSeed
165 RandomizableContainerBlockEntity.canOpen missing inline crates/horizon-client/src/game/advancements.rs::Game::container_locked_for unsure: minor: the spectator and loot-table open checks were not found the negative of it: a spectator opens any container, and otherwise the lock decides (LockCode.canUnlock)
166 RandomizableContainerBlockEntity.collectImplicitComponents port crates/horizon-client/src/block_entity_item.rs::collect the block's LootTable and LootTableSeed leave on the item as minecraft:container_loot
167 RandomizableContainerBlockEntity.createMenu port crates/horizon-client/src/game.rs::Game::open_menu gap: minor: the spectator and loot-table checks are not ported here
168 SculkSensorBlockEntity.loadAdditional port crates/horizon-client-render/src/sculk.rs::SculkEntity::load
169 SculkSensorBlockEntity.saveAdditional port crates/horizon-client-render/src/sculk.rs::SculkEntity::to_nbt
170 SculkSensorBlockEntity.VibrationUser.canReceiveVibration port crates/horizon-client-render/src/sculk.rs::valid_vibration
171 SculkSensorBlockEntity.VibrationUser.onReceiveVibration port crates/horizon-client-render/src/sculk.rs::Data::schedule
172 TestBlockEntity missing skip:debug minor: game-test block entity; reset, trigger and logging are not ported, NBT only game-test tooling: reset, trigger and logging belong to the test runner, which Horizon does not have; its NBT is kept
173 TestInstanceBlockEntity missing skip:debug minor: game-test structure tooling; export and chunk forcing are not ported game-test tooling: export and chunk forcing belong to the test runner, which Horizon does not have
174 TrialSpawnerConfig.calculateTargetSimultaneousMobs port crates/horizon-client/src/mobs/trial_spawner.rs::TrialConfig::target_simultaneous_mobs
175 TrialSpawnerConfig.calculateTargetTotalMobs port crates/horizon-client/src/mobs/trial_spawner.rs::TrialConfig::target_total_mobs
176 TrialSpawnerConfig.withSpawning skip:java builder-style setter on the config record
204 LinearPalette skip:java palette storage; Horizon's chunk.rs keeps block ids in plain arrays
205 PalettedContainerFactory skip:java factory for palette storage; Horizon's chunk.rs keeps block ids in plain arrays
206 SingleValuePalette skip:java palette storage; Horizon's chunk.rs keeps block ids in plain arrays
207 UpgradeData missing unsure: minor: block upgrades of old chunks on load are not ported the block upgrades an old chunk takes when it loads (the Sides bits a save carries); the datafix writes and keeps UpgradeData (datafix/chunk_leaves.rs) but no load path applies the upgrades
208 ChunkDependencies.getRadiusOf inline crates/horizon-client-render/src/streaming.rs::Ranges::margins fixed margins instead of a per-status radius
209 ChunkPyramid.Builder skip:java builder for the status pyramid; Horizon uses fixed margins
210 ChunkStatusTasks.buildTerrain inline crates/horizon-world/src/worldgen/generator.rs::Pipeline::generate_with_context terrain pass
+17 -16
View File
@@ -1,9 +1,10 @@
SetStewEffectFunction.Builder skip:java builder for the stew effect list; Horizon keeps the effects in loot.rs Context::run (set_stew_effect)
SetWritableBookPagesFunction missing minor: no vanilla loot table uses set_writable_book_pages; Horizon's loot run has no arm for it
SetWrittenBookPagesFunction missing minor: no vanilla loot table uses set_written_book_pages; Horizon's loot run has no arm for it
SetWritableBookPagesFunction missing `horizon-world/src/loot.rs::modify` has no `set_writable_book_pages` arm, and no vanilla loot table asks for one
SetWrittenBookPagesFunction missing no `set_written_book_pages` arm in `loot.rs::modify` (the arms run from `sequence` and `set_count` through `set_potion` to `set_stew_effect`); no vanilla loot table asks for one
SmeltItemFunction.run port crates/horizon-world/src/loot.rs::Context::run furnace_smelt arm: smelting recipe, use_input_count and stack cap
SmeltItemFunction.smelted skip:java static builder factory
ToggleTooltips home crates/horizon-client/src/game/commands/items.rs the `/item modify` arm toggle_tooltips writes the tooltip_display component's hide_tooltip and hidden_components as run does; the loot run has no arm, and no vanilla loot table uses it
ToggleTooltips home crates/horizon-client/src/game/commands/items.rs the `/item modify` arm of cmd_item_modify writes the tooltip_display component's hide_tooltip and hidden_components as run does; the loot run has no arm, and no vanilla loot table uses it
ToggleTooltips.run inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item_modify the `minecraft:toggle_tooltips` arm of the `/item modify` command adds each false toggle to `tooltip_display`'s hidden_components and keeps hide_tooltip; the loot run has no arm, so a loot table's toggle_tooltips is ignored (no vanilla loot table uses one)
LootContextParamSets skip:data loot parameter set registry and validation; Horizon reads its loot tables
AllOfCondition.allOf skip:java static builder
AllOfCondition.combine inline crates/horizon-items/src/items.rs::LootContext::condition all_of arm: every term must hold
@@ -16,11 +17,11 @@ CompositeLootItemCondition.Builder skip:java builder (addTerm)
ConditionUserBuilder skip:java builder interface for loot conditions
DamageSourceCondition.test port crates/horizon-world/src/loot.rs::Context::condition damage_source_properties arm (mob death loot)
EnchantmentActiveCheck.test inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed the soul speed modifiers switch on and off here; the ENCHANTMENT_ACTIVE flag itself is not read as a condition
EntityHasScoreCondition.hasScore missing minor: no vanilla loot table uses entity_scores
EntityHasScoreCondition.test missing minor: no vanilla loot table uses entity_scores
EntityHasScoreCondition.hasScore missing `loot.rs::condition` has no `entity_scores` arm, and no vanilla loot table asks for one
EntityHasScoreCondition.test missing same as `hasScore`: the condition dispatch (`inverted`, `all_of`, `any_of`, `entity_properties`, `killed_by_player`, the chance and location checks) has no scoreboard arm
EntityHasScoreCondition.validate skip:data datapack validation
EnvironmentAttributeCheck.createCodec skip:java codec
EnvironmentAttributeCheck.test missing minor: no vanilla data uses environment_attribute_check
EnvironmentAttributeCheck.test missing no `environment_attribute_check` arm in `loot.rs::condition`; no vanilla data uses it
ExplosionCondition.test port crates/horizon-items/src/items.rs::LootContext::condition survives_explosion arm: 1 / radius under an explosion (block loot; only block tables use it)
FloatValueCheck.validate skip:data datapack validation
IntValueCheck.validate skip:data datapack validation
@@ -39,7 +40,7 @@ MatchBlock.blockMatches skip:java static builder
MatchBlock.getReferencedContextParams skip:data validation metadata for context params
MatchBlock.test port crates/horizon-items/src/items.rs::LootContext::condition match_block arm: block id, state and property ranges; gap: minor: the block-entity part of the predicate is not read
MatchTool.test port crates/horizon-items/src/items.rs::LootContext::condition match_tool arm; gap: minor: item ids, tags and enchantment levels only, other ItemPredicate parts are not read
TimeCheck missing minor: time_check is used by no vanilla data file (datapacks only)
TimeCheck missing no `time_check` arm in `loot.rs::condition`; no vanilla data file uses it, so only a datapack reaches it
WeatherCheck.test port crates/horizon-client/src/game/trident.rs::Game::channel_lightning channeling's thunder check (thundering only); gap: minor: raining is not read, channeling only uses thundering
NbtProviders skip:data registry bootstrap
AggregateProvider skip:java codec only
@@ -58,13 +59,13 @@ PowerProvider.mapCodec skip:java codec
PowerProvider.validate skip:data datapack validation
RangeProvider.mapCodec skip:java codec
RangeProvider.validate skip:data datapack validation
StoredNumberAccess missing minor: storage-backed numbers; no vanilla data uses storage providers
StoredNumberAccess missing no storage-backed number provider: `loot.rs::level_value` and the int and float readers know constant, uniform, binomial, linear, clamped, fraction, levels_squared and lookup only
UnaryProvider skip:java codec only
floats/Average.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float avg arm
floats/ContextFloatProviders skip:data registry bootstrap
floats/Difference.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float sub arm
floats/EnchantmentLevelProvider.getFloatUnsafe port crates/horizon-items/src/enchant.rs::Enchantments::condition random_chance arm: chance at the enchantment level; gap: minor: only the random_chance use is read
floats/EnvironmentAttributeValue missing minor: no vanilla data uses environment_attribute providers
floats/EnvironmentAttributeValue missing no `environment_attribute` provider in `loot.rs::level_value`
floats/Length.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float length arm
floats/Maximum.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float max arm
floats/Minimum.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float min arm
@@ -73,7 +74,7 @@ floats/Product.getFloatUnsafe port crates/horizon-client/src/game/commands/compu
floats/Quotient.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float div arm
ResolvableFloat.getFromItem port crates/horizon-client-render/src/providers.rs::Providers::speed furnace fuel speed multiplier (COOKING_FUEL); gap: minor: only the furnace fuel component is read
ResolvableFloat.wrap skip:java codec helper
floats/StorageValue missing minor: no vanilla data uses storage providers
floats/StorageValue missing no `storage` provider in `loot.rs::level_value`
floats/Sum.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float add arm
floats/Truncate.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float truncate arm
floats/UniformGenerator.getFloatUnsafe port crates/horizon-world/src/loot.rs::Context::float uniform arm: min + random * (max - min)
@@ -85,7 +86,7 @@ ints/ContextIntProviders.bootstrap skip:data registry bootstrap
ints/ContextIntProviders.compostable port crates/horizon-client/src/game/composter.rs::Game::compostable the item's compostable layer provider; evaluated when the composter takes it (Game::composter_add)
ints/ContextIntProviders.cooking port crates/horizon-client-render/src/providers.rs::Providers::burn_time fuel burn times (the cooking/time_* providers)
ints/Difference.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int sub arm
ints/EnvironmentAttributeValue missing minor: no vanilla data uses environment_attribute providers
ints/EnvironmentAttributeValue missing no `environment_attribute` provider in the int reader (`loot.rs::int`)
ints/FloorModulus.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int floor_mod arm
ints/FloorQuotient.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int floor_div arm
ints/Maximum.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int max arm
@@ -95,10 +96,10 @@ ints/Product.getIntUnsafe port crates/horizon-client/src/game/commands/compute.r
ints/Quotient.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int div arm
ResolvableInt.getFromItem port crates/horizon-client-render/src/providers.rs::Providers::burn_time furnace fuel burn time (COOKING_FUEL); gap: minor: only the furnace fuel component is read
ResolvableInt.wrap skip:java codec helper
ints/ScoreboardValue.getIntUnsafe missing minor: no vanilla data uses score providers
ints/ScoreboardValue.getScoreInfo missing minor: no vanilla data uses score providers
ints/ScoreboardValue.getIntUnsafe missing no `score` provider in `loot.rs::int`
ints/ScoreboardValue.getScoreInfo missing no `score` provider in `loot.rs::int`
ints/ScoreboardValue.validate skip:data datapack validation
ints/StorageValue missing minor: no vanilla data uses storage providers
ints/StorageValue missing no `storage` provider in `loot.rs::int`
ints/Sum.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int add arm
ints/UniformGenerator.getIntUnsafe port crates/horizon-world/src/loot.rs::Context::int uniform arm: inclusive random int between min and max
ints/WeightedListValue.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_select weighted list pick (the default branch)
@@ -118,7 +119,7 @@ DirectoryValidator.validateSymlink skip:server server check of symlinks in worl
PathAllowList.getForFileSystem skip:server server file allow-list
PathAllowList.readPlain skip:server server file allow-list
PathAllowList.ConfigEntry.parse skip:server server file allow-list
BlockHitResult.hitBorder missing minor: BlockHit has no world-border flag; vanilla projectiles check it on a border hit
BlockHitResult.hitBorder missing `BlockHit` (`horizon-core/src/geometry/mod.rs`) carries the location, the direction, the block and `inside`, no world-border flag, so a projectile's border hit is not told apart from any other
BlockHitResult.withDirection skip:java copy helper (Rust struct update)
BlockHitResult.withPosition skip:java copy helper (Rust struct update)
HitResult.distanceTo port crates/horizon-client/src/game/fishing.rs::rebound the bobber's block-hit bounce: the squared distance to the hook scales the normalised velocity (`FishingHook.onHitBlock`)
@@ -149,7 +150,7 @@ IdenticalMerger.forMergedIndexes port crates/horizon-core/src/geometry/shape/joi
IndirectMerger.<init> skip:java list wrapper constructor
IndirectMerger.forMergedIndexes port crates/horizon-core/src/geometry/shape/join.rs::merge
IndirectMerger.getList skip:java list accessor
MinecartCollisionContext missing player: vanilla lets a rail cart pass through the block under its rail and the slope block of an ascending rail (MinecartCollisionContext); minecarts.rs move_cart has no such exclusion, so carts may bump on those blocks (read from code, not checked in game)
MinecartCollisionContext missing `game/minecarts.rs::move_cart` has no exclusion for the block a rail rests on or the slope block of an ascending rail, so a cart's move collides with them as with any other block
NonOverlappingMerger.forMergedIndexes port crates/horizon-core/src/geometry/shape/join.rs::disjoint
NonOverlappingMerger.forNonSwappedIndexes port crates/horizon-core/src/geometry/shape/join.rs::disjoint
NonOverlappingMerger.getDouble skip:java list accessor
1 SetStewEffectFunction.Builder skip:java builder for the stew effect list; Horizon keeps the effects in loot.rs Context::run (set_stew_effect)
2 SetWritableBookPagesFunction missing minor: no vanilla loot table uses set_writable_book_pages; Horizon's loot run has no arm for it `horizon-world/src/loot.rs::modify` has no `set_writable_book_pages` arm, and no vanilla loot table asks for one
3 SetWrittenBookPagesFunction missing minor: no vanilla loot table uses set_written_book_pages; Horizon's loot run has no arm for it no `set_written_book_pages` arm in `loot.rs::modify` (the arms run from `sequence` and `set_count` through `set_potion` to `set_stew_effect`); no vanilla loot table asks for one
4 SmeltItemFunction.run port crates/horizon-world/src/loot.rs::Context::run furnace_smelt arm: smelting recipe, use_input_count and stack cap
5 SmeltItemFunction.smelted skip:java static builder factory
6 ToggleTooltips home crates/horizon-client/src/game/commands/items.rs the `/item modify` arm toggle_tooltips writes the tooltip_display component's hide_tooltip and hidden_components as run does; the loot run has no arm, and no vanilla loot table uses it the `/item modify` arm of cmd_item_modify writes the tooltip_display component's hide_tooltip and hidden_components as run does; the loot run has no arm, and no vanilla loot table uses it
7 ToggleTooltips.run inline crates/horizon-client/src/game/commands/items.rs::Game::cmd_item_modify the `minecraft:toggle_tooltips` arm of the `/item modify` command adds each false toggle to `tooltip_display`'s hidden_components and keeps hide_tooltip; the loot run has no arm, so a loot table's toggle_tooltips is ignored (no vanilla loot table uses one)
8 LootContextParamSets skip:data loot parameter set registry and validation; Horizon reads its loot tables
9 AllOfCondition.allOf skip:java static builder
10 AllOfCondition.combine inline crates/horizon-items/src/items.rs::LootContext::condition all_of arm: every term must hold
17 ConditionUserBuilder skip:java builder interface for loot conditions
18 DamageSourceCondition.test port crates/horizon-world/src/loot.rs::Context::condition damage_source_properties arm (mob death loot)
19 EnchantmentActiveCheck.test inline crates/horizon-client/src/game/enchant_effects.rs::Game::tick_soul_speed the soul speed modifiers switch on and off here; the ENCHANTMENT_ACTIVE flag itself is not read as a condition
20 EntityHasScoreCondition.hasScore missing minor: no vanilla loot table uses entity_scores `loot.rs::condition` has no `entity_scores` arm, and no vanilla loot table asks for one
21 EntityHasScoreCondition.test missing minor: no vanilla loot table uses entity_scores same as `hasScore`: the condition dispatch (`inverted`, `all_of`, `any_of`, `entity_properties`, `killed_by_player`, the chance and location checks) has no scoreboard arm
22 EntityHasScoreCondition.validate skip:data datapack validation
23 EnvironmentAttributeCheck.createCodec skip:java codec
24 EnvironmentAttributeCheck.test missing minor: no vanilla data uses environment_attribute_check no `environment_attribute_check` arm in `loot.rs::condition`; no vanilla data uses it
25 ExplosionCondition.test port crates/horizon-items/src/items.rs::LootContext::condition survives_explosion arm: 1 / radius under an explosion (block loot; only block tables use it)
26 FloatValueCheck.validate skip:data datapack validation
27 IntValueCheck.validate skip:data datapack validation
40 MatchBlock.getReferencedContextParams skip:data validation metadata for context params
41 MatchBlock.test port crates/horizon-items/src/items.rs::LootContext::condition match_block arm: block id, state and property ranges; gap: minor: the block-entity part of the predicate is not read
42 MatchTool.test port crates/horizon-items/src/items.rs::LootContext::condition match_tool arm; gap: minor: item ids, tags and enchantment levels only, other ItemPredicate parts are not read
43 TimeCheck missing minor: time_check is used by no vanilla data file (datapacks only) no `time_check` arm in `loot.rs::condition`; no vanilla data file uses it, so only a datapack reaches it
44 WeatherCheck.test port crates/horizon-client/src/game/trident.rs::Game::channel_lightning channeling's thunder check (thundering only); gap: minor: raining is not read, channeling only uses thundering
45 NbtProviders skip:data registry bootstrap
46 AggregateProvider skip:java codec only
59 PowerProvider.validate skip:data datapack validation
60 RangeProvider.mapCodec skip:java codec
61 RangeProvider.validate skip:data datapack validation
62 StoredNumberAccess missing minor: storage-backed numbers; no vanilla data uses storage providers no storage-backed number provider: `loot.rs::level_value` and the int and float readers know constant, uniform, binomial, linear, clamped, fraction, levels_squared and lookup only
63 UnaryProvider skip:java codec only
64 floats/Average.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float avg arm
65 floats/ContextFloatProviders skip:data registry bootstrap
66 floats/Difference.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float sub arm
67 floats/EnchantmentLevelProvider.getFloatUnsafe port crates/horizon-items/src/enchant.rs::Enchantments::condition random_chance arm: chance at the enchantment level; gap: minor: only the random_chance use is read
68 floats/EnvironmentAttributeValue missing minor: no vanilla data uses environment_attribute providers no `environment_attribute` provider in `loot.rs::level_value`
69 floats/Length.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float length arm
70 floats/Maximum.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float max arm
71 floats/Minimum.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float min arm
74 floats/Quotient.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float div arm
75 ResolvableFloat.getFromItem port crates/horizon-client-render/src/providers.rs::Providers::speed furnace fuel speed multiplier (COOKING_FUEL); gap: minor: only the furnace fuel component is read
76 ResolvableFloat.wrap skip:java codec helper
77 floats/StorageValue missing minor: no vanilla data uses storage providers no `storage` provider in `loot.rs::level_value`
78 floats/Sum.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float add arm
79 floats/Truncate.getFloatUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_float truncate arm
80 floats/UniformGenerator.getFloatUnsafe port crates/horizon-world/src/loot.rs::Context::float uniform arm: min + random * (max - min)
86 ints/ContextIntProviders.compostable port crates/horizon-client/src/game/composter.rs::Game::compostable the item's compostable layer provider; evaluated when the composter takes it (Game::composter_add)
87 ints/ContextIntProviders.cooking port crates/horizon-client-render/src/providers.rs::Providers::burn_time fuel burn times (the cooking/time_* providers)
88 ints/Difference.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int sub arm
89 ints/EnvironmentAttributeValue missing minor: no vanilla data uses environment_attribute providers no `environment_attribute` provider in the int reader (`loot.rs::int`)
90 ints/FloorModulus.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int floor_mod arm
91 ints/FloorQuotient.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int floor_div arm
92 ints/Maximum.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int max arm
96 ints/Quotient.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int div arm
97 ResolvableInt.getFromItem port crates/horizon-client-render/src/providers.rs::Providers::burn_time furnace fuel burn time (COOKING_FUEL); gap: minor: only the furnace fuel component is read
98 ResolvableInt.wrap skip:java codec helper
99 ints/ScoreboardValue.getIntUnsafe missing minor: no vanilla data uses score providers no `score` provider in `loot.rs::int`
100 ints/ScoreboardValue.getScoreInfo missing minor: no vanilla data uses score providers no `score` provider in `loot.rs::int`
101 ints/ScoreboardValue.validate skip:data datapack validation
102 ints/StorageValue missing minor: no vanilla data uses storage providers no `storage` provider in `loot.rs::int`
103 ints/Sum.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_int add arm
104 ints/UniformGenerator.getIntUnsafe port crates/horizon-world/src/loot.rs::Context::int uniform arm: inclusive random int between min and max
105 ints/WeightedListValue.getIntUnsafe port crates/horizon-client/src/game/commands/compute.rs::Game::provider_select weighted list pick (the default branch)
119 PathAllowList.getForFileSystem skip:server server file allow-list
120 PathAllowList.readPlain skip:server server file allow-list
121 PathAllowList.ConfigEntry.parse skip:server server file allow-list
122 BlockHitResult.hitBorder missing minor: BlockHit has no world-border flag; vanilla projectiles check it on a border hit `BlockHit` (`horizon-core/src/geometry/mod.rs`) carries the location, the direction, the block and `inside`, no world-border flag, so a projectile's border hit is not told apart from any other
123 BlockHitResult.withDirection skip:java copy helper (Rust struct update)
124 BlockHitResult.withPosition skip:java copy helper (Rust struct update)
125 HitResult.distanceTo port crates/horizon-client/src/game/fishing.rs::rebound the bobber's block-hit bounce: the squared distance to the hook scales the normalised velocity (`FishingHook.onHitBlock`)
150 IndirectMerger.<init> skip:java list wrapper constructor
151 IndirectMerger.forMergedIndexes port crates/horizon-core/src/geometry/shape/join.rs::merge
152 IndirectMerger.getList skip:java list accessor
153 MinecartCollisionContext missing player: vanilla lets a rail cart pass through the block under its rail and the slope block of an ascending rail (MinecartCollisionContext); minecarts.rs move_cart has no such exclusion, so carts may bump on those blocks (read from code, not checked in game) `game/minecarts.rs::move_cart` has no exclusion for the block a rail rests on or the slope block of an ascending rail, so a cart's move collides with them as with any other block
154 NonOverlappingMerger.forMergedIndexes port crates/horizon-core/src/geometry/shape/join.rs::disjoint
155 NonOverlappingMerger.forNonSwappedIndexes port crates/horizon-core/src/geometry/shape/join.rs::disjoint
156 NonOverlappingMerger.getDouble skip:java list accessor
+5 -5
View File
@@ -9,7 +9,7 @@ EnderDragonRenderer.submit port crates/horizon-client/src/game/dragon.rs::Game::
EnderDragonRenderer.submitRays port crates/horizon-client/src/game/dragon.rs::Game::dragon_rays
EnderDragonRenderer.submitCrystalBeams port crates/horizon-client/src/game/dragon.rs::Game::crystal_beam
EnderDragonRenderer.extractRenderState inline crates/horizon-client/src/game/dragon.rs::Game::dragon_geometry flap, death fade, hurt overlay, nearest-crystal beam and egg distance read there
EndermanRenderer.getRenderOffset port crates/horizon-client/src/mobs/render.rs EndermanRenderer.getRenderOffset's random side offset for a creepy enderman
EndermanRenderer.getRenderOffset port crates/horizon-client/src/mobs/render.rs::enderman_render_offset EndermanRenderer.getRenderOffset's random side offset for a creepy enderman
EndermanRenderer.extractRenderState inline crates/horizon-client/src/mobs/enderman.rs::anim gap: minor: carried block is drawn by game/mob_blocks.rs, not from this pose
EntityRenderDispatcher.getRenderer skip:other renderer lookup; Horizon dispatches by mob kind in game.rs::Game::entity_geometry and game/creatures.rs::Game::mob_geometry
EntityRenderDispatcher.getAvatarRenderer port crates/horizon-client/src/mobs/render.rs::body slim or wide arm model by skin
@@ -65,7 +65,7 @@ HumanoidMobRenderer.getEquipmentIfRenderable port crates/horizon-client/src/mobs
IllagerRenderer.extractRenderState port crates/horizon-client/src/mobs/illager.rs::holding_crossbow crossbow charge and aggressive pose
IllusionerRenderer.extractRenderState port crates/horizon-client/src/mobs/illusioner.rs::offsets mirror-image offsets; casting flag
IronGolemRenderer.extractRenderState port crates/horizon-client/src/mobs/golem.rs::client_tick gap: player: the offered poppy is not drawn in the golem hand and cracks from damage are not drawn
IronGolemRenderer.setupRotations port crates/horizon-client/src/mobs/render.rs::base_transform_at (IronGolem branch) the 6.5-degree triangle wave over a 13-block walk phase
IronGolemRenderer.setupRotations port crates/horizon-client/src/mobs/render.rs::base_transform_at the 6.5-degree triangle wave over a 13-block walk phase
ItemEntityRenderer.extractRenderState port crates/horizon-client/src/game/item_draw.rs::Game::dropped_item_geometry bob offset, spin and spread
ItemEntityRenderer.renderMultipleFromCount port crates/horizon-client/src/item_render.rs::cluster_poses copies of a stack laid out by depth
ItemFrameRenderer.submit port crates/horizon-client/src/game/frames.rs::Game::frame_geometry frame model, item or map
@@ -96,7 +96,7 @@ PaintingRenderer.vertex inline crates/horizon-client/src/game/paintings.rs::Game
PandaRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::panda sit, lie, sneeze and roll states
ParrotRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::parrot pose and flap angle
PhantomRenderer.extractRenderState port crates/horizon-client/src/mobs/phantom.rs::anim gap: player: phantom size is not read, so phantoms never grow
PhantomRenderer.scale port crates/horizon-client/src/mobs/render.rs::species_scale (Species::Phantom) 1 + 0.15 * size, with the model's 1.3125 offset
PhantomRenderer.scale port crates/horizon-client/src/mobs/render.rs::species_scale 1 + 0.15 * size, with the model's 1.3125 offset
PhantomRenderer.setupRotations port crates/horizon-client/src/mobs/phantom.rs::anim gap: minor: body pitch (xRot) is not applied; wings and tail are
PigRenderer.bakeModels skip:data model baking; Horizon loads model data from files
PigRenderer.submit port crates/horizon-client/src/mobs/render.rs::body cold or normal model by variant
@@ -107,7 +107,7 @@ PiglinRenderer.extractRenderState port crates/horizon-client/src/mobs/anim.rs::h
PiglinRenderer.isShaking port crates/horizon-client/src/mobs/render.rs::shaking converting piglin shakes
PolarBearRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::polar_bear standing scale
PufferfishRenderer.submit port crates/horizon-client/src/mobs/render.rs::body small, medium or big model by size
PufferfishRenderer.setupRotations port crates/horizon-client/src/mobs/render.rs::pufferfish_bob / pufferfish_bob_y the 0.08-block bob
PufferfishRenderer.setupRotations port crates/horizon-client/src/mobs/render.rs::pufferfish_bob the 0.08-block bob
RabbitRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::rabbit hop and idle head-tilt animations; Toast name in mobs/render.rs::body
RavagerRenderer.extractRenderState port crates/horizon-client/src/mobs/ravager.rs::anim stun, bite and roar clocks
SalmonRenderer.setupRotations port crates/horizon-client/src/mobs/aquatic.rs::rotations the 4.3-amplitude swim wiggle and the flop
@@ -132,7 +132,7 @@ ThrownItemRenderer.submit port crates/horizon-client/src/game/creatures.rs::Game
ThrownItemRenderer.extractRenderState inline crates/horizon-client/src/game/creatures.rs::Game::mob_geometry item sprite read there
ThrownTridentRenderer.submit port crates/horizon-client/src/game/trident.rs::Game::trident_geometry gap: minor: foil glint is not drawn on a thrown trident
ThrownTridentRenderer.extractRenderState inline crates/horizon-client/src/game/trident.rs::Game::trident_geometry yaw, pitch and foil read there
TippableArrowRenderer.extractRenderState port crates/horizon-client/src/projectiles.rs brewing::potion_color gives a tipped arrow its potion colour
TippableArrowRenderer.extractRenderState port crates/horizon-client/src/projectiles.rs::Projectile::arrow_effect_color brewing::potion_color gives a tipped arrow its potion colour
TntMinecartRenderer.submitMinecartContents port crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry TNT block swell and white flash
TntMinecartRenderer.submitWhiteSolidBlock inline crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry white overlay built inline
TntMinecartRenderer.extractRenderState inline crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry fuse read from the cart
1 DolphinRenderer.extractRenderState port crates/horizon-client/src/game/mob_blocks.rs::Game::held_item_geometry gap: minor: isMoving (dolphin motion flag) is not read
9 EnderDragonRenderer.submitRays port crates/horizon-client/src/game/dragon.rs::Game::dragon_rays
10 EnderDragonRenderer.submitCrystalBeams port crates/horizon-client/src/game/dragon.rs::Game::crystal_beam
11 EnderDragonRenderer.extractRenderState inline crates/horizon-client/src/game/dragon.rs::Game::dragon_geometry flap, death fade, hurt overlay, nearest-crystal beam and egg distance read there
12 EndermanRenderer.getRenderOffset port crates/horizon-client/src/mobs/render.rs crates/horizon-client/src/mobs/render.rs::enderman_render_offset EndermanRenderer.getRenderOffset's random side offset for a creepy enderman
13 EndermanRenderer.extractRenderState inline crates/horizon-client/src/mobs/enderman.rs::anim gap: minor: carried block is drawn by game/mob_blocks.rs, not from this pose
14 EntityRenderDispatcher.getRenderer skip:other renderer lookup; Horizon dispatches by mob kind in game.rs::Game::entity_geometry and game/creatures.rs::Game::mob_geometry
15 EntityRenderDispatcher.getAvatarRenderer port crates/horizon-client/src/mobs/render.rs::body slim or wide arm model by skin
65 IllagerRenderer.extractRenderState port crates/horizon-client/src/mobs/illager.rs::holding_crossbow crossbow charge and aggressive pose
66 IllusionerRenderer.extractRenderState port crates/horizon-client/src/mobs/illusioner.rs::offsets mirror-image offsets; casting flag
67 IronGolemRenderer.extractRenderState port crates/horizon-client/src/mobs/golem.rs::client_tick gap: player: the offered poppy is not drawn in the golem hand and cracks from damage are not drawn
68 IronGolemRenderer.setupRotations port crates/horizon-client/src/mobs/render.rs::base_transform_at (IronGolem branch) crates/horizon-client/src/mobs/render.rs::base_transform_at the 6.5-degree triangle wave over a 13-block walk phase
69 ItemEntityRenderer.extractRenderState port crates/horizon-client/src/game/item_draw.rs::Game::dropped_item_geometry bob offset, spin and spread
70 ItemEntityRenderer.renderMultipleFromCount port crates/horizon-client/src/item_render.rs::cluster_poses copies of a stack laid out by depth
71 ItemFrameRenderer.submit port crates/horizon-client/src/game/frames.rs::Game::frame_geometry frame model, item or map
96 PandaRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::panda sit, lie, sneeze and roll states
97 ParrotRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::parrot pose and flap angle
98 PhantomRenderer.extractRenderState port crates/horizon-client/src/mobs/phantom.rs::anim gap: player: phantom size is not read, so phantoms never grow
99 PhantomRenderer.scale port crates/horizon-client/src/mobs/render.rs::species_scale (Species::Phantom) crates/horizon-client/src/mobs/render.rs::species_scale 1 + 0.15 * size, with the model's 1.3125 offset
100 PhantomRenderer.setupRotations port crates/horizon-client/src/mobs/phantom.rs::anim gap: minor: body pitch (xRot) is not applied; wings and tail are
101 PigRenderer.bakeModels skip:data model baking; Horizon loads model data from files
102 PigRenderer.submit port crates/horizon-client/src/mobs/render.rs::body cold or normal model by variant
107 PiglinRenderer.isShaking port crates/horizon-client/src/mobs/render.rs::shaking converting piglin shakes
108 PolarBearRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::polar_bear standing scale
109 PufferfishRenderer.submit port crates/horizon-client/src/mobs/render.rs::body small, medium or big model by size
110 PufferfishRenderer.setupRotations port crates/horizon-client/src/mobs/render.rs::pufferfish_bob / pufferfish_bob_y crates/horizon-client/src/mobs/render.rs::pufferfish_bob the 0.08-block bob
111 RabbitRenderer.extractRenderState port crates/horizon-client/src/mobs/body_anim.rs::rabbit hop and idle head-tilt animations; Toast name in mobs/render.rs::body
112 RavagerRenderer.extractRenderState port crates/horizon-client/src/mobs/ravager.rs::anim stun, bite and roar clocks
113 SalmonRenderer.setupRotations port crates/horizon-client/src/mobs/aquatic.rs::rotations the 4.3-amplitude swim wiggle and the flop
132 ThrownItemRenderer.extractRenderState inline crates/horizon-client/src/game/creatures.rs::Game::mob_geometry item sprite read there
133 ThrownTridentRenderer.submit port crates/horizon-client/src/game/trident.rs::Game::trident_geometry gap: minor: foil glint is not drawn on a thrown trident
134 ThrownTridentRenderer.extractRenderState inline crates/horizon-client/src/game/trident.rs::Game::trident_geometry yaw, pitch and foil read there
135 TippableArrowRenderer.extractRenderState port crates/horizon-client/src/projectiles.rs crates/horizon-client/src/projectiles.rs::Projectile::arrow_effect_color brewing::potion_color gives a tipped arrow its potion colour
136 TntMinecartRenderer.submitMinecartContents port crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry TNT block swell and white flash
137 TntMinecartRenderer.submitWhiteSolidBlock inline crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry white overlay built inline
138 TntMinecartRenderer.extractRenderState inline crates/horizon-client/src/game/minecarts.rs::Game::cart_geometry fuse read from the cart
+11 -11
View File
@@ -5,7 +5,7 @@ CloudRenderer.isEastEmpty inline crates/horizon-client-render/src/clouds.rs::Mes
CloudRenderer.isSouthEmpty inline crates/horizon-client-render/src/clouds.rs::Mesh::prepare neighbour-empty flags passed to face_mask in N,E,S,W order
CloudRenderer.isWestEmpty inline crates/horizon-client-render/src/clouds.rs::Mesh::prepare neighbour-empty flags passed to face_mask in N,E,S,W order
CloudRenderer.render port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame gap: minor: one cloud pipeline; fancy vs flat is chosen by the mesh fast flag, not by pipeline
CloudRenderer.renderOit port crates/horizon-client-render/src/gfx/oit.rs the clouds draw through the OIT pipeline there, with their own depth beside the world's
CloudRenderer.renderOit port crates/horizon-client-render/src/gfx/oit.rs::Oit::render_clouds the clouds draw through the OIT pipeline there, with their own depth beside the world's
CloudRenderer.buildMesh port crates/horizon-client-render/src/clouds.rs::Mesh::prepare cell ring walk via cells(radius), nearest first
CloudRenderer.tryBuildCell inline crates/horizon-client-render/src/clouds.rs::Mesh::prepare skip empty cells, then build the cell's faces
CloudRenderer.encodeFace inline crates/horizon-client-render/src/clouds.rs::Mesh::prepare 3-byte face records replaced by vertex quads
@@ -40,7 +40,7 @@ FaceInfo.Extent.select skip:data corner selection; Horizon's unit-cube corner t
FaceInfo.VertexInfo.select skip:data corner selection; Horizon's unit-cube corner table PORTAL_FACES in block_entity_render.rs has the same order
FirstPersonHandsAndItemsRenderer.calculateMapTilt inline crates/horizon-client/src/game/maps.rs::Game::two_handed_map_geometry tilt computed inline with the calculateMapTilt comment
FirstPersonHandsAndItemsRenderer.renderPlayerHand port crates/horizon-client/src/game.rs::Game::hand_geometry empty main hand's arm model with the player skin
FirstPersonHandsAndItemsRenderer.renderMapHand port crates/horizon-client/src/first_person.rs::map_arm_poses (MapHold::TwoHanded) both arms behind a two-handed map, at the jar's rotations and offsets
FirstPersonHandsAndItemsRenderer.renderMapHand port crates/horizon-client/src/first_person.rs::map_arm_poses both arms behind a two-handed map, at the jar's rotations and offsets
FirstPersonHandsAndItemsRenderer.renderOneHandedMap port crates/horizon-client/src/first_person.rs::one_handed_map_pose gap: player: the arm holding a map is not drawn; the map hand branch skips the arm
FirstPersonHandsAndItemsRenderer.renderTwoHandedMap port crates/horizon-client/src/game/maps.rs::Game::two_handed_map_geometry gap: player: the arms that hold a two-handed map are not drawn
FirstPersonHandsAndItemsRenderer.renderMap port crates/horizon-client/src/game/maps.rs::Game::held_map map background with its 7-pixel border, then the map contents
@@ -57,17 +57,17 @@ GameRenderer.processBlurEffect port crates/horizon-client-render/src/menu_blur.r
GameRenderer.preloadUiShader skip:other GPU pipeline precompile at load; Horizon builds its pipelines in Renderer::new
GameRenderer.update port crates/horizon-client-render/src/camera.rs::AvatarBob::update_bob gap: minor: the requested post-effect list (from /postEffect) is not built
GameRenderer.extract skip:other render-state copy for the vanilla render thread; Horizon renders from game state
GameRenderer.preparePostEffects port crates/horizon-client-render/src/gfx.rs::post_effects (post_chain::PostEffects, built at gfx.rs:8424) vanilla post chains are made on first use from the request the game builds
GameRenderer.applyPostEffects port crates/horizon-client-render/src/gfx.rs::post_effects the same chain runs after the world pass
GameRenderer.tryTakeScreenshotIfNeeded port crates/horizon-client/src/game.rs::tick_world_icon (called at game.rs:17852) a local world without an icon requests a 64x64 ScreenshotRequest for saves/<folder>/icon.png once the streamer, uploads and backlog are idle and more than 10 sections are drawn
GameRenderer.preparePostEffects port crates/horizon-client-render/src/post_chain.rs::PostEffects::apply vanilla post chains are made on first use from the request the game builds
GameRenderer.applyPostEffects port crates/horizon-client-render/src/post_chain.rs::PostEffects::apply the same chain runs after the world pass
GameRenderer.tryTakeScreenshotIfNeeded port crates/horizon-client/src/game.rs::Game::tick_world_icon a local world without an icon requests a 64x64 ScreenshotRequest for saves/<folder>/icon.png once the streamer, uploads and backlog are idle and more than 10 sections are drawn
GameRenderer.takeAutoScreenshot inline crates/horizon-client/src/game.rs::tick_world_icon the same retry, once a second until the file exists; the path comes from game/integrated.rs:485
GameRenderer.integrate3DHudDepth inline crates/horizon-client-render/src/gfx.rs::post_effects runs with a post chain applied, which is vanilla's consistentDepthRequired condition
GameRenderer.integrate3DHudDepth inline crates/horizon-client-render/src/post_chain.rs::PostEffects::apply runs with a post chain applied, which is vanilla's consistentDepthRequired condition
GameRenderer.extractWindow skip:other render-state copy of the window size; Horizon reads the window directly
GameRenderer.extractOptions skip:other render-state copy of options; Horizon reads settings directly
GameRenderer.extractCamera skip:other render-state copy of camera and fog; Horizon computes them in camera.rs and fog.rs
GameRenderer.resetData inline crates/horizon-client/src/game.rs::Game::finish_close_world boss overlay reset on world close; camera and map-texture resets not verified here
GameRenderer.projectPointToScreen skip:other no callers in vanilla 26.3
GameRenderer.useImprovedTransparency port crates/horizon-client-render/src/gfx.rs::improved_transparency / oit: Option<oit::Oit> the option builds the OIT pass
GameRenderer.useImprovedTransparency port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame the option builds the OIT pass
GlobalSettingsUniform.update skip:other global uniform (window size, time, gamma, glint) written per pass; Horizon sets its own uniforms
GpuWarnlistManager.getAllWarnings missing minor: no GPU driver warnlist; Horizon does not check the driver against known-bad lists
GpuWarnlistManager.compilePatterns missing minor: no GPU driver warnlist patterns
@@ -82,9 +82,9 @@ LevelRenderer.addMainPass port crates/horizon-client-render/src/gfx.rs::Renderer
LevelRenderer.executeAlwaysOnTop port crates/horizon-client/src/game.rs::Game::render always-on-top debug lines and crosshair drawn over the world
LevelRenderer.executeSolid port crates/horizon-client-render/src/gfx.rs::Renderer::draw_sections solid section draws in arena page order
LevelRenderer.prepareTranslucents port crates/horizon-client-render/src/gfx/translucency.rs::Sorter::sort translucent quads sorted from the camera each frame
LevelRenderer.executeOit port crates/horizon-client-render/src/gfx/oit.rs + gfx::mark_oit (gfx/entity_batch.rs:31) translucent geometry is marked and resolved by the OIT pass
LevelRenderer.executeDepthBoundsCull inline crates/horizon-client-render/src/gfx/oit.rs part of the same pass
LevelRenderer.executeOitWaterMask port crates/horizon-client-render/src/gfx.rs::WATER_MASK_OVERLAY the water mask overlay of the same pass
LevelRenderer.executeOit port crates/horizon-client-render/src/gfx/oit.rs::Oit::run translucent geometry is marked and resolved by the OIT pass
LevelRenderer.executeDepthBoundsCull inline crates/horizon-client-render/src/gfx/oit.rs::Oit::execute_depth_bounds_cull part of the same pass
LevelRenderer.executeOitWaterMask port crates/horizon-client-render/src/gfx/oit.rs::Oit::execute_water_mask the water mask overlay of the same pass
LevelRenderer.extractSectionDrawGroups port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame section draws grouped per layer and arena page from the visible sections
LevelRenderer.prepareChunkRenders port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame per-layer draw list built for the frame, reversed for translucent layers
LevelRenderer.performTransientBlockRemovals inline crates/horizon-client/src/game/transient_blocks.rs::add_transient_block transient blocks are tracked and dropped by that list
@@ -93,7 +93,7 @@ LevelRenderer.compileSections port crates/horizon-client-render/src/streaming.rs
LevelRenderer.submitEntities port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame entity models drawn with their batches
LevelRenderer.submitBlockEntities port crates/horizon-client-render/src/gfx.rs::Renderer::block_entities block entity list submitted for drawing
LevelRenderer.submitBlockDestroyAnimation port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame crumbling overlay drawn on the block being broken
LevelRenderer.submitTransientBlocks port crates/horizon-client/src/game/remote.rs: C::AddTransientBlock => add_transient_block the packet is handled and the block is drawn
LevelRenderer.submitTransientBlocks port crates/horizon-client/src/game/transient_blocks.rs::Game::transient_block_geometry the packet is handled and the block is drawn
LevelRenderer.submitHitOutline port crates/horizon-client/src/game.rs::Game::outline_boxes outline of the block under the crosshair
LevelRenderer.close skip:other GPU resource teardown
LevelRenderer.invalidateCompiledGeometry port crates/horizon-client-render/src/gfx.rs::Renderer::clear_sections all section meshes dropped and rebuilt when settings are applied
1 CloudRenderer.getSizeForCloudDistance skip:java GPU ring-buffer capacity for the cloud face buffer; Horizon sizes its vertex buffer from the built mesh
5 CloudRenderer.isSouthEmpty inline crates/horizon-client-render/src/clouds.rs::Mesh::prepare neighbour-empty flags passed to face_mask in N,E,S,W order
6 CloudRenderer.isWestEmpty inline crates/horizon-client-render/src/clouds.rs::Mesh::prepare neighbour-empty flags passed to face_mask in N,E,S,W order
7 CloudRenderer.render port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame gap: minor: one cloud pipeline; fancy vs flat is chosen by the mesh fast flag, not by pipeline
8 CloudRenderer.renderOit port crates/horizon-client-render/src/gfx/oit.rs crates/horizon-client-render/src/gfx/oit.rs::Oit::render_clouds the clouds draw through the OIT pipeline there, with their own depth beside the world's
9 CloudRenderer.buildMesh port crates/horizon-client-render/src/clouds.rs::Mesh::prepare cell ring walk via cells(radius), nearest first
10 CloudRenderer.tryBuildCell inline crates/horizon-client-render/src/clouds.rs::Mesh::prepare skip empty cells, then build the cell's faces
11 CloudRenderer.encodeFace inline crates/horizon-client-render/src/clouds.rs::Mesh::prepare 3-byte face records replaced by vertex quads
40 FaceInfo.VertexInfo.select skip:data corner selection; Horizon's unit-cube corner table PORTAL_FACES in block_entity_render.rs has the same order
41 FirstPersonHandsAndItemsRenderer.calculateMapTilt inline crates/horizon-client/src/game/maps.rs::Game::two_handed_map_geometry tilt computed inline with the calculateMapTilt comment
42 FirstPersonHandsAndItemsRenderer.renderPlayerHand port crates/horizon-client/src/game.rs::Game::hand_geometry empty main hand's arm model with the player skin
43 FirstPersonHandsAndItemsRenderer.renderMapHand port crates/horizon-client/src/first_person.rs::map_arm_poses (MapHold::TwoHanded) crates/horizon-client/src/first_person.rs::map_arm_poses both arms behind a two-handed map, at the jar's rotations and offsets
44 FirstPersonHandsAndItemsRenderer.renderOneHandedMap port crates/horizon-client/src/first_person.rs::one_handed_map_pose gap: player: the arm holding a map is not drawn; the map hand branch skips the arm
45 FirstPersonHandsAndItemsRenderer.renderTwoHandedMap port crates/horizon-client/src/game/maps.rs::Game::two_handed_map_geometry gap: player: the arms that hold a two-handed map are not drawn
46 FirstPersonHandsAndItemsRenderer.renderMap port crates/horizon-client/src/game/maps.rs::Game::held_map map background with its 7-pixel border, then the map contents
57 GameRenderer.preloadUiShader skip:other GPU pipeline precompile at load; Horizon builds its pipelines in Renderer::new
58 GameRenderer.update port crates/horizon-client-render/src/camera.rs::AvatarBob::update_bob gap: minor: the requested post-effect list (from /postEffect) is not built
59 GameRenderer.extract skip:other render-state copy for the vanilla render thread; Horizon renders from game state
60 GameRenderer.preparePostEffects port crates/horizon-client-render/src/gfx.rs::post_effects (post_chain::PostEffects, built at gfx.rs:8424) crates/horizon-client-render/src/post_chain.rs::PostEffects::apply vanilla post chains are made on first use from the request the game builds
61 GameRenderer.applyPostEffects port crates/horizon-client-render/src/gfx.rs::post_effects crates/horizon-client-render/src/post_chain.rs::PostEffects::apply the same chain runs after the world pass
62 GameRenderer.tryTakeScreenshotIfNeeded port crates/horizon-client/src/game.rs::tick_world_icon (called at game.rs:17852) crates/horizon-client/src/game.rs::Game::tick_world_icon a local world without an icon requests a 64x64 ScreenshotRequest for saves/<folder>/icon.png once the streamer, uploads and backlog are idle and more than 10 sections are drawn
63 GameRenderer.takeAutoScreenshot inline crates/horizon-client/src/game.rs::tick_world_icon the same retry, once a second until the file exists; the path comes from game/integrated.rs:485
64 GameRenderer.integrate3DHudDepth inline crates/horizon-client-render/src/gfx.rs::post_effects crates/horizon-client-render/src/post_chain.rs::PostEffects::apply runs with a post chain applied, which is vanilla's consistentDepthRequired condition
65 GameRenderer.extractWindow skip:other render-state copy of the window size; Horizon reads the window directly
66 GameRenderer.extractOptions skip:other render-state copy of options; Horizon reads settings directly
67 GameRenderer.extractCamera skip:other render-state copy of camera and fog; Horizon computes them in camera.rs and fog.rs
68 GameRenderer.resetData inline crates/horizon-client/src/game.rs::Game::finish_close_world boss overlay reset on world close; camera and map-texture resets not verified here
69 GameRenderer.projectPointToScreen skip:other no callers in vanilla 26.3
70 GameRenderer.useImprovedTransparency port crates/horizon-client-render/src/gfx.rs::improved_transparency / oit: Option<oit::Oit> crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame the option builds the OIT pass
71 GlobalSettingsUniform.update skip:other global uniform (window size, time, gamma, glint) written per pass; Horizon sets its own uniforms
72 GpuWarnlistManager.getAllWarnings missing minor: no GPU driver warnlist; Horizon does not check the driver against known-bad lists
73 GpuWarnlistManager.compilePatterns missing minor: no GPU driver warnlist patterns
82 LevelRenderer.executeAlwaysOnTop port crates/horizon-client/src/game.rs::Game::render always-on-top debug lines and crosshair drawn over the world
83 LevelRenderer.executeSolid port crates/horizon-client-render/src/gfx.rs::Renderer::draw_sections solid section draws in arena page order
84 LevelRenderer.prepareTranslucents port crates/horizon-client-render/src/gfx/translucency.rs::Sorter::sort translucent quads sorted from the camera each frame
85 LevelRenderer.executeOit port crates/horizon-client-render/src/gfx/oit.rs + gfx::mark_oit (gfx/entity_batch.rs:31) crates/horizon-client-render/src/gfx/oit.rs::Oit::run translucent geometry is marked and resolved by the OIT pass
86 LevelRenderer.executeDepthBoundsCull inline crates/horizon-client-render/src/gfx/oit.rs crates/horizon-client-render/src/gfx/oit.rs::Oit::execute_depth_bounds_cull part of the same pass
87 LevelRenderer.executeOitWaterMask port crates/horizon-client-render/src/gfx.rs::WATER_MASK_OVERLAY crates/horizon-client-render/src/gfx/oit.rs::Oit::execute_water_mask the water mask overlay of the same pass
88 LevelRenderer.extractSectionDrawGroups port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame section draws grouped per layer and arena page from the visible sections
89 LevelRenderer.prepareChunkRenders port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame per-layer draw list built for the frame, reversed for translucent layers
90 LevelRenderer.performTransientBlockRemovals inline crates/horizon-client/src/game/transient_blocks.rs::add_transient_block transient blocks are tracked and dropped by that list
93 LevelRenderer.submitEntities port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame entity models drawn with their batches
94 LevelRenderer.submitBlockEntities port crates/horizon-client-render/src/gfx.rs::Renderer::block_entities block entity list submitted for drawing
95 LevelRenderer.submitBlockDestroyAnimation port crates/horizon-client-render/src/gfx.rs::Renderer::draw_frame crumbling overlay drawn on the block being broken
96 LevelRenderer.submitTransientBlocks port crates/horizon-client/src/game/remote.rs: C::AddTransientBlock => add_transient_block crates/horizon-client/src/game/transient_blocks.rs::Game::transient_block_geometry the packet is handled and the block is drawn
97 LevelRenderer.submitHitOutline port crates/horizon-client/src/game.rs::Game::outline_boxes outline of the block under the crosshair
98 LevelRenderer.close skip:other GPU resource teardown
99 LevelRenderer.invalidateCompiledGeometry port crates/horizon-client-render/src/gfx.rs::Renderer::clear_sections all section meshes dropped and rebuilt when settings are applied
+14 -14
View File
@@ -9,7 +9,7 @@ ItemFrame.getSlot inline crates/horizon-client/src/game/commands/items.rs::Game:
ItemFrame.onSyncedDataUpdated skip:other network data-sync hook; the item is read from the frame field on demand
ItemFrame.setRotation inline crates/horizon-client/src/game/frames.rs::Game::interact_frame the rotate-by-one step; the neighbour update is in frame_output_changed
ItemFrame.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::item_frame_from_nbt gap: minor: Facing defaults to 2 where vanilla defaults to DOWN
ItemFrame.getFrameItemStackWithData missing minor: a broken frame never carries its custom name; drop_frame_items builds a plain item_frame stack
ItemFrame.getFrameItemStackWithData missing `game/frames.rs::drop_frame_items` builds a plain `item_frame` stack, so a broken frame never carries its custom name
ItemFrame.getVisualRotationYInDegrees inline crates/horizon-client/src/game/frames.rs::Game::frame_geometry the 45 degree step per rotation step is drawn in frame_geometry
LeashFenceKnotEntity.recalculateBoundingBox port crates/horizon-client/src/mobs/leash.rs::knot_state position and box from the block position
LeashFenceKnotEntity.getOrCreateKnot inline crates/horizon-client/src/game/leads.rs::Game::interact_knot knots are not entities in game; the knot is used when a lead is tied
@@ -35,15 +35,15 @@ ItemEntity.hurtClient skip:other client-side hurt prediction; items are not att
ItemEntity.hurtServer inline crates/horizon-client/src/game/explosion.rs::Game::explosion_hurt_entities gap: minor: explosions destroy items at damage 5 without subtracting health; fire and lava hurt are in environment_tick
ItemEntity.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::item_entity_to_nbt
ItemEntity.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::item_entity_from_nbt a missing Item returns None, which matches the discard
ItemEntity.getName missing minor: a dropped item's name (custom or item name) is not used anywhere in Horizon
ItemEntity.getName missing a dropped item's name (custom or the item's own) is not read anywhere
ItemEntity.teleport inline crates/horizon-client/src/entities.rs::merge_with_neighbours the arrival merge is the normal per-tick merge, not a teleport step
ItemEntity.getVisualRotationYInDegrees inline crates/horizon-client/src/game/item_draw.rs::Game::dropped_item_geometry spin is applied in radians there; no degree conversion
ItemEntity.getSlot missing unsure: no slot 0 access for dropped items found in commands.rs
ItemEntity.getSlot missing a dropped item is not addressable as an entity slot: `game/commands.rs` has no item entity case
PrimedTnt.defineSynchedData skip:other fuse and block state are struct fields; spawn sets fuse 80; the block is always TNT
PrimedTnt.explode inline crates/horizon-client/src/game/tnt.rs::Game::tick_tnt the power is the tnt's own field (`explosion_power`, 4 by default); gap: minor: no used-portal damage calculator
PrimedTnt.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::tnt_to_nbt the fuse and the block state are saved
PrimedTnt.restoreFrom missing minor: PrimedTnt has no owner field, so the igniter is not kept through portal moves
PrimedTnt.teleport missing unsure: no used-portal flag on PrimedTnt; the portal damage calculator is not applied
PrimedTnt.restoreFrom missing `PrimedTnt` has no owner field, so the igniter is not kept through a dimension change
PrimedTnt.teleport missing `PrimedTnt` carries no used-portal flag and the portal damage calculator is not applied to it
Blaze.registerGoals port crates/horizon-client/src/mobs/nether.rs::register_blaze gap: minor: MoveTowardsRestriction goal (priority 5) is not registered
Blaze.createAttributes skip:data attribute defaults come from data tables
Blaze.isCharged inline crates/horizon-client/src/mobs/nether.rs::BlazeAttack::tick the charged state is m.extra.flag, set in the attack goal tick
@@ -88,7 +88,7 @@ Guardian.checkGuardianSpawnRules port crates/horizon-client/src/mobs/spawn_rules
Guardian.GuardianAttackGoal.canContinueToUse port crates/horizon-client/src/mobs/guardian.rs::GuardianAttack::can_continue
Guardian.GuardianAttackSelector.test inline crates/horizon-client/src/mobs/guardian.rs::register the selector is the player, squid and axolotl beyond 9 filters in register
Monster.updateNoActionTime port crates/horizon-client/src/mobs/ai.rs::server_ai_step the idle counter is counted up there and read by the despawn path in raids.rs
Monster.getProjectile missing unsure: no held-ammo choice for mobs found in mobs/*.rs
Monster.getProjectile missing no held-ammo choice in `mobs/*.rs`: a monster's shot takes its projectile kind from the species, not from the stack it holds
PatrollingMonster.addAdditionalSaveData port crates/horizon-client/src/mobs/raider.rs::write_nbt
PatrollingMonster.readAdditionalSaveData port crates/horizon-client/src/mobs/raider.rs::read_nbt
PatrollingMonster.checkPatrollingMonsterSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules the Pillager arm: block light 8 or less, then mob rules
@@ -103,12 +103,12 @@ Phantom.PhantomBodyRotationControl.clientTick inline crates/horizon-client/src/m
Phantom.PhantomMoveTargetGoal.touchingTarget inline crates/horizon-client/src/mobs/phantom.rs::CircleAroundAnchor::tick the squared distance under 4 check near the move target
Phantom.PhantomSweepAttackGoal.canContinueToUse port crates/horizon-client/src/mobs/phantom.rs::SweepAttack::can_continue the cat hiss and scare check
Ravager.registerGoals port crates/horizon-client/src/mobs/ravager.rs::register
Ravager.updateControlFlags missing unsure: no rider control-flag switch for ravagers in ravager.rs
Ravager.updateControlFlags missing the goal set has no control flags, so a mob driving a boat does not switch off the ravager's own move, jump and look goals
Ravager.createAttributes skip:data attribute defaults come from data tables
Ravager.addAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt AttackTick, StunTick and RoarTick are written there
Ravager.readAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_from_nbt the same three tick values are read there
Ravager.stunEffect port crates/horizon-client/src/mobs/ravager.rs::ai_step the stun particle is emitted from ai_step
Ravager.hasLineOfSight missing unsure: no stunned or roaring line-of-sight cut-off in ravager.rs
Ravager.hasLineOfSight missing `ravager::immobile` knows the biting, stunned and roaring states, but no target goal is gated on it, so an immobile ravager still sees and takes targets
Ravager.applyRoarKnockbackClient inline crates/horizon-client/src/mobs/ravager.rs::roar the roar push is applied in roar
Ravager.strongKnockback inline crates/horizon-client/src/mobs/ravager.rs::roar the push of 4 along the direction is in roar
Ravager.addRoarParticleEffects inline crates/horizon-client/src/mobs/ravager.rs::roar the poof particles are emitted in roar
@@ -119,18 +119,18 @@ Shulker.addAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mo
Shulker.findNewAttachment inline crates/horizon-client/src/mobs/shulker.rs::tick keeps its face or teleports in tick, through attachable and teleport
Shulker.makeBoundingBox port crates/horizon-client/src/mobs/shulker.rs::progress_aabb
Shulker.getDefaultDimensions inline crates/horizon-client/src/mobs/shulker.rs::progress_aabb gap: minor: the down-face height scale is not seen here
Shulker.startRiding missing unsure: no attach-face reset to DOWN when a shulker mounts a vehicle in shulker.rs
Shulker.stopRiding missing unsure: no body yaw reset after a shulker dismounts in shulker.rs
Shulker.move missing unsure: no shulker-box move type (teleport) found in the piston code
Shulker.startRiding missing no attach-face reset on boarding: `mobs/shulker.rs` sets `extra.variant3` from `Attachable` and the save only, so a shulker on a vehicle keeps the face it had
Shulker.stopRiding inline crates/horizon-client/src/mobs/shulker.rs::tick the body yaw is pinned to 0 on every tick (`shulker::tick`, and `client_tick` for a remote one), so a dismounted shulker shows no leftover body yaw without a reset of its own
Shulker.move missing the `SHULKER_BOX` mover type has no counterpart: `shulker::teleport` runs from the tick and the bullet hit only, so a shulker box pushed by a piston never takes its shulker along
Shulker.findAttachableSurface port crates/horizon-client/src/mobs/shulker.rs::attachable
Shulker.isPositionBlocked inline crates/horizon-client/src/mobs/shulker.rs::can_stay_at the blocked-position check is part of can_stay_at
Shulker.onSyncedDataUpdated skip:other network sync hook; the box is computed from the face on demand
Shulker.getRenderPosition missing unsure: no render slide after a shulker teleport in shulker.rs
Shulker.getRenderPosition missing no render slide: `client_tick` eases the lid and squares the body, and nothing interpolates the position across a teleport
Shulker.setVariant inline crates/horizon-client/src/mobs/shulker.rs::register colour comes from spawn data; NO_COLOR when unset
Shulker.getColor inline crates/horizon-client/src/mobs/shulker.rs::texture colour is read from extra.color for the texture
Shulker.get skip:other component getter; Horizon reads extra.color directly
Shulker.applyImplicitComponent missing minor: a shulker spawned with a SHULKER_COLOR component ignores it
Shulker.ShulkerDefenseAttackGoal.getTargetSearchArea missing minor: shulkers do not defend nearby shulkers; there is no defense target goal
Shulker.applyImplicitComponent missing a shulker spawned from a shulker box item ignores its colour: the implicit components are applied to a mob coming out of a bucket only (`entity_nbt::bucket_components`)
Shulker.ShulkerDefenseAttackGoal.getTargetSearchArea missing no defense target goal: `mobs/shulker.rs::register` gives the shulker `HurtByTarget` with `alert` (a hurt shulker wakes the targetless ones around it) and a player search box 4 deep along the attach axis, nothing that defends another shulker
Shulker.ShulkerNearestAttackGoal.getTargetSearchArea inline crates/horizon-client/src/mobs/shulker.rs::register the 4-deep search box is the when filter in register
Shulker.ShulkerPeekGoal.canContinueToUse port crates/horizon-client/src/mobs/shulker.rs::Peek::can_continue
Silverfish.registerGoals port crates/horizon-client/src/mobs/hostile.rs::register
1 ItemFrame.defineSynchedData skip:other entity data defaults; ItemFrame starts with item None and rotation 0 in place_item_frame
9 ItemFrame.onSyncedDataUpdated skip:other network data-sync hook; the item is read from the frame field on demand
10 ItemFrame.setRotation inline crates/horizon-client/src/game/frames.rs::Game::interact_frame the rotate-by-one step; the neighbour update is in frame_output_changed
11 ItemFrame.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::item_frame_from_nbt gap: minor: Facing defaults to 2 where vanilla defaults to DOWN
12 ItemFrame.getFrameItemStackWithData missing minor: a broken frame never carries its custom name; drop_frame_items builds a plain item_frame stack `game/frames.rs::drop_frame_items` builds a plain `item_frame` stack, so a broken frame never carries its custom name
13 ItemFrame.getVisualRotationYInDegrees inline crates/horizon-client/src/game/frames.rs::Game::frame_geometry the 45 degree step per rotation step is drawn in frame_geometry
14 LeashFenceKnotEntity.recalculateBoundingBox port crates/horizon-client/src/mobs/leash.rs::knot_state position and box from the block position
15 LeashFenceKnotEntity.getOrCreateKnot inline crates/horizon-client/src/game/leads.rs::Game::interact_knot knots are not entities in game; the knot is used when a lead is tied
35 ItemEntity.hurtServer inline crates/horizon-client/src/game/explosion.rs::Game::explosion_hurt_entities gap: minor: explosions destroy items at damage 5 without subtracting health; fire and lava hurt are in environment_tick
36 ItemEntity.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::item_entity_to_nbt
37 ItemEntity.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::item_entity_from_nbt a missing Item returns None, which matches the discard
38 ItemEntity.getName missing minor: a dropped item's name (custom or item name) is not used anywhere in Horizon a dropped item's name (custom or the item's own) is not read anywhere
39 ItemEntity.teleport inline crates/horizon-client/src/entities.rs::merge_with_neighbours the arrival merge is the normal per-tick merge, not a teleport step
40 ItemEntity.getVisualRotationYInDegrees inline crates/horizon-client/src/game/item_draw.rs::Game::dropped_item_geometry spin is applied in radians there; no degree conversion
41 ItemEntity.getSlot missing unsure: no slot 0 access for dropped items found in commands.rs a dropped item is not addressable as an entity slot: `game/commands.rs` has no item entity case
42 PrimedTnt.defineSynchedData skip:other fuse and block state are struct fields; spawn sets fuse 80; the block is always TNT
43 PrimedTnt.explode inline crates/horizon-client/src/game/tnt.rs::Game::tick_tnt the power is the tnt's own field (`explosion_power`, 4 by default); gap: minor: no used-portal damage calculator
44 PrimedTnt.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::tnt_to_nbt the fuse and the block state are saved
45 PrimedTnt.restoreFrom missing minor: PrimedTnt has no owner field, so the igniter is not kept through portal moves `PrimedTnt` has no owner field, so the igniter is not kept through a dimension change
46 PrimedTnt.teleport missing unsure: no used-portal flag on PrimedTnt; the portal damage calculator is not applied `PrimedTnt` carries no used-portal flag and the portal damage calculator is not applied to it
47 Blaze.registerGoals port crates/horizon-client/src/mobs/nether.rs::register_blaze gap: minor: MoveTowardsRestriction goal (priority 5) is not registered
48 Blaze.createAttributes skip:data attribute defaults come from data tables
49 Blaze.isCharged inline crates/horizon-client/src/mobs/nether.rs::BlazeAttack::tick the charged state is m.extra.flag, set in the attack goal tick
88 Guardian.GuardianAttackGoal.canContinueToUse port crates/horizon-client/src/mobs/guardian.rs::GuardianAttack::can_continue
89 Guardian.GuardianAttackSelector.test inline crates/horizon-client/src/mobs/guardian.rs::register the selector is the player, squid and axolotl beyond 9 filters in register
90 Monster.updateNoActionTime port crates/horizon-client/src/mobs/ai.rs::server_ai_step the idle counter is counted up there and read by the despawn path in raids.rs
91 Monster.getProjectile missing unsure: no held-ammo choice for mobs found in mobs/*.rs no held-ammo choice in `mobs/*.rs`: a monster's shot takes its projectile kind from the species, not from the stack it holds
92 PatrollingMonster.addAdditionalSaveData port crates/horizon-client/src/mobs/raider.rs::write_nbt
93 PatrollingMonster.readAdditionalSaveData port crates/horizon-client/src/mobs/raider.rs::read_nbt
94 PatrollingMonster.checkPatrollingMonsterSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules the Pillager arm: block light 8 or less, then mob rules
103 Phantom.PhantomMoveTargetGoal.touchingTarget inline crates/horizon-client/src/mobs/phantom.rs::CircleAroundAnchor::tick the squared distance under 4 check near the move target
104 Phantom.PhantomSweepAttackGoal.canContinueToUse port crates/horizon-client/src/mobs/phantom.rs::SweepAttack::can_continue the cat hiss and scare check
105 Ravager.registerGoals port crates/horizon-client/src/mobs/ravager.rs::register
106 Ravager.updateControlFlags missing unsure: no rider control-flag switch for ravagers in ravager.rs the goal set has no control flags, so a mob driving a boat does not switch off the ravager's own move, jump and look goals
107 Ravager.createAttributes skip:data attribute defaults come from data tables
108 Ravager.addAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_to_nbt AttackTick, StunTick and RoarTick are written there
109 Ravager.readAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::mob_from_nbt the same three tick values are read there
110 Ravager.stunEffect port crates/horizon-client/src/mobs/ravager.rs::ai_step the stun particle is emitted from ai_step
111 Ravager.hasLineOfSight missing unsure: no stunned or roaring line-of-sight cut-off in ravager.rs `ravager::immobile` knows the biting, stunned and roaring states, but no target goal is gated on it, so an immobile ravager still sees and takes targets
112 Ravager.applyRoarKnockbackClient inline crates/horizon-client/src/mobs/ravager.rs::roar the roar push is applied in roar
113 Ravager.strongKnockback inline crates/horizon-client/src/mobs/ravager.rs::roar the push of 4 along the direction is in roar
114 Ravager.addRoarParticleEffects inline crates/horizon-client/src/mobs/ravager.rs::roar the poof particles are emitted in roar
119 Shulker.findNewAttachment inline crates/horizon-client/src/mobs/shulker.rs::tick keeps its face or teleports in tick, through attachable and teleport
120 Shulker.makeBoundingBox port crates/horizon-client/src/mobs/shulker.rs::progress_aabb
121 Shulker.getDefaultDimensions inline crates/horizon-client/src/mobs/shulker.rs::progress_aabb gap: minor: the down-face height scale is not seen here
122 Shulker.startRiding missing unsure: no attach-face reset to DOWN when a shulker mounts a vehicle in shulker.rs no attach-face reset on boarding: `mobs/shulker.rs` sets `extra.variant3` from `Attachable` and the save only, so a shulker on a vehicle keeps the face it had
123 Shulker.stopRiding missing inline crates/horizon-client/src/mobs/shulker.rs::tick unsure: no body yaw reset after a shulker dismounts in shulker.rs the body yaw is pinned to 0 on every tick (`shulker::tick`, and `client_tick` for a remote one), so a dismounted shulker shows no leftover body yaw without a reset of its own
124 Shulker.move missing unsure: no shulker-box move type (teleport) found in the piston code the `SHULKER_BOX` mover type has no counterpart: `shulker::teleport` runs from the tick and the bullet hit only, so a shulker box pushed by a piston never takes its shulker along
125 Shulker.findAttachableSurface port crates/horizon-client/src/mobs/shulker.rs::attachable
126 Shulker.isPositionBlocked inline crates/horizon-client/src/mobs/shulker.rs::can_stay_at the blocked-position check is part of can_stay_at
127 Shulker.onSyncedDataUpdated skip:other network sync hook; the box is computed from the face on demand
128 Shulker.getRenderPosition missing unsure: no render slide after a shulker teleport in shulker.rs no render slide: `client_tick` eases the lid and squares the body, and nothing interpolates the position across a teleport
129 Shulker.setVariant inline crates/horizon-client/src/mobs/shulker.rs::register colour comes from spawn data; NO_COLOR when unset
130 Shulker.getColor inline crates/horizon-client/src/mobs/shulker.rs::texture colour is read from extra.color for the texture
131 Shulker.get skip:other component getter; Horizon reads extra.color directly
132 Shulker.applyImplicitComponent missing minor: a shulker spawned with a SHULKER_COLOR component ignores it a shulker spawned from a shulker box item ignores its colour: the implicit components are applied to a mob coming out of a bucket only (`entity_nbt::bucket_components`)
133 Shulker.ShulkerDefenseAttackGoal.getTargetSearchArea missing minor: shulkers do not defend nearby shulkers; there is no defense target goal no defense target goal: `mobs/shulker.rs::register` gives the shulker `HurtByTarget` with `alert` (a hurt shulker wakes the targetless ones around it) and a player search box 4 deep along the attach axis, nothing that defends another shulker
134 Shulker.ShulkerNearestAttackGoal.getTargetSearchArea inline crates/horizon-client/src/mobs/shulker.rs::register the 4-deep search box is the when filter in register
135 Shulker.ShulkerPeekGoal.canContinueToUse port crates/horizon-client/src/mobs/shulker.rs::Peek::can_continue
136 Silverfish.registerGoals port crates/horizon-client/src/mobs/hostile.rs::register
+11 -11
View File
@@ -6,7 +6,7 @@ ZombieVillager.initializeZombieVillagerData port crates/horizon-client/src/mobs/
ZombieVillager.handleEntityEvent port crates/horizon-client/src/mobs/zombie_villager.rs::start_converting cure sound (event 16) is queued when the cure starts
ZombieVillager.setVillagerData inline crates/horizon-client/src/game/zombie_villagers.rs::carry_over gap: minor: the cure path copies type and profession there; no setter for the implicit component path
ZombieVillager.get skip:other DataComponents getter for the villager variant; Horizon has no entity component map
ZombieVillager.applyImplicitComponent missing minor: a zombie villager spawned from an item carrying a villager variant component keeps a random type; Horizon applies implicit components only to buckets (load_bucket_data)
ZombieVillager.applyImplicitComponent missing an implicit component is applied only to a mob coming out of a bucket (`entity_nbt::bucket_components`), so a zombie villager spawned from an item carrying a villager variant component keeps a random type
ZombifiedPiglin.createAttributes skip:data attribute defaults are entity-type data read in Mob::new (mobs.rs)
ZombifiedPiglin.maybePlayFirstAngerSound port crates/horizon-client/src/mobs/undead.rs::zombified_piglin_step first-anger counter and sound run in the step
ZombifiedPiglin.maybeAlertOthers port crates/horizon-client/src/mobs/undead.rs::zombified_piglin_step alert timer and line-of-sight check run in the step
@@ -39,8 +39,8 @@ Villager.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::re
Villager.getAmbientSound port crates/horizon-client/src/mobs/ai.rs::play_ambient gap: minor: the trade ambient sound is not played while trading
Villager.rewardTradeXp port crates/horizon-client/src/merchant.rs::Session::notify_trade gap: minor: no 10 second Regeneration on the villager at level-up
Villager.die port crates/horizon-client/src/game/villagers.rs::Game::villager_murdered gap: minor: releaseAllPois is not done on death
Villager.releaseAllPois missing unsure: releasePoi frees the bed, job site, potential job site and meeting point claims; Horizon has no claim store, only the block occupied property, so the effect is unclear
Villager.releasePoi missing unsure: same as releaseAllPois; the claim store is not clear in Horizon
Villager.releaseAllPois missing claims are per-mob fields (`extra.bed`, `extra.home` for the job site, `extra.meeting`) with no world-side point-of-interest store to release from; freeing a claim for another villager while this one lives has no counterpart
Villager.releasePoi missing same as `releaseAllPois`: a single claim is that mob's field, dropped with the mob
Villager.eatUntilFull port crates/horizon-client/src/mobs/villager.rs::Ext::eat_and_digest
Villager.shouldIncreaseLevel inline crates/horizon-client/src/merchant.rs::Session::notify_trade level-up check: can level up and xp at the maximum
Villager.increaseMerchantCareer inline crates/horizon-client/src/merchant.rs::Session::notify_trade level plus the new level trade set
@@ -53,7 +53,7 @@ Villager.updateTrades port crates/horizon-client/src/game/trading.rs::Game::ensu
Villager.maybeDecayGossip port crates/horizon-client/src/mobs/villager.rs::Ext::maybe_decay
Villager.golemSpawnConditionsMet port crates/horizon-client/src/mobs/villager.rs::Ext::wants_golem sleep memory check; the golem cooldown is an extra check here
Villager.get skip:other DataComponents getter for the villager variant; Horizon has no entity component map
Villager.applyImplicitComponent missing minor: a villager spawned from an item carrying a villager variant component keeps its biome type; Horizon applies implicit components only to buckets
Villager.applyImplicitComponent missing the same bucket-only path (`entity_nbt::bucket_components`), so a villager spawned from an item keeps the type its biome gives it
VillagerData.withType skip:java immutable record copy; Horizon keeps type and profession as Mob fields
VillagerData.withProfession skip:java immutable record copy; the profession change is villager::set_profession
VillagerProfession.bootstrap skip:data registry bootstrap of professions; Horizon reads them from data tables
@@ -87,9 +87,9 @@ Inventory.removeFromSelected port crates/horizon-world/src/player_inventory.rs::
Player.defineSynchedData skip:java synched data definitions; Horizon keeps the fields in game and player state
Player.turtleHelmetTick port crates/horizon-client/src/game/potions.rs::Game::turtle_helmet_tick
Player.isEquipped inline crates/horizon-client/src/game/potions.rs::Game::turtle_helmet_tick the head-slot check for the turtle helmet
Player.canPlayerFitWithinBlocksAndEntitiesWhen missing unsure: the pose fit check (no collision in the new pose box) was not found in Horizon
Player.canPlayerFitWithinBlocksAndEntitiesWhen port crates/horizon-client/src/pose.rs::MotionWorld::can_fit_pose the box of the pose's dimensions at the player's position, deflated by 1e-7, tested against the player's collision; `update_player_pose` asks it for the swimming, the desired and the crouching pose in that order, as `updatePlayerPose` does
Player.increaseScore skip:other the score is experience.total in Horizon (death screen); there is no separate score counter
Player.getWeaponItem missing unsure: the riptide spin-attack item (autoSpinAttackItemStack) was not found; Horizon keeps spin_attack parts only
Player.getWeaponItem missing the riptide spin attack keeps only `auto_spin_attack_ticks` and `auto_spin_attack_dmg`, not the stack it started with (`autoSpinAttackItemStack`), so there is no item to answer with mid-spin
Player.destroyVanishingCursedItems port crates/horizon-client/src/game.rs::Game::drop_inventory vanishing-curse stacks are removed from the death drops
Player.hasCorrectToolForDrops port crates/horizon-client/src/game.rs::Game::tick_mining the correct-tool test for harvest drops
Player.readAdditionalSaveData port crates/horizon-client/src/save.rs::Level::read_player
@@ -100,12 +100,12 @@ Player.canFallAtLeast inline crates/horizon-world/src/edge_backoff.rs::back_off_
Player.deflectProjectile port crates/horizon-client/src/game/deflect.rs::Game::attack_redirectable
Player.baseDamageScaleFactor inline crates/horizon-client/src/game/mace.rs::Game::sweep_damage
Player.isEffectiveAi skip:other client-side AI gate; Horizon has no effective-AI check in its loop
Player.hasContainerOpen missing unsure: the test for an open container menu (not the inventory menu) was not located
Player.hasContainerOpen missing no test for an open container menu: screens are `self.screen`, and nothing distinguishes a container menu from the inventory menu
Player.startSleepInBed port crates/horizon-client/src/game.rs::Game::try_sleep
Player.isSleepingLongEnough port crates/horizon-client/src/game/lan_actions.rs::sleep_counts 100 ticks in bed is what counts a sleeper as deep for SleepStatus.update
Player.updateSwimming port crates/horizon-client/src/motion.rs::MotionWorld::swimming
Player.getXpNeededForNextLevel port crates/horizon-client/src/entities.rs::Experience::needed
Player.hasEnoughFoodToDoExhaustiveManoeuvres missing unsure: the food-or-flying test for exhaustive moves was not found in motion.rs
Player.hasEnoughFoodToDoExhaustiveManoeuvres missing no food-or-flying gate anywhere in `motion.rs`: the exhaustion of a jump or a move is added without asking the food level
Player.mayUseItemAt port crates/horizon-client/src/game.rs::Game::use_hand the may-build test before use-on in adventure mode
Player.isSwimming port crates/horizon-client/src/motion.rs::MotionWorld::swimming
Player.decorateDisplayNameComponent port crates/horizon-client/src/game/commands.rs::player_display_name click suggests /tell, hover shows the player entity, plus the insertion
@@ -133,10 +133,10 @@ StackedContents.RecipePicker.getVisitedIngredientIndex skip:java bitset index a
StackedContents.RecipePicker.getVisitiedItemIndex skip:java bitset index arithmetic; plain bool vectors in Horizon
StackedContents.RecipePicker.clearAllVisited port crates/horizon-client/src/menu/place.rs::Picker::clear_visited
StackedItemContents.getBiggestCraftableStack port crates/horizon-client/src/menu/place.rs::Stacked::try_pick_all the largest craftable count, found by a bounded search
EvokerFangs.readAdditionalSaveData missing minor: fangs are not saved; a save in mid-bite drops them
EvokerFangs.addAdditionalSaveData missing minor: fangs are not saved; the warmup and owner are not written
EvokerFangs.readAdditionalSaveData missing fangs are not saved: `entity_nbt.rs` has no evoker fangs writer, so a save in mid-bite drops them
EvokerFangs.addAdditionalSaveData missing no writer for the warmup delay or the owner either
EvokerFangs.getAnimationProgress port crates/horizon-client/src/game/fangs.rs::Game::fangs_geometry the bite progress that positions the fang model
EyeOfEnder.setItem skip:ui the item sprite a flying eye shows; Horizon draws the eye itself
EyeOfEnder.shouldRenderAtSqrDistance skip:ui render distance culling only
EyeOfEnder.spawnParticles port crates/horizon-client/src/game/end.rs::Game::eye_break_particles the portal crumbs drawn in toward the eye
EyeOfEnder.readAdditionalSaveData missing minor: flying eyes are not saved; a save mid-flight loses them
EyeOfEnder.readAdditionalSaveData missing a flying eye of ender is not saved: `entity_nbt.rs` has no writer for it (nor `saved_with_chunk` a kind for it), so a save mid-flight loses it
1 ZombieVillager.defineSynchedData skip:java SynchedEntityData definitions; Horizon keeps the fields on Mob and sets them at spawn
6 ZombieVillager.handleEntityEvent port crates/horizon-client/src/mobs/zombie_villager.rs::start_converting cure sound (event 16) is queued when the cure starts
7 ZombieVillager.setVillagerData inline crates/horizon-client/src/game/zombie_villagers.rs::carry_over gap: minor: the cure path copies type and profession there; no setter for the implicit component path
8 ZombieVillager.get skip:other DataComponents getter for the villager variant; Horizon has no entity component map
9 ZombieVillager.applyImplicitComponent missing minor: a zombie villager spawned from an item carrying a villager variant component keeps a random type; Horizon applies implicit components only to buckets (load_bucket_data) an implicit component is applied only to a mob coming out of a bucket (`entity_nbt::bucket_components`), so a zombie villager spawned from an item carrying a villager variant component keeps a random type
10 ZombifiedPiglin.createAttributes skip:data attribute defaults are entity-type data read in Mob::new (mobs.rs)
11 ZombifiedPiglin.maybePlayFirstAngerSound port crates/horizon-client/src/mobs/undead.rs::zombified_piglin_step first-anger counter and sound run in the step
12 ZombifiedPiglin.maybeAlertOthers port crates/horizon-client/src/mobs/undead.rs::zombified_piglin_step alert timer and line-of-sight check run in the step
39 Villager.getAmbientSound port crates/horizon-client/src/mobs/ai.rs::play_ambient gap: minor: the trade ambient sound is not played while trading
40 Villager.rewardTradeXp port crates/horizon-client/src/merchant.rs::Session::notify_trade gap: minor: no 10 second Regeneration on the villager at level-up
41 Villager.die port crates/horizon-client/src/game/villagers.rs::Game::villager_murdered gap: minor: releaseAllPois is not done on death
42 Villager.releaseAllPois missing unsure: releasePoi frees the bed, job site, potential job site and meeting point claims; Horizon has no claim store, only the block occupied property, so the effect is unclear claims are per-mob fields (`extra.bed`, `extra.home` for the job site, `extra.meeting`) with no world-side point-of-interest store to release from; freeing a claim for another villager while this one lives has no counterpart
43 Villager.releasePoi missing unsure: same as releaseAllPois; the claim store is not clear in Horizon same as `releaseAllPois`: a single claim is that mob's field, dropped with the mob
44 Villager.eatUntilFull port crates/horizon-client/src/mobs/villager.rs::Ext::eat_and_digest
45 Villager.shouldIncreaseLevel inline crates/horizon-client/src/merchant.rs::Session::notify_trade level-up check: can level up and xp at the maximum
46 Villager.increaseMerchantCareer inline crates/horizon-client/src/merchant.rs::Session::notify_trade level plus the new level trade set
53 Villager.maybeDecayGossip port crates/horizon-client/src/mobs/villager.rs::Ext::maybe_decay
54 Villager.golemSpawnConditionsMet port crates/horizon-client/src/mobs/villager.rs::Ext::wants_golem sleep memory check; the golem cooldown is an extra check here
55 Villager.get skip:other DataComponents getter for the villager variant; Horizon has no entity component map
56 Villager.applyImplicitComponent missing minor: a villager spawned from an item carrying a villager variant component keeps its biome type; Horizon applies implicit components only to buckets the same bucket-only path (`entity_nbt::bucket_components`), so a villager spawned from an item keeps the type its biome gives it
57 VillagerData.withType skip:java immutable record copy; Horizon keeps type and profession as Mob fields
58 VillagerData.withProfession skip:java immutable record copy; the profession change is villager::set_profession
59 VillagerProfession.bootstrap skip:data registry bootstrap of professions; Horizon reads them from data tables
87 Player.defineSynchedData skip:java synched data definitions; Horizon keeps the fields in game and player state
88 Player.turtleHelmetTick port crates/horizon-client/src/game/potions.rs::Game::turtle_helmet_tick
89 Player.isEquipped inline crates/horizon-client/src/game/potions.rs::Game::turtle_helmet_tick the head-slot check for the turtle helmet
90 Player.canPlayerFitWithinBlocksAndEntitiesWhen missing port crates/horizon-client/src/pose.rs::MotionWorld::can_fit_pose unsure: the pose fit check (no collision in the new pose box) was not found in Horizon the box of the pose's dimensions at the player's position, deflated by 1e-7, tested against the player's collision; `update_player_pose` asks it for the swimming, the desired and the crouching pose in that order, as `updatePlayerPose` does
91 Player.increaseScore skip:other the score is experience.total in Horizon (death screen); there is no separate score counter
92 Player.getWeaponItem missing unsure: the riptide spin-attack item (autoSpinAttackItemStack) was not found; Horizon keeps spin_attack parts only the riptide spin attack keeps only `auto_spin_attack_ticks` and `auto_spin_attack_dmg`, not the stack it started with (`autoSpinAttackItemStack`), so there is no item to answer with mid-spin
93 Player.destroyVanishingCursedItems port crates/horizon-client/src/game.rs::Game::drop_inventory vanishing-curse stacks are removed from the death drops
94 Player.hasCorrectToolForDrops port crates/horizon-client/src/game.rs::Game::tick_mining the correct-tool test for harvest drops
95 Player.readAdditionalSaveData port crates/horizon-client/src/save.rs::Level::read_player
100 Player.deflectProjectile port crates/horizon-client/src/game/deflect.rs::Game::attack_redirectable
101 Player.baseDamageScaleFactor inline crates/horizon-client/src/game/mace.rs::Game::sweep_damage
102 Player.isEffectiveAi skip:other client-side AI gate; Horizon has no effective-AI check in its loop
103 Player.hasContainerOpen missing unsure: the test for an open container menu (not the inventory menu) was not located no test for an open container menu: screens are `self.screen`, and nothing distinguishes a container menu from the inventory menu
104 Player.startSleepInBed port crates/horizon-client/src/game.rs::Game::try_sleep
105 Player.isSleepingLongEnough port crates/horizon-client/src/game/lan_actions.rs::sleep_counts 100 ticks in bed is what counts a sleeper as deep for SleepStatus.update
106 Player.updateSwimming port crates/horizon-client/src/motion.rs::MotionWorld::swimming
107 Player.getXpNeededForNextLevel port crates/horizon-client/src/entities.rs::Experience::needed
108 Player.hasEnoughFoodToDoExhaustiveManoeuvres missing unsure: the food-or-flying test for exhaustive moves was not found in motion.rs no food-or-flying gate anywhere in `motion.rs`: the exhaustion of a jump or a move is added without asking the food level
109 Player.mayUseItemAt port crates/horizon-client/src/game.rs::Game::use_hand the may-build test before use-on in adventure mode
110 Player.isSwimming port crates/horizon-client/src/motion.rs::MotionWorld::swimming
111 Player.decorateDisplayNameComponent port crates/horizon-client/src/game/commands.rs::player_display_name click suggests /tell, hover shows the player entity, plus the insertion
133 StackedContents.RecipePicker.getVisitiedItemIndex skip:java bitset index arithmetic; plain bool vectors in Horizon
134 StackedContents.RecipePicker.clearAllVisited port crates/horizon-client/src/menu/place.rs::Picker::clear_visited
135 StackedItemContents.getBiggestCraftableStack port crates/horizon-client/src/menu/place.rs::Stacked::try_pick_all the largest craftable count, found by a bounded search
136 EvokerFangs.readAdditionalSaveData missing minor: fangs are not saved; a save in mid-bite drops them fangs are not saved: `entity_nbt.rs` has no evoker fangs writer, so a save in mid-bite drops them
137 EvokerFangs.addAdditionalSaveData missing minor: fangs are not saved; the warmup and owner are not written no writer for the warmup delay or the owner either
138 EvokerFangs.getAnimationProgress port crates/horizon-client/src/game/fangs.rs::Game::fangs_geometry the bite progress that positions the fang model
139 EyeOfEnder.setItem skip:ui the item sprite a flying eye shows; Horizon draws the eye itself
140 EyeOfEnder.shouldRenderAtSqrDistance skip:ui render distance culling only
141 EyeOfEnder.spawnParticles port crates/horizon-client/src/game/end.rs::Game::eye_break_particles the portal crumbs drawn in toward the eye
142 EyeOfEnder.readAdditionalSaveData missing minor: flying eyes are not saved; a save mid-flight loses them a flying eye of ender is not saved: `entity_nbt.rs` has no writer for it (nor `saved_with_chunk` a kind for it), so a save mid-flight loses it
+12 -12
View File
@@ -20,16 +20,16 @@ FishingHook.handleEntityEvent inline crates/horizon-client/src/game/fishing.rs::
FishingHook.getPlayerOwner skip:other owner is implicit: the host's hook is self.hook and a guest's is in that guest's play state
FishingHook.getAddEntityPacket port crates/horizon-client/src/game/fishing.rs::Hook::track add-entity payload: the owner entity id is the bobber's data field
LlamaSpit.onHitEntity port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit spit hit: 1.0 damage with the spit source to a player (mob_hits_player) or a mob
Projectile.applyImplicitComponent missing minor: a projectile given a can_break component by its item keeps no break predicate; Horizon applies implicit components only to buckets
Projectile.applyImplicitComponent missing an implicit component is applied only to a mob coming out of a bucket (`entity_nbt::bucket_components`, `game/creatures.rs:5058`), so a projectile spawned from an item with a `can_break` component keeps no break predicate
Projectile.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::put_projectile Owner and LeftOwner only when set, HasBeenShot once it has ticked (a save before the first tick keeps what it loaded)
Projectile.ownedBy inline crates/horizon-client/src/projectiles.rs::Projectile::tick the owner is skipped in the target loop until the projectile has left it
Projectile.readAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::throwable_from_nbt no shared reader: every kind's own reader takes Owner, LeftOwner and NoGravity (this one, `fireball_from_nbt`, `arrow_from_nbt`); HasBeenShot is read for arrows only, and no projectile keeps a `can_break` predicate
Projectile.restoreFrom missing minor: the owner is not carried over a dimension change; Horizon has no projectile dimension change
Projectile.restoreFrom missing no projectile `restoreFrom`: the owner is not carried over a dimension change
Projectile.isOutsideOwnerCollisionRange inline crates/horizon-client/src/projectiles.rs::Projectile::tick gap: minor: left_owner tests the owner's box inflated 1.0 at the position only; vanilla sweeps along the motion and checks the owner's passengers too
Projectile.getMovementToShoot port crates/horizon-client/src/projectiles.rs::Projectile::shoot direction plus triangle spread 0.0172275 times uncertainty, scaled by power
Projectile.onAboveBubbleColumn port crates/horizon-client/src/arrows.rs::Ticker::bubble_column gap: minor: arrows only; thrown ender pearls get their push in creatures.rs tick_projectiles and other projectiles get none
Projectile.onInsideBubbleColumn port crates/horizon-client/src/arrows.rs::Ticker::bubble_column gap: minor: arrows only; thrown ender pearls get their push in creatures.rs tick_projectiles and other projectiles get none
Projectile.canBreakBlockInAdventureMode missing minor: the can_break predicate and adventure-mode rules for projectile block breaking are not modelled
Projectile.canBreakBlockInAdventureMode missing a projectile's block breaking ignores the `can_break` predicate and the adventure-mode rules
Projectile.spawnProjectileFromRotation port crates/horizon-client/src/projectiles.rs::Projectile::shoot_from_rotation gap: minor: the aim and inherited velocity only; the add-to-world step is the caller's
Projectile.spawnProjectile inline crates/horizon-client/src/game/creatures.rs::Game::throw_item each shooter adds its own projectile (throw_item, shoot_crossbow, shoot_projectile_item, the bow in game.rs); throw_item is the player throw path
Projectile.applyOnProjectileSpawned port crates/horizon-client/src/game/enchant_effects.rs::Game::bow_projectile_ignite gap: minor: Flame ignite only; Piercing and Punch go through Projectile::set_bonus
@@ -43,14 +43,14 @@ ProjectileUtil.getHitResultOnViewVector port crates/horizon-client/src/game/pick
ProjectileUtil.getHitResult port crates/horizon-client/src/projectiles.rs::Projectile::tick block clip first, then the nearest entity box on the segment; same core as the move-vector sweep
ProjectileUtil.rotateTowardsMovement port crates/horizon-client/src/projectiles.rs::Projectile::update_rotation gap: minor: rotation is set at once, vanilla lerps it by rotationSpeed
ProjectileUtil.getMobArrow port crates/horizon-client/src/mobs/undead.rs::perform_ranged_attack gap: minor: builds the skeleton-family arrow with base damage from power; illager crossbow arrows are built in mobs/illager.rs::shoot_crossbow
ShulkerBullet.addAdditionalSaveData missing minor: the final target and move direction are not saved; Horizon does not save flying projectiles
ShulkerBullet.readAdditionalSaveData missing minor: the final target and move direction are not read back; Horizon does not save flying projectiles
ShulkerBullet.canHitEntity missing minor: no-physics entities are not skipped by the bullet's target search; Projectile::tick checks owner and boxes only
ShulkerBullet.addAdditionalSaveData missing `entity_nbt.rs::saved_with_chunk` leaves `ShulkerBullet` out of the kinds a chunk writes, so a bullet in flight is dropped with the save; the final target and the move direction have no writer
ShulkerBullet.readAdditionalSaveData missing the same gap on the load side: nothing reads a bullet's final target or move direction back
ShulkerBullet.canHitEntity missing the bullet's target search (`Projectile::tick` on the client, `projectiles` in `game/creatures.rs`) tests the owner and the boxes only; a no-physics entity is not skipped
ShulkerBullet.onHitBlock port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit shulker bullet block hit: hit sound and two explosion particles at the bullet
ShulkerBullet.destroy inline crates/horizon-client/src/game/creatures.rs::Game::tick_projectiles the bullet is dropped from the flight list on any hit; the game event is not sent
ShulkerBullet.hurtServer port crates/horizon-client/src/game/deflect.rs::Game::hit_shulker_bullet
ThrowableProjectile.applyInertia port crates/horizon-client/src/projectiles.rs::Projectile::tick default arm: gravity, then drag 0.8 in water and 0.99 in air; gap: minor: no bubble particles in water
ThrowableProjectile.handleFirstTickBubbleColumn missing minor: a thrown item that spawns inside a bubble column is not pushed on its first tick
ThrowableProjectile.handleFirstTickBubbleColumn missing a thrown item spawned inside a bubble column is not pushed on its first tick: `game/creatures.rs` has bubble-column particles and the pearl's own column hooks, nothing for a throwable's first tick
AbstractArrow.<init> port crates/horizon-client/src/arrows.rs::ArrowState::new arrow defaults: pickup, pickup item, shake, pierce; the owner and drawn ammo come from set_owner and set_item
AbstractArrow.defineSynchedData skip:java flags, pierce level and in-ground sync fields; Horizon keeps them on ArrowState and Projectile
AbstractArrow.lerpMotion skip:other client interpolation of a server motion packet; Horizon's remote arrows take the server position each update
@@ -68,7 +68,7 @@ AbstractArrow.tryPickup port crates/horizon-client/src/arrows.rs::ArrowState::pl
AbstractArrow.setFlag port crates/horizon-client/src/projectiles.rs::Projectile::set_critical gap: minor: only the critical flag; the no-physics flag is a plain field
AbstractArrow.setPickupItemStack port crates/horizon-client/src/arrows.rs::ArrowState::set_item an empty stack falls back to the default pickup item
AbstractArrow.isNoPhysics inline crates/horizon-client/src/arrows.rs::Ticker::tick no_physics is a field read by the tick
AbstractArrow.getSlot missing minor: the arrow's pickup stack is not reachable as an entity slot, so item commands on it do nothing
AbstractArrow.getSlot missing an arrow's pickup stack is not addressable as an entity slot, so item commands on an arrow do nothing
AbstractArrow.Pickup.byOrdinal port crates/horizon-client/src/arrows.rs::Pickup::from_byte out of range is DISALLOWED
Arrow.updateColor port crates/horizon-client/src/projectiles.rs::Projectile::arrow_effect_color the tipped arrow's opaque potion colour for its flight and hit particles (-1 for none)
Arrow.makeParticle port crates/horizon-client/src/game/remote_projectiles.rs::Game::arrow_makeparticle ticked in #3514; `Arrow.makeParticle` is cited at crates/horizon-client/src/game/remote_projectiles.rs:218
@@ -82,11 +82,11 @@ ThrownTrident.readAdditionalSaveData port crates/horizon-client/src/entity_nbt.r
ThrownTrident.getLoyaltyFromItem port crates/horizon-client/src/game/trident.rs::Game::release_trident loyalty level read from the thrown stack when the trident is released
ThrownTrident.tickDespawn inline crates/horizon-client/src/arrows.rs::Ticker::tick gap: minor: the 1200-tick life limit applies to every trident; the loyalty exemption is not checked
AbstractHurtingProjectile.shouldRenderAtSqrDistance skip:ui distance culling for the fireball model; Horizon culls with its own range check
AbstractHurtingProjectile.canHitEntity missing minor: no-physics entities are not skipped by the fireball's target search
AbstractHurtingProjectile.canHitEntity missing same as `ShulkerBullet.canHitEntity`: the fireball's target search has no no-physics filter
WitherSkull.getBlockExplosionResistance port crates/horizon-client/src/game/explosion.rs::blast_resistance a dangerous skull's blast caps a breakable block's resistance at 0.8; `#wither_immune` blocks keep theirs
WitherSkull.onHit port crates/horizon-client/src/game/wither.rs::Game::wither_skull_hit skull hit damage, wither effect, then the blast at the impact point
AbstractWindCharge.makeBoundingBox port crates/horizon-client/src/projectiles.rs::ProjectileKind::size gap: minor: gives the width only; the box sits 0.15 below the position in vanilla
AbstractWindCharge.canHitEntity missing minor: no filter for end crystals or other wind charges found in the target list; unsure whether the caller excludes them
AbstractWindCharge.canHitEntity missing the wind charge's target list is `creatures.rs::wind_charge_hitboxes`, which collects the other live, unstuck wind charges as hittable boxes: nothing there drops a wind charge or an end crystal, so the filter `canHitEntity` makes is not applied
AbstractWindCharge.onHitBlock port crates/horizon-client/src/game/wind.rs::Game::wind_charge_hit a burst a quarter block out along the hit face
WindCharge.deflect port crates/horizon-client/src/game/wind.rs::reverse gap: minor: turns the charge at half speed; the noDeflectTicks delay is not modelled
WindCharge.shouldRenderAtSqrDistance skip:ui distance culling for the wind charge model; Horizon culls with its own range check
@@ -97,10 +97,10 @@ ThrownEnderpearl.getOwner skip:other owner is kept as a Who value and resolved
ThrownEnderpearl.findOwnerIncludingDeadPlayer skip:other owner lookup by UUID across dead players; Horizon keeps the owner id directly
ThrownEnderpearl.onHitEntity port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit a mob hit takes zero damage; the pearl still lands
ThrownEnderpearl.onHit port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit gap: minor: no 32 portal particles and no dimension change; the owner is teleported to where the pearl was, with 5 damage and a 5 percent endermite
ThrownEnderpearl.isAllowedToTeleportOwner missing minor: a pearl whose owner is dead or asleep when it lands still teleports the owner
ThrownEnderpearl.isAllowedToTeleportOwner missing `game/creatures.rs` teleports the owner on a pearl's hit without asking whether they are dead or sleeping
ThrownEnderpearl.playSound port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit teleport sound at the landing point
ThrownEnderpearl.teleport port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit moves the player to the pearl's previous position and clears velocity and fall distance
ThrownEnderpearl.canTeleport missing minor: a pearl from the End to the Overworld needs the owner's seen-credits flag before it may teleport
ThrownEnderpearl.canTeleport missing no seen-credits test: a pearl whose owner came from the End teleports them back to the Overworld regardless
ThrownEnderpearl.onInsideBlock port crates/horizon-client/src/game/end.rs::Game::pearl_owner_in_gateway a pearl that flies into an End gateway sends its owner through and fires enter_block
Raid.<init> port crates/horizon-client/src/raid.rs::Raid::new centre, difficulty-based group count, cooldown and active flag
Raid.getAllRaiders port crates/horizon-client/src/game/raids.rs::Game::raid_members the raiders of this raid, from the mobs carrying its id
1 FireworkRocketEntity.<init> port crates/horizon-client/src/game/fireworks.rs::Game::new_rocket launch velocity triangle and lifetime 10*(1+flight)+randoms; the owner and attached target are set by the callers
20 FishingHook.getPlayerOwner skip:other owner is implicit: the host's hook is self.hook and a guest's is in that guest's play state
21 FishingHook.getAddEntityPacket port crates/horizon-client/src/game/fishing.rs::Hook::track add-entity payload: the owner entity id is the bobber's data field
22 LlamaSpit.onHitEntity port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit spit hit: 1.0 damage with the spit source to a player (mob_hits_player) or a mob
23 Projectile.applyImplicitComponent missing minor: a projectile given a can_break component by its item keeps no break predicate; Horizon applies implicit components only to buckets an implicit component is applied only to a mob coming out of a bucket (`entity_nbt::bucket_components`, `game/creatures.rs:5058`), so a projectile spawned from an item with a `can_break` component keeps no break predicate
24 Projectile.addAdditionalSaveData port crates/horizon-client/src/entity_nbt.rs::put_projectile Owner and LeftOwner only when set, HasBeenShot once it has ticked (a save before the first tick keeps what it loaded)
25 Projectile.ownedBy inline crates/horizon-client/src/projectiles.rs::Projectile::tick the owner is skipped in the target loop until the projectile has left it
26 Projectile.readAdditionalSaveData inline crates/horizon-client/src/entity_nbt.rs::throwable_from_nbt no shared reader: every kind's own reader takes Owner, LeftOwner and NoGravity (this one, `fireball_from_nbt`, `arrow_from_nbt`); HasBeenShot is read for arrows only, and no projectile keeps a `can_break` predicate
27 Projectile.restoreFrom missing minor: the owner is not carried over a dimension change; Horizon has no projectile dimension change no projectile `restoreFrom`: the owner is not carried over a dimension change
28 Projectile.isOutsideOwnerCollisionRange inline crates/horizon-client/src/projectiles.rs::Projectile::tick gap: minor: left_owner tests the owner's box inflated 1.0 at the position only; vanilla sweeps along the motion and checks the owner's passengers too
29 Projectile.getMovementToShoot port crates/horizon-client/src/projectiles.rs::Projectile::shoot direction plus triangle spread 0.0172275 times uncertainty, scaled by power
30 Projectile.onAboveBubbleColumn port crates/horizon-client/src/arrows.rs::Ticker::bubble_column gap: minor: arrows only; thrown ender pearls get their push in creatures.rs tick_projectiles and other projectiles get none
31 Projectile.onInsideBubbleColumn port crates/horizon-client/src/arrows.rs::Ticker::bubble_column gap: minor: arrows only; thrown ender pearls get their push in creatures.rs tick_projectiles and other projectiles get none
32 Projectile.canBreakBlockInAdventureMode missing minor: the can_break predicate and adventure-mode rules for projectile block breaking are not modelled a projectile's block breaking ignores the `can_break` predicate and the adventure-mode rules
33 Projectile.spawnProjectileFromRotation port crates/horizon-client/src/projectiles.rs::Projectile::shoot_from_rotation gap: minor: the aim and inherited velocity only; the add-to-world step is the caller's
34 Projectile.spawnProjectile inline crates/horizon-client/src/game/creatures.rs::Game::throw_item each shooter adds its own projectile (throw_item, shoot_crossbow, shoot_projectile_item, the bow in game.rs); throw_item is the player throw path
35 Projectile.applyOnProjectileSpawned port crates/horizon-client/src/game/enchant_effects.rs::Game::bow_projectile_ignite gap: minor: Flame ignite only; Piercing and Punch go through Projectile::set_bonus
43 ProjectileUtil.getHitResult port crates/horizon-client/src/projectiles.rs::Projectile::tick block clip first, then the nearest entity box on the segment; same core as the move-vector sweep
44 ProjectileUtil.rotateTowardsMovement port crates/horizon-client/src/projectiles.rs::Projectile::update_rotation gap: minor: rotation is set at once, vanilla lerps it by rotationSpeed
45 ProjectileUtil.getMobArrow port crates/horizon-client/src/mobs/undead.rs::perform_ranged_attack gap: minor: builds the skeleton-family arrow with base damage from power; illager crossbow arrows are built in mobs/illager.rs::shoot_crossbow
46 ShulkerBullet.addAdditionalSaveData missing minor: the final target and move direction are not saved; Horizon does not save flying projectiles `entity_nbt.rs::saved_with_chunk` leaves `ShulkerBullet` out of the kinds a chunk writes, so a bullet in flight is dropped with the save; the final target and the move direction have no writer
47 ShulkerBullet.readAdditionalSaveData missing minor: the final target and move direction are not read back; Horizon does not save flying projectiles the same gap on the load side: nothing reads a bullet's final target or move direction back
48 ShulkerBullet.canHitEntity missing minor: no-physics entities are not skipped by the bullet's target search; Projectile::tick checks owner and boxes only the bullet's target search (`Projectile::tick` on the client, `projectiles` in `game/creatures.rs`) tests the owner and the boxes only; a no-physics entity is not skipped
49 ShulkerBullet.onHitBlock port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit shulker bullet block hit: hit sound and two explosion particles at the bullet
50 ShulkerBullet.destroy inline crates/horizon-client/src/game/creatures.rs::Game::tick_projectiles the bullet is dropped from the flight list on any hit; the game event is not sent
51 ShulkerBullet.hurtServer port crates/horizon-client/src/game/deflect.rs::Game::hit_shulker_bullet
52 ThrowableProjectile.applyInertia port crates/horizon-client/src/projectiles.rs::Projectile::tick default arm: gravity, then drag 0.8 in water and 0.99 in air; gap: minor: no bubble particles in water
53 ThrowableProjectile.handleFirstTickBubbleColumn missing minor: a thrown item that spawns inside a bubble column is not pushed on its first tick a thrown item spawned inside a bubble column is not pushed on its first tick: `game/creatures.rs` has bubble-column particles and the pearl's own column hooks, nothing for a throwable's first tick
54 AbstractArrow.<init> port crates/horizon-client/src/arrows.rs::ArrowState::new arrow defaults: pickup, pickup item, shake, pierce; the owner and drawn ammo come from set_owner and set_item
55 AbstractArrow.defineSynchedData skip:java flags, pierce level and in-ground sync fields; Horizon keeps them on ArrowState and Projectile
56 AbstractArrow.lerpMotion skip:other client interpolation of a server motion packet; Horizon's remote arrows take the server position each update
68 AbstractArrow.setFlag port crates/horizon-client/src/projectiles.rs::Projectile::set_critical gap: minor: only the critical flag; the no-physics flag is a plain field
69 AbstractArrow.setPickupItemStack port crates/horizon-client/src/arrows.rs::ArrowState::set_item an empty stack falls back to the default pickup item
70 AbstractArrow.isNoPhysics inline crates/horizon-client/src/arrows.rs::Ticker::tick no_physics is a field read by the tick
71 AbstractArrow.getSlot missing minor: the arrow's pickup stack is not reachable as an entity slot, so item commands on it do nothing an arrow's pickup stack is not addressable as an entity slot, so item commands on an arrow do nothing
72 AbstractArrow.Pickup.byOrdinal port crates/horizon-client/src/arrows.rs::Pickup::from_byte out of range is DISALLOWED
73 Arrow.updateColor port crates/horizon-client/src/projectiles.rs::Projectile::arrow_effect_color the tipped arrow's opaque potion colour for its flight and hit particles (-1 for none)
74 Arrow.makeParticle port crates/horizon-client/src/game/remote_projectiles.rs::Game::arrow_makeparticle ticked in #3514; `Arrow.makeParticle` is cited at crates/horizon-client/src/game/remote_projectiles.rs:218
82 ThrownTrident.getLoyaltyFromItem port crates/horizon-client/src/game/trident.rs::Game::release_trident loyalty level read from the thrown stack when the trident is released
83 ThrownTrident.tickDespawn inline crates/horizon-client/src/arrows.rs::Ticker::tick gap: minor: the 1200-tick life limit applies to every trident; the loyalty exemption is not checked
84 AbstractHurtingProjectile.shouldRenderAtSqrDistance skip:ui distance culling for the fireball model; Horizon culls with its own range check
85 AbstractHurtingProjectile.canHitEntity missing minor: no-physics entities are not skipped by the fireball's target search same as `ShulkerBullet.canHitEntity`: the fireball's target search has no no-physics filter
86 WitherSkull.getBlockExplosionResistance port crates/horizon-client/src/game/explosion.rs::blast_resistance a dangerous skull's blast caps a breakable block's resistance at 0.8; `#wither_immune` blocks keep theirs
87 WitherSkull.onHit port crates/horizon-client/src/game/wither.rs::Game::wither_skull_hit skull hit damage, wither effect, then the blast at the impact point
88 AbstractWindCharge.makeBoundingBox port crates/horizon-client/src/projectiles.rs::ProjectileKind::size gap: minor: gives the width only; the box sits 0.15 below the position in vanilla
89 AbstractWindCharge.canHitEntity missing minor: no filter for end crystals or other wind charges found in the target list; unsure whether the caller excludes them the wind charge's target list is `creatures.rs::wind_charge_hitboxes`, which collects the other live, unstuck wind charges as hittable boxes: nothing there drops a wind charge or an end crystal, so the filter `canHitEntity` makes is not applied
90 AbstractWindCharge.onHitBlock port crates/horizon-client/src/game/wind.rs::Game::wind_charge_hit a burst a quarter block out along the hit face
91 WindCharge.deflect port crates/horizon-client/src/game/wind.rs::reverse gap: minor: turns the charge at half speed; the noDeflectTicks delay is not modelled
92 WindCharge.shouldRenderAtSqrDistance skip:ui distance culling for the wind charge model; Horizon culls with its own range check
97 ThrownEnderpearl.findOwnerIncludingDeadPlayer skip:other owner lookup by UUID across dead players; Horizon keeps the owner id directly
98 ThrownEnderpearl.onHitEntity port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit a mob hit takes zero damage; the pearl still lands
99 ThrownEnderpearl.onHit port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit gap: minor: no 32 portal particles and no dimension change; the owner is teleported to where the pearl was, with 5 damage and a 5 percent endermite
100 ThrownEnderpearl.isAllowedToTeleportOwner missing minor: a pearl whose owner is dead or asleep when it lands still teleports the owner `game/creatures.rs` teleports the owner on a pearl's hit without asking whether they are dead or sleeping
101 ThrownEnderpearl.playSound port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit teleport sound at the landing point
102 ThrownEnderpearl.teleport port crates/horizon-client/src/game/creatures.rs::Game::projectile_hit moves the player to the pearl's previous position and clears velocity and fall distance
103 ThrownEnderpearl.canTeleport missing minor: a pearl from the End to the Overworld needs the owner's seen-credits flag before it may teleport no seen-credits test: a pearl whose owner came from the End teleports them back to the Overworld regardless
104 ThrownEnderpearl.onInsideBlock port crates/horizon-client/src/game/end.rs::Game::pearl_owner_in_gateway a pearl that flies into an End gateway sends its owner through and fires enter_block
105 Raid.<init> port crates/horizon-client/src/raid.rs::Raid::new centre, difficulty-based group count, cooldown and active flag
106 Raid.getAllRaiders port crates/horizon-client/src/game/raids.rs::Game::raid_members the raiders of this raid, from the mobs carrying its id
+12 -12
View File
@@ -34,7 +34,7 @@ StonecutterMenu.removed port crates/horizon-client/src/game/stations.rs::Game::c
ArrowItem.asProjectile port crates/horizon-client/src/game/dispenser.rs::Game::shoot_projectile_item dispensed arrows are built in the arrow arm of the item shot
BlockItem.updateBlockEntityComponents port crates/horizon-client/src/block_entity_item.rs::place the placed block entity takes the item's components
BlockItem.canPlace port crates/horizon-world/src/level/place.rs::Sim::place the placement checks, including the survive check, run in Sim::place
BlockItem.onDestroyed missing unsure: a container item whose item entity is destroyed should spill its contents; no Horizon code does this
BlockItem.onDestroyed missing no `onDestroyed` on any item: an item entity of a shulker box or a bundle destroyed by fire or lava spills nothing
BoatItem.getBoat port crates/horizon-client/src/game/boats.rs::Game::place_boat boat placed at the hit point with the item's stack config
BucketItem.playEmptySound port crates/horizon-world/src/level/buckets.rs::Sim::empty_sound
BundleItem.getWeightSafe port crates/horizon-items/src/bundle.rs::Contents::weight_fraction float fraction; vanilla compares an exact Fraction
@@ -42,7 +42,7 @@ BundleItem.getBarWidth port crates/horizon-items/src/bundle.rs::Contents::bar
BundleItem.getBarColor port crates/horizon-items/src/bundle.rs::Contents::bar
BundleItem.removeOneItemFromBundle port crates/horizon-items/src/bundle.rs::Contents::remove_one
BundleItem.getTooltipImage port crates/horizon-items/src/bundle.rs::tooltip_layout gap: minor: the tooltip_display rule that hides the bundle contents is not checked here
BundleItem.onDestroyed missing unsure: a bundle whose item entity is destroyed should spill its contents; no Horizon code does this
BundleItem.onDestroyed missing same as `BlockItem.onDestroyed`: nothing spills a destroyed item entity's contents
BundleItem.playRemoveOneSound port crates/horizon-client/src/game.rs::Game::drop_bundle_content the menu queues the same sound in menu/bundle.rs
BundleItem.playInsertSound port crates/horizon-client/src/menu/bundle.rs::Menu::carried_bundle_click queues the insert or insert-fail sound
BundleItem.playDropContentsSound port crates/horizon-client/src/game.rs::Game::drop_bundle_content
@@ -54,8 +54,8 @@ CrossbowItem.createProjectile port crates/horizon-client/src/arrows.rs::Ticker::
CrossbowItem.getShotPitch inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow the shot pitch per index is inline
CrossbowItem.getRandomShotPitch inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow the random pitch formula is inline
CrossbowItem.getPowerForTime inline crates/horizon-client/src/game/crossbow.rs::Game::tick_crossbow the charge fraction capped at one is the tick check against the charge time
DyeColor.getMixedColor missing player: a baby sheep, wolf or cat from parents of two dye colours gets a mixed colour; Horizon has no breeding dye mix (only sheep_color for spawns)
DyeColor.findColorMixInRecipes missing player: part of the same breeding dye mix; the crafting recipe lookup for two dyes is not done for offspring
DyeColor.getMixedColor port crates/horizon-client/src/game/creatures.rs::Game::dye_mix the colour of the crafting recipe that mixes the two dyes, else one of the two picked at random (`next_boolean`); `creatures.rs:3153`, `:3176` and `:3222` give the offspring of a sheep, wolf or cat its colour with it
DyeColor.findColorMixInRecipes port crates/horizon-client/src/game/creatures.rs::Game::dye_color_mix_in_recipes the two dyes laid out as a 2x2 crafting grid through `Recipes::find`, and the result read back as a dye colour by its `_dye` suffix
FireworkRocketItem.createDispenseConfig port crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile rocket config: offset 0.5000099, uncertainty 1, power 0.5, event 1004
FireworkRocketItem.getEntityJustOutsideOfBlockPos port crates/horizon-client/src/game/dispenser.rs::Game::dispense_position the position just outside the dispenser on the facing side
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
@@ -84,7 +84,7 @@ Item.Properties.horseArmor skip:data registration builder
Item.Properties.nautilusArmor skip:data registration builder
Item.Properties.requiredFeatures skip:data registration builder
Item.Properties.delayedHolderComponent skip:data registration builder
Item.Properties.loweredMobVisibility missing unsure: the mob_visibility component is not read by Horizon's mob targeting
Item.Properties.loweredMobVisibility missing the `mob_visibility` component is parsed (it is in `item_model.rs`'s component table and the protocol's item hash), but no mob targeting reads it
Item.Properties.finalizeInitializer skip:data registration builder
Item.TooltipContext.of skip:java tooltip context factory; Horizon passes the level and player to the tooltip functions
ItemStack.validateStrict skip:java codec-side validation of components and stack size
@@ -97,7 +97,7 @@ ItemStack.isBroken inline crates/horizon-client/src/game.rs::Game::damage_held t
ItemStack.nextDamageWillBreak inline crates/horizon-client/src/game/trident.rs::Game::use_trident gap: minor: only the trident's check is ported here
ItemStack.processDurabilityChange port crates/horizon-client/src/game/enchant_effects.rs::Game::durability_loss the Unbreaking-adjusted loss
ItemStack.applyDamage port crates/horizon-client/src/game.rs::Game::damage_held gap: minor: no item durability advancement trigger fires
ItemStack.hurtAndConvertOnBreak missing unsure: a carrot or fungus on a stick that breaks should become a fishing rod; no conversion was found
ItemStack.hurtAndConvertOnBreak missing nothing converts a broken carrot on a stick or fungus on a stick into a fishing rod
ItemStack.hurtEnemy port crates/horizon-client/src/game/mace.rs::Game::weapon_wear the per-hit durability loss of a weapon
ItemStack.interactLivingEntity port crates/horizon-client/src/game.rs::Game::interact_entity equip-on-interact is in the mob paths (nautili.rs, ghasts.rs)
ItemStack.copy skip:java value copy
@@ -111,21 +111,21 @@ ItemStack.hashStackList skip:java hash for collections
ItemStack.onCraftedBy inline crates/horizon-client/src/game.rs::Game::click_inventory gap: minor: the crafted statistic is added only on the inventory grid craft path here
ItemStack.set skip:other component write; Horizon stores component data in typed fields
ItemStack.update skip:java functional update helper
ItemStack.applyComponentsAndValidate missing unsure: a validated component patch has no single Horizon counterpart; /item modify is a separate path
ItemStack.applyComponentsAndValidate missing a validated component patch has no single counterpart: `/item modify` goes through its own path
ItemStack.getStyledHoverName port crates/horizon-client/src/game/stations.rs::Game::stack_hover_name gap: minor: plain text; the rarity colour and italic style are not applied here
ItemStack.addToTooltip port crates/horizon-client/src/item_tooltip.rs::lines
ItemStack.addUnitComponentToTooltip inline crates/horizon-client/src/game/stations.rs::Game::stack_tooltip_lines unit lines such as unbreakable, gated by the tooltip display
ItemStack.canPlaceOnBlockInAdventureMode port crates/horizon-client/src/game/game_mode.rs::Game::can_place_on_in_adventure the can_place_on predicates gate a placement in adventure mode
ItemStack.canBreakBlockInAdventureMode missing player: in adventure mode a block can be broken only when the held item's can_break list allows it; Horizon does not read that component
ItemStack.getDamageSource missing unsure: a stack's damage_type component is not read when a weapon hits; no Horizon reader found
ItemStack.canBreakBlockInAdventureMode port crates/horizon-client/src/game/game_mode.rs::Game::adventure_predicate the main-hand stack's `can_break` predicate against the block at the position (one predicate or a list, block ids or tags, exact or ranged state properties; a predicate with `nbt` or `components` never matches), read by `game_mode.rs:200`; the same fn answers `can_place_on`
ItemStack.getDamageSource missing a stack's `damage_type` component is not read when a weapon hits: the attack builds its damage from the mob's own sources (`mobs::Damage`), and `damage_type` is only used for the enchantment damage tags
ItemStackTemplate.fromNonEmptyStack port crates/horizon-items/src/item_data.rs::template_tag stack to template tag
ItemStackTemplate.withCount skip:java record copy with a new count
ItemStackTemplate.apply port crates/horizon-assets/src/item_model.rs::stack_from_template
ItemUtils.onContainerDestroyed missing unsure: destroyed container items do not spill their contents; see BlockItem.onDestroyed
ItemUtils.onContainerDestroyed missing the shared spill of a destroyed container item has no counterpart; see `BlockItem.onDestroyed`
Items.registerBlock skip:data item registry bootstrap
Items.registerItem skip:data item registry bootstrap
JukeboxSong.hasFinished port crates/horizon-items/src/items.rs::Song::finished
JukeboxSongPlayer.setSongWithoutPlaying missing unsure: no load path restores a jukebox's song start tick
JukeboxSongPlayer.setSongWithoutPlaying inline crates/horizon-client/src/save.rs::decode_block_entity the load sets the record and `ticks_since_song_started` straight into the `Jukebox` block entity, which is the whole of `setSongWithoutPlaying`; the song carries on from that tick in `tick_jukebox`
JukeboxSongPlayer.play port crates/horizon-client/src/game/jukebox.rs::Game::start_song the song starts at the jukebox with its sound
JukeboxSongPlayer.shouldEmitJukeboxPlayingEvent inline crates/horizon-client/src/game/jukebox.rs::Game::tick_jukebox
JukeboxSongPlayer.spawnMusicParticles inline crates/horizon-client/src/game/jukebox.rs::Game::tick_jukebox the note particle is emitted in tick_jukebox
@@ -138,7 +138,7 @@ MapItem.createNewSavedData port crates/horizon-client/src/game/maps.rs::MapData:
MapItem.getCorrectStateForFluidBlock inline crates/horizon-client/src/game/maps.rs::Game::update_map the map colour pass
MapItem.inventoryTick port crates/horizon-client/src/game/maps.rs::Game::tick_maps
MapItem.lockMap port crates/horizon-client/src/menu.rs::Menu::map_crafted the lock branch of the post processing
MapItem.useOn missing player: using a map on a banner block should toggle that banner's marker on the map; Horizon has no banner toggle
MapItem.useOn port crates/horizon-client/src/game/maps.rs::Game::use_map_on_banner a map used on a block in the `minecraft:banners` tag toggles that banner's marker (`MapData::toggle_banner`); a failed toggle ends the use without a swing, and `game/remote.rs` predicts the same for its own map
MobBucketItem.emptyContents port crates/horizon-world/src/level/buckets.rs::Sim::bucket_empty the MobWater and Nothing contents
PotionItem.getDefaultInstance skip:data default stack data
PotionItem.useOn port crates/horizon-client/src/game/potions.rs::Game::use_water_bottle_on_block a water bottle poured on #convertible_to_mud makes mud and empties the bottle
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
34 ArrowItem.asProjectile port crates/horizon-client/src/game/dispenser.rs::Game::shoot_projectile_item dispensed arrows are built in the arrow arm of the item shot
35 BlockItem.updateBlockEntityComponents port crates/horizon-client/src/block_entity_item.rs::place the placed block entity takes the item's components
36 BlockItem.canPlace port crates/horizon-world/src/level/place.rs::Sim::place the placement checks, including the survive check, run in Sim::place
37 BlockItem.onDestroyed missing unsure: a container item whose item entity is destroyed should spill its contents; no Horizon code does this no `onDestroyed` on any item: an item entity of a shulker box or a bundle destroyed by fire or lava spills nothing
38 BoatItem.getBoat port crates/horizon-client/src/game/boats.rs::Game::place_boat boat placed at the hit point with the item's stack config
39 BucketItem.playEmptySound port crates/horizon-world/src/level/buckets.rs::Sim::empty_sound
40 BundleItem.getWeightSafe port crates/horizon-items/src/bundle.rs::Contents::weight_fraction float fraction; vanilla compares an exact Fraction
42 BundleItem.getBarColor port crates/horizon-items/src/bundle.rs::Contents::bar
43 BundleItem.removeOneItemFromBundle port crates/horizon-items/src/bundle.rs::Contents::remove_one
44 BundleItem.getTooltipImage port crates/horizon-items/src/bundle.rs::tooltip_layout gap: minor: the tooltip_display rule that hides the bundle contents is not checked here
45 BundleItem.onDestroyed missing unsure: a bundle whose item entity is destroyed should spill its contents; no Horizon code does this same as `BlockItem.onDestroyed`: nothing spills a destroyed item entity's contents
46 BundleItem.playRemoveOneSound port crates/horizon-client/src/game.rs::Game::drop_bundle_content the menu queues the same sound in menu/bundle.rs
47 BundleItem.playInsertSound port crates/horizon-client/src/menu/bundle.rs::Menu::carried_bundle_click queues the insert or insert-fail sound
48 BundleItem.playDropContentsSound port crates/horizon-client/src/game.rs::Game::drop_bundle_content
54 CrossbowItem.getShotPitch inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow the shot pitch per index is inline
55 CrossbowItem.getRandomShotPitch inline crates/horizon-client/src/game/crossbow.rs::Game::shoot_crossbow the random pitch formula is inline
56 CrossbowItem.getPowerForTime inline crates/horizon-client/src/game/crossbow.rs::Game::tick_crossbow the charge fraction capped at one is the tick check against the charge time
57 DyeColor.getMixedColor missing port crates/horizon-client/src/game/creatures.rs::Game::dye_mix player: a baby sheep, wolf or cat from parents of two dye colours gets a mixed colour; Horizon has no breeding dye mix (only sheep_color for spawns) the colour of the crafting recipe that mixes the two dyes, else one of the two picked at random (`next_boolean`); `creatures.rs:3153`, `:3176` and `:3222` give the offspring of a sheep, wolf or cat its colour with it
58 DyeColor.findColorMixInRecipes missing port crates/horizon-client/src/game/creatures.rs::Game::dye_color_mix_in_recipes player: part of the same breeding dye mix; the crafting recipe lookup for two dyes is not done for offspring the two dyes laid out as a 2x2 crafting grid through `Recipes::find`, and the result read back as a dye colour by its `_dye` suffix
59 FireworkRocketItem.createDispenseConfig port crates/horizon-client/src/game/dispenser.rs::Game::dispense_projectile rocket config: offset 0.5000099, uncertainty 1, power 0.5, event 1004
60 FireworkRocketItem.getEntityJustOutsideOfBlockPos port crates/horizon-client/src/game/dispenser.rs::Game::dispense_position the position just outside the dispenser on the facing side
61 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
84 Item.Properties.nautilusArmor skip:data registration builder
85 Item.Properties.requiredFeatures skip:data registration builder
86 Item.Properties.delayedHolderComponent skip:data registration builder
87 Item.Properties.loweredMobVisibility missing unsure: the mob_visibility component is not read by Horizon's mob targeting the `mob_visibility` component is parsed (it is in `item_model.rs`'s component table and the protocol's item hash), but no mob targeting reads it
88 Item.Properties.finalizeInitializer skip:data registration builder
89 Item.TooltipContext.of skip:java tooltip context factory; Horizon passes the level and player to the tooltip functions
90 ItemStack.validateStrict skip:java codec-side validation of components and stack size
97 ItemStack.nextDamageWillBreak inline crates/horizon-client/src/game/trident.rs::Game::use_trident gap: minor: only the trident's check is ported here
98 ItemStack.processDurabilityChange port crates/horizon-client/src/game/enchant_effects.rs::Game::durability_loss the Unbreaking-adjusted loss
99 ItemStack.applyDamage port crates/horizon-client/src/game.rs::Game::damage_held gap: minor: no item durability advancement trigger fires
100 ItemStack.hurtAndConvertOnBreak missing unsure: a carrot or fungus on a stick that breaks should become a fishing rod; no conversion was found nothing converts a broken carrot on a stick or fungus on a stick into a fishing rod
101 ItemStack.hurtEnemy port crates/horizon-client/src/game/mace.rs::Game::weapon_wear the per-hit durability loss of a weapon
102 ItemStack.interactLivingEntity port crates/horizon-client/src/game.rs::Game::interact_entity equip-on-interact is in the mob paths (nautili.rs, ghasts.rs)
103 ItemStack.copy skip:java value copy
111 ItemStack.onCraftedBy inline crates/horizon-client/src/game.rs::Game::click_inventory gap: minor: the crafted statistic is added only on the inventory grid craft path here
112 ItemStack.set skip:other component write; Horizon stores component data in typed fields
113 ItemStack.update skip:java functional update helper
114 ItemStack.applyComponentsAndValidate missing unsure: a validated component patch has no single Horizon counterpart; /item modify is a separate path a validated component patch has no single counterpart: `/item modify` goes through its own path
115 ItemStack.getStyledHoverName port crates/horizon-client/src/game/stations.rs::Game::stack_hover_name gap: minor: plain text; the rarity colour and italic style are not applied here
116 ItemStack.addToTooltip port crates/horizon-client/src/item_tooltip.rs::lines
117 ItemStack.addUnitComponentToTooltip inline crates/horizon-client/src/game/stations.rs::Game::stack_tooltip_lines unit lines such as unbreakable, gated by the tooltip display
118 ItemStack.canPlaceOnBlockInAdventureMode port crates/horizon-client/src/game/game_mode.rs::Game::can_place_on_in_adventure the can_place_on predicates gate a placement in adventure mode
119 ItemStack.canBreakBlockInAdventureMode missing port crates/horizon-client/src/game/game_mode.rs::Game::adventure_predicate player: in adventure mode a block can be broken only when the held item's can_break list allows it; Horizon does not read that component the main-hand stack's `can_break` predicate against the block at the position (one predicate or a list, block ids or tags, exact or ranged state properties; a predicate with `nbt` or `components` never matches), read by `game_mode.rs:200`; the same fn answers `can_place_on`
120 ItemStack.getDamageSource missing unsure: a stack's damage_type component is not read when a weapon hits; no Horizon reader found a stack's `damage_type` component is not read when a weapon hits: the attack builds its damage from the mob's own sources (`mobs::Damage`), and `damage_type` is only used for the enchantment damage tags
121 ItemStackTemplate.fromNonEmptyStack port crates/horizon-items/src/item_data.rs::template_tag stack to template tag
122 ItemStackTemplate.withCount skip:java record copy with a new count
123 ItemStackTemplate.apply port crates/horizon-assets/src/item_model.rs::stack_from_template
124 ItemUtils.onContainerDestroyed missing unsure: destroyed container items do not spill their contents; see BlockItem.onDestroyed the shared spill of a destroyed container item has no counterpart; see `BlockItem.onDestroyed`
125 Items.registerBlock skip:data item registry bootstrap
126 Items.registerItem skip:data item registry bootstrap
127 JukeboxSong.hasFinished port crates/horizon-items/src/items.rs::Song::finished
128 JukeboxSongPlayer.setSongWithoutPlaying missing inline crates/horizon-client/src/save.rs::decode_block_entity unsure: no load path restores a jukebox's song start tick the load sets the record and `ticks_since_song_started` straight into the `Jukebox` block entity, which is the whole of `setSongWithoutPlaying`; the song carries on from that tick in `tick_jukebox`
129 JukeboxSongPlayer.play port crates/horizon-client/src/game/jukebox.rs::Game::start_song the song starts at the jukebox with its sound
130 JukeboxSongPlayer.shouldEmitJukeboxPlayingEvent inline crates/horizon-client/src/game/jukebox.rs::Game::tick_jukebox
131 JukeboxSongPlayer.spawnMusicParticles inline crates/horizon-client/src/game/jukebox.rs::Game::tick_jukebox the note particle is emitted in tick_jukebox
138 MapItem.getCorrectStateForFluidBlock inline crates/horizon-client/src/game/maps.rs::Game::update_map the map colour pass
139 MapItem.inventoryTick port crates/horizon-client/src/game/maps.rs::Game::tick_maps
140 MapItem.lockMap port crates/horizon-client/src/menu.rs::Menu::map_crafted the lock branch of the post processing
141 MapItem.useOn missing port crates/horizon-client/src/game/maps.rs::Game::use_map_on_banner player: using a map on a banner block should toggle that banner's marker on the map; Horizon has no banner toggle a map used on a block in the `minecraft:banners` tag toggles that banner's marker (`MapData::toggle_banner`); a failed toggle ends the use without a swing, and `game/remote.rs` predicts the same for its own map
142 MobBucketItem.emptyContents port crates/horizon-world/src/level/buckets.rs::Sim::bucket_empty the MobWater and Nothing contents
143 PotionItem.getDefaultInstance skip:data default stack data
144 PotionItem.useOn port crates/horizon-client/src/game/potions.rs::Game::use_water_bottle_on_block a water bottle poured on #convertible_to_mud makes mud and empties the bottle
+6 -6
View File
@@ -19,7 +19,7 @@ MobEffectsPredicate.matches inline crates/horizon-client/src/advancements.rs::en
MobEffectsPredicate.Builder.and skip:java builder for effect predicates in code
MobEffectsPredicate.MobEffectInstancePredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: ambient and visible flags are not checked; amplifier and duration are
NbtPredicate.matches missing minor: custom_data and entity NBT predicates are not read; no vanilla advancement uses them
SingleComponentItemPredicate.matches inline crates/horizon-client/src/advancements.rs::item gap: minor: only enchantments, damage and potion contents are read; other components are ignored
SingleComponentItemPredicate.matches inline crates/horizon-client/src/advancements.rs::item gap: minor: enchantments, damage and potion contents are read from components and predicates; adventure/play_jukebox_in_meadows asks for minecraft:jukebox_playable, which is ignored, so any tool passes that match_tool test
SlotsPredicate.matches missing minor: slot-range item test is not read; no vanilla advancement uses it
SlotsPredicate.matchSlots missing minor: helper of SlotsPredicate.matches; not read
StatePropertiesPredicate.matches port crates/horizon-client/src/advancements.rs::block the state property test of block()
@@ -32,8 +32,8 @@ StatePropertiesPredicate.RangedMatcher.match inline crates/horizon-client/src/ad
TagPredicate.codec skip:java codec
TagPredicate.is skip:java builder
TagPredicate.isNot skip:java builder
TagPredicate.matches inline crates/horizon-client/src/advancements.rs::id_matches gap: minor: the #tag form is read; the negated expected=false form is not kept
CubeMobPredicate.matches missing minor: type_specific/cube_mob (slime size) is not read; no vanilla advancement uses it
TagPredicate.matches inline crates/horizon-client/src/advancements.rs::damage_source each of the damage type's tags compared against the predicate's expected flag; DamageSourcePredicate.matches is the only caller (adventure/blowback asks for #minecraft:is_projectile with expected true)
CubeMobPredicate.matches inline crates/horizon-world/src/loot.rs::Context::entity_matches the loot reader's minecraft:type_specific/cube_mob arm, the size bounds against the mob's own size; entity() in advancements.rs has no arm for it and answers false, so an advancement asking for one never matches
DistanceToPlayerPredicate.matches port crates/horizon-client/src/advancements.rs::distance the distance key of entity(): from the player to the entity
EntityEquipmentPredicate.captainPredicate skip:data datagen builder for the raid captain predicate (VanillaAdventureAdvancements)
EntityEquipmentPredicate.matches inline crates/horizon-client/src/advancements.rs::entity the equipment key: the seven slots
@@ -55,7 +55,7 @@ EntityTagPredicate.containsAllOf missing minor: helper of entity_tags; not read
EntityTypePredicate.of skip:java builder from an entity type or tag
EntityTypePredicate.matches port crates/horizon-client/src/advancements.rs::id_matches the type key of entity(): an id or #tag
FishingHookPredicate.matches inline crates/horizon-client/src/advancements.rs::entity the type_specific/fishing_hook arm: open-water test
LightningBoltPredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: blocks_set_on_fire is checked against 0, not the bolt's own count; the lightning trigger never fires
LightningBoltPredicate.matches inline crates/horizon-client/src/advancements.rs::entity the minecraft:type_specific/lightning arm: blocks_set_on_fire against the bolt's own count (game/lightning.rs fills it from the fires the bolt set) and entity_struck against the mobs around it
MovementAffectedByPredicate.matches missing minor: movement_affected_by key is not read; no vanilla advancement uses it
MovementPredicate.matches missing minor: movement key (speed, fall distance) is not read; no vanilla advancement uses it
PassengerPredicate.matches inline crates/horizon-client/src/advancements.rs::entity the `passenger` key: any passenger of the vehicle matches the nested predicate
@@ -66,8 +66,8 @@ PlayerPredicate.Builder.checkAdvancementDone skip:java builder
PlayerPredicate.Builder.checkAdvancementCriterions skip:java builder
PlayerPredicate.Builder.build skip:java builder
PlayerPredicate.StatMatcher.createTypedCodec skip:java codec
RaiderPredicate.matches missing minor: type_specific/raider is not read in entity(); no vanilla advancement uses it
SheepPredicate.matches missing minor: type_specific/sheep is not read; no vanilla advancement uses it
RaiderPredicate.matches inline crates/horizon-world/src/loot.rs::Context::entity_matches the loot reader's minecraft:type_specific/raider arm: captain matches, and has_raid matches no raid running here; the advancement reader answers false for the key
SheepPredicate.matches inline crates/horizon-world/src/loot.rs::Context::entity_matches the loot reader's minecraft:type_specific/sheep arm, the sheared flag; the advancement reader answers false for the key
SteppingOnPredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: no on-ground test; the block is read 0.5 below the feet
TargetedEntityPredicate.matches missing minor: targeted_entity key is not read; no vanilla advancement uses it
TeamPredicate.matches missing minor: team key is not read though scoreboard teams exist; no vanilla advancement uses it
1 InputPredicate.matches inline crates/horizon-client/src/advancements.rs::input gap: only the sneak input is read; forward, back, jump and sprint have no source here yet and count as unconstrained
19 MobEffectsPredicate.Builder.and skip:java builder for effect predicates in code
20 MobEffectsPredicate.MobEffectInstancePredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: ambient and visible flags are not checked; amplifier and duration are
21 NbtPredicate.matches missing minor: custom_data and entity NBT predicates are not read; no vanilla advancement uses them
22 SingleComponentItemPredicate.matches inline crates/horizon-client/src/advancements.rs::item gap: minor: only enchantments, damage and potion contents are read; other components are ignored gap: minor: enchantments, damage and potion contents are read from components and predicates; adventure/play_jukebox_in_meadows asks for minecraft:jukebox_playable, which is ignored, so any tool passes that match_tool test
23 SlotsPredicate.matches missing minor: slot-range item test is not read; no vanilla advancement uses it
24 SlotsPredicate.matchSlots missing minor: helper of SlotsPredicate.matches; not read
25 StatePropertiesPredicate.matches port crates/horizon-client/src/advancements.rs::block the state property test of block()
32 TagPredicate.codec skip:java codec
33 TagPredicate.is skip:java builder
34 TagPredicate.isNot skip:java builder
35 TagPredicate.matches inline crates/horizon-client/src/advancements.rs::id_matches crates/horizon-client/src/advancements.rs::damage_source gap: minor: the #tag form is read; the negated expected=false form is not kept each of the damage type's tags compared against the predicate's expected flag; DamageSourcePredicate.matches is the only caller (adventure/blowback asks for #minecraft:is_projectile with expected true)
36 CubeMobPredicate.matches missing inline crates/horizon-world/src/loot.rs::Context::entity_matches minor: type_specific/cube_mob (slime size) is not read; no vanilla advancement uses it the loot reader's minecraft:type_specific/cube_mob arm, the size bounds against the mob's own size; entity() in advancements.rs has no arm for it and answers false, so an advancement asking for one never matches
37 DistanceToPlayerPredicate.matches port crates/horizon-client/src/advancements.rs::distance the distance key of entity(): from the player to the entity
38 EntityEquipmentPredicate.captainPredicate skip:data datagen builder for the raid captain predicate (VanillaAdventureAdvancements)
39 EntityEquipmentPredicate.matches inline crates/horizon-client/src/advancements.rs::entity the equipment key: the seven slots
55 EntityTypePredicate.of skip:java builder from an entity type or tag
56 EntityTypePredicate.matches port crates/horizon-client/src/advancements.rs::id_matches the type key of entity(): an id or #tag
57 FishingHookPredicate.matches inline crates/horizon-client/src/advancements.rs::entity the type_specific/fishing_hook arm: open-water test
58 LightningBoltPredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: blocks_set_on_fire is checked against 0, not the bolt's own count; the lightning trigger never fires the minecraft:type_specific/lightning arm: blocks_set_on_fire against the bolt's own count (game/lightning.rs fills it from the fires the bolt set) and entity_struck against the mobs around it
59 MovementAffectedByPredicate.matches missing minor: movement_affected_by key is not read; no vanilla advancement uses it
60 MovementPredicate.matches missing minor: movement key (speed, fall distance) is not read; no vanilla advancement uses it
61 PassengerPredicate.matches inline crates/horizon-client/src/advancements.rs::entity the `passenger` key: any passenger of the vehicle matches the nested predicate
66 PlayerPredicate.Builder.checkAdvancementCriterions skip:java builder
67 PlayerPredicate.Builder.build skip:java builder
68 PlayerPredicate.StatMatcher.createTypedCodec skip:java codec
69 RaiderPredicate.matches missing inline crates/horizon-world/src/loot.rs::Context::entity_matches minor: type_specific/raider is not read in entity(); no vanilla advancement uses it the loot reader's minecraft:type_specific/raider arm: captain matches, and has_raid matches no raid running here; the advancement reader answers false for the key
70 SheepPredicate.matches missing inline crates/horizon-world/src/loot.rs::Context::entity_matches minor: type_specific/sheep is not read; no vanilla advancement uses it the loot reader's minecraft:type_specific/sheep arm, the sheared flag; the advancement reader answers false for the key
71 SteppingOnPredicate.matches inline crates/horizon-client/src/advancements.rs::entity gap: minor: no on-ground test; the block is read 0.5 below the feet
72 TargetedEntityPredicate.matches missing minor: targeted_entity key is not read; no vanilla advancement uses it
73 TeamPredicate.matches missing minor: team key is not read though scoreboard teams exist; no vanilla advancement uses it
+4 -4
View File
@@ -64,8 +64,8 @@ KeyboardHandler.copyCreateBlockCommand port crates/horizon-client/src/game/debug
KeyboardHandler.copyCreateEntityCommand port crates/horizon-client/src/game/debug_keys.rs::Game::copy_recreate_command
KeyboardHandler.charTyped port crates/horizon-client/src/game.rs::Game::window_event typed text routed to the open screen
KeyboardHandler.textInput port crates/horizon-client/src/game.rs::Game::window_event typed text routed to the open screen
KeyboardHandler.textEditing port crates/horizon-client/src/game.rs::ime_preedit WindowEvent::Ime(Preedit) draws the composition in the focused field
KeyboardHandler.submitPreeditEvent inline crates/horizon-client/src/game.rs: WindowEvent::Ime arm the preedit reaches the screen's field directly, and Commit types the text
KeyboardHandler.textEditing port crates/horizon-client/src/game/ime.rs::Game::ime_preedit WindowEvent::Ime(Preedit) draws the composition in the focused field
KeyboardHandler.submitPreeditEvent inline crates/horizon-client-render/src/preedit.rs::set_preedit the preedit reaches the screen's field directly, and Commit types the text
Minecraft.<init> inline crates/horizon-client/src/game.rs::run_loading gap: minor: no unclean-start recovery; startedCleanly is only a default in options_file.rs
Minecraft.onGameLoadFinished missing minor: startedCleanly is never written after load
Minecraft.createTitle missing minor: the window title is always Horizon (with_title in game.rs), no version or world type
@@ -117,10 +117,10 @@ Minecraft.updateReportEnvironment skip:service reporting context for chat repor
Minecraft.getGameProfile skip:service Mojang profile fetch; Horizon's account is local (auth.rs)
Minecraft.showOnlyReducedInfo port crates/horizon-client/src/game/debug_keys.rs::Game::reduced_debug_info
Minecraft.grabPanoramixScreenshot skip:debug only runs with SharedConstants.DEBUG_PANORAMA_SCREENSHOT
Minecraft.onTextInputFocusChange inline crates/horizon-client/src/game.rs::sync_ime the IME is synced to the focused widget each frame
Minecraft.onTextInputFocusChange inline crates/horizon-client/src/game/ime.rs::Game::sync_ime the IME is synced to the focused widget each frame
Minecraft.getTickTargetMillis port crates/horizon-client/src/clock.rs::TickRateManager::milliseconds_per_tick gap: minor: the max with the default tick time is not in this fn
MouseHandler.fillMousePositionDetails skip:debug crash report mouse details
MouseHandler.onDrop port crates/horizon-client/src/game.rs:16709 -> title::drop_file a file dropped on the window joins the create screen's data packs
MouseHandler.onDrop port crates/horizon-client/src/title.rs::Title::drop_file a file dropped on the window joins the create screen's data packs
MouseHandler.getScaledYPos port crates/horizon-client-input/src/window.rs::scaled_pos
MouseHandler.resyncMousePosition skip:other winit reports the cursor position; no SDL global-state resync
MouseHandler.releaseMouse port crates/horizon-client/src/game.rs::Game::capture release path (capture false)
1 PlayerHurtEntityTrigger.TriggerInstance.playerHurtEntityWithDamage skip:data criterion factory; Horizon reads the criterion from the advancement JSON
64 KeyboardHandler.copyCreateEntityCommand port crates/horizon-client/src/game/debug_keys.rs::Game::copy_recreate_command
65 KeyboardHandler.charTyped port crates/horizon-client/src/game.rs::Game::window_event typed text routed to the open screen
66 KeyboardHandler.textInput port crates/horizon-client/src/game.rs::Game::window_event typed text routed to the open screen
67 KeyboardHandler.textEditing port crates/horizon-client/src/game.rs::ime_preedit crates/horizon-client/src/game/ime.rs::Game::ime_preedit WindowEvent::Ime(Preedit) draws the composition in the focused field
68 KeyboardHandler.submitPreeditEvent inline crates/horizon-client/src/game.rs: WindowEvent::Ime arm crates/horizon-client-render/src/preedit.rs::set_preedit the preedit reaches the screen's field directly, and Commit types the text
69 Minecraft.<init> inline crates/horizon-client/src/game.rs::run_loading gap: minor: no unclean-start recovery; startedCleanly is only a default in options_file.rs
70 Minecraft.onGameLoadFinished missing minor: startedCleanly is never written after load
71 Minecraft.createTitle missing minor: the window title is always Horizon (with_title in game.rs), no version or world type
117 Minecraft.getGameProfile skip:service Mojang profile fetch; Horizon's account is local (auth.rs)
118 Minecraft.showOnlyReducedInfo port crates/horizon-client/src/game/debug_keys.rs::Game::reduced_debug_info
119 Minecraft.grabPanoramixScreenshot skip:debug only runs with SharedConstants.DEBUG_PANORAMA_SCREENSHOT
120 Minecraft.onTextInputFocusChange inline crates/horizon-client/src/game.rs::sync_ime crates/horizon-client/src/game/ime.rs::Game::sync_ime the IME is synced to the focused widget each frame
121 Minecraft.getTickTargetMillis port crates/horizon-client/src/clock.rs::TickRateManager::milliseconds_per_tick gap: minor: the max with the default tick time is not in this fn
122 MouseHandler.fillMousePositionDetails skip:debug crash report mouse details
123 MouseHandler.onDrop port crates/horizon-client/src/game.rs:16709 -> title::drop_file crates/horizon-client/src/title.rs::Title::drop_file a file dropped on the window joins the create screen's data packs
124 MouseHandler.getScaledYPos port crates/horizon-client-input/src/window.rs::scaled_pos
125 MouseHandler.resyncMousePosition skip:other winit reports the cursor position; no SDL global-state resync
126 MouseHandler.releaseMouse port crates/horizon-client/src/game.rs::Game::capture release path (capture false)
+9 -9
View File
@@ -1,6 +1,6 @@
OptionInstance.SliderableEnum.toSliderValue port crates/horizon-client-render/src/options_ui.rs::Opt::kind slider position of the graphics preset (index / 3)
OptionInstance.SliderableEnum.next inline crates/horizon-client-render/src/options_ui.rs Opt::Preset is a 0..3 slider (unit/step at options_ui.rs:335, 417, 844), so stepping happens through the slider rather than a cycle arm
OptionInstance.SliderableEnum.previous inline crates/horizon-client-render/src/options_ui.rs same slider
OptionInstance.SliderableEnum.next inline crates/horizon-client-render/src/options_ui.rs::Opt::set_slider Opt::Preset is a 0..3 slider (unit/step at options_ui.rs:335, 417, 844), so stepping happens through the slider rather than a cycle arm
OptionInstance.SliderableEnum.previous inline crates/horizon-client-render/src/options_ui.rs::Opt::set_slider same slider
OptionInstance.SliderableEnum.fromSliderValue port crates/horizon-client-render/src/options_ui.rs::Opt::set_slider gap: minor: rounds v*3 where vanilla floors v*4
OptionInstance.SliderableEnum.validateValue port crates/horizon-client-render/src/options_file.rs::Spec::validate named spec accepts the four preset indexes
OptionInstance.SliderableOrCyclableValueSet.createButton port crates/horizon-client-render/src/options_ui.rs::option_widget cycle or slider button by option kind
@@ -8,14 +8,14 @@ OptionInstance.SliderableValueSet.createButton port crates/horizon-client-render
OptionInstance.UnitDouble.validateValue port crates/horizon-client-render/src/options_file.rs::Spec::validate unit range 0..1
OptionInstance.UnitDouble.codec port crates/horizon-client-render/src/options_file.rs::codec_double
Options.updateResourcePacks port crates/horizon-client/src/game/options.rs::Game::handle_option_events gap: minor: saves and reloads on every packs event; vanilla only when the list changed
Options.maxAnisotropyValue port crates/horizon-client/src/game/options.rs + horizon-client-render::gfx::set_texture_filtering the options.txt maxAnisotropyBit (and textureFiltering) reach the sampler
Options.maxAnisotropyValue port crates/horizon-client-render/src/gfx.rs::sampler_anisotropy the options.txt maxAnisotropyBit (and textureFiltering) reach the sampler
Options.createSoundSliderOptionInstance port crates/horizon-client-render/src/audio.rs::Audio::sync_volumes gap: player: no now-playing toast when master or music changes
Options.setGraphicsPresetToCustom inline crates/horizon-client-render/src/options_ui.rs::Opt::set_slider gap: minor: cycle buttons set custom in Opt::cycle as well
Options.processOptions inline crates/horizon-client-render/src/options_file.rs::OptionsFile::parse gap: minor: the save direction is OptionsFile::serialize
Options.dataFix port crates/horizon-client-render/src/options_fixes.rs::update
Options.getFullscreenVideoModeString inline crates/horizon-client-render/src/options_file.rs options.txt keeps fullscreen_resolution as the monitor index the option was picked from instead of a 'WxH@Hz' string; the window is restored from that index
Options.getFullscreenVideoModeString inline crates/horizon-client-input/src/window.rs::VideoMode::write options.txt keeps fullscreenResolution as `VideoMode::write`'s mode JSON, not as a separate 'WxH@Hz' string; `Settings.video_mode_index` reads it back to place the Video screen's mode slider
Options.buildPlayerInformation port crates/horizon-client/src/net.rs::run gap: player: only view distance and chat visibility are sent; language, skin parts, main hand, particles and text filtering stay at defaults
Options.setModelPart port crates/horizon-client-render/src/options_file.rs::MODEL_PARTS + options_ui model part keys the seven modelPart_* options are read back and sent as the PlayerModelPart set
Options.setModelPart port crates/horizon-client-render/src/options_file.rs::OptionsFile::model_customisation the seven modelPart_* options are read back and sent as the PlayerModelPart set
Options.loadSelectedResourcePacks port crates/horizon-client-render/src/packs.rs::enabled gap: minor: dropped packs are not written back to resourcePacks
Options.readListOfStrings port crates/horizon-client-render/src/options_file.rs::read_list
Options.dumpOptionsForReport skip:debug options dump for the debug report
@@ -87,14 +87,14 @@ ClientMannequin.tick missing minor: unsure: the mannequin avatar state (walk bo
AbstractSelectionList.getRowLeft port crates/horizon-client-render/src/pack_screen.rs::List::row_left
CycleButton.setValue skip:ui widgets read the settings each frame; no separate value setter
CreateFlatWorldScreen.DetailsList.LayerEntry.getNarration skip:ui screen-reader text
InputQuirks.isQuitShortcutDown port crates/horizon-client/src/game.rs the close path reads settings.quit_shortcuts and input::is_quit_shortcut_down, so with the option off the shortcut no longer closes the window
InputQuirks.isQuitShortcutDown port crates/horizon-client-input/src/input.rs::is_quit_shortcut_down the close path reads settings.quit_shortcuts and input::is_quit_shortcut_down, so with the option off the shortcut no longer closes the window
InputWithModifiers.isSelection inline crates/horizon-client/src/game/key_focus.rs::nav_of Enter, Space and keypad Enter all map to Nav::Select, which presses the focused widget
InputWithModifiers.isConfirmation inline crates/horizon-client-render/src/chat.rs::ChatScreen::key Enter and keypad Enter only, as the text-field screens do (chat, sign, command block, books); no shared predicate
InputWithModifiers.isCopy port crates/horizon-client-input/src/clipboard.rs::edit_shortcut
InputWithModifiers.isPaste port crates/horizon-client-input/src/clipboard.rs::edit_shortcut
InputWithModifiers.isCut port crates/horizon-client-input/src/clipboard.rs::edit_shortcut
PreeditEvent.fromSdlTextEditing port crates/horizon-client/src/game.rs::ime_preedit WindowEvent::Ime(Preedit) feeds the composition text to the focused field
PreeditEvent.toFormattedText inline crates/horizon-client/src/game.rs::ime_preedit the composing text is drawn as the field's preedit, not as formatted chat
PreeditEvent.fromSdlTextEditing port crates/horizon-client-render/src/preedit.rs::PreeditEvent::from_sdl_text_editing WindowEvent::Ime(Preedit) feeds the composition text to the focused field
PreeditEvent.toFormattedText inline crates/horizon-client-render/src/preedit.rs::PreeditEvent::to_formatted_text the composing text is drawn as the field's preedit, not as formatted chat
GameConfig.FolderData.getExternalAssetSource skip:data Horizon reads assets from the prepared resources folder
net/minecraft/client/main/Main#getQuickPlayVariant port crates/horizon-client/src/game.rs::Options::read gap: minor: only the quick play flags of the launch parser; realms branch not checked
net/minecraft/client/main/Main#parseArgument inline crates/horizon-client/src/game.rs::Options::read
@@ -135,7 +135,7 @@ LocalPlayer.resetPos inline crates/horizon-client/src/game/screens.rs::Game::res
LocalPlayer.shouldStopRunSprinting port crates/horizon-client/src/sprint.rs::Conditions::should_stop gap: player: minor_horizontal_collision is always false, so wall slides stop the sprint
LocalPlayer.shouldStopSwimSprinting port crates/horizon-client/src/sprint.rs::Conditions::should_stop
LocalPlayer.tickDeath missing minor: unsure: the dead local player is not removed after 20 ticks; the death screen flow covers it
LocalPlayer.rideTick port crates/horizon-client/src/game/boats.rs::Game::tick_riding the keys are latched into the boat's input and the seat follows its turn
LocalPlayer.rideTick port crates/horizon-client/src/game/remote_vehicles.rs::Game::remote_ride the keys are latched into the boat's input and the seat follows its turn
LocalPlayer.isHorizontalCollisionMinor port crates/horizon-client/src/motion.rs::EntityMoveWorld::minor_horizontal_collision the sprint keeps when a move hits a wall at under 8 degrees
LocalPlayer.isMoving inline crates/horizon-client/src/auto_jump.rs::can_auto_jump
LocalPlayer.isSprintingPossible port crates/horizon-client/src/sprint.rs::Conditions::possible
1 OptionInstance.SliderableEnum.toSliderValue port crates/horizon-client-render/src/options_ui.rs::Opt::kind slider position of the graphics preset (index / 3)
2 OptionInstance.SliderableEnum.next inline crates/horizon-client-render/src/options_ui.rs crates/horizon-client-render/src/options_ui.rs::Opt::set_slider Opt::Preset is a 0..3 slider (unit/step at options_ui.rs:335, 417, 844), so stepping happens through the slider rather than a cycle arm
3 OptionInstance.SliderableEnum.previous inline crates/horizon-client-render/src/options_ui.rs crates/horizon-client-render/src/options_ui.rs::Opt::set_slider same slider
4 OptionInstance.SliderableEnum.fromSliderValue port crates/horizon-client-render/src/options_ui.rs::Opt::set_slider gap: minor: rounds v*3 where vanilla floors v*4
5 OptionInstance.SliderableEnum.validateValue port crates/horizon-client-render/src/options_file.rs::Spec::validate named spec accepts the four preset indexes
6 OptionInstance.SliderableOrCyclableValueSet.createButton port crates/horizon-client-render/src/options_ui.rs::option_widget cycle or slider button by option kind
8 OptionInstance.UnitDouble.validateValue port crates/horizon-client-render/src/options_file.rs::Spec::validate unit range 0..1
9 OptionInstance.UnitDouble.codec port crates/horizon-client-render/src/options_file.rs::codec_double
10 Options.updateResourcePacks port crates/horizon-client/src/game/options.rs::Game::handle_option_events gap: minor: saves and reloads on every packs event; vanilla only when the list changed
11 Options.maxAnisotropyValue port crates/horizon-client/src/game/options.rs + horizon-client-render::gfx::set_texture_filtering crates/horizon-client-render/src/gfx.rs::sampler_anisotropy the options.txt maxAnisotropyBit (and textureFiltering) reach the sampler
12 Options.createSoundSliderOptionInstance port crates/horizon-client-render/src/audio.rs::Audio::sync_volumes gap: player: no now-playing toast when master or music changes
13 Options.setGraphicsPresetToCustom inline crates/horizon-client-render/src/options_ui.rs::Opt::set_slider gap: minor: cycle buttons set custom in Opt::cycle as well
14 Options.processOptions inline crates/horizon-client-render/src/options_file.rs::OptionsFile::parse gap: minor: the save direction is OptionsFile::serialize
15 Options.dataFix port crates/horizon-client-render/src/options_fixes.rs::update
16 Options.getFullscreenVideoModeString inline crates/horizon-client-render/src/options_file.rs crates/horizon-client-input/src/window.rs::VideoMode::write options.txt keeps fullscreen_resolution as the monitor index the option was picked from instead of a 'WxH@Hz' string; the window is restored from that index options.txt keeps fullscreenResolution as `VideoMode::write`'s mode JSON, not as a separate 'WxH@Hz' string; `Settings.video_mode_index` reads it back to place the Video screen's mode slider
17 Options.buildPlayerInformation port crates/horizon-client/src/net.rs::run gap: player: only view distance and chat visibility are sent; language, skin parts, main hand, particles and text filtering stay at defaults
18 Options.setModelPart port crates/horizon-client-render/src/options_file.rs::MODEL_PARTS + options_ui model part keys crates/horizon-client-render/src/options_file.rs::OptionsFile::model_customisation the seven modelPart_* options are read back and sent as the PlayerModelPart set
19 Options.loadSelectedResourcePacks port crates/horizon-client-render/src/packs.rs::enabled gap: minor: dropped packs are not written back to resourcePacks
20 Options.readListOfStrings port crates/horizon-client-render/src/options_file.rs::read_list
21 Options.dumpOptionsForReport skip:debug options dump for the debug report
87 AbstractSelectionList.getRowLeft port crates/horizon-client-render/src/pack_screen.rs::List::row_left
88 CycleButton.setValue skip:ui widgets read the settings each frame; no separate value setter
89 CreateFlatWorldScreen.DetailsList.LayerEntry.getNarration skip:ui screen-reader text
90 InputQuirks.isQuitShortcutDown port crates/horizon-client/src/game.rs crates/horizon-client-input/src/input.rs::is_quit_shortcut_down the close path reads settings.quit_shortcuts and input::is_quit_shortcut_down, so with the option off the shortcut no longer closes the window
91 InputWithModifiers.isSelection inline crates/horizon-client/src/game/key_focus.rs::nav_of Enter, Space and keypad Enter all map to Nav::Select, which presses the focused widget
92 InputWithModifiers.isConfirmation inline crates/horizon-client-render/src/chat.rs::ChatScreen::key Enter and keypad Enter only, as the text-field screens do (chat, sign, command block, books); no shared predicate
93 InputWithModifiers.isCopy port crates/horizon-client-input/src/clipboard.rs::edit_shortcut
94 InputWithModifiers.isPaste port crates/horizon-client-input/src/clipboard.rs::edit_shortcut
95 InputWithModifiers.isCut port crates/horizon-client-input/src/clipboard.rs::edit_shortcut
96 PreeditEvent.fromSdlTextEditing port crates/horizon-client/src/game.rs::ime_preedit crates/horizon-client-render/src/preedit.rs::PreeditEvent::from_sdl_text_editing WindowEvent::Ime(Preedit) feeds the composition text to the focused field
97 PreeditEvent.toFormattedText inline crates/horizon-client/src/game.rs::ime_preedit crates/horizon-client-render/src/preedit.rs::PreeditEvent::to_formatted_text the composing text is drawn as the field's preedit, not as formatted chat
98 GameConfig.FolderData.getExternalAssetSource skip:data Horizon reads assets from the prepared resources folder
99 net/minecraft/client/main/Main#getQuickPlayVariant port crates/horizon-client/src/game.rs::Options::read gap: minor: only the quick play flags of the launch parser; realms branch not checked
100 net/minecraft/client/main/Main#parseArgument inline crates/horizon-client/src/game.rs::Options::read
135 LocalPlayer.shouldStopRunSprinting port crates/horizon-client/src/sprint.rs::Conditions::should_stop gap: player: minor_horizontal_collision is always false, so wall slides stop the sprint
136 LocalPlayer.shouldStopSwimSprinting port crates/horizon-client/src/sprint.rs::Conditions::should_stop
137 LocalPlayer.tickDeath missing minor: unsure: the dead local player is not removed after 20 ticks; the death screen flow covers it
138 LocalPlayer.rideTick port crates/horizon-client/src/game/boats.rs::Game::tick_riding crates/horizon-client/src/game/remote_vehicles.rs::Game::remote_ride the keys are latched into the boat's input and the seat follows its turn
139 LocalPlayer.isHorizontalCollisionMinor port crates/horizon-client/src/motion.rs::EntityMoveWorld::minor_horizontal_collision the sprint keeps when a move hits a wall at under 8 degrees
140 LocalPlayer.isMoving inline crates/horizon-client/src/auto_jump.rs::can_auto_jump
141 LocalPlayer.isSprintingPossible port crates/horizon-client/src/sprint.rs::Conditions::possible
+13 -13
View File
@@ -31,7 +31,7 @@ SectionPos.blockToSection port crates/horizon-core/src/position.rs::BlockPos::se
SectionPos.origin inline crates/horizon-client-render/src/gfx/resident.rs::origin gap: minor: camera-relative origin; the block corner is the same sum without the camera
SectionPos.asLong port crates/horizon-core/src/position.rs::SectionPos::pack asLong(BlockPos): BlockPos::section then this pack; bit layout matches
SectionPos.blocksInside inline crates/horizon-client/src/mobs/villager_brain.rs::chunk_pois the POI scan visits every block of each section
SectionPos.aroundChunk missing minor: unsure: ensureLoadedAndValid pre-loads POI chunks around a point; Horizon's chunk_pois reads loaded chunks only
SectionPos.aroundChunk missing `mobs/villager_brain.rs::chunk_pois` reads the chunks it has, with no `ensureLoadedAndValid`: a search near an unloaded chunk sees fewer points of interest
SectionPos.betweenClosedStream skip:java Java stream over a 3-D section box; the loops in pois_in_range and chunk_pois do the iteration
SectionPos.aroundAndAtBlockPos port crates/horizon-client-render/src/light.rs::LightChanges::dirty_sections the sections within one block of a changed cell
TypedInstance.is skip:java holder type checks; Horizon compares registry names
@@ -78,15 +78,15 @@ DataComponentInitializers.Initializer.add skip:data item default component init
DataComponentInitializers.SingleComponentInitializer.asInitializer skip:data item default component initializers; Horizon reads item defaults from its tables
DataComponentLookup.getOrCreateStorage skip:java cache
DataComponentLookup.scanForComponents skip:java index build behind the lookup
DataComponentLookup.findAll missing minor: unsure: DyeColor mixing and the recipe fuel list read it; Horizon finds dyes and fuels by name and table
DataComponentLookup.ComponentStorage.findMatching missing minor: unsure: spawn egg from an entity type; Horizon builds egg ids by name
DataComponentLookup.findAll missing no component-driven item search: dyes are found by their `_dye` name and fuels by the table in `crafting.rs`/`furnace.rs`
DataComponentLookup.ComponentStorage.findMatching missing a spawn egg is built from the entity type's id by name, with no component lookup
DataComponentMap.makeCodecFromMap skip:java codec
DataComponentMap.iterator skip:java iterator
DataComponentMap.Builder.setUnchecked skip:java builder
DataComponentMap.Builder.addAll skip:java builder
DataComponentMap.Builder.buildFromMapTrusted skip:java builder
DataComponentPatch.getFromPatchAndPrototype skip:java patch-or-default read; typed StackData fields plus the item table cover it
DataComponentPatch.forget missing minor: unsure: BlockEntity.setComponents strips implicit components with forget; not checked in Horizon's block entity placement
DataComponentPatch.forget missing a block entity's implicit components are not stripped on placement: `block_entity_item.rs` merges the explicit ones over the implicit
DataComponentPatch.split skip:java added and removed parts kept as separate fields (protocol Stack.removed, StackData)
DataComponentPatch.equals skip:java equality
DataComponentPatch.toString skip:debug debug string
@@ -109,20 +109,20 @@ PatchedDataComponentMap.asPatch skip:java generic component map; Horizon sets t
PatchedDataComponentMap.copy skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data
PatchedDataComponentMap.equals skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data
TypedDataComponent.encodeValue skip:java codec
AttributeModifiersPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly
AttributeModifiersPredicate.EntryPredicate.test missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly
BundlePredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly
ContainerPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly
AttributeModifiersPredicate.matches missing `advancements.rs::item` reads `items`, `count`, the `potion_contents` and `damage` components and the `enchantments` and `damage` predicates; a `minecraft:attribute_modifiers` predicate is ignored, so the criterion matches too broadly
AttributeModifiersPredicate.EntryPredicate.test missing same as `matches`: no entry of an `attribute_modifiers` predicate is tested
BundlePredicate.matches missing ignored by `advancements.rs::item`, so a bundle contents criterion matches any bundle
ContainerPredicate.matches missing ignored by `advancements.rs::item`, so a container contents criterion matches too broadly
net/minecraft/core/component/predicates/DamagePredicate#matches inline crates/horizon-client/src/advancements.rs::item gap: minor: only the damage range; the durability range (max damage minus damage) is not checked
DataComponentPredicate.Single.wrapCodec skip:java codec
DataComponentPredicate.Type.unpackType skip:java codec helper
EnchantmentsPredicate.matches inline crates/horizon-client/src/advancements.rs::item the enchantments predicate inside item()
FireworkExplosionPredicate.FireworkPredicate.test missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly
FireworksPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly
JukeboxPlayablePredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly
FireworkExplosionPredicate.FireworkPredicate.test missing ignored by `advancements.rs::item`
FireworksPredicate.matches missing ignored by `advancements.rs::item`, so a fireworks criterion matches any firework
JukeboxPlayablePredicate.matches missing ignored by `advancements.rs::item`
PotionsPredicate.matches port crates/horizon-client/src/advancements.rs::potions
WritableBookPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly
WrittenBookPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly
WritableBookPredicate.matches missing ignored by `advancements.rs::item`
WrittenBookPredicate.matches missing ignored by `advancements.rs::item`
BoatDispenseItemBehavior.execute port crates/horizon-client/src/game/dispenser.rs::Game::dispense_boat
DefaultDispenseItemBehavior.dispense port crates/horizon-client/src/game/dispenser.rs::Game::dispense_default the click and dispense events of dispense_default
DefaultDispenseItemBehavior.addToInventoryOrDispense port crates/horizon-client/src/game/dispenser.rs::Game::consume_with_remainder
1 Registry.byNameCodec skip:java codec over the registry by name
31 SectionPos.origin inline crates/horizon-client-render/src/gfx/resident.rs::origin gap: minor: camera-relative origin; the block corner is the same sum without the camera
32 SectionPos.asLong port crates/horizon-core/src/position.rs::SectionPos::pack asLong(BlockPos): BlockPos::section then this pack; bit layout matches
33 SectionPos.blocksInside inline crates/horizon-client/src/mobs/villager_brain.rs::chunk_pois the POI scan visits every block of each section
34 SectionPos.aroundChunk missing minor: unsure: ensureLoadedAndValid pre-loads POI chunks around a point; Horizon's chunk_pois reads loaded chunks only `mobs/villager_brain.rs::chunk_pois` reads the chunks it has, with no `ensureLoadedAndValid`: a search near an unloaded chunk sees fewer points of interest
35 SectionPos.betweenClosedStream skip:java Java stream over a 3-D section box; the loops in pois_in_range and chunk_pois do the iteration
36 SectionPos.aroundAndAtBlockPos port crates/horizon-client-render/src/light.rs::LightChanges::dirty_sections the sections within one block of a changed cell
37 TypedInstance.is skip:java holder type checks; Horizon compares registry names
78 DataComponentInitializers.SingleComponentInitializer.asInitializer skip:data item default component initializers; Horizon reads item defaults from its tables
79 DataComponentLookup.getOrCreateStorage skip:java cache
80 DataComponentLookup.scanForComponents skip:java index build behind the lookup
81 DataComponentLookup.findAll missing minor: unsure: DyeColor mixing and the recipe fuel list read it; Horizon finds dyes and fuels by name and table no component-driven item search: dyes are found by their `_dye` name and fuels by the table in `crafting.rs`/`furnace.rs`
82 DataComponentLookup.ComponentStorage.findMatching missing minor: unsure: spawn egg from an entity type; Horizon builds egg ids by name a spawn egg is built from the entity type's id by name, with no component lookup
83 DataComponentMap.makeCodecFromMap skip:java codec
84 DataComponentMap.iterator skip:java iterator
85 DataComponentMap.Builder.setUnchecked skip:java builder
86 DataComponentMap.Builder.addAll skip:java builder
87 DataComponentMap.Builder.buildFromMapTrusted skip:java builder
88 DataComponentPatch.getFromPatchAndPrototype skip:java patch-or-default read; typed StackData fields plus the item table cover it
89 DataComponentPatch.forget missing minor: unsure: BlockEntity.setComponents strips implicit components with forget; not checked in Horizon's block entity placement a block entity's implicit components are not stripped on placement: `block_entity_item.rs` merges the explicit ones over the implicit
90 DataComponentPatch.split skip:java added and removed parts kept as separate fields (protocol Stack.removed, StackData)
91 DataComponentPatch.equals skip:java equality
92 DataComponentPatch.toString skip:debug debug string
109 PatchedDataComponentMap.copy skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data
110 PatchedDataComponentMap.equals skip:java generic component map; Horizon sets typed StackData fields and ItemStack::tidy drops default-equal data
111 TypedDataComponent.encodeValue skip:java codec
112 AttributeModifiersPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly `advancements.rs::item` reads `items`, `count`, the `potion_contents` and `damage` components and the `enchantments` and `damage` predicates; a `minecraft:attribute_modifiers` predicate is ignored, so the criterion matches too broadly
113 AttributeModifiersPredicate.EntryPredicate.test missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly same as `matches`: no entry of an `attribute_modifiers` predicate is tested
114 BundlePredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly ignored by `advancements.rs::item`, so a bundle contents criterion matches any bundle
115 ContainerPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly ignored by `advancements.rs::item`, so a container contents criterion matches too broadly
116 net/minecraft/core/component/predicates/DamagePredicate#matches inline crates/horizon-client/src/advancements.rs::item gap: minor: only the damage range; the durability range (max damage minus damage) is not checked
117 DataComponentPredicate.Single.wrapCodec skip:java codec
118 DataComponentPredicate.Type.unpackType skip:java codec helper
119 EnchantmentsPredicate.matches inline crates/horizon-client/src/advancements.rs::item the enchantments predicate inside item()
120 FireworkExplosionPredicate.FireworkPredicate.test missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly ignored by `advancements.rs::item`
121 FireworksPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly ignored by `advancements.rs::item`, so a fireworks criterion matches any firework
122 JukeboxPlayablePredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly ignored by `advancements.rs::item`
123 PotionsPredicate.matches port crates/horizon-client/src/advancements.rs::potions
124 WritableBookPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly ignored by `advancements.rs::item`
125 WrittenBookPredicate.matches missing minor: unsure: item() in advancements.rs ignores this key, so a criterion using it matches too broadly ignored by `advancements.rs::item`
126 BoatDispenseItemBehavior.execute port crates/horizon-client/src/game/dispenser.rs::Game::dispense_boat
127 DefaultDispenseItemBehavior.dispense port crates/horizon-client/src/game/dispenser.rs::Game::dispense_default the click and dispense events of dispense_default
128 DefaultDispenseItemBehavior.addToInventoryOrDispense port crates/horizon-client/src/game/dispenser.rs::Game::consume_with_remainder
+1 -1
View File
@@ -89,7 +89,7 @@ SignedMessageValidator.KeyBased.validate port crates/horizon-protocol/src/secure
SignedMessageValidator.KeyBased.updateAndValidate port crates/horizon-protocol/src/secure_chat.rs::MessageValidator::update_and_validate a failed message breaks the chain: nothing more is accepted
Style.create skip:java builder from optionals; Style is a plain struct
Style.checkEmptyAfterChange skip:java canonical empty style instance
Style.withShadowColor missing minor: Style has no shadow colour, so a component's shadow_color is dropped
Style.withShadowColor port crates/horizon-client-core/src/component.rs::shadow_color the style's shadow colour is kept on Style (text.rs:92) as packed ARGB, read from a packed int or a normalized RGBA array and written back out
Style.withBold port crates/horizon-client-core/src/text.rs::Text::bold gap: minor: sets true only; a null or false setter is not kept
Style.withItalic port crates/horizon-client-core/src/text.rs::Text::italic gap: minor: sets true only; a null or false setter is not kept
Style.withUnderlined port crates/horizon-client-core/src/text.rs::Text::underlined gap: minor: sets true only; a null or false setter is not kept
Can't render this file because it contains an unexpected character in line 18 and column 126.
+2 -2
View File
@@ -33,8 +33,8 @@ ClientboundBossEventPacket.createUpdateStylePacket inline crates/horizon-client/
ClientboundBossEventPacket.createUpdatePropertiesPacket inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events UpdateProperties when darken, boss music or fog differ
ClientboundBossEventPacket.encodeProperties port crates/horizon-protocol/src/packets/play.rs::boss_flags the three flag bits (darken, music, fog)
ClientboundBossEventPacket.AddOperation.dispatch port crates/horizon-client/src/boss_overlay.rs::BossOverlay::apply
ClientboundChunksBiomesPacket.ChunkBiomeData.calculateChunkSize missing minor: biome-only chunk updates are never sent; no ChunksBiomes packet in ids.rs
ClientboundChunksBiomesPacket.ChunkBiomeData.extractChunkData missing minor: biome-only chunk updates are never sent; no ChunksBiomes packet in ids.rs
ClientboundChunksBiomesPacket.ChunkBiomeData.calculateChunkSize inline crates/horizon-protocol/src/packets/play.rs::ChunkBiomeData::from_biomes the buffer grows as each section's container is written, so no size is computed or prefixed on the wire
ClientboundChunksBiomesPacket.ChunkBiomeData.extractChunkData port crates/horizon-protocol/src/packets/play.rs::ChunkBiomeData::from_biomes the column's sections' biome containers written one after another into the buffer
ClientboundCommandSuggestionsPacket.toSuggestions port crates/horizon-client/src/game/remote.rs::Game::handle_command_suggestions start, length and tooltip become the suggestion range
ClientboundCommandsPacket.validateEntries missing minor: an impossible command tree is not rejected; from_packet skips bad child indexes
ClientboundCommandsPacket.enumerateNodes port crates/horizon-client/src/lan/commands.rs::CommandTree::packet_with breadth-first node ids on the LAN host
1 IdDispatchCodec.decode skip:java codec dispatch; packet_enum! matches the id in packets/mod.rs
33 ClientboundBossEventPacket.createUpdatePropertiesPacket inline crates/horizon-client/src/game/bossbars.rs::Game::diff_boss_events UpdateProperties when darken, boss music or fog differ
34 ClientboundBossEventPacket.encodeProperties port crates/horizon-protocol/src/packets/play.rs::boss_flags the three flag bits (darken, music, fog)
35 ClientboundBossEventPacket.AddOperation.dispatch port crates/horizon-client/src/boss_overlay.rs::BossOverlay::apply
36 ClientboundChunksBiomesPacket.ChunkBiomeData.calculateChunkSize missing inline crates/horizon-protocol/src/packets/play.rs::ChunkBiomeData::from_biomes minor: biome-only chunk updates are never sent; no ChunksBiomes packet in ids.rs the buffer grows as each section's container is written, so no size is computed or prefixed on the wire
37 ClientboundChunksBiomesPacket.ChunkBiomeData.extractChunkData missing port crates/horizon-protocol/src/packets/play.rs::ChunkBiomeData::from_biomes minor: biome-only chunk updates are never sent; no ChunksBiomes packet in ids.rs the column's sections' biome containers written one after another into the buffer
38 ClientboundCommandSuggestionsPacket.toSuggestions port crates/horizon-client/src/game/remote.rs::Game::handle_command_suggestions start, length and tooltip become the suggestion range
39 ClientboundCommandsPacket.validateEntries missing minor: an impossible command tree is not rejected; from_packet skips bad child indexes
40 ClientboundCommandsPacket.enumerateNodes port crates/horizon-client/src/lan/commands.rs::CommandTree::packet_with breadth-first node ids on the LAN host
+14 -13
View File
@@ -2,9 +2,9 @@ LinkFSPath skip:other Java path of the in-memory link filesystem; Horizon links
LinkFSProvider skip:other Java NIO provider of the link filesystem; Horizon reads host folders and zips directly
LinkFileSystem skip:other link filesystem and its getPath; Horizon builds overlay folders instead (packs.rs::build_overlay)
LinkFileSystem.Builder skip:other builder of the link filesystem tree; no equivalent, see packs.rs::build_overlay
PackFormat.fullCodec skip:java codec for supported_formats; packs.rs::compatible reads the same array
PackFormat.fullCodec port crates/horizon-client-render/src/packs.rs::format_codec supported_formats / pack_format as an int, or a list of 1 to 256 non-negative ints whose second is the minor
PackFormat.validateHolderList inline crates/horizon-client-render/src/packs.rs::overlays_of gap: minor: a broken overlay entry is dropped alone; vanilla rejects the whole overlays list
PackFormat.lastPreMinorVersion inline crates/horizon-client-render/src/packs.rs::last_pre_minor the cut-off the old-format validator compares against
PackFormat.lastPreMinorVersion port crates/horizon-client-render/src/packs.rs::last_pre_minor the last major without a minor version, 81 for data packs and 64 for resource packs, which the old-format validator compares against
PackFormat.packCodec skip:java codec wiring; the validation it runs is missing (IntermediaryFormat.validate)
PackFormat.of skip:java constructor; Horizon keeps (major, minor) tuples (packs.rs::RESOURCE_FORMAT)
PackFormat.compareTo skip:java ordering; Horizon compares the (major, minor) tuples directly (packs.rs::compatibility)
@@ -35,7 +35,7 @@ PackRepository.isAbleToClearAnyPack port crates/horizon-client-render/src/packs.
PackRepository.addPack inline crates/horizon-client-render/src/packs.rs::SelectionModel::activate gap: minor: vanilla appends by id with no position rules
PackRepository.removePack inline crates/horizon-client-render/src/packs.rs::SelectionModel::activate gap: minor: vanilla removes by id even when the pack is required; the screen never offers that
PackRepository.getAvailablePacks inline crates/horizon-client-render/src/packs.rs::SelectionModel::rebuild rebuild also adds required packs (vanilla and active downloaded server packs from Game::pack_infos)
PackRepository.getRequestedFeatureFlags missing minor: no pack feature flags are read, so a pack's requested features are never checked
PackRepository.getRequestedFeatureFlags missing no pack's `feature_flags` are read from its metadata: the only feature list is the world's own `enabled_features` (`world_loading.rs`), which the game rules, the datapack function filter and the command dispatcher read
PackSource.decorateWithSource port crates/horizon-client-render/src/pack_screen.rs::PackInfo::extended_description source label after the pack name (built-in, world, server)
ServerPacksSource.createVanillaPackSource port crates/horizon-client-render/src/datapack.rs::DataPack::builtin the built-in vanilla data source (id vanilla), used by functions.rs
ServerPacksSource.createPackRepository port crates/horizon-client-render/src/datapack.rs::discover gap: minor: finds world datapacks; the enable list is kept by functions.rs::reload_datapacks
@@ -91,6 +91,7 @@ PermissionSetUnion.hasPermission skip:other no unions; a sender has one level
PermissionSetUnion.union skip:other no unions; a sender has one level
PermissionSetUnion.ensureNoUnionsWithinUnions skip:java constructor guard against nested unions
PermissionTypes.bootstrap skip:data registry bootstrap of the permission codecs
BanListEntry.<init> port crates/horizon-server-files/src/user_lists.rs::BanEntry::new both vanilla constructors land here: a missing source becomes (Unknown); gap: minor: created is stamped with now instead of being passed, and a file's stored created date is kept by fields() through from_json
BanListEntry.equals port crates/horizon-server-files/src/user_lists.rs::BanEntry::eq same source, expiry, reason and user
BanListEntry.hashCode skip:java hash for map keys; bans are keyed by uuid or ip string
CachedUserNameToIdResolver.lookupGameProfile port crates/horizon-server-files/src/user_cache.rs::UserCache::get_by_name cache first, then the profile lookup, then offline if allowed
@@ -104,15 +105,15 @@ IpBanList.remove port crates/horizon-client/src/game/dedicated.rs::Game::console
IpBanList.clear skip:server only the JSON-RPC ban API calls clear (MinecraftBanListServiceImpl); Horizon has no JSON-RPC server
IpBanListEntry.createIpInfo port crates/horizon-server-files/src/user_lists.rs::BanEntry::from_json reads the ip field of an entry
IpBanListEntry.serialize port crates/horizon-server-files/src/user_lists.rs::BanEntry::serialize writes the ip field and the ban fields
OldUsersConverter.readOldListFormat missing minor: reads the pre-1.7 flat ban and op lists; Horizon reads only its json files
OldUsersConverter.lookupPlayers missing minor: name to uuid lookup for the old list conversion
OldUsersConverter.convertUserBanlist missing minor: converts an old banned-players.txt when the server starts
OldUsersConverter.convertIpBanlist missing minor: converts an old banned-ips.txt when the server starts
OldUsersConverter.convertOpsList missing minor: converts an old ops.txt when the server starts
OldUsersConverter.convertWhiteList missing minor: converts an old whitelist.txt when the server starts
OldUsersConverter.convertMobOwnerIfNecessary missing minor: name owners on old tamed mobs are not turned into uuids; datafix handles OwnerUUID only
OldUsersConverter.convertPlayers missing minor: old world/players .dat files are not moved to playerdata
OldUsersConverter.readOldListFormat missing the server reads `ops.json`, `whitelist.json` and `banned-players.json` (`game/dedicated.rs:439-441`); the pre-1.7 text lists have no reader
OldUsersConverter.lookupPlayers missing part of the old list conversion, which has no counterpart: names are resolved by the json lists' own uuids
OldUsersConverter.convertUserBanlist missing no conversion of an old `banned-players.txt`: the ban list is `banned-players.json` (`game/dedicated.rs:441`)
OldUsersConverter.convertIpBanlist missing no conversion of an old `banned-ips.txt`
OldUsersConverter.convertOpsList missing no conversion of an old `ops.txt`: the op list is `ops.json` (`game/dedicated.rs:439`)
OldUsersConverter.convertWhiteList missing no conversion of an old `whitelist.txt`: the whitelist is `whitelist.json` (`game/dedicated.rs:440`)
OldUsersConverter.convertMobOwnerIfNecessary missing a name owner on an old tamed mob is not turned into a uuid; the datafix handles `OwnerUUID` only
OldUsersConverter.convertPlayers missing old `world/players/*.dat` files are not moved into `playerdata`
OldUsersConverter.ensureDirectoryExists skip:other helper of the file moves, which Horizon does not do
OldUsersConverter.areOldUserlistsRemoved missing minor: no start-up refusal while old list files remain
OldUsersConverter.renameOldFile missing minor: converted files are not renamed to .converted
OldUsersConverter.areOldUserlistsRemoved missing the server does not refuse to start while old list files are still there
OldUsersConverter.renameOldFile missing nothing renames a converted file to `.converted`
OldUsersConverter.parseDate port crates/horizon-server-files/src/user_lists.rs::parse_date parses the ban date format
1 LinkFSPath skip:other Java path of the in-memory link filesystem; Horizon links host paths (packs.rs::link)
2 LinkFSProvider skip:other Java NIO provider of the link filesystem; Horizon reads host folders and zips directly
3 LinkFileSystem skip:other link filesystem and its getPath; Horizon builds overlay folders instead (packs.rs::build_overlay)
4 LinkFileSystem.Builder skip:other builder of the link filesystem tree; no equivalent, see packs.rs::build_overlay
5 PackFormat.fullCodec skip:java port crates/horizon-client-render/src/packs.rs::format_codec codec for supported_formats; packs.rs::compatible reads the same array supported_formats / pack_format as an int, or a list of 1 to 256 non-negative ints whose second is the minor
6 PackFormat.validateHolderList inline crates/horizon-client-render/src/packs.rs::overlays_of gap: minor: a broken overlay entry is dropped alone; vanilla rejects the whole overlays list
7 PackFormat.lastPreMinorVersion inline port crates/horizon-client-render/src/packs.rs::last_pre_minor the cut-off the old-format validator compares against the last major without a minor version, 81 for data packs and 64 for resource packs, which the old-format validator compares against
8 PackFormat.packCodec skip:java codec wiring; the validation it runs is missing (IntermediaryFormat.validate)
9 PackFormat.of skip:java constructor; Horizon keeps (major, minor) tuples (packs.rs::RESOURCE_FORMAT)
10 PackFormat.compareTo skip:java ordering; Horizon compares the (major, minor) tuples directly (packs.rs::compatibility)
35 PackRepository.addPack inline crates/horizon-client-render/src/packs.rs::SelectionModel::activate gap: minor: vanilla appends by id with no position rules
36 PackRepository.removePack inline crates/horizon-client-render/src/packs.rs::SelectionModel::activate gap: minor: vanilla removes by id even when the pack is required; the screen never offers that
37 PackRepository.getAvailablePacks inline crates/horizon-client-render/src/packs.rs::SelectionModel::rebuild rebuild also adds required packs (vanilla and active downloaded server packs from Game::pack_infos)
38 PackRepository.getRequestedFeatureFlags missing minor: no pack feature flags are read, so a pack's requested features are never checked no pack's `feature_flags` are read from its metadata: the only feature list is the world's own `enabled_features` (`world_loading.rs`), which the game rules, the datapack function filter and the command dispatcher read
39 PackSource.decorateWithSource port crates/horizon-client-render/src/pack_screen.rs::PackInfo::extended_description source label after the pack name (built-in, world, server)
40 ServerPacksSource.createVanillaPackSource port crates/horizon-client-render/src/datapack.rs::DataPack::builtin the built-in vanilla data source (id vanilla), used by functions.rs
41 ServerPacksSource.createPackRepository port crates/horizon-client-render/src/datapack.rs::discover gap: minor: finds world datapacks; the enable list is kept by functions.rs::reload_datapacks
91 PermissionSetUnion.union skip:other no unions; a sender has one level
92 PermissionSetUnion.ensureNoUnionsWithinUnions skip:java constructor guard against nested unions
93 PermissionTypes.bootstrap skip:data registry bootstrap of the permission codecs
94 BanListEntry.<init> port crates/horizon-server-files/src/user_lists.rs::BanEntry::new both vanilla constructors land here: a missing source becomes (Unknown); gap: minor: created is stamped with now instead of being passed, and a file's stored created date is kept by fields() through from_json
95 BanListEntry.equals port crates/horizon-server-files/src/user_lists.rs::BanEntry::eq same source, expiry, reason and user
96 BanListEntry.hashCode skip:java hash for map keys; bans are keyed by uuid or ip string
97 CachedUserNameToIdResolver.lookupGameProfile port crates/horizon-server-files/src/user_cache.rs::UserCache::get_by_name cache first, then the profile lookup, then offline if allowed
105 IpBanList.clear skip:server only the JSON-RPC ban API calls clear (MinecraftBanListServiceImpl); Horizon has no JSON-RPC server
106 IpBanListEntry.createIpInfo port crates/horizon-server-files/src/user_lists.rs::BanEntry::from_json reads the ip field of an entry
107 IpBanListEntry.serialize port crates/horizon-server-files/src/user_lists.rs::BanEntry::serialize writes the ip field and the ban fields
108 OldUsersConverter.readOldListFormat missing minor: reads the pre-1.7 flat ban and op lists; Horizon reads only its json files the server reads `ops.json`, `whitelist.json` and `banned-players.json` (`game/dedicated.rs:439-441`); the pre-1.7 text lists have no reader
109 OldUsersConverter.lookupPlayers missing minor: name to uuid lookup for the old list conversion part of the old list conversion, which has no counterpart: names are resolved by the json lists' own uuids
110 OldUsersConverter.convertUserBanlist missing minor: converts an old banned-players.txt when the server starts no conversion of an old `banned-players.txt`: the ban list is `banned-players.json` (`game/dedicated.rs:441`)
111 OldUsersConverter.convertIpBanlist missing minor: converts an old banned-ips.txt when the server starts no conversion of an old `banned-ips.txt`
112 OldUsersConverter.convertOpsList missing minor: converts an old ops.txt when the server starts no conversion of an old `ops.txt`: the op list is `ops.json` (`game/dedicated.rs:439`)
113 OldUsersConverter.convertWhiteList missing minor: converts an old whitelist.txt when the server starts no conversion of an old `whitelist.txt`: the whitelist is `whitelist.json` (`game/dedicated.rs:440`)
114 OldUsersConverter.convertMobOwnerIfNecessary missing minor: name owners on old tamed mobs are not turned into uuids; datafix handles OwnerUUID only a name owner on an old tamed mob is not turned into a uuid; the datafix handles `OwnerUUID` only
115 OldUsersConverter.convertPlayers missing minor: old world/players .dat files are not moved to playerdata old `world/players/*.dat` files are not moved into `playerdata`
116 OldUsersConverter.ensureDirectoryExists skip:other helper of the file moves, which Horizon does not do
117 OldUsersConverter.areOldUserlistsRemoved missing minor: no start-up refusal while old list files remain the server does not refuse to start while old list files are still there
118 OldUsersConverter.renameOldFile missing minor: converted files are not renamed to .converted nothing renames a converted file to `.converted`
119 OldUsersConverter.parseDate port crates/horizon-server-files/src/user_lists.rs::parse_date parses the ban date format
+17 -17
View File
@@ -28,8 +28,8 @@ CrudeIncrementalIntIdentityHashBiMap.copy skip:java identity bimap over registr
Crypt.generateKeyPair port crates/horizon-protocol/src/rsa.rs::RsaPrivateKey::generate RSA key pair with the bit size passed in; vanilla passes 1024
Crypt.digestData port crates/horizon-protocol/src/crypto.rs::server_hash returns the signed hex string; vanilla returns bytes and the caller converts them
Crypt.rsaStringToKey port crates/horizon-client-render/src/auth.rs::pem_der the PEM armour around the services' keys
Crypt.rsaPublicKeyToString missing minor: PEM writer for the account chat public key; Horizon has no PEM writer
Crypt.pemRsaPrivateKeyToString missing minor: PEM writer for the account chat private key; Horizon has no PEM writer
Crypt.rsaPublicKeyToString missing the writer half of the key-pair codec Crypt's static initialiser builds (its only caller); Horizon reads a PEM key pair (auth.rs::pem_der, parse_certificates) and writes none
Crypt.pemRsaPrivateKeyToString missing the writer half of the key-pair codec Crypt's static initialiser builds (its only caller); Horizon reads a PEM key pair (auth.rs::pem_der, parse_certificates) and writes none
Crypt.byteToPrivateKey port crates/horizon-protocol/src/rsa.rs::RsaPrivateKey::from_der the account chat key pair's PEM key is decoded and read as PKCS8EncodedKeySpec (a bare PKCS#1 key too) in auth.rs::parse_certificates
Crypt.byteToPublicKey port crates/horizon-protocol/src/rsa.rs::RsaPublicKey::from_der X.509 SubjectPublicKeyInfo decode
Crypt.decryptByteToSecretKey port crates/horizon-protocol/src/rsa.rs::RsaPrivateKey::decrypt RSA PKCS#1 v1.5 decrypt of the shared secret; the bytes are used as the AES key as given
@@ -63,17 +63,17 @@ DependencySorter.addDependencyIfNotCyclic inline crates/horizon-world/src/worldg
DependencySorter.orderByDependencies inline crates/horizon-world/src/worldgen/tags.rs::TagDefinitions::build required edges first, then optional ones, then the visit order
DirectoryLock.create port crates/horizon-client/src/save.rs::SessionLock::acquire exclusive file lock on session.lock with the two-byte snowman content
DirectoryLock.isLocked port crates/horizon-client/src/save.rs::SessionLock::is_locked trying for the world's session.lock tells a locked world; title.rs uses it to flag the entry
Ease.inBack missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.inElastic missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.inExpo missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.inOutBounce missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.inOutCirc missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.inOutQuad missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.inOutQuart missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.inOutQuint missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.outBounce missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.outElastic missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.outExpo missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only
Ease.inBack missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.inElastic missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.inExpo missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.inOutBounce missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.inOutCirc missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.inOutQuad missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.inOutQuart missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.inOutQuint missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.outBounce missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.outElastic missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
Ease.outExpo missing a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
EasingType.cubicBezier inline crates/horizon-client-render/src/environment.rs::Environment::load_dimension reads the four control points of a cubic_bezier ease; gap: minor: no range check, see CubicBezierControls.validate
EasingType.CubicBezier.curveFromControls inline crates/horizon-client-render/src/environment.rs::Easing::apply the curve coefficients closure
EasingType.CubicBezier.solveT inline crates/horizon-client-render/src/environment.rs::Easing::apply Newton steps with the 1e-5 tolerance
@@ -82,7 +82,7 @@ EasingType.CubicBezier.equals skip:java value equality
EasingType.CubicBezier.toString skip:debug debug string
EasingType.CubicBezier.CubicCurve.sample inline crates/horizon-client-render/src/environment.rs::Easing::apply the sample closure
EasingType.CubicBezier.CubicCurve.sampleGradient inline crates/horizon-client-render/src/environment.rs::Easing::apply the gradient closure
EasingType.CubicBezierControls.validate missing minor: vanilla rejects control points outside [0;1] when a timeline loads; Horizon takes any value
EasingType.CubicBezierControls.validate missing vanilla rejects control points outside [0;1] when a timeline loads; environment.rs::Environment::load_dimension takes the four numbers as given
EncoderCache.Key.equals skip:java cache key equality
EncoderCache.Key.hashCode skip:java cache key hashing
ExceptionCollector.add skip:java suppressed exception collector
@@ -117,7 +117,7 @@ FastBufferedInputStream.skip skip:java buffered stream helper; Rust readers do
FastBufferedInputStream.fill skip:java buffered stream helper; Rust readers do their own buffering
FileSystemUtil.safeGetPath skip:other opens a jar or zip filesystem for a URI; Horizon reads archives with the zip crate
FileUtil.sanitizeName port crates/horizon-client/src/title.rs::Title::target_folder gap: minor: replaces every non-alphanumeric character with _; vanilla only replaces illegal file characters, dots, slashes and quotes
FileUtil.isPathPortable skip:other portable path check; no caller in the 26.3 source
FileUtil.isPathPortable port crates/horizon-client/src/game/structure_blocks.rs::portable_path the structure path's check: no empty, `.` or `..` segment; vanilla also tests the part's characters (isPathPartPortable)
FileUtil.decomposePath skip:data resource path handling; Horizon reads its data from tables and files
FileUtil.resolvePath skip:data resource path handling; Horizon reads its data from tables and files
FileUtil.isValidPathSegment skip:data resource path handling; Horizon reads its data from tables and files
@@ -127,8 +127,8 @@ FileUtil.isEmptyPath skip:data resource path handling; Horizon reads its data f
FileZipper.add skip:debug profiling report archive (Minecraft.archiveProfilingReport, PerfCommand); Horizon writes no zip report
FileZipper.close skip:debug profiling report archive (Minecraft.archiveProfilingReport, PerfCommand); Horizon writes no zip report
FormattedCharSequence.forward port crates/horizon-client-core/src/text.rs::Text::styled styled text object; the per-character walk is Text::chars
FormattedCharSequence.backward skip:other two-argument reverse walk; no caller in the 26.3 source (SubStringSource uses the three-argument form)
FormattedCharSequence.decorateOutput missing minor: char-code modifier for reversed text, used only for right-to-left languages; Horizon has no right-to-left layout
FormattedCharSequence.backward skip:other the reverse walk SubStringSource.substring does, which is skip:ui (the HUD draws each span with its own style)
FormattedCharSequence.decorateOutput inline crates/horizon-client-render/src/bidi.rs::reorder the mirror a right-to-left character is drawn as; vanilla's sink mirrors every code point when the language reads right to left, here only the code points in a right-to-left run
FormattedCharSequence.composite port crates/horizon-client-core/src/text.rs::Text::empty zero parts give the empty sequence
FormattedCharSequence.fromPair port crates/horizon-client-core/src/text.rs::Text::append concatenation of two sequences
FutureChain.append skip:other ordering helper for the async chat-signature checks in ServerGamePacketListenerImpl; a scheduling wrapper, no game behaviour of its own
1 BinaryAnimator.tick port crates/horizon-client/src/mobs/axolotl.rs::Animation::tick one axolotl Animation holds the four animators; old/value pair and the clamp to 10 as vanilla
28 Crypt.generateKeyPair port crates/horizon-protocol/src/rsa.rs::RsaPrivateKey::generate RSA key pair with the bit size passed in; vanilla passes 1024
29 Crypt.digestData port crates/horizon-protocol/src/crypto.rs::server_hash returns the signed hex string; vanilla returns bytes and the caller converts them
30 Crypt.rsaStringToKey port crates/horizon-client-render/src/auth.rs::pem_der the PEM armour around the services' keys
31 Crypt.rsaPublicKeyToString missing minor: PEM writer for the account chat public key; Horizon has no PEM writer the writer half of the key-pair codec Crypt's static initialiser builds (its only caller); Horizon reads a PEM key pair (auth.rs::pem_der, parse_certificates) and writes none
32 Crypt.pemRsaPrivateKeyToString missing minor: PEM writer for the account chat private key; Horizon has no PEM writer the writer half of the key-pair codec Crypt's static initialiser builds (its only caller); Horizon reads a PEM key pair (auth.rs::pem_der, parse_certificates) and writes none
33 Crypt.byteToPrivateKey port crates/horizon-protocol/src/rsa.rs::RsaPrivateKey::from_der the account chat key pair's PEM key is decoded and read as PKCS8EncodedKeySpec (a bare PKCS#1 key too) in auth.rs::parse_certificates
34 Crypt.byteToPublicKey port crates/horizon-protocol/src/rsa.rs::RsaPublicKey::from_der X.509 SubjectPublicKeyInfo decode
35 Crypt.decryptByteToSecretKey port crates/horizon-protocol/src/rsa.rs::RsaPrivateKey::decrypt RSA PKCS#1 v1.5 decrypt of the shared secret; the bytes are used as the AES key as given
63 DependencySorter.orderByDependencies inline crates/horizon-world/src/worldgen/tags.rs::TagDefinitions::build required edges first, then optional ones, then the visit order
64 DirectoryLock.create port crates/horizon-client/src/save.rs::SessionLock::acquire exclusive file lock on session.lock with the two-byte snowman content
65 DirectoryLock.isLocked port crates/horizon-client/src/save.rs::SessionLock::is_locked trying for the world's session.lock tells a locked world; title.rs uses it to flag the entry
66 Ease.inBack missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
67 Ease.inElastic missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
68 Ease.inExpo missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
69 Ease.inOutBounce missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
70 Ease.inOutCirc missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
71 Ease.inOutQuad missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
72 Ease.inOutQuart missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
73 Ease.inOutQuint missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
74 Ease.outBounce missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
75 Ease.outElastic missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
76 Ease.outExpo missing minor: named easing reached only through the EasingType registry; vanilla timeline data uses cubic_bezier and constant only a named easing only a datapack's timeline can reach: the four shipped timelines ask for constant, cubic_bezier or no ease, and environment.rs::Environment::load_dimension reads those two
77 EasingType.cubicBezier inline crates/horizon-client-render/src/environment.rs::Environment::load_dimension reads the four control points of a cubic_bezier ease; gap: minor: no range check, see CubicBezierControls.validate
78 EasingType.CubicBezier.curveFromControls inline crates/horizon-client-render/src/environment.rs::Easing::apply the curve coefficients closure
79 EasingType.CubicBezier.solveT inline crates/horizon-client-render/src/environment.rs::Easing::apply Newton steps with the 1e-5 tolerance
82 EasingType.CubicBezier.toString skip:debug debug string
83 EasingType.CubicBezier.CubicCurve.sample inline crates/horizon-client-render/src/environment.rs::Easing::apply the sample closure
84 EasingType.CubicBezier.CubicCurve.sampleGradient inline crates/horizon-client-render/src/environment.rs::Easing::apply the gradient closure
85 EasingType.CubicBezierControls.validate missing minor: vanilla rejects control points outside [0;1] when a timeline loads; Horizon takes any value vanilla rejects control points outside [0;1] when a timeline loads; environment.rs::Environment::load_dimension takes the four numbers as given
86 EncoderCache.Key.equals skip:java cache key equality
87 EncoderCache.Key.hashCode skip:java cache key hashing
88 ExceptionCollector.add skip:java suppressed exception collector
117 FastBufferedInputStream.fill skip:java buffered stream helper; Rust readers do their own buffering
118 FileSystemUtil.safeGetPath skip:other opens a jar or zip filesystem for a URI; Horizon reads archives with the zip crate
119 FileUtil.sanitizeName port crates/horizon-client/src/title.rs::Title::target_folder gap: minor: replaces every non-alphanumeric character with _; vanilla only replaces illegal file characters, dots, slashes and quotes
120 FileUtil.isPathPortable skip:other port crates/horizon-client/src/game/structure_blocks.rs::portable_path portable path check; no caller in the 26.3 source the structure path's check: no empty, `.` or `..` segment; vanilla also tests the part's characters (isPathPartPortable)
121 FileUtil.decomposePath skip:data resource path handling; Horizon reads its data from tables and files
122 FileUtil.resolvePath skip:data resource path handling; Horizon reads its data from tables and files
123 FileUtil.isValidPathSegment skip:data resource path handling; Horizon reads its data from tables and files
127 FileZipper.add skip:debug profiling report archive (Minecraft.archiveProfilingReport, PerfCommand); Horizon writes no zip report
128 FileZipper.close skip:debug profiling report archive (Minecraft.archiveProfilingReport, PerfCommand); Horizon writes no zip report
129 FormattedCharSequence.forward port crates/horizon-client-core/src/text.rs::Text::styled styled text object; the per-character walk is Text::chars
130 FormattedCharSequence.backward skip:other two-argument reverse walk; no caller in the 26.3 source (SubStringSource uses the three-argument form) the reverse walk SubStringSource.substring does, which is skip:ui (the HUD draws each span with its own style)
131 FormattedCharSequence.decorateOutput missing inline crates/horizon-client-render/src/bidi.rs::reorder minor: char-code modifier for reversed text, used only for right-to-left languages; Horizon has no right-to-left layout the mirror a right-to-left character is drawn as; vanilla's sink mirrors every code point when the language reads right to left, here only the code points in a right-to-left run
132 FormattedCharSequence.composite port crates/horizon-client-core/src/text.rs::Text::empty zero parts give the empty sequence
133 FormattedCharSequence.fromPair port crates/horizon-client-core/src/text.rs::Text::append concatenation of two sequences
134 FutureChain.append skip:other ordering helper for the async chat-signature checks in ServerGamePacketListenerImpl; a scheduling wrapper, no game behaviour of its own
+15
View File
@@ -77,6 +77,20 @@ def mobs(ox, oz):
return out
def glowing(ox, oz):
"""1000 glowing mobs (the outline post effect): the mobs scene's kinds and grid."""
out, i = [], 0
for layer in range(5):
for row in range(10):
for col in range(20):
kind = MOBS[i % len(MOBS)]
x, y, z = ox + 0.5 - 14.25 + 1.5 * col, FLOOR_Y + 1 + 2.5 * layer, oz + 8 + 2 * row
out.append(f"summon {kind} {num(x)} {num(y)} {num(z)} "
"{NoAI:1b,NoGravity:1b,PersistenceRequired:1b,Silent:1b,Glowing:1b}")
i += 1
return out
def eyes(ox, oz):
"""1000 mobs with glowing eyes (spiders, cave spiders, endermen): the mobs scene's grid."""
out, i = [], 0
@@ -207,6 +221,7 @@ STRESS = [
("stress_particles", particles),
("stress_held", held),
("stress_eyes", eyes),
("stress_glowing", glowing),
]
NAMES = [name for name, _ in STRESS]
+8
View File
@@ -68,6 +68,14 @@ def scenarios():
add('skeleton_day_burn', 120, ['mob minecraft:skeleton 0.5 -60 0.5 0 32 persistent'])
# NoAI only skips serverAiStep: Mob.aiStep's burnUndead still sets a NoAI zombie alight (#3430).
add('zombie_noai_day_burn', 120, ['mob minecraft:zombie 0.5 -60 0.5 0 33 persistent noai'])
# AgeableMob.aiStep runs with NoAI, including both signs and age-lock controls.
for species in ('pig', 'cow', 'horse', 'turtle'):
for age in (-2, 2):
for noai in (False, True):
for locked in (False, True):
flags = (' noai' if noai else '') + (' agelocked' if locked else '')
add(f'{species}_age_{age}_noai_{noai}_locked_{locked}', 4,
[f'mob minecraft:{species} 0.5 -60 0.5 0 34 persistent age={age}{flags}'])
# The wither (summoned, not built: no invulnerable spawn phase).
add('wither_player', 200, ['player 10.5 -60 0.5', 'mob minecraft:wither 0.5 -58 0.5 0 41 persistent'])
# A villager with a job site, a bed and a bell.
+13 -6
View File
@@ -1671,16 +1671,23 @@ mod tests {
"native_item_damaged: the let _ = (…); placeholder needs its dispatch call; run cargo run -p xtask -- mod-api"
);
assert_eq!(std::fs::read_to_string(&hooks).unwrap(), placeholder);
let fragment = root.join("tools/native-hook-fragments/native_item_damaged.json");
let shipped_fragment = std::fs::read_to_string(&fragment).unwrap();
let mut json: Value = serde_json::from_str(&shipped_fragment).unwrap();
json["hooks"][0].as_object_mut().unwrap().remove("mod_api");
std::fs::write(&fragment, json.to_string()).unwrap();
// A stamped hook lives in the catalog, not in a fragment.
let catalog_path = root.join("native-hooks.json");
let shipped_catalog = std::fs::read_to_string(&catalog_path).unwrap();
let mut json: Value = serde_json::from_str(&shipped_catalog).unwrap();
let hook = json["hooks"]
.as_array_mut()
.unwrap()
.iter_mut()
.find(|hook| hook["name"] == "item_damaged")
.unwrap();
hook.as_object_mut().unwrap().remove("mod_api").unwrap();
std::fs::write(&catalog_path, json.to_string()).unwrap();
assert_eq!(
super::generate(&root, false).unwrap_err(),
"hook item_damaged has no mod_api entry: add one"
);
std::fs::write(&fragment, shipped_fragment).unwrap();
std::fs::write(&catalog_path, shipped_catalog).unwrap();
super::generate(&root, false).unwrap();
// The generator writes LF; a Windows checkout (core.autocrlf) has CRLF.
let lf = |s: &str| s.replace("\r\n", "\n");