mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Sync item entities immediately after velocity jumps and slime bounces (#5080)
Co-Authored-By: Antigravity <[email protected]>
This commit is contained in:
1 parent
571ec1b76e
commit
b4980bc779
7 files changed
+71
No files matched your search
@@ -76,6 +76,9 @@ pub struct ItemEntity {
|
||||
/// `Entity.tickCount`: ticks since the item entered the level, which
|
||||
/// paces `ItemEntity.mergeWithNeighbours`.
|
||||
pub ticks: u32,
|
||||
/// `Entity.needsSync` / `syncPosition`: immediate `ServerEntity.sendChanges`
|
||||
/// after a bounce or velocity jump (`> 0.1` in `ItemEntity.tick`).
|
||||
pub needs_sync: bool,
|
||||
}
|
||||
/// Environment results needed by the entity owner, including lethal lava damage.
|
||||
#[derive(Debug)]
|
||||
@@ -232,6 +235,7 @@ impl ItemEntity {
|
||||
self.pickup_delay -= 1;
|
||||
}
|
||||
let (water_height, lava_height) = self.update_fluids(world, fluids);
|
||||
let old_velocity = self.velocity;
|
||||
let in_water = water_height > f64::from(0.1f32);
|
||||
if in_water {
|
||||
// ItemEntity.setUnderwaterMovement
|
||||
@@ -329,6 +333,9 @@ impl ItemEntity {
|
||||
&mut self.velocity,
|
||||
);
|
||||
}
|
||||
if self.velocity.subtract(old_velocity).length_squared() > 0.01 {
|
||||
self.needs_sync = true;
|
||||
}
|
||||
self.age += 1;
|
||||
// ItemEntity.tick refreshes immersion after moving.
|
||||
self.update_fluids(world, fluids);
|
||||
@@ -341,6 +348,7 @@ impl ItemEntity {
|
||||
pub fn piston_slime_bounce(&mut self, movement: Direction) {
|
||||
self.velocity = horizon_world::piston_push::bounce_velocity(movement, self.velocity);
|
||||
self.on_ground = false;
|
||||
self.needs_sync = true;
|
||||
}
|
||||
/// `HoneyBlock.entityInside` for the honey blocks this item's box is in
|
||||
/// (`Entity.checkInsideBlocks`, deflated by `1.0E-5`). Slide sounds and
|
||||
@@ -1163,6 +1171,7 @@ mod fire_environment_tests {
|
||||
pickup_delay: 0,
|
||||
bob_offset: 0.0,
|
||||
ticks: 0,
|
||||
needs_sync: false,
|
||||
};
|
||||
let mut wood = make("minecraft:oak_planks");
|
||||
let mut metal = make("minecraft:netherite_ingot");
|
||||
@@ -1260,6 +1269,7 @@ mod merge_tests {
|
||||
pickup_delay: 0,
|
||||
bob_offset: 0.0,
|
||||
ticks: age,
|
||||
needs_sync: false,
|
||||
}
|
||||
}
|
||||
fn max_stack(stack: &ItemStack) -> u32 {
|
||||
@@ -1533,6 +1543,7 @@ mod honey_slide_tests {
|
||||
pickup_delay: 0,
|
||||
bob_offset: 0.0,
|
||||
ticks: 10,
|
||||
needs_sync: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1660,6 +1671,7 @@ mod slime_bounce_tests {
|
||||
pickup_delay: 0,
|
||||
bob_offset: 0.0,
|
||||
ticks: 10,
|
||||
needs_sync: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1669,9 +1681,12 @@ mod slime_bounce_tests {
|
||||
item.piston_slime_bounce(Direction::Up);
|
||||
assert_eq!(item.velocity, Vec3::new(0.0, 1.0, 0.0));
|
||||
assert!(!item.on_ground);
|
||||
assert!(item.needs_sync);
|
||||
item.needs_sync = false;
|
||||
item.piston_slime_bounce(Direction::East);
|
||||
assert_eq!(item.velocity, Vec3::new(1.0, 1.0, 0.0));
|
||||
assert!(!item.on_ground);
|
||||
assert!(item.needs_sync);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1682,4 +1697,49 @@ mod slime_bounce_tests {
|
||||
assert!(bounced > 0.0);
|
||||
assert_eq!(bounced, 0.8 * 0.98);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_slime_bounce_marks_the_item_for_immediate_sync() {
|
||||
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 = crate::blocks::BlockFacts::load(&root, assets.registry).unwrap();
|
||||
let fluids = crate::motion::FluidResources::new(&assets, &facts).unwrap();
|
||||
let collision = crate::collisions::load(&root, assets.registry).unwrap();
|
||||
let mut world = World::test_scene(&assets, &render, 3).unwrap();
|
||||
let slime = assets
|
||||
.registry
|
||||
.default_state("minecraft:slime_block")
|
||||
.unwrap();
|
||||
for x in 19..=21 {
|
||||
for z in 19..=21 {
|
||||
world.set(x, 3, z, slime).unwrap();
|
||||
}
|
||||
}
|
||||
// ItemEntity.tick sets needsSync when getDeltaMovement().subtract(oldMovement).lengthSqr() > 0.01.
|
||||
let mut falling = grounded_item();
|
||||
falling.position = Vec3::new(20.5, 4.2, 20.5);
|
||||
falling.previous = falling.position;
|
||||
falling.velocity = Vec3::new(0.0, -0.5, 0.0);
|
||||
falling.on_ground = false;
|
||||
falling.age = 0;
|
||||
falling.id = 1;
|
||||
falling.tick(&world, assets.registry, &facts, &collision, false, &fluids);
|
||||
assert_eq!(falling.position.y, 4.0);
|
||||
assert!(falling.velocity.y > 0.0);
|
||||
assert!(falling.on_ground);
|
||||
assert!(falling.needs_sync);
|
||||
|
||||
// An idle tick where velocity only steps by -0.04 (0.04^2 = 0.0016 <= 0.01)
|
||||
// does not set needs_sync.
|
||||
falling.velocity = Vec3::ZERO;
|
||||
falling.needs_sync = false;
|
||||
falling.tick(&world, assets.registry, &facts, &collision, false, &fluids);
|
||||
assert!(!falling.needs_sync);
|
||||
}
|
||||
}
|
||||
@@ -243,6 +243,7 @@ mod tests {
|
||||
pickup_delay: 0,
|
||||
bob_offset: 0.0,
|
||||
ticks: 0,
|
||||
needs_sync: false,
|
||||
};
|
||||
let mut still = item.clone();
|
||||
assert!(still.tick(&world, assets.registry, &facts, &collision, false, &fluids));
|
||||
|
||||
@@ -1898,6 +1898,7 @@ pub fn item_entity_from_nbt(tag: &Tag, id: u32, bob_offset: f32) -> Option<ItemE
|
||||
pickup_delay: number(tag.get("PickupDelay")).unwrap_or(0).clamp(0, 32767) as u32,
|
||||
bob_offset,
|
||||
ticks: 0,
|
||||
needs_sync: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2848,6 +2849,7 @@ mod tests {
|
||||
pickup_delay: 10,
|
||||
ticks: 0,
|
||||
bob_offset: 0.0,
|
||||
needs_sync: false,
|
||||
};
|
||||
let tag = item_entity_to_nbt(&e, [1, 2, 3, 4], None);
|
||||
assert_eq!(kind(&tag).as_deref(), Some("minecraft:item"));
|
||||
|
||||
@@ -3206,6 +3206,7 @@ impl Game<'_> {
|
||||
pickup_delay,
|
||||
bob_offset: bob,
|
||||
ticks: 0,
|
||||
needs_sync: false,
|
||||
});
|
||||
}
|
||||
/// `Block.getDrops` for a block a player breaks: the block's loot table
|
||||
|
||||
@@ -1730,6 +1730,8 @@ impl Game<'_> {
|
||||
e.position = [d.position.x, d.position.y, d.position.z];
|
||||
e.on_ground = d.on_ground;
|
||||
e.velocity = [d.velocity.x, d.velocity.y, d.velocity.z];
|
||||
e.precise = d.needs_sync;
|
||||
e.needs_sync = d.needs_sync;
|
||||
// Entity.DATA_SHARED_FLAGS_ID: glowing 0x40 (the Glowing tag).
|
||||
if self.entity_glowing(d.id) {
|
||||
e.meta.push((0, serializer::BYTE, Value::Byte(0x40)));
|
||||
@@ -2021,6 +2023,9 @@ impl Game<'_> {
|
||||
for p in &mut self.creatures.projectiles {
|
||||
p.needs_sync = false;
|
||||
}
|
||||
for d in &mut self.dropped {
|
||||
d.needs_sync = false;
|
||||
}
|
||||
// `runBlockEvents`' broadcast precedes the tick's block changes
|
||||
// (`ChunkHolder.broadcastChanges` in `tick_level` below): the
|
||||
// guest re-runs a piston's `triggerEvent` while its level still
|
||||
|
||||
@@ -1256,6 +1256,7 @@ mod item_column_tests {
|
||||
pickup_delay: 0,
|
||||
bob_offset: 0.0,
|
||||
ticks: 0,
|
||||
needs_sync: false,
|
||||
}
|
||||
}
|
||||
/// `hopper_suck`'s test: some item box meets the hopper's SUCK_AABB.
|
||||
|
||||
@@ -4698,6 +4698,7 @@ impl Game<'_> {
|
||||
pickup_delay: 0,
|
||||
bob_offset,
|
||||
ticks: 0,
|
||||
needs_sync: false,
|
||||
});
|
||||
}
|
||||
/// `Entity.lerpMotion` (and `recreateFromPacket`'s motion) for the
|
||||
|
||||
Reference in new issue
Block a user