From ecacefc5536ada1cb8e75fa4d1d1a9e909fa3726 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 11:42:44 +0100 Subject: [PATCH 01/20] Arrow Entity handling and damage. --- Cargo.lock | 16 +--- Cargo.toml | 4 - crates/hyperion/Cargo.toml | 3 +- .../hyperion/src/egress/sync_entity_state.rs | 80 ++++++++++++++----- crates/hyperion/src/lib.rs | 1 + crates/hyperion/src/simulation/event.rs | 14 ++++ .../lib.rs => hyperion/src/spatial/mod.rs} | 2 +- .../hyperion/src/storage/event/queue/mod.rs | 4 +- crates/hyperion/tests/spatial.rs | 3 +- crates/spatial/.gitignore | 1 - crates/spatial/Cargo.toml | 18 ----- crates/spatial/README.md | 1 - events/tag/Cargo.toml | 1 - events/tag/src/command/raycast.rs | 2 +- events/tag/src/lib.rs | 2 +- events/tag/src/module/bow.rs | 70 ++++++++++++++-- 16 files changed, 147 insertions(+), 75 deletions(-) rename crates/{spatial/src/lib.rs => hyperion/src/spatial/mod.rs} (99%) delete mode 100644 crates/spatial/.gitignore delete mode 100644 crates/spatial/Cargo.toml delete mode 100644 crates/spatial/README.md diff --git a/Cargo.lock b/Cargo.lock index fd6c7c89..0c21fc9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2737,6 +2737,7 @@ dependencies = [ "bitfield-struct", "bitvec", "bumpalo", + "bvh-region", "bytemuck", "byteorder", "bytes", @@ -2770,6 +2771,7 @@ dependencies = [ "ndarray", "no_denormals", "once_cell", + "ordered-float", "ouroboros", "parking_lot", "rayon", @@ -2781,7 +2783,6 @@ dependencies = [ "serde_json", "sha2", "simd-utils", - "spatial", "system-order", "thiserror 2.0.8", "tokio", @@ -5395,18 +5396,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spatial" -version = "0.1.0" -dependencies = [ - "bvh-region", - "flecs_ecs", - "geometry", - "hyperion", - "ordered-float", - "rayon", -] - [[package]] name = "spin" version = "0.9.8" @@ -5586,7 +5575,6 @@ dependencies = [ "rayon", "roaring", "rustc-hash 2.1.0", - "spatial", "tikv-jemallocator", "tracing", "tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml index 0c4eeaaa..1c1dab2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ members = [ 'crates/hyperion-text', 'crates/hyperion-utils', 'crates/simd-utils', - 'crates/spatial', 'crates/system-order', 'events/tag', 'tools/packet-inspector', @@ -228,9 +227,6 @@ version = '0.10.8' [workspace.dependencies.simd-utils] path = 'crates/simd-utils' -[workspace.dependencies.spatial] -path = 'crates/spatial' - [workspace.dependencies.syntect] default-features = false version = '5.2.0' diff --git a/crates/hyperion/Cargo.toml b/crates/hyperion/Cargo.toml index 23014917..f54fce0a 100644 --- a/crates/hyperion/Cargo.toml +++ b/crates/hyperion/Cargo.toml @@ -11,6 +11,7 @@ anyhow = { workspace = true } base64 = { workspace = true } bitfield-struct = { workspace = true } bitvec = { workspace = true } +bvh-region = { workspace = true } bumpalo = { workspace = true } bytemuck = { workspace = true } byteorder = { workspace = true } @@ -70,12 +71,12 @@ valence_protocol = { workspace = true } valence_registry = { workspace = true } valence_server = { workspace = true } valence_text = { workspace = true } +ordered-float = { workspace = true } [dev-dependencies] approx = { workspace = true } divan = { workspace = true } fastrand = { workspace = true } -spatial = { workspace = true } [lints] workspace = true diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index 6c24615b..bfa5253a 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -5,6 +5,7 @@ use flecs_ecs::prelude::*; use glam::Vec3; use hyperion_inventory::PlayerInventory; use hyperion_utils::EntityExt; +use itertools::Either; use tracing::{debug, error}; use valence_protocol::{ ByteAngle, RawBytes, VarInt, @@ -20,9 +21,12 @@ use crate::{ animation::ActiveAnimation, blocks::Blocks, entity_kind::EntityKind, + event, handlers::is_grounded, metadata::{MetadataChanges, get_and_clear_metadata}, }, + spatial::get_first_collision, + storage::Events, }; #[derive(Component)] @@ -413,7 +417,8 @@ impl Module for EntityStateSyncModule { return; } - let world = it.system().world(); + let system = it.system(); + let world = system.world(); let _entity = it.entity(row); if velocity.0 != Vec3::ZERO { @@ -424,34 +429,65 @@ impl Module for EntityStateSyncModule { let center = **position; - let ray = geometry::ray::Ray::new(center, velocity.0); + let distance = velocity.0.length(); - #[allow(clippy::excessive_nesting)] - world.get::<&mut Blocks>(|blocks| { - let Some(collision) = blocks.first_collision(ray) else { - // Drag (0.99 / 20.0) - // 1.0 - (0.99 / 20.0) * 0.05 - velocity.0 *= 0.997_525; + debug!("Creatign Ray"); - // Gravity (20 MPSS) - velocity.0.y -= 0.05; + let ray = geometry::ray::Ray::new(center, velocity.0) * distance; - // Terminal Velocity (100.0) - velocity.0 = velocity.0.clamp_length(0.0, 100.0); - return; - }; + debug!("ray = {ray:?}"); - debug!("collision = {collision:?}"); + let Some(collision) = get_first_collision(ray, &world) else { + // Drag (0.99 / 20.0) + // 1.0 - (0.99 / 20.0) * 0.05 + velocity.0 *= 0.997_525; - velocity.0 = Vec3::ZERO; + // Gravity (20 MPSS) + velocity.0.y -= 0.05; - // Set arrow position to the collision location - **position = collision.normal; + // Terminal Velocity (100.0) + velocity.0 = velocity.0.clamp_length(0.0, 100.0); + return; + }; - blocks - .set_block(collision.location, BlockState::DIRT) - .unwrap(); - }); + debug!("Collision: {collision:?}"); + + match collision { + Either::Left(entity) => { + let entity = entity.entity_view(world); + debug!("entity: {entity:?}"); + // send event + world.get::<&mut Events>(|events| events.push( + event::ProjectileEntityEvent { + client: *entity, + projectile: *_entity, + }, + &world + )); + } + Either::Right(collision) => { + debug!("block: {collision:?}"); + // send event + world.get::<&mut Events>(|events| events.push( + event::ProjectileBlockEvent { + collision: collision, + projectile: *_entity, + }, + &world + )); + } + } + + /* debug!("collision = {collision:?}"); + + velocity.0 = Vec3::ZERO; */ + + /* // Set arrow position to the collision location + **position = collision.normal; + + blocks + .set_block(collision.location, BlockState::DIRT) + .unwrap(); */ } }); diff --git a/crates/hyperion/src/lib.rs b/crates/hyperion/src/lib.rs index b1e800b3..97fbc349 100644 --- a/crates/hyperion/src/lib.rs +++ b/crates/hyperion/src/lib.rs @@ -88,6 +88,7 @@ pub mod ingress; pub mod net; pub mod simulation; pub mod storage; +pub mod spatial; /// Relationship for previous values #[derive(Component)] diff --git a/crates/hyperion/src/simulation/event.rs b/crates/hyperion/src/simulation/event.rs index ba561351..b8fc80a6 100644 --- a/crates/hyperion/src/simulation/event.rs +++ b/crates/hyperion/src/simulation/event.rs @@ -9,6 +9,8 @@ use valence_server::{ItemKind, entity::item_frame::ItemStack}; use crate::simulation::skin::PlayerSkin; +use super::blocks::RayCollision; + #[derive(Component, Default, Debug)] pub struct ItemDropEvent { pub item: ItemStack, @@ -146,3 +148,15 @@ pub struct ClientStatusEvent { pub client: Entity, pub status: ClientStatusCommand, } + +#[derive(Clone, Debug)] +pub struct ProjectileEntityEvent { + pub client: Entity, + pub projectile: Entity, +} + +#[derive(Clone, Debug)] +pub struct ProjectileBlockEvent { + pub collision: RayCollision, + pub projectile: Entity, +} diff --git a/crates/spatial/src/lib.rs b/crates/hyperion/src/spatial/mod.rs similarity index 99% rename from crates/spatial/src/lib.rs rename to crates/hyperion/src/spatial/mod.rs index 22970529..3e0da6cb 100644 --- a/crates/spatial/src/lib.rs +++ b/crates/hyperion/src/spatial/mod.rs @@ -7,7 +7,7 @@ use flecs_ecs::{ prelude::Module, }; use geometry::{aabb::Aabb, ray::Ray}; -use hyperion::{ +use super::{ egress::player_join::RayonWorldStages, glam::Vec3, simulation::{ diff --git a/crates/hyperion/src/storage/event/queue/mod.rs b/crates/hyperion/src/storage/event/queue/mod.rs index f29012ba..c37ed6c8 100644 --- a/crates/hyperion/src/storage/event/queue/mod.rs +++ b/crates/hyperion/src/storage/event/queue/mod.rs @@ -58,7 +58,9 @@ define_events! { event::SwingArm, event::ToggleDoor, event::ReleaseUseItem, - event::ClientStatusEvent + event::ClientStatusEvent, + event::ProjectileEntityEvent, + event::ProjectileBlockEvent, } pub trait ReducedLifetime { diff --git a/crates/hyperion/tests/spatial.rs b/crates/hyperion/tests/spatial.rs index 5b63b988..bb4cbd4c 100644 --- a/crates/hyperion/tests/spatial.rs +++ b/crates/hyperion/tests/spatial.rs @@ -12,8 +12,7 @@ use flecs_ecs::core::{QueryBuilderImpl, SystemAPI, World, WorldGet, flecs}; use geometry::{aabb::Aabb, ray::Ray}; use glam::Vec3; use hyperion::{ - HyperionCore, - simulation::{EntitySize, Position, entity_kind::EntityKind}, + simulation::{entity_kind::EntityKind, EntitySize, Position}, spatial, HyperionCore }; use spatial::{Spatial, SpatialIndex, SpatialModule}; diff --git a/crates/spatial/.gitignore b/crates/spatial/.gitignore deleted file mode 100644 index ea8c4bf7..00000000 --- a/crates/spatial/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/crates/spatial/Cargo.toml b/crates/spatial/Cargo.toml deleted file mode 100644 index 80950da9..00000000 --- a/crates/spatial/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "spatial" -version.workspace = true -edition.workspace = true -authors = ["Andrew Gazelka "] -readme = "README.md" -publish = false - -[dependencies] -bvh-region.workspace = true -flecs_ecs.workspace = true -geometry.workspace = true -hyperion.workspace = true -ordered-float.workspace = true -rayon.workspace = true - -[lints] -workspace = true diff --git a/crates/spatial/README.md b/crates/spatial/README.md deleted file mode 100644 index dbff859e..00000000 --- a/crates/spatial/README.md +++ /dev/null @@ -1 +0,0 @@ -# spatial \ No newline at end of file diff --git a/events/tag/Cargo.toml b/events/tag/Cargo.toml index 52a94f49..97d8c093 100644 --- a/events/tag/Cargo.toml +++ b/events/tag/Cargo.toml @@ -22,7 +22,6 @@ hyperion-utils = { workspace = true } rayon = { workspace = true } roaring = { workspace = true } rustc-hash = { workspace = true } -spatial = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } tracing-tracy = { workspace = true } diff --git a/events/tag/src/command/raycast.rs b/events/tag/src/command/raycast.rs index 8b566f4b..db575863 100644 --- a/events/tag/src/command/raycast.rs +++ b/events/tag/src/command/raycast.rs @@ -2,11 +2,11 @@ use clap::Parser; use flecs_ecs::core::{Entity, EntityView, EntityViewGet, WorldProvider}; use hyperion::{ glam::Vec3, + spatial::get_first_collision, simulation::{Pitch, Position, Yaw, entity_kind::EntityKind}, }; use hyperion_clap::{CommandPermission, MinecraftCommand}; use rayon::iter::Either; -use spatial::get_first_collision; use tracing::debug; #[derive(Parser, CommandPermission, Debug)] diff --git a/events/tag/src/lib.rs b/events/tag/src/lib.rs index f8e89676..2467f173 100644 --- a/events/tag/src/lib.rs +++ b/events/tag/src/lib.rs @@ -13,7 +13,7 @@ use module::{block::BlockModule, vanish::VanishModule}; mod module; use derive_more::{Deref, DerefMut}; -use hyperion::{glam::IVec3, simulation::Position}; +use hyperion::{glam::IVec3, simulation::Position, spatial}; use hyperion_rank_tree::Team; use module::{attack::AttackModule, level::LevelModule, regeneration::RegenerationModule}; use spatial::SpatialIndex; diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 822ed696..64c918c0 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -3,13 +3,9 @@ use flecs_ecs::{ prelude::*, }; use hyperion::{ - ItemKind, ItemStack, - glam::Vec3, - simulation::{ - Pitch, Position, Spawn, Uuid, Velocity, Yaw, bow::BowCharging, entity_kind::EntityKind, - event, get_direction_from_rotation, - }, - storage::EventQueue, + glam::Vec3, net::Compose, simulation::{ + bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, Pitch, Position, Spawn, Uuid, Velocity, Yaw + }, storage::{EventQueue, Events}, ItemKind, ItemStack }; use hyperion_inventory::PlayerInventory; use tracing::debug; @@ -17,8 +13,21 @@ use tracing::debug; #[derive(Component)] pub struct BowModule; +#[derive(Component)] +pub struct Owner { + pub entity: Entity, +} + +impl Owner { + pub fn new(entity: Entity) -> Self { + Self { entity } + } +} + impl Module for BowModule { fn module(world: &World) { + world.component::(); + system!( "handle_bow_release", world, @@ -26,6 +35,7 @@ impl Module for BowModule { ) .term_at(0u32) .singleton() + .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); @@ -103,10 +113,56 @@ impl Module for BowModule { .set(Velocity::new(velocity.x, velocity.y, velocity.z)) .set(Pitch::new(**pitch)) .set(Yaw::new(**yaw)) + //.set(Owner::new(*player)) .enqueue(Spawn); }, ); } }); + + system!( + "arrow_entity_hit", + world, + &mut EventQueue, + ) + .singleton() + .multi_threaded() + .kind::() + .each_iter(move |it, _, event_queue| { + let _system = it.system(); + let world = it.world(); + + for event in event_queue.drain() { + + debug!("arrow_entity_hit: {event:?}"); + event + .projectile + .entity_view(world) + .get::<&Owner>(| owner| { + debug!("Sending attack event"); + world.get::<&Events>(|events| { + events.push( + event::AttackEntity { + origin: owner.entity, + target: event.client, + damage: 1.0, + }, + &world, + ) + }); + + // Updating arrows in entity + debug!("Updating arrows in entity"); + event.client + .entity_view(world) + .get::<&mut ArrowsInEntity>(|arrows| { + arrows.0 += 1; + }); + + }); + + event.projectile.entity_view(world).destruct(); + } + }); } } From c604a533cf9a192d6d7602c9e7bdf6c1b7bdd242 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 17:39:00 +0100 Subject: [PATCH 02/20] Arrow hitting blocks and damaging entities --- .../hyperion/src/egress/sync_entity_state.rs | 9 +- crates/hyperion/src/simulation/blocks/mod.rs | 4 +- events/tag/src/module/bow.rs | 94 +++++++++++-------- 3 files changed, 56 insertions(+), 51 deletions(-) diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index bfa5253a..d644afa1 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -429,14 +429,11 @@ impl Module for EntityStateSyncModule { let center = **position; + // getting max distance let distance = velocity.0.length(); - debug!("Creatign Ray"); - let ray = geometry::ray::Ray::new(center, velocity.0) * distance; - debug!("ray = {ray:?}"); - let Some(collision) = get_first_collision(ray, &world) else { // Drag (0.99 / 20.0) // 1.0 - (0.99 / 20.0) * 0.05 @@ -450,12 +447,9 @@ impl Module for EntityStateSyncModule { return; }; - debug!("Collision: {collision:?}"); - match collision { Either::Left(entity) => { let entity = entity.entity_view(world); - debug!("entity: {entity:?}"); // send event world.get::<&mut Events>(|events| events.push( event::ProjectileEntityEvent { @@ -466,7 +460,6 @@ impl Module for EntityStateSyncModule { )); } Either::Right(collision) => { - debug!("block: {collision:?}"); // send event world.get::<&mut Events>(|events| events.push( event::ProjectileBlockEvent { diff --git a/crates/hyperion/src/simulation/blocks/mod.rs b/crates/hyperion/src/simulation/blocks/mod.rs index 5719bfc1..0f531a96 100644 --- a/crates/hyperion/src/simulation/blocks/mod.rs +++ b/crates/hyperion/src/simulation/blocks/mod.rs @@ -154,8 +154,8 @@ impl Blocks { continue; }; - let collision_point = ray.origin() + ray.direction() * min_dist.into_inner(); - let collision_normal = (collision_point - origin).normalize(); + let collision_normal = ray.origin() + ray.direction() * min_dist.into_inner(); + //let collision_normal = (collision_point - origin).normalize(); match &min { Some(current_min) => { diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 64c918c0..22dbac0d 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -3,9 +3,14 @@ use flecs_ecs::{ prelude::*, }; use hyperion::{ - glam::Vec3, net::Compose, simulation::{ - bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, Pitch, Position, Spawn, Uuid, Velocity, Yaw - }, storage::{EventQueue, Events}, ItemKind, ItemStack + ItemKind, ItemStack, + glam::Vec3, + net::Compose, + simulation::{ + Pitch, Position, Spawn, Uuid, Velocity, Yaw, bow::BowCharging, entity_kind::EntityKind, + event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, + }, + storage::{EventQueue, Events}, }; use hyperion_inventory::PlayerInventory; use tracing::debug; @@ -15,7 +20,7 @@ pub struct BowModule; #[derive(Component)] pub struct Owner { - pub entity: Entity, + entity: Entity, } impl Owner { @@ -33,9 +38,7 @@ impl Module for BowModule { world, &mut EventQueue, ) - .term_at(0u32) .singleton() - .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); @@ -87,8 +90,6 @@ impl Module for BowModule { }) .unwrap_or(0.0); - debug!("charge: {charge}"); - // Calculate the direction vector from the player's rotation let direction = get_direction_from_rotation(**yaw, **pitch); // Calculate the velocity of the arrow based on the charge (3.0 is max velocity) @@ -97,13 +98,6 @@ impl Module for BowModule { let spawn_pos = Vec3::new(position.x, position.y + 1.62, position.z) + direction * 0.5; - debug!( - "Arrow velocity: ({}, {}, {})", - velocity.x, velocity.y, velocity.z - ); - - debug!("Arrow Yaw: {}, Arrow Pitch: {}", **yaw, **pitch); - // Spawn arrow world .entity() @@ -113,7 +107,7 @@ impl Module for BowModule { .set(Velocity::new(velocity.x, velocity.y, velocity.z)) .set(Pitch::new(**pitch)) .set(Yaw::new(**yaw)) - //.set(Owner::new(*player)) + .set(Owner::new(*player)) .enqueue(Spawn); }, ); @@ -126,42 +120,60 @@ impl Module for BowModule { &mut EventQueue, ) .singleton() - .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); let world = it.world(); for event in event_queue.drain() { + let (damage, owner) = event + .projectile + .entity_view(world) + .get::<(&Velocity, &Owner)>(|(velocity, owner)| { + (velocity.0.length() * 2.0, owner.entity) + }); + event + .client + .entity_view(world) + .get::<&mut ArrowsInEntity>(|arrows| { + arrows.0 += 1; + }); + + // Destroy the arrow + event.projectile.entity_view(world).destruct(); - debug!("arrow_entity_hit: {event:?}"); + world.get::<&Events>(|events| { + events.push( + event::AttackEntity { + origin: owner, + target: event.client, + damage: damage, + }, + &world, + ); + }) + } + }); + + system!( + "arrow_block_hit", + world, + &mut EventQueue, + ) + .singleton() + .kind::() + .each_iter(move |it, _, event_queue| { + let _system = it.system(); + let world = it.world(); + + for event in event_queue.drain() { event .projectile .entity_view(world) - .get::<&Owner>(| owner| { - debug!("Sending attack event"); - world.get::<&Events>(|events| { - events.push( - event::AttackEntity { - origin: owner.entity, - target: event.client, - damage: 1.0, - }, - &world, - ) - }); - - // Updating arrows in entity - debug!("Updating arrows in entity"); - event.client - .entity_view(world) - .get::<&mut ArrowsInEntity>(|arrows| { - arrows.0 += 1; - }); - + .get::<(&mut Position, &mut Velocity)>(|(position, velocity)| { + velocity.0 = Vec3::ZERO; + **position = event.collision.normal; }); - - event.projectile.entity_view(world).destruct(); } }); } From 1bb1901971ce445235b5a8bf039876f5aec31279 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 18:35:06 +0100 Subject: [PATCH 03/20] Despawn Arrow and clamp velocity properly :) --- .../hyperion/src/egress/sync_entity_state.rs | 4 +-- events/tag/src/module/bow.rs | 30 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index d644afa1..ee15fd64 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -442,8 +442,8 @@ impl Module for EntityStateSyncModule { // Gravity (20 MPSS) velocity.0.y -= 0.05; - // Terminal Velocity (100.0) - velocity.0 = velocity.0.clamp_length(0.0, 100.0); + // Terminal Velocity max (100.0) + velocity.0 = velocity.0.clamp_length_max(100.0); return; }; diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 22dbac0d..a607d892 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -2,10 +2,12 @@ use flecs_ecs::{ core::{EntityViewGet, World}, prelude::*, }; +use glam::I16Vec2; use hyperion::{ ItemKind, ItemStack, glam::Vec3, net::Compose, + valence_protocol::packets::play, simulation::{ Pitch, Position, Spawn, Uuid, Velocity, Yaw, bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, @@ -13,7 +15,9 @@ use hyperion::{ storage::{EventQueue, Events}, }; use hyperion_inventory::PlayerInventory; +use hyperion_utils::EntityExt; use tracing::debug; +use valence_protocol::VarInt; #[derive(Component)] pub struct BowModule; @@ -117,21 +121,33 @@ impl Module for BowModule { system!( "arrow_entity_hit", world, + &Compose($), &mut EventQueue, ) .singleton() .kind::() - .each_iter(move |it, _, event_queue| { - let _system = it.system(); + .each_iter(move |it, _, (compose, event_queue)| { + let system = it.system(); let world = it.world(); for event in event_queue.drain() { - let (damage, owner) = event + let (damage, owner, chunk_pos) = event .projectile .entity_view(world) .get::<(&Velocity, &Owner)>(|(velocity, owner)| { - (velocity.0.length() * 2.0, owner.entity) + if owner.entity == event.client { + return (0.0, owner.entity, I16Vec2::ZERO); + } + let chunck_pos = event.client.entity_view(world).get::<&Position>(|pos| { + pos.to_chunk() + }); + (velocity.0.length() * 2.0, owner.entity, chunck_pos) }); + + if damage == 0.0 && owner == event.client { + continue; + } + event .client .entity_view(world) @@ -139,7 +155,11 @@ impl Module for BowModule { arrows.0 += 1; }); - // Destroy the arrow + let packet = play::EntitiesDestroyS2c { + entity_ids: vec![VarInt(event.projectile.minecraft_id() as i32)].into(), + }; + compose.broadcast_local(&packet, chunk_pos, system).send().unwrap(); + event.projectile.entity_view(world).destruct(); world.get::<&Events>(|events| { From 263adfd0b6d8b021b790514ad9e6ae127023fc4e Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 19:35:48 +0100 Subject: [PATCH 04/20] hand animations :thumbsup: --- crates/hyperion/src/simulation/animation.rs | 1 + crates/hyperion/src/simulation/handlers.rs | 13 ++++++------- events/tag/src/module/bow.rs | 12 +++--------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/crates/hyperion/src/simulation/animation.rs b/crates/hyperion/src/simulation/animation.rs index 42621cba..aadbf51d 100644 --- a/crates/hyperion/src/simulation/animation.rs +++ b/crates/hyperion/src/simulation/animation.rs @@ -6,6 +6,7 @@ use valence_protocol::{VarInt, packets::play::EntityAnimationS2c}; #[repr(u8)] pub enum Kind { SwingMainArm = 0, + UseItem = 1, LeaveBed = 2, SwingOffHand = 3, Critical = 4, diff --git a/crates/hyperion/src/simulation/handlers.rs b/crates/hyperion/src/simulation/handlers.rs index d0585912..d08c6d83 100644 --- a/crates/hyperion/src/simulation/handlers.rs +++ b/crates/hyperion/src/simulation/handlers.rs @@ -23,16 +23,11 @@ use valence_protocol::{ use valence_text::IntoText; use super::{ - ConfirmBlockSequences, EntitySize, Position, - animation::{self, ActiveAnimation}, - block_bounds, - blocks::Blocks, - bow::BowCharging, - event::ClientStatusEvent, + animation::{self, ActiveAnimation, Kind}, block_bounds, blocks::Blocks, bow::BowCharging, event::ClientStatusEvent, ConfirmBlockSequences, EntitySize, Position }; use crate::{ net::{Compose, ConnectionId, decoder::BorrowedPacketFrame}, - simulation::{Pitch, Yaw, aabb, event, event::PluginMessage, metadata::entity::Pose}, + simulation::{Pitch, Yaw, aabb, event, event::PluginMessage, metadata::entity::Pose, metadata::living_entity::HandStates}, storage::{ ClickSlotEvent, CommandCompletionRequest, Events, GlobalEventHandlers, InteractEvent, }, @@ -298,6 +293,8 @@ fn player_action(mut data: &[u8], query: &PacketSwitchQuery<'_>) -> anyhow::Resu item: query.inventory.get_cursor().item, }; + query.id.entity_view(query.world).set(HandStates::new(0)); + query.events.push(event, query.world); } action => bail!("unimplemented {action:?}"), @@ -364,6 +361,8 @@ pub fn player_interact_item( return; } entity.set(BowCharging::now()); + + entity.set(HandStates::new(1)); }); } } diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index a607d892..8ca3f10f 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -4,15 +4,9 @@ use flecs_ecs::{ }; use glam::I16Vec2; use hyperion::{ - ItemKind, ItemStack, - glam::Vec3, - net::Compose, - valence_protocol::packets::play, - simulation::{ - Pitch, Position, Spawn, Uuid, Velocity, Yaw, bow::BowCharging, entity_kind::EntityKind, - event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, - }, - storage::{EventQueue, Events}, + glam::Vec3, net::Compose, simulation::{ + bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::{ArrowsInEntity, HandStates}, Pitch, Position, Spawn, Uuid, Velocity, Yaw + }, storage::{EventQueue, Events}, valence_protocol::packets::play, ItemKind, ItemStack }; use hyperion_inventory::PlayerInventory; use hyperion_utils::EntityExt; From c5f07d8bea5cd79673b5326b2278d8ba85f54c2f Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 19:43:02 +0100 Subject: [PATCH 05/20] fix bow? --- events/tag/src/module/bow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 8ca3f10f..57987fc4 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -34,7 +34,7 @@ impl Module for BowModule { system!( "handle_bow_release", world, - &mut EventQueue, + &mut EventQueue($), ) .singleton() .kind::() From 973cf57d98880bb645df41e88d879f949afe2a1c Mon Sep 17 00:00:00 2001 From: e1pupper Date: Fri, 20 Dec 2024 01:32:39 +0100 Subject: [PATCH 06/20] first_collision rewrite. somewhat fixed arrow through floor --- .../hyperion/src/egress/sync_entity_state.rs | 2 + crates/hyperion/src/simulation/blocks/mod.rs | 87 +++++++------------ events/tag/src/module/bow.rs | 4 +- 3 files changed, 37 insertions(+), 56 deletions(-) diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index ee15fd64..5a468361 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -432,6 +432,8 @@ impl Module for EntityStateSyncModule { // getting max distance let distance = velocity.0.length(); + debug!("distance = {distance}"); + let ray = geometry::ray::Ray::new(center, velocity.0) * distance; let Some(collision) = get_first_collision(ray, &world) else { diff --git a/crates/hyperion/src/simulation/blocks/mod.rs b/crates/hyperion/src/simulation/blocks/mod.rs index 0f531a96..d5a138a9 100644 --- a/crates/hyperion/src/simulation/blocks/mod.rs +++ b/crates/hyperion/src/simulation/blocks/mod.rs @@ -120,66 +120,45 @@ impl Blocks { #[must_use] pub fn first_collision(&self, ray: Ray) -> Option { - // Calculate exact start position (the block we're in) - let start_pos = ray.origin(); - - // Calculate end position with a small offset to handle edge cases - let end_pos = ray.origin() + ray.direction(); - - // Convert to block coordinates, expanding bounds to ensure we catch all blocks - let min_block = start_pos.min(end_pos).floor().as_ivec3(); - let max_block = start_pos.max(end_pos).ceil().as_ivec3(); - - // Set up voxel traversal through the blocks - let traversal = ray.voxel_traversal(min_block, max_block); - - let mut min: Option = None; - - for cell in traversal { - // if there is no block at this cell, return None - let block = self.get_block(cell)?; - - let origin = Vec3::new(cell.x as f32, cell.y as f32, cell.z as f32); - - let min_dist = block - .collision_shapes() - .map(|shape| { - geometry::aabb::Aabb::new(shape.min().as_vec3(), shape.max().as_vec3()) - }) - .map(|shape| shape + origin) - .filter_map(|shape| shape.intersect_ray(&ray)) - .min(); - - let Some(min_dist) = min_dist else { - continue; - }; - - let collision_normal = ray.origin() + ray.direction() * min_dist.into_inner(); - //let collision_normal = (collision_point - origin).normalize(); - - match &min { - Some(current_min) => { - if min_dist.into_inner() < current_min.distance { - min = Some(RayCollision { - distance: min_dist.into_inner(), - location: cell, - normal: collision_normal, - block, - }); - } - } - None => { - min = Some(RayCollision { - distance: min_dist.into_inner(), - location: cell, - normal: collision_normal, + // Get ray properties + let direction = ray.direction().normalize(); + let max_distance = ray.direction().length(); + let step_size = 0.1; // Small increment to check along ray + + // Walk along ray + let mut current_distance = 0.0; + while current_distance <= max_distance { + let current_pos = ray.origin() + direction * current_distance; + let block_pos = current_pos.floor().as_ivec3(); + + if let Some(block) = self.get_block(block_pos) { + let origin = Vec3::new(block_pos.x as f32, block_pos.y as f32, block_pos.z as f32); + + let collision = block + .collision_shapes() + .map(|shape| { + geometry::aabb::Aabb::new(shape.min().as_vec3(), shape.max().as_vec3()) + }) + .map(|shape| shape + origin) + .filter_map(|shape| shape.intersect_ray(&ray)) + .min(); + + if let Some(dist) = collision { + let hit_point = ray.origin() + direction * dist.into_inner(); + + return Some(RayCollision { + distance: dist.into_inner(), + location: block_pos, + normal: hit_point, block, }); } } + + current_distance += step_size; } - min + None } #[must_use] diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 57987fc4..cd2552b3 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -174,8 +174,8 @@ impl Module for BowModule { world, &mut EventQueue, ) - .singleton() - .kind::() + .multi_threaded() + .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); let world = it.world(); From 58d2e3fb67b27f88c3be55f3458503dbe2130b04 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 11:42:44 +0100 Subject: [PATCH 07/20] Arrow Entity handling and damage. --- Cargo.lock | 16 +--- Cargo.toml | 4 - crates/hyperion/Cargo.toml | 3 +- .../hyperion/src/egress/sync_entity_state.rs | 80 ++++++++++++++----- crates/hyperion/src/lib.rs | 1 + crates/hyperion/src/simulation/event.rs | 14 ++++ .../lib.rs => hyperion/src/spatial/mod.rs} | 2 +- .../hyperion/src/storage/event/queue/mod.rs | 4 +- crates/hyperion/tests/spatial.rs | 3 +- crates/spatial/.gitignore | 1 - crates/spatial/Cargo.toml | 18 ----- crates/spatial/README.md | 1 - events/tag/Cargo.toml | 1 - events/tag/src/command/raycast.rs | 2 +- events/tag/src/lib.rs | 2 +- events/tag/src/module/bow.rs | 70 ++++++++++++++-- 16 files changed, 147 insertions(+), 75 deletions(-) rename crates/{spatial/src/lib.rs => hyperion/src/spatial/mod.rs} (99%) delete mode 100644 crates/spatial/.gitignore delete mode 100644 crates/spatial/Cargo.toml delete mode 100644 crates/spatial/README.md diff --git a/Cargo.lock b/Cargo.lock index 7eef5604..c73d32cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2743,6 +2743,7 @@ dependencies = [ "bitfield-struct", "bitvec", "bumpalo", + "bvh-region", "bytemuck", "byteorder", "bytes", @@ -2776,6 +2777,7 @@ dependencies = [ "ndarray", "no_denormals", "once_cell", + "ordered-float", "ouroboros", "parking_lot", "rayon", @@ -2787,7 +2789,6 @@ dependencies = [ "serde_json", "sha2", "simd-utils", - "spatial", "system-order", "thiserror 2.0.8", "tokio", @@ -5425,18 +5426,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spatial" -version = "0.1.0" -dependencies = [ - "bvh-region", - "flecs_ecs", - "geometry", - "hyperion", - "ordered-float", - "rayon", -] - [[package]] name = "spin" version = "0.9.8" @@ -5616,7 +5605,6 @@ dependencies = [ "rayon", "roaring", "rustc-hash 2.1.0", - "spatial", "tikv-jemallocator", "tracing", "tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml index 9bbe435a..7d1068ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ members = [ 'crates/hyperion-text', 'crates/hyperion-utils', 'crates/simd-utils', - 'crates/spatial', 'crates/system-order', 'events/tag', 'tools/packet-inspector', @@ -231,9 +230,6 @@ version = '0.10.8' [workspace.dependencies.simd-utils] path = 'crates/simd-utils' -[workspace.dependencies.spatial] -path = 'crates/spatial' - [workspace.dependencies.syntect] default-features = false version = '5.2.0' diff --git a/crates/hyperion/Cargo.toml b/crates/hyperion/Cargo.toml index 23014917..f54fce0a 100644 --- a/crates/hyperion/Cargo.toml +++ b/crates/hyperion/Cargo.toml @@ -11,6 +11,7 @@ anyhow = { workspace = true } base64 = { workspace = true } bitfield-struct = { workspace = true } bitvec = { workspace = true } +bvh-region = { workspace = true } bumpalo = { workspace = true } bytemuck = { workspace = true } byteorder = { workspace = true } @@ -70,12 +71,12 @@ valence_protocol = { workspace = true } valence_registry = { workspace = true } valence_server = { workspace = true } valence_text = { workspace = true } +ordered-float = { workspace = true } [dev-dependencies] approx = { workspace = true } divan = { workspace = true } fastrand = { workspace = true } -spatial = { workspace = true } [lints] workspace = true diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index 6c24615b..bfa5253a 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -5,6 +5,7 @@ use flecs_ecs::prelude::*; use glam::Vec3; use hyperion_inventory::PlayerInventory; use hyperion_utils::EntityExt; +use itertools::Either; use tracing::{debug, error}; use valence_protocol::{ ByteAngle, RawBytes, VarInt, @@ -20,9 +21,12 @@ use crate::{ animation::ActiveAnimation, blocks::Blocks, entity_kind::EntityKind, + event, handlers::is_grounded, metadata::{MetadataChanges, get_and_clear_metadata}, }, + spatial::get_first_collision, + storage::Events, }; #[derive(Component)] @@ -413,7 +417,8 @@ impl Module for EntityStateSyncModule { return; } - let world = it.system().world(); + let system = it.system(); + let world = system.world(); let _entity = it.entity(row); if velocity.0 != Vec3::ZERO { @@ -424,34 +429,65 @@ impl Module for EntityStateSyncModule { let center = **position; - let ray = geometry::ray::Ray::new(center, velocity.0); + let distance = velocity.0.length(); - #[allow(clippy::excessive_nesting)] - world.get::<&mut Blocks>(|blocks| { - let Some(collision) = blocks.first_collision(ray) else { - // Drag (0.99 / 20.0) - // 1.0 - (0.99 / 20.0) * 0.05 - velocity.0 *= 0.997_525; + debug!("Creatign Ray"); - // Gravity (20 MPSS) - velocity.0.y -= 0.05; + let ray = geometry::ray::Ray::new(center, velocity.0) * distance; - // Terminal Velocity (100.0) - velocity.0 = velocity.0.clamp_length(0.0, 100.0); - return; - }; + debug!("ray = {ray:?}"); - debug!("collision = {collision:?}"); + let Some(collision) = get_first_collision(ray, &world) else { + // Drag (0.99 / 20.0) + // 1.0 - (0.99 / 20.0) * 0.05 + velocity.0 *= 0.997_525; - velocity.0 = Vec3::ZERO; + // Gravity (20 MPSS) + velocity.0.y -= 0.05; - // Set arrow position to the collision location - **position = collision.normal; + // Terminal Velocity (100.0) + velocity.0 = velocity.0.clamp_length(0.0, 100.0); + return; + }; - blocks - .set_block(collision.location, BlockState::DIRT) - .unwrap(); - }); + debug!("Collision: {collision:?}"); + + match collision { + Either::Left(entity) => { + let entity = entity.entity_view(world); + debug!("entity: {entity:?}"); + // send event + world.get::<&mut Events>(|events| events.push( + event::ProjectileEntityEvent { + client: *entity, + projectile: *_entity, + }, + &world + )); + } + Either::Right(collision) => { + debug!("block: {collision:?}"); + // send event + world.get::<&mut Events>(|events| events.push( + event::ProjectileBlockEvent { + collision: collision, + projectile: *_entity, + }, + &world + )); + } + } + + /* debug!("collision = {collision:?}"); + + velocity.0 = Vec3::ZERO; */ + + /* // Set arrow position to the collision location + **position = collision.normal; + + blocks + .set_block(collision.location, BlockState::DIRT) + .unwrap(); */ } }); diff --git a/crates/hyperion/src/lib.rs b/crates/hyperion/src/lib.rs index b1e800b3..97fbc349 100644 --- a/crates/hyperion/src/lib.rs +++ b/crates/hyperion/src/lib.rs @@ -88,6 +88,7 @@ pub mod ingress; pub mod net; pub mod simulation; pub mod storage; +pub mod spatial; /// Relationship for previous values #[derive(Component)] diff --git a/crates/hyperion/src/simulation/event.rs b/crates/hyperion/src/simulation/event.rs index ba561351..b8fc80a6 100644 --- a/crates/hyperion/src/simulation/event.rs +++ b/crates/hyperion/src/simulation/event.rs @@ -9,6 +9,8 @@ use valence_server::{ItemKind, entity::item_frame::ItemStack}; use crate::simulation::skin::PlayerSkin; +use super::blocks::RayCollision; + #[derive(Component, Default, Debug)] pub struct ItemDropEvent { pub item: ItemStack, @@ -146,3 +148,15 @@ pub struct ClientStatusEvent { pub client: Entity, pub status: ClientStatusCommand, } + +#[derive(Clone, Debug)] +pub struct ProjectileEntityEvent { + pub client: Entity, + pub projectile: Entity, +} + +#[derive(Clone, Debug)] +pub struct ProjectileBlockEvent { + pub collision: RayCollision, + pub projectile: Entity, +} diff --git a/crates/spatial/src/lib.rs b/crates/hyperion/src/spatial/mod.rs similarity index 99% rename from crates/spatial/src/lib.rs rename to crates/hyperion/src/spatial/mod.rs index 22970529..3e0da6cb 100644 --- a/crates/spatial/src/lib.rs +++ b/crates/hyperion/src/spatial/mod.rs @@ -7,7 +7,7 @@ use flecs_ecs::{ prelude::Module, }; use geometry::{aabb::Aabb, ray::Ray}; -use hyperion::{ +use super::{ egress::player_join::RayonWorldStages, glam::Vec3, simulation::{ diff --git a/crates/hyperion/src/storage/event/queue/mod.rs b/crates/hyperion/src/storage/event/queue/mod.rs index f29012ba..c37ed6c8 100644 --- a/crates/hyperion/src/storage/event/queue/mod.rs +++ b/crates/hyperion/src/storage/event/queue/mod.rs @@ -58,7 +58,9 @@ define_events! { event::SwingArm, event::ToggleDoor, event::ReleaseUseItem, - event::ClientStatusEvent + event::ClientStatusEvent, + event::ProjectileEntityEvent, + event::ProjectileBlockEvent, } pub trait ReducedLifetime { diff --git a/crates/hyperion/tests/spatial.rs b/crates/hyperion/tests/spatial.rs index 5b63b988..bb4cbd4c 100644 --- a/crates/hyperion/tests/spatial.rs +++ b/crates/hyperion/tests/spatial.rs @@ -12,8 +12,7 @@ use flecs_ecs::core::{QueryBuilderImpl, SystemAPI, World, WorldGet, flecs}; use geometry::{aabb::Aabb, ray::Ray}; use glam::Vec3; use hyperion::{ - HyperionCore, - simulation::{EntitySize, Position, entity_kind::EntityKind}, + simulation::{entity_kind::EntityKind, EntitySize, Position}, spatial, HyperionCore }; use spatial::{Spatial, SpatialIndex, SpatialModule}; diff --git a/crates/spatial/.gitignore b/crates/spatial/.gitignore deleted file mode 100644 index ea8c4bf7..00000000 --- a/crates/spatial/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/crates/spatial/Cargo.toml b/crates/spatial/Cargo.toml deleted file mode 100644 index 80950da9..00000000 --- a/crates/spatial/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "spatial" -version.workspace = true -edition.workspace = true -authors = ["Andrew Gazelka "] -readme = "README.md" -publish = false - -[dependencies] -bvh-region.workspace = true -flecs_ecs.workspace = true -geometry.workspace = true -hyperion.workspace = true -ordered-float.workspace = true -rayon.workspace = true - -[lints] -workspace = true diff --git a/crates/spatial/README.md b/crates/spatial/README.md deleted file mode 100644 index dbff859e..00000000 --- a/crates/spatial/README.md +++ /dev/null @@ -1 +0,0 @@ -# spatial \ No newline at end of file diff --git a/events/tag/Cargo.toml b/events/tag/Cargo.toml index 52a94f49..97d8c093 100644 --- a/events/tag/Cargo.toml +++ b/events/tag/Cargo.toml @@ -22,7 +22,6 @@ hyperion-utils = { workspace = true } rayon = { workspace = true } roaring = { workspace = true } rustc-hash = { workspace = true } -spatial = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } tracing-tracy = { workspace = true } diff --git a/events/tag/src/command/raycast.rs b/events/tag/src/command/raycast.rs index 8b566f4b..db575863 100644 --- a/events/tag/src/command/raycast.rs +++ b/events/tag/src/command/raycast.rs @@ -2,11 +2,11 @@ use clap::Parser; use flecs_ecs::core::{Entity, EntityView, EntityViewGet, WorldProvider}; use hyperion::{ glam::Vec3, + spatial::get_first_collision, simulation::{Pitch, Position, Yaw, entity_kind::EntityKind}, }; use hyperion_clap::{CommandPermission, MinecraftCommand}; use rayon::iter::Either; -use spatial::get_first_collision; use tracing::debug; #[derive(Parser, CommandPermission, Debug)] diff --git a/events/tag/src/lib.rs b/events/tag/src/lib.rs index f8e89676..2467f173 100644 --- a/events/tag/src/lib.rs +++ b/events/tag/src/lib.rs @@ -13,7 +13,7 @@ use module::{block::BlockModule, vanish::VanishModule}; mod module; use derive_more::{Deref, DerefMut}; -use hyperion::{glam::IVec3, simulation::Position}; +use hyperion::{glam::IVec3, simulation::Position, spatial}; use hyperion_rank_tree::Team; use module::{attack::AttackModule, level::LevelModule, regeneration::RegenerationModule}; use spatial::SpatialIndex; diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 822ed696..64c918c0 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -3,13 +3,9 @@ use flecs_ecs::{ prelude::*, }; use hyperion::{ - ItemKind, ItemStack, - glam::Vec3, - simulation::{ - Pitch, Position, Spawn, Uuid, Velocity, Yaw, bow::BowCharging, entity_kind::EntityKind, - event, get_direction_from_rotation, - }, - storage::EventQueue, + glam::Vec3, net::Compose, simulation::{ + bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, Pitch, Position, Spawn, Uuid, Velocity, Yaw + }, storage::{EventQueue, Events}, ItemKind, ItemStack }; use hyperion_inventory::PlayerInventory; use tracing::debug; @@ -17,8 +13,21 @@ use tracing::debug; #[derive(Component)] pub struct BowModule; +#[derive(Component)] +pub struct Owner { + pub entity: Entity, +} + +impl Owner { + pub fn new(entity: Entity) -> Self { + Self { entity } + } +} + impl Module for BowModule { fn module(world: &World) { + world.component::(); + system!( "handle_bow_release", world, @@ -26,6 +35,7 @@ impl Module for BowModule { ) .term_at(0u32) .singleton() + .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); @@ -103,10 +113,56 @@ impl Module for BowModule { .set(Velocity::new(velocity.x, velocity.y, velocity.z)) .set(Pitch::new(**pitch)) .set(Yaw::new(**yaw)) + //.set(Owner::new(*player)) .enqueue(Spawn); }, ); } }); + + system!( + "arrow_entity_hit", + world, + &mut EventQueue, + ) + .singleton() + .multi_threaded() + .kind::() + .each_iter(move |it, _, event_queue| { + let _system = it.system(); + let world = it.world(); + + for event in event_queue.drain() { + + debug!("arrow_entity_hit: {event:?}"); + event + .projectile + .entity_view(world) + .get::<&Owner>(| owner| { + debug!("Sending attack event"); + world.get::<&Events>(|events| { + events.push( + event::AttackEntity { + origin: owner.entity, + target: event.client, + damage: 1.0, + }, + &world, + ) + }); + + // Updating arrows in entity + debug!("Updating arrows in entity"); + event.client + .entity_view(world) + .get::<&mut ArrowsInEntity>(|arrows| { + arrows.0 += 1; + }); + + }); + + event.projectile.entity_view(world).destruct(); + } + }); } } From f6129d3e19b7ae39d18b4a840d2cda433c4446a8 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 17:39:00 +0100 Subject: [PATCH 08/20] Arrow hitting blocks and damaging entities --- .../hyperion/src/egress/sync_entity_state.rs | 9 +- crates/hyperion/src/simulation/blocks/mod.rs | 4 +- events/tag/src/module/bow.rs | 94 +++++++++++-------- 3 files changed, 56 insertions(+), 51 deletions(-) diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index bfa5253a..d644afa1 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -429,14 +429,11 @@ impl Module for EntityStateSyncModule { let center = **position; + // getting max distance let distance = velocity.0.length(); - debug!("Creatign Ray"); - let ray = geometry::ray::Ray::new(center, velocity.0) * distance; - debug!("ray = {ray:?}"); - let Some(collision) = get_first_collision(ray, &world) else { // Drag (0.99 / 20.0) // 1.0 - (0.99 / 20.0) * 0.05 @@ -450,12 +447,9 @@ impl Module for EntityStateSyncModule { return; }; - debug!("Collision: {collision:?}"); - match collision { Either::Left(entity) => { let entity = entity.entity_view(world); - debug!("entity: {entity:?}"); // send event world.get::<&mut Events>(|events| events.push( event::ProjectileEntityEvent { @@ -466,7 +460,6 @@ impl Module for EntityStateSyncModule { )); } Either::Right(collision) => { - debug!("block: {collision:?}"); // send event world.get::<&mut Events>(|events| events.push( event::ProjectileBlockEvent { diff --git a/crates/hyperion/src/simulation/blocks/mod.rs b/crates/hyperion/src/simulation/blocks/mod.rs index 5719bfc1..0f531a96 100644 --- a/crates/hyperion/src/simulation/blocks/mod.rs +++ b/crates/hyperion/src/simulation/blocks/mod.rs @@ -154,8 +154,8 @@ impl Blocks { continue; }; - let collision_point = ray.origin() + ray.direction() * min_dist.into_inner(); - let collision_normal = (collision_point - origin).normalize(); + let collision_normal = ray.origin() + ray.direction() * min_dist.into_inner(); + //let collision_normal = (collision_point - origin).normalize(); match &min { Some(current_min) => { diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 64c918c0..22dbac0d 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -3,9 +3,14 @@ use flecs_ecs::{ prelude::*, }; use hyperion::{ - glam::Vec3, net::Compose, simulation::{ - bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, Pitch, Position, Spawn, Uuid, Velocity, Yaw - }, storage::{EventQueue, Events}, ItemKind, ItemStack + ItemKind, ItemStack, + glam::Vec3, + net::Compose, + simulation::{ + Pitch, Position, Spawn, Uuid, Velocity, Yaw, bow::BowCharging, entity_kind::EntityKind, + event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, + }, + storage::{EventQueue, Events}, }; use hyperion_inventory::PlayerInventory; use tracing::debug; @@ -15,7 +20,7 @@ pub struct BowModule; #[derive(Component)] pub struct Owner { - pub entity: Entity, + entity: Entity, } impl Owner { @@ -33,9 +38,7 @@ impl Module for BowModule { world, &mut EventQueue, ) - .term_at(0u32) .singleton() - .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); @@ -87,8 +90,6 @@ impl Module for BowModule { }) .unwrap_or(0.0); - debug!("charge: {charge}"); - // Calculate the direction vector from the player's rotation let direction = get_direction_from_rotation(**yaw, **pitch); // Calculate the velocity of the arrow based on the charge (3.0 is max velocity) @@ -97,13 +98,6 @@ impl Module for BowModule { let spawn_pos = Vec3::new(position.x, position.y + 1.62, position.z) + direction * 0.5; - debug!( - "Arrow velocity: ({}, {}, {})", - velocity.x, velocity.y, velocity.z - ); - - debug!("Arrow Yaw: {}, Arrow Pitch: {}", **yaw, **pitch); - // Spawn arrow world .entity() @@ -113,7 +107,7 @@ impl Module for BowModule { .set(Velocity::new(velocity.x, velocity.y, velocity.z)) .set(Pitch::new(**pitch)) .set(Yaw::new(**yaw)) - //.set(Owner::new(*player)) + .set(Owner::new(*player)) .enqueue(Spawn); }, ); @@ -126,42 +120,60 @@ impl Module for BowModule { &mut EventQueue, ) .singleton() - .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); let world = it.world(); for event in event_queue.drain() { + let (damage, owner) = event + .projectile + .entity_view(world) + .get::<(&Velocity, &Owner)>(|(velocity, owner)| { + (velocity.0.length() * 2.0, owner.entity) + }); + event + .client + .entity_view(world) + .get::<&mut ArrowsInEntity>(|arrows| { + arrows.0 += 1; + }); + + // Destroy the arrow + event.projectile.entity_view(world).destruct(); - debug!("arrow_entity_hit: {event:?}"); + world.get::<&Events>(|events| { + events.push( + event::AttackEntity { + origin: owner, + target: event.client, + damage: damage, + }, + &world, + ); + }) + } + }); + + system!( + "arrow_block_hit", + world, + &mut EventQueue, + ) + .singleton() + .kind::() + .each_iter(move |it, _, event_queue| { + let _system = it.system(); + let world = it.world(); + + for event in event_queue.drain() { event .projectile .entity_view(world) - .get::<&Owner>(| owner| { - debug!("Sending attack event"); - world.get::<&Events>(|events| { - events.push( - event::AttackEntity { - origin: owner.entity, - target: event.client, - damage: 1.0, - }, - &world, - ) - }); - - // Updating arrows in entity - debug!("Updating arrows in entity"); - event.client - .entity_view(world) - .get::<&mut ArrowsInEntity>(|arrows| { - arrows.0 += 1; - }); - + .get::<(&mut Position, &mut Velocity)>(|(position, velocity)| { + velocity.0 = Vec3::ZERO; + **position = event.collision.normal; }); - - event.projectile.entity_view(world).destruct(); } }); } From b413b32942c0962d4a0934e0d086c0e1f7caf42b Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 18:35:06 +0100 Subject: [PATCH 09/20] Despawn Arrow and clamp velocity properly :) --- .../hyperion/src/egress/sync_entity_state.rs | 4 +-- events/tag/src/module/bow.rs | 30 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index d644afa1..ee15fd64 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -442,8 +442,8 @@ impl Module for EntityStateSyncModule { // Gravity (20 MPSS) velocity.0.y -= 0.05; - // Terminal Velocity (100.0) - velocity.0 = velocity.0.clamp_length(0.0, 100.0); + // Terminal Velocity max (100.0) + velocity.0 = velocity.0.clamp_length_max(100.0); return; }; diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 22dbac0d..a607d892 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -2,10 +2,12 @@ use flecs_ecs::{ core::{EntityViewGet, World}, prelude::*, }; +use glam::I16Vec2; use hyperion::{ ItemKind, ItemStack, glam::Vec3, net::Compose, + valence_protocol::packets::play, simulation::{ Pitch, Position, Spawn, Uuid, Velocity, Yaw, bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, @@ -13,7 +15,9 @@ use hyperion::{ storage::{EventQueue, Events}, }; use hyperion_inventory::PlayerInventory; +use hyperion_utils::EntityExt; use tracing::debug; +use valence_protocol::VarInt; #[derive(Component)] pub struct BowModule; @@ -117,21 +121,33 @@ impl Module for BowModule { system!( "arrow_entity_hit", world, + &Compose($), &mut EventQueue, ) .singleton() .kind::() - .each_iter(move |it, _, event_queue| { - let _system = it.system(); + .each_iter(move |it, _, (compose, event_queue)| { + let system = it.system(); let world = it.world(); for event in event_queue.drain() { - let (damage, owner) = event + let (damage, owner, chunk_pos) = event .projectile .entity_view(world) .get::<(&Velocity, &Owner)>(|(velocity, owner)| { - (velocity.0.length() * 2.0, owner.entity) + if owner.entity == event.client { + return (0.0, owner.entity, I16Vec2::ZERO); + } + let chunck_pos = event.client.entity_view(world).get::<&Position>(|pos| { + pos.to_chunk() + }); + (velocity.0.length() * 2.0, owner.entity, chunck_pos) }); + + if damage == 0.0 && owner == event.client { + continue; + } + event .client .entity_view(world) @@ -139,7 +155,11 @@ impl Module for BowModule { arrows.0 += 1; }); - // Destroy the arrow + let packet = play::EntitiesDestroyS2c { + entity_ids: vec![VarInt(event.projectile.minecraft_id() as i32)].into(), + }; + compose.broadcast_local(&packet, chunk_pos, system).send().unwrap(); + event.projectile.entity_view(world).destruct(); world.get::<&Events>(|events| { From d2715d24972e72cefc490bd4a1977d3c010b3ddb Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 19:35:48 +0100 Subject: [PATCH 10/20] hand animations :thumbsup: --- crates/hyperion/src/simulation/animation.rs | 1 + crates/hyperion/src/simulation/handlers.rs | 13 ++++++------- events/tag/src/module/bow.rs | 12 +++--------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/crates/hyperion/src/simulation/animation.rs b/crates/hyperion/src/simulation/animation.rs index 42621cba..aadbf51d 100644 --- a/crates/hyperion/src/simulation/animation.rs +++ b/crates/hyperion/src/simulation/animation.rs @@ -6,6 +6,7 @@ use valence_protocol::{VarInt, packets::play::EntityAnimationS2c}; #[repr(u8)] pub enum Kind { SwingMainArm = 0, + UseItem = 1, LeaveBed = 2, SwingOffHand = 3, Critical = 4, diff --git a/crates/hyperion/src/simulation/handlers.rs b/crates/hyperion/src/simulation/handlers.rs index d0585912..d08c6d83 100644 --- a/crates/hyperion/src/simulation/handlers.rs +++ b/crates/hyperion/src/simulation/handlers.rs @@ -23,16 +23,11 @@ use valence_protocol::{ use valence_text::IntoText; use super::{ - ConfirmBlockSequences, EntitySize, Position, - animation::{self, ActiveAnimation}, - block_bounds, - blocks::Blocks, - bow::BowCharging, - event::ClientStatusEvent, + animation::{self, ActiveAnimation, Kind}, block_bounds, blocks::Blocks, bow::BowCharging, event::ClientStatusEvent, ConfirmBlockSequences, EntitySize, Position }; use crate::{ net::{Compose, ConnectionId, decoder::BorrowedPacketFrame}, - simulation::{Pitch, Yaw, aabb, event, event::PluginMessage, metadata::entity::Pose}, + simulation::{Pitch, Yaw, aabb, event, event::PluginMessage, metadata::entity::Pose, metadata::living_entity::HandStates}, storage::{ ClickSlotEvent, CommandCompletionRequest, Events, GlobalEventHandlers, InteractEvent, }, @@ -298,6 +293,8 @@ fn player_action(mut data: &[u8], query: &PacketSwitchQuery<'_>) -> anyhow::Resu item: query.inventory.get_cursor().item, }; + query.id.entity_view(query.world).set(HandStates::new(0)); + query.events.push(event, query.world); } action => bail!("unimplemented {action:?}"), @@ -364,6 +361,8 @@ pub fn player_interact_item( return; } entity.set(BowCharging::now()); + + entity.set(HandStates::new(1)); }); } } diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index a607d892..8ca3f10f 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -4,15 +4,9 @@ use flecs_ecs::{ }; use glam::I16Vec2; use hyperion::{ - ItemKind, ItemStack, - glam::Vec3, - net::Compose, - valence_protocol::packets::play, - simulation::{ - Pitch, Position, Spawn, Uuid, Velocity, Yaw, bow::BowCharging, entity_kind::EntityKind, - event, get_direction_from_rotation, metadata::living_entity::ArrowsInEntity, - }, - storage::{EventQueue, Events}, + glam::Vec3, net::Compose, simulation::{ + bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::{ArrowsInEntity, HandStates}, Pitch, Position, Spawn, Uuid, Velocity, Yaw + }, storage::{EventQueue, Events}, valence_protocol::packets::play, ItemKind, ItemStack }; use hyperion_inventory::PlayerInventory; use hyperion_utils::EntityExt; From 60a7470d0895791b03288e21dd9b7b85c3f24670 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Thu, 19 Dec 2024 19:43:02 +0100 Subject: [PATCH 11/20] fix bow? --- events/tag/src/module/bow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 8ca3f10f..57987fc4 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -34,7 +34,7 @@ impl Module for BowModule { system!( "handle_bow_release", world, - &mut EventQueue, + &mut EventQueue($), ) .singleton() .kind::() From 7142da67a223aae7c51f31630f54cec028d103e2 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Fri, 20 Dec 2024 01:32:39 +0100 Subject: [PATCH 12/20] first_collision rewrite. somewhat fixed arrow through floor --- .../hyperion/src/egress/sync_entity_state.rs | 2 + crates/hyperion/src/simulation/blocks/mod.rs | 87 +++++++------------ events/tag/src/module/bow.rs | 4 +- 3 files changed, 37 insertions(+), 56 deletions(-) diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index ee15fd64..5a468361 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -432,6 +432,8 @@ impl Module for EntityStateSyncModule { // getting max distance let distance = velocity.0.length(); + debug!("distance = {distance}"); + let ray = geometry::ray::Ray::new(center, velocity.0) * distance; let Some(collision) = get_first_collision(ray, &world) else { diff --git a/crates/hyperion/src/simulation/blocks/mod.rs b/crates/hyperion/src/simulation/blocks/mod.rs index 0f531a96..d5a138a9 100644 --- a/crates/hyperion/src/simulation/blocks/mod.rs +++ b/crates/hyperion/src/simulation/blocks/mod.rs @@ -120,66 +120,45 @@ impl Blocks { #[must_use] pub fn first_collision(&self, ray: Ray) -> Option { - // Calculate exact start position (the block we're in) - let start_pos = ray.origin(); - - // Calculate end position with a small offset to handle edge cases - let end_pos = ray.origin() + ray.direction(); - - // Convert to block coordinates, expanding bounds to ensure we catch all blocks - let min_block = start_pos.min(end_pos).floor().as_ivec3(); - let max_block = start_pos.max(end_pos).ceil().as_ivec3(); - - // Set up voxel traversal through the blocks - let traversal = ray.voxel_traversal(min_block, max_block); - - let mut min: Option = None; - - for cell in traversal { - // if there is no block at this cell, return None - let block = self.get_block(cell)?; - - let origin = Vec3::new(cell.x as f32, cell.y as f32, cell.z as f32); - - let min_dist = block - .collision_shapes() - .map(|shape| { - geometry::aabb::Aabb::new(shape.min().as_vec3(), shape.max().as_vec3()) - }) - .map(|shape| shape + origin) - .filter_map(|shape| shape.intersect_ray(&ray)) - .min(); - - let Some(min_dist) = min_dist else { - continue; - }; - - let collision_normal = ray.origin() + ray.direction() * min_dist.into_inner(); - //let collision_normal = (collision_point - origin).normalize(); - - match &min { - Some(current_min) => { - if min_dist.into_inner() < current_min.distance { - min = Some(RayCollision { - distance: min_dist.into_inner(), - location: cell, - normal: collision_normal, - block, - }); - } - } - None => { - min = Some(RayCollision { - distance: min_dist.into_inner(), - location: cell, - normal: collision_normal, + // Get ray properties + let direction = ray.direction().normalize(); + let max_distance = ray.direction().length(); + let step_size = 0.1; // Small increment to check along ray + + // Walk along ray + let mut current_distance = 0.0; + while current_distance <= max_distance { + let current_pos = ray.origin() + direction * current_distance; + let block_pos = current_pos.floor().as_ivec3(); + + if let Some(block) = self.get_block(block_pos) { + let origin = Vec3::new(block_pos.x as f32, block_pos.y as f32, block_pos.z as f32); + + let collision = block + .collision_shapes() + .map(|shape| { + geometry::aabb::Aabb::new(shape.min().as_vec3(), shape.max().as_vec3()) + }) + .map(|shape| shape + origin) + .filter_map(|shape| shape.intersect_ray(&ray)) + .min(); + + if let Some(dist) = collision { + let hit_point = ray.origin() + direction * dist.into_inner(); + + return Some(RayCollision { + distance: dist.into_inner(), + location: block_pos, + normal: hit_point, block, }); } } + + current_distance += step_size; } - min + None } #[must_use] diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 57987fc4..cd2552b3 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -174,8 +174,8 @@ impl Module for BowModule { world, &mut EventQueue, ) - .singleton() - .kind::() + .multi_threaded() + .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); let world = it.world(); From b4f69ebfa07a9b1d8086db74f6b9ffeb61f0011c Mon Sep 17 00:00:00 2001 From: e1pupper Date: Fri, 20 Dec 2024 04:57:38 +0100 Subject: [PATCH 13/20] updated some stuff and when position updates. --- .../hyperion/src/egress/sync_entity_state.rs | 25 ++++--- crates/hyperion/src/simulation/blocks/mod.rs | 21 +++++- crates/hyperion/src/simulation/mod.rs | 2 +- events/tag/src/module/bow.rs | 66 ++++++++++++------- 4 files changed, 76 insertions(+), 38 deletions(-) diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index 5a468361..5c29835d 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -422,30 +422,29 @@ impl Module for EntityStateSyncModule { let _entity = it.entity(row); if velocity.0 != Vec3::ZERO { - position.x += velocity.0.x; - position.y += velocity.0.y; - position.z += velocity.0.z; // let (new_yaw, new_pitch) = get_rotation_from_velocity(velocity.0); - + let center = **position; - + // getting max distance let distance = velocity.0.length(); - - debug!("distance = {distance}"); - + let ray = geometry::ray::Ray::new(center, velocity.0) * distance; - + let Some(collision) = get_first_collision(ray, &world) else { + debug!("Velocity = {:?}", velocity.0); // Drag (0.99 / 20.0) // 1.0 - (0.99 / 20.0) * 0.05 - velocity.0 *= 0.997_525; - + velocity.0 *= 1.0 - (0.99 / 20.0) * 0.05; + // Gravity (20 MPSS) velocity.0.y -= 0.05; - + // Terminal Velocity max (100.0) - velocity.0 = velocity.0.clamp_length_max(100.0); + velocity.0 = velocity.0.clamp_length(0.0, 100.0); + position.x += velocity.0.x; + position.y += velocity.0.y; + position.z += velocity.0.z; return; }; diff --git a/crates/hyperion/src/simulation/blocks/mod.rs b/crates/hyperion/src/simulation/blocks/mod.rs index d5a138a9..f2bac2de 100644 --- a/crates/hyperion/src/simulation/blocks/mod.rs +++ b/crates/hyperion/src/simulation/blocks/mod.rs @@ -146,10 +146,29 @@ impl Blocks { if let Some(dist) = collision { let hit_point = ray.origin() + direction * dist.into_inner(); + let epsilon = 1e-5; + let offset = hit_point - origin; + + let normal = if (offset.x - 0.0).abs() < epsilon { + Vec3::new(-1.0, 0.0, 0.0) + } else if (offset.x - 1.0).abs() < epsilon { + Vec3::new(1.0, 0.0, 0.0) + } else if (offset.y - 0.0).abs() < epsilon { + Vec3::new(0.0, -1.0, 0.0) + } else if (offset.y - 1.0).abs() < epsilon { + Vec3::new(0.0, 1.0, 0.0) + } else if (offset.z - 0.0).abs() < epsilon { + Vec3::new(0.0, 0.0, -1.0) + } else if (offset.z - 1.0).abs() < epsilon { + Vec3::new(0.0, 0.0, 1.0) + } else { + Vec3::ZERO // Default normal if none of the faces match + }; + return Some(RayCollision { distance: dist.into_inner(), location: block_pos, - normal: hit_point, + normal, block, }); } diff --git a/crates/hyperion/src/simulation/mod.rs b/crates/hyperion/src/simulation/mod.rs index 01f09003..6e1ca845 100644 --- a/crates/hyperion/src/simulation/mod.rs +++ b/crates/hyperion/src/simulation/mod.rs @@ -727,7 +727,7 @@ pub struct Visible; #[must_use] pub fn get_rotation_from_velocity(velocity: Vec3) -> (f32, f32) { - let yaw = (-velocity.x).atan2(velocity.z).to_degrees().abs(); // Correct yaw calculation + let yaw = (-velocity.x).atan2(velocity.z).to_degrees(); // Correct yaw calculation let pitch = (-velocity.y).atan2(velocity.length()).to_degrees(); // Correct pitch calculation (yaw, pitch) } diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index cd2552b3..a02c10f7 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -4,14 +4,21 @@ use flecs_ecs::{ }; use glam::I16Vec2; use hyperion::{ - glam::Vec3, net::Compose, simulation::{ - bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::{ArrowsInEntity, HandStates}, Pitch, Position, Spawn, Uuid, Velocity, Yaw - }, storage::{EventQueue, Events}, valence_protocol::packets::play, ItemKind, ItemStack + ItemKind, ItemStack, + glam::Vec3, + net::Compose, + simulation::{ + Pitch, Position, Spawn, Uuid, Velocity, Yaw, bow::BowCharging, entity_kind::EntityKind, + event, get_direction_from_rotation, get_rotation_from_velocity, + metadata::living_entity::ArrowsInEntity, + }, + storage::{EventQueue, Events}, + valence_protocol::packets::play, }; use hyperion_inventory::PlayerInventory; use hyperion_utils::EntityExt; use tracing::debug; -use valence_protocol::VarInt; +use valence_protocol::{ByteAngle, VarInt}; #[derive(Component)] pub struct BowModule; @@ -125,19 +132,21 @@ impl Module for BowModule { let world = it.world(); for event in event_queue.drain() { - let (damage, owner, chunk_pos) = event - .projectile - .entity_view(world) - .get::<(&Velocity, &Owner)>(|(velocity, owner)| { - if owner.entity == event.client { - return (0.0, owner.entity, I16Vec2::ZERO); - } - let chunck_pos = event.client.entity_view(world).get::<&Position>(|pos| { - pos.to_chunk() + let (damage, owner, chunk_pos) = + event + .projectile + .entity_view(world) + .get::<(&Velocity, &Owner)>(|(velocity, owner)| { + if owner.entity == event.client { + return (0.0, owner.entity, I16Vec2::ZERO); + } + let chunck_pos = event + .client + .entity_view(world) + .get::<&Position>(|pos| pos.to_chunk()); + (velocity.0.length() * 2.0, owner.entity, chunck_pos) }); - (velocity.0.length() * 2.0, owner.entity, chunck_pos) - }); - + if damage == 0.0 && owner == event.client { continue; } @@ -148,11 +157,14 @@ impl Module for BowModule { .get::<&mut ArrowsInEntity>(|arrows| { arrows.0 += 1; }); - + let packet = play::EntitiesDestroyS2c { entity_ids: vec![VarInt(event.projectile.minecraft_id() as i32)].into(), }; - compose.broadcast_local(&packet, chunk_pos, system).send().unwrap(); + compose + .broadcast_local(&packet, chunk_pos, system) + .send() + .unwrap(); event.projectile.entity_view(world).destruct(); @@ -181,13 +193,21 @@ impl Module for BowModule { let world = it.world(); for event in event_queue.drain() { - event - .projectile - .entity_view(world) - .get::<(&mut Position, &mut Velocity)>(|(position, velocity)| { + event.projectile.entity_view(world).get::<( + &mut Position, + &mut Velocity, + &mut Yaw, + &mut Pitch, + )>( + |(position, velocity, _yaw, _pitch)| { + let (new_yaw, new_pitch) = get_rotation_from_velocity(velocity.0); + debug!("Arrow hit block at {:?}", event.collision); velocity.0 = Vec3::ZERO; **position = event.collision.normal; - }); + /* **yaw = new_yaw; + **pitch = new_pitch; */ + }, + ); } }); } From a33b4cf87f2ee931d23f3530b88cc0ee21ef33d3 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Fri, 20 Dec 2024 05:11:41 +0100 Subject: [PATCH 14/20] stash apply --- .../hyperion/src/egress/sync_entity_state.rs | 5 +- crates/hyperion/src/simulation/bow.rs | 40 ----- crates/hyperion/src/simulation/handlers.rs | 36 ++-- crates/hyperion/src/simulation/mod.rs | 6 +- events/tag/src/module/bow.rs | 167 ++++++++++++++---- 5 files changed, 166 insertions(+), 88 deletions(-) delete mode 100644 crates/hyperion/src/simulation/bow.rs diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index 9ebc6e06..428a094e 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -423,7 +423,6 @@ impl Module for EntityStateSyncModule { if velocity.0 != Vec3::ZERO { // let (new_yaw, new_pitch) = get_rotation_from_velocity(velocity.0); - let center = **position; // getting max distance @@ -443,6 +442,10 @@ impl Module for EntityStateSyncModule { // Terminal Velocity max (100.0) velocity.0 = velocity.0.clamp_length_max(100.0); + + position.x += velocity.0.x; + position.y += velocity.0.y; + position.z += velocity.0.z; return; }; diff --git a/crates/hyperion/src/simulation/bow.rs b/crates/hyperion/src/simulation/bow.rs deleted file mode 100644 index 093d7d7e..00000000 --- a/crates/hyperion/src/simulation/bow.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::{ - fmt::Display, - time::{Duration, SystemTime}, -}; - -use flecs_ecs::prelude::*; -use humantime::format_duration; - -#[derive(Component, Debug)] -pub struct BowCharging { - pub start_time: SystemTime, -} - -impl Display for BowCharging { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let duration = self.start_time.elapsed().unwrap_or_default(); - - write!(f, "{}", format_duration(duration)) - } -} - -impl BowCharging { - #[must_use] - pub fn now() -> Self { - Self { - start_time: SystemTime::now(), - } - } - - #[must_use] - pub fn get_charge(&self) -> f32 { - let elapsed = self.start_time.elapsed().unwrap_or(Duration::ZERO); - let secs = elapsed.as_secs_f32(); - // Minecraft bow charge mechanics: - // - Takes 1 second to fully charge - // - Minimum charge is 0.0 - // - Maximum charge is 1.0 - secs.min(1.0) - } -} diff --git a/crates/hyperion/src/simulation/handlers.rs b/crates/hyperion/src/simulation/handlers.rs index d08c6d83..feb42e92 100644 --- a/crates/hyperion/src/simulation/handlers.rs +++ b/crates/hyperion/src/simulation/handlers.rs @@ -23,11 +23,19 @@ use valence_protocol::{ use valence_text::IntoText; use super::{ - animation::{self, ActiveAnimation, Kind}, block_bounds, blocks::Blocks, bow::BowCharging, event::ClientStatusEvent, ConfirmBlockSequences, EntitySize, Position + ConfirmBlockSequences, EntitySize, Position, + animation::{self, ActiveAnimation, Kind}, + block_bounds, + blocks::Blocks, + event::ClientStatusEvent, }; use crate::{ net::{Compose, ConnectionId, decoder::BorrowedPacketFrame}, - simulation::{Pitch, Yaw, aabb, event, event::PluginMessage, metadata::entity::Pose, metadata::living_entity::HandStates}, + simulation::{ + Pitch, Yaw, aabb, event, + event::PluginMessage, + metadata::{entity::Pose, living_entity::HandStates}, + }, storage::{ ClickSlotEvent, CommandCompletionRequest, Events, GlobalEventHandlers, InteractEvent, }, @@ -350,21 +358,27 @@ pub fn player_interact_item( let cursor = query.inventory.get_cursor(); if !cursor.is_empty() { + let flecs_event = event::ItemInteract { + entity: query.id, + hand, + sequence: sequence.0, + }; if cursor.item == ItemKind::WrittenBook { let packet = play::OpenWrittenBookS2c { hand }; query.compose.unicast(&packet, query.io_ref, query.system)?; } else if cursor.item == ItemKind::Bow { // Start charging bow - let entity = query.world.entity_from_id(query.id); - entity.get::>(|charging| { - if charging.is_some() { - return; - } - entity.set(BowCharging::now()); - - entity.set(HandStates::new(1)); - }); + // let entity = query.world.entity_from_id(query.id); + // entity.get::>(|charging| { + // if charging.is_some() { + // return; + // } + // entity.set(BowCharging::now()); + // + // entity.set(HandStates::new(1)); + // }); } + query.events.push(flecs_event, query.world); } query.handlers.interact.trigger_all(query, &event); diff --git a/crates/hyperion/src/simulation/mod.rs b/crates/hyperion/src/simulation/mod.rs index 6e1ca845..a64ae8c1 100644 --- a/crates/hyperion/src/simulation/mod.rs +++ b/crates/hyperion/src/simulation/mod.rs @@ -1,6 +1,5 @@ use std::{borrow::Borrow, collections::HashMap, hash::Hash, sync::Arc}; -use bow::BowCharging; use bytemuck::{Pod, Zeroable}; use derive_more::{Constructor, Deref, DerefMut, Display, From}; use flecs_ecs::prelude::*; @@ -28,7 +27,6 @@ use crate::{ pub mod animation; pub mod blocks; -pub mod bow; pub mod command; pub mod entity_kind; pub mod event; @@ -628,8 +626,8 @@ impl Module for SimModule { world.component::(); - world.component::(); - component!(world, BowCharging).opaque_func(meta_ser_stringify_type_display::); + /* world.component::(); + component!(world, BowCharging).opaque_func(meta_ser_stringify_type_display::); */ observer!( world, diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index cd2552b3..73600b2e 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -1,12 +1,22 @@ +use std::time::{Duration, Instant, SystemTime}; + use flecs_ecs::{ core::{EntityViewGet, World}, prelude::*, }; use glam::I16Vec2; use hyperion::{ - glam::Vec3, net::Compose, simulation::{ - bow::BowCharging, entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::{ArrowsInEntity, HandStates}, Pitch, Position, Spawn, Uuid, Velocity, Yaw - }, storage::{EventQueue, Events}, valence_protocol::packets::play, ItemKind, ItemStack + ItemKind, ItemStack, + glam::Vec3, + net::Compose, + simulation::{ + Pitch, Player, Position, Spawn, Uuid, Velocity, Yaw, + entity_kind::EntityKind, + event, get_direction_from_rotation, + metadata::living_entity::{ArrowsInEntity, HandStates}, + }, + storage::{EventQueue, Events}, + valence_protocol::packets::play, }; use hyperion_inventory::PlayerInventory; use hyperion_utils::EntityExt; @@ -27,14 +37,65 @@ impl Owner { } } +#[derive(Component)] +pub struct LastFireTime { + pub time: SystemTime, +} + +impl LastFireTime { + pub fn now() -> Self { + Self { + time: SystemTime::now(), + } + } + + // if above 150ms, can fire + pub fn can_fire(&self) -> bool { + let elapsed = self.time.elapsed().unwrap_or(Duration::ZERO); + elapsed.as_millis() > 150 + } +} + +#[derive(Component)] +pub struct BowCharging { + pub start_time: SystemTime, +} + +impl BowCharging { + #[must_use] + pub fn now() -> Self { + Self { + start_time: SystemTime::now(), + } + } + + #[must_use] + pub fn get_charge(&self) -> f32 { + let elapsed = self.start_time.elapsed().unwrap_or(Duration::ZERO); + let secs = elapsed.as_secs_f32(); + // Minecraft bow charge mechanics: + // - Takes 1.2 second to fully charge + // - Minimum charge is 0.000001 + // - Maximum charge is 1.0 + secs.min(1.2).max(0.01) + } +} + impl Module for BowModule { fn module(world: &World) { world.component::(); + world.component::(); + world.component::(); + + world + .component::() + .add_trait::<(flecs::With, LastFireTime)>() + .add_trait::<(flecs::With, BowCharging)>(); system!( - "handle_bow_release", + "handle_bow_use", world, - &mut EventQueue($), + &mut EventQueue, ) .singleton() .kind::() @@ -42,6 +103,33 @@ impl Module for BowModule { let _system = it.system(); let world = it.world(); + for event in event_queue.drain() { + event + .entity + .entity_view(world) + .get::<&PlayerInventory>(|inventory| { + let cursor = inventory.get_cursor(); + if cursor.item != ItemKind::Bow { + return; + } + + event.entity.entity_view(world).set(BowCharging::now()); + event.entity.entity_view(world).set(HandStates::new(1)); + }); + } + }); + + system!( + "handle_bow_release", + world, + &mut EventQueue($), + ) + .multi_threaded() + .kind::() + .each_iter(move |it, _, event_queue| { + let _system = it.system(); + let world = it.world(); + for event in event_queue.drain() { if event.item != ItemKind::Bow { continue; @@ -49,10 +137,22 @@ impl Module for BowModule { let player = world.entity_from_id(event.from); + // Check the cooldown + let can_fire = + player.get::<&LastFireTime>(|last_fire_time| last_fire_time.can_fire()); + + if !can_fire { + continue; + } + + // Update the last fire time + player.set(LastFireTime::now()); + #[allow(clippy::excessive_nesting)] player.get::<(&mut PlayerInventory, &Position, &Yaw, &Pitch)>( |(inventory, position, yaw, pitch)| { - // check if the player has enough arrows in their inventory + debug!("Player {} released the bow", player.id()); + // Check if the player has enough arrows in their inventory let items: Vec<(u16, &ItemStack)> = inventory.items().collect(); let mut has_arrow = false; for (slot, item) in items { @@ -78,15 +178,13 @@ impl Module for BowModule { } // Get how charged the bow is - let charge = event - .from - .entity_view(world) - .try_get::<&BowCharging>(|charging| { - let charge = charging.get_charge(); - event.from.entity_view(world).remove::(); - charge - }) - .unwrap_or(0.0); + let charge = player.get::<&BowCharging>(|charging| charging.get_charge()); + + debug!( + "Player {} fired an arrow with charge {}", + player.id(), + charge + ); // Calculate the direction vector from the player's rotation let direction = get_direction_from_rotation(**yaw, **pitch); @@ -96,9 +194,9 @@ impl Module for BowModule { let spawn_pos = Vec3::new(position.x, position.y + 1.62, position.z) + direction * 0.5; - // Spawn arrow - world - .entity() + debug!("Arrow spawn position: {:?}", spawn_pos); + + world.entity() .add_enum(EntityKind::Arrow) .set(Uuid::new_v4()) .set(Position::new(spawn_pos.x, spawn_pos.y, spawn_pos.z)) @@ -125,19 +223,21 @@ impl Module for BowModule { let world = it.world(); for event in event_queue.drain() { - let (damage, owner, chunk_pos) = event - .projectile - .entity_view(world) - .get::<(&Velocity, &Owner)>(|(velocity, owner)| { - if owner.entity == event.client { - return (0.0, owner.entity, I16Vec2::ZERO); - } - let chunck_pos = event.client.entity_view(world).get::<&Position>(|pos| { - pos.to_chunk() + let (damage, owner, chunk_pos) = + event + .projectile + .entity_view(world) + .get::<(&Velocity, &Owner)>(|(velocity, owner)| { + if owner.entity == event.client { + return (0.0, owner.entity, I16Vec2::ZERO); + } + let chunck_pos = event + .client + .entity_view(world) + .get::<&Position>(|pos| pos.to_chunk()); + (velocity.0.length() * 2.0, owner.entity, chunck_pos) }); - (velocity.0.length() * 2.0, owner.entity, chunck_pos) - }); - + if damage == 0.0 && owner == event.client { continue; } @@ -148,11 +248,14 @@ impl Module for BowModule { .get::<&mut ArrowsInEntity>(|arrows| { arrows.0 += 1; }); - + let packet = play::EntitiesDestroyS2c { entity_ids: vec![VarInt(event.projectile.minecraft_id() as i32)].into(), }; - compose.broadcast_local(&packet, chunk_pos, system).send().unwrap(); + compose + .broadcast_local(&packet, chunk_pos, system) + .send() + .unwrap(); event.projectile.entity_view(world).destruct(); From 799b3588396aea3fee3513cb71973200735c769f Mon Sep 17 00:00:00 2001 From: Andrew Gazelka Date: Fri, 20 Dec 2024 00:05:33 -0800 Subject: [PATCH 15/20] fix --- crates/hyperion/src/lib.rs | 2 +- crates/hyperion/src/simulation/event.rs | 3 +-- crates/hyperion/src/simulation/mod.rs | 4 ++-- crates/hyperion/src/spatial/mod.rs | 5 +++-- crates/hyperion/tests/spatial.rs | 4 +++- events/tag/src/command/raycast.rs | 2 +- events/tag/src/module/bow.rs | 4 ++-- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/crates/hyperion/src/lib.rs b/crates/hyperion/src/lib.rs index 97fbc349..104a5343 100644 --- a/crates/hyperion/src/lib.rs +++ b/crates/hyperion/src/lib.rs @@ -87,8 +87,8 @@ pub mod egress; pub mod ingress; pub mod net; pub mod simulation; -pub mod storage; pub mod spatial; +pub mod storage; /// Relationship for previous values #[derive(Component)] diff --git a/crates/hyperion/src/simulation/event.rs b/crates/hyperion/src/simulation/event.rs index b8fc80a6..8c37ff95 100644 --- a/crates/hyperion/src/simulation/event.rs +++ b/crates/hyperion/src/simulation/event.rs @@ -7,9 +7,8 @@ use valence_generated::block::BlockState; use valence_protocol::Hand; use valence_server::{ItemKind, entity::item_frame::ItemStack}; -use crate::simulation::skin::PlayerSkin; - use super::blocks::RayCollision; +use crate::simulation::skin::PlayerSkin; #[derive(Component, Default, Debug)] pub struct ItemDropEvent { diff --git a/crates/hyperion/src/simulation/mod.rs b/crates/hyperion/src/simulation/mod.rs index a64ae8c1..20e49c68 100644 --- a/crates/hyperion/src/simulation/mod.rs +++ b/crates/hyperion/src/simulation/mod.rs @@ -626,8 +626,8 @@ impl Module for SimModule { world.component::(); - /* world.component::(); - component!(world, BowCharging).opaque_func(meta_ser_stringify_type_display::); */ + // world.component::(); + // component!(world, BowCharging).opaque_func(meta_ser_stringify_type_display::); observer!( world, diff --git a/crates/hyperion/src/spatial/mod.rs b/crates/hyperion/src/spatial/mod.rs index 3e0da6cb..bc994c8a 100644 --- a/crates/hyperion/src/spatial/mod.rs +++ b/crates/hyperion/src/spatial/mod.rs @@ -7,6 +7,9 @@ use flecs_ecs::{ prelude::Module, }; use geometry::{aabb::Aabb, ray::Ray}; +use ordered_float::NotNan; +use rayon::iter::Either; + use super::{ egress::player_join::RayonWorldStages, glam::Vec3, @@ -15,8 +18,6 @@ use super::{ blocks::{Blocks, RayCollision}, }, }; -use ordered_float::NotNan; -use rayon::iter::Either; #[derive(Component)] pub struct SpatialModule; diff --git a/crates/hyperion/tests/spatial.rs b/crates/hyperion/tests/spatial.rs index bb4cbd4c..e65ed951 100644 --- a/crates/hyperion/tests/spatial.rs +++ b/crates/hyperion/tests/spatial.rs @@ -12,7 +12,9 @@ use flecs_ecs::core::{QueryBuilderImpl, SystemAPI, World, WorldGet, flecs}; use geometry::{aabb::Aabb, ray::Ray}; use glam::Vec3; use hyperion::{ - simulation::{entity_kind::EntityKind, EntitySize, Position}, spatial, HyperionCore + HyperionCore, + simulation::{EntitySize, Position, entity_kind::EntityKind}, + spatial, }; use spatial::{Spatial, SpatialIndex, SpatialModule}; diff --git a/events/tag/src/command/raycast.rs b/events/tag/src/command/raycast.rs index db575863..284f1c54 100644 --- a/events/tag/src/command/raycast.rs +++ b/events/tag/src/command/raycast.rs @@ -2,8 +2,8 @@ use clap::Parser; use flecs_ecs::core::{Entity, EntityView, EntityViewGet, WorldProvider}; use hyperion::{ glam::Vec3, - spatial::get_first_collision, simulation::{Pitch, Position, Yaw, entity_kind::EntityKind}, + spatial::get_first_collision, }; use hyperion_clap::{CommandPermission, MinecraftCommand}; use rayon::iter::Either; diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 73600b2e..d360d8bb 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -124,7 +124,6 @@ impl Module for BowModule { world, &mut EventQueue($), ) - .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); @@ -196,7 +195,8 @@ impl Module for BowModule { debug!("Arrow spawn position: {:?}", spawn_pos); - world.entity() + world + .entity() .add_enum(EntityKind::Arrow) .set(Uuid::new_v4()) .set(Position::new(spawn_pos.x, spawn_pos.y, spawn_pos.z)) From 28a4e2fb1c407ea5fd04ea754d12b6dd4f9f92ec Mon Sep 17 00:00:00 2001 From: e1pupper Date: Sat, 21 Dec 2024 13:50:03 +0100 Subject: [PATCH 16/20] Voxel Traversal and Ray changes. --- crates/geometry/src/aabb.rs | 75 ++++-------- crates/geometry/src/ray.rs | 115 +++++------------- .../hyperion/src/egress/sync_entity_state.rs | 2 - crates/hyperion/src/simulation/blocks/mod.rs | 59 +++++---- events/tag/src/module/bow.rs | 7 +- 5 files changed, 97 insertions(+), 161 deletions(-) diff --git a/crates/geometry/src/aabb.rs b/crates/geometry/src/aabb.rs index 81183c6f..20629867 100644 --- a/crates/geometry/src/aabb.rs +++ b/crates/geometry/src/aabb.rs @@ -279,64 +279,39 @@ impl Aabb { } let dir = ray.direction(); + let inv_dir = ray.inv_direction(); - // For each axis, handle zero direction: + // Initialize t_min and t_max to the range of possible values let (mut t_min, mut t_max) = (f32::NEG_INFINITY, f32::INFINITY); // X-axis - if dir.x == 0.0 { - // Ray is parallel to X slab - if origin.x < self.min.x || origin.x > self.max.x { - return None; // no intersection if outside slab - } - // else: no constraint from X (t_min, t_max remain infinite) - } else { - let inv_dx = 1.0 / dir.x; - let tx1 = (self.min.x - origin.x) * inv_dx; - let tx2 = (self.max.x - origin.x) * inv_dx; - let t_low = tx1.min(tx2); - let t_high = tx1.max(tx2); - t_min = t_min.max(t_low); - t_max = t_max.min(t_high); - if t_min > t_max { - return None; - } + if dir.x != 0.0 { + let tx1 = (self.min.x - origin.x) * inv_dir.x; + let tx2 = (self.max.x - origin.x) * inv_dir.x; + t_min = t_min.max(tx1.min(tx2)); + t_max = t_max.min(tx1.max(tx2)); + } else if origin.x < self.min.x || origin.x > self.max.x { + return None; // Ray is parallel to X slab and outside the slab } - // Y-axis (do the same zero-check logic) - if dir.y == 0.0 { - if origin.y < self.min.y || origin.y > self.max.y { - return None; - } - } else { - let inv_dy = 1.0 / dir.y; - let ty1 = (self.min.y - origin.y) * inv_dy; - let ty2 = (self.max.y - origin.y) * inv_dy; - let t_low = ty1.min(ty2); - let t_high = ty1.max(ty2); - t_min = t_min.max(t_low); - t_max = t_max.min(t_high); - if t_min > t_max { - return None; - } + // Y-axis + if dir.y != 0.0 { + let ty1 = (self.min.y - origin.y) * inv_dir.y; + let ty2 = (self.max.y - origin.y) * inv_dir.y; + t_min = t_min.max(ty1.min(ty2)); + t_max = t_max.min(ty1.max(ty2)); + } else if origin.y < self.min.y || origin.y > self.max.y { + return None; // Ray is parallel to Y slab and outside the slab } - // Z-axis (same pattern) - if dir.z == 0.0 { - if origin.z < self.min.z || origin.z > self.max.z { - return None; - } - } else { - let inv_dz = 1.0 / dir.z; - let tz1 = (self.min.z - origin.z) * inv_dz; - let tz2 = (self.max.z - origin.z) * inv_dz; - let t_low = tz1.min(tz2); - let t_high = tz1.max(tz2); - t_min = t_min.max(t_low); - t_max = t_max.min(t_high); - if t_min > t_max { - return None; - } + // Z-axis + if dir.z != 0.0 { + let tz1 = (self.min.z - origin.z) * inv_dir.z; + let tz2 = (self.max.z - origin.z) * inv_dir.z; + t_min = t_min.max(tz1.min(tz2)); + t_max = t_max.min(tz1.max(tz2)); + } else if origin.z < self.min.z || origin.z > self.max.z { + return None; // Ray is parallel to Z slab and outside the slab } // At this point, t_min and t_max define the intersection range. diff --git a/crates/geometry/src/ray.rs b/crates/geometry/src/ray.rs index 4ca2ee9b..6d46acb8 100644 --- a/crates/geometry/src/ray.rs +++ b/crates/geometry/src/ray.rs @@ -59,98 +59,45 @@ impl Ray { /// Efficiently traverse through grid cells that the ray intersects using an optimized DDA algorithm. /// Returns an iterator over the grid cells ([`IVec3`]) that the ray passes through. pub fn voxel_traversal(&self, bounds_min: IVec3, bounds_max: IVec3) -> VoxelTraversal { - // Convert ray origin to grid coordinates and handle negative coordinates correctly let current_pos = self.origin.as_ivec3(); - // Ensure that the traversal includes the current block - let min_block = IVec3::new( - current_pos.x.min(bounds_min.x), - current_pos.y.min(bounds_min.y), - current_pos.z.min(bounds_min.z), - ); - - let max_block = IVec3::new( - current_pos.x.max(bounds_max.x), - current_pos.y.max(bounds_max.y), - current_pos.z.max(bounds_max.z), + // Determine stepping direction for each axis + let step = IVec3::new( + self.direction.x.signum() as i32, + self.direction.y.signum() as i32, + self.direction.z.signum() as i32, ); - // Calculate step direction for each axis - let step = IVec3::new( - if self.direction.x > 0.0 { - 1 - } else if self.direction.x < 0.0 { - -1 + // Calculate distance to next voxel boundary for each axis + let next_boundary = Vec3::new( + if step.x > 0 { + current_pos.x as f32 + 1.0 - self.origin.x } else { - 0 + self.origin.x - current_pos.x as f32 }, - if self.direction.y > 0.0 { - 1 - } else if self.direction.y < 0.0 { - -1 + if step.y > 0 { + current_pos.y as f32 + 1.0 - self.origin.y } else { - 0 + self.origin.y - current_pos.y as f32 }, - if self.direction.z > 0.0 { - 1 - } else if self.direction.z < 0.0 { - -1 + if step.z > 0 { + current_pos.z as f32 + 1.0 - self.origin.z } else { - 0 + self.origin.z - current_pos.z as f32 }, ); - // Calculate t_max - distance to next voxel boundary for each axis + // Calculate t_max and t_delta using precomputed inv_direction let t_max = Vec3::new( - if self.direction.x == 0.0 { - f32::INFINITY - } else { - let next_x = if self.direction.x > 0.0 { - current_pos.x as f32 + 1.0 - self.origin.x - } else { - self.origin.x - current_pos.x as f32 - }; - next_x * self.inv_direction.x.abs() - }, - if self.direction.y == 0.0 { - f32::INFINITY - } else { - let next_y = if self.direction.y > 0.0 { - current_pos.y as f32 + 1.0 - self.origin.y - } else { - self.origin.y - current_pos.y as f32 - }; - next_y * self.inv_direction.y.abs() - }, - if self.direction.z == 0.0 { - f32::INFINITY - } else { - let next_z = if self.direction.z > 0.0 { - current_pos.z as f32 + 1.0 - self.origin.z - } else { - self.origin.z - current_pos.z as f32 - }; - next_z * self.inv_direction.z.abs() - }, + next_boundary.x * self.inv_direction.x.abs(), + next_boundary.y * self.inv_direction.y.abs(), + next_boundary.z * self.inv_direction.z.abs(), ); - // Calculate t_delta - distance between voxel boundaries let t_delta = Vec3::new( - if self.direction.x == 0.0 { - f32::INFINITY - } else { - self.inv_direction.x.abs() - }, - if self.direction.y == 0.0 { - f32::INFINITY - } else { - self.inv_direction.y.abs() - }, - if self.direction.z == 0.0 { - f32::INFINITY - } else { - self.inv_direction.z.abs() - }, + self.inv_direction.x.abs(), + self.inv_direction.y.abs(), + self.inv_direction.z.abs(), ); VoxelTraversal { @@ -158,8 +105,8 @@ impl Ray { step, t_max, t_delta, - bounds_min: min_block, - bounds_max: max_block, + bounds_min, + bounds_max, } } } @@ -201,12 +148,14 @@ impl Iterator for VoxelTraversal { self.current_pos.z += self.step.z; self.t_max.z += self.t_delta.z; } - } else if self.t_max.y < self.t_max.z { - self.current_pos.y += self.step.y; - self.t_max.y += self.t_delta.y; } else { - self.current_pos.z += self.step.z; - self.t_max.z += self.t_delta.z; + if self.t_max.y < self.t_max.z { + self.current_pos.y += self.step.y; + self.t_max.y += self.t_delta.y; + } else { + self.current_pos.z += self.step.z; + self.t_max.z += self.t_delta.z; + } } Some(current) diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index 428a094e..7ceb672a 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -428,8 +428,6 @@ impl Module for EntityStateSyncModule { // getting max distance let distance = velocity.0.length(); - debug!("distance = {distance}"); - let ray = geometry::ray::Ray::new(center, velocity.0) * distance; let Some(collision) = get_first_collision(ray, &world) else { diff --git a/crates/hyperion/src/simulation/blocks/mod.rs b/crates/hyperion/src/simulation/blocks/mod.rs index d5a138a9..cbea6ae2 100644 --- a/crates/hyperion/src/simulation/blocks/mod.rs +++ b/crates/hyperion/src/simulation/blocks/mod.rs @@ -18,7 +18,7 @@ use rayon::iter::ParallelIterator; use roaring::RoaringBitmap; use rustc_hash::FxBuildHasher; use shared::WorldShared; -use tracing::error; +use tracing::{debug, error}; use valence_generated::block::BlockState; use valence_server::layer::chunk::Chunk; @@ -61,6 +61,7 @@ pub enum TrySetBlockDeltaError { pub struct RayCollision { pub distance: f32, pub location: IVec3, + pub point: Vec3, pub normal: Vec3, pub block: BlockState, } @@ -120,42 +121,54 @@ impl Blocks { #[must_use] pub fn first_collision(&self, ray: Ray) -> Option { - // Get ray properties - let direction = ray.direction().normalize(); - let max_distance = ray.direction().length(); - let step_size = 0.1; // Small increment to check along ray + // Define bounds for the voxel traversal + let bounds_min = IVec3::new(i32::MIN / 2, -64, i32::MIN / 2); + let bounds_max = IVec3::new(i32::MAX / 2, 320, i32::MAX / 2); - // Walk along ray - let mut current_distance = 0.0; - while current_distance <= max_distance { - let current_pos = ray.origin() + direction * current_distance; - let block_pos = current_pos.floor().as_ivec3(); - - if let Some(block) = self.get_block(block_pos) { - let origin = Vec3::new(block_pos.x as f32, block_pos.y as f32, block_pos.z as f32); + // Use voxel traversal to efficiently walk through blocks + for cell in ray.voxel_traversal(bounds_min, bounds_max) { + if let Some(block) = self.get_block(cell) { + debug!("Checking block at {:?}", cell); + let origin = cell.as_vec3(); + // Check collision with block shapes let collision = block .collision_shapes() .map(|shape| { - geometry::aabb::Aabb::new(shape.min().as_vec3(), shape.max().as_vec3()) + let aabb = + geometry::aabb::Aabb::new(shape.min().as_vec3(), shape.max().as_vec3()); + debug!("Checking shape AABB: {:?}", aabb); + aabb + }) + .map(|shape| { + let translated_shape = shape + origin; + debug!("Translated shape AABB: {:?}", translated_shape); + translated_shape + }) + .filter_map(|shape| { + let intersection = shape.intersect_ray(&ray); + debug!("Intersection result: {:?}", intersection); + intersection }) - .map(|shape| shape + origin) - .filter_map(|shape| shape.intersect_ray(&ray)) .min(); + debug!("Final collision result: {:?}", collision); + + if let Some(distance) = collision { + let distance = distance.into_inner(); + let collision_point = ray.origin() + ray.direction() * distance; + let collision_normal = (collision_point - origin).normalize(); - if let Some(dist) = collision { - let hit_point = ray.origin() + direction * dist.into_inner(); + debug!("Collision point: {:?}", collision_point); return Some(RayCollision { - distance: dist.into_inner(), - location: block_pos, - normal: hit_point, + distance, + location: cell, + point: collision_point, + normal: collision_normal, block, }); } } - - current_distance += step_size; } None diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 73600b2e..4fbf21f1 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -98,6 +98,7 @@ impl Module for BowModule { &mut EventQueue, ) .singleton() + .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); @@ -124,7 +125,6 @@ impl Module for BowModule { world, &mut EventQueue($), ) - .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); @@ -272,12 +272,12 @@ impl Module for BowModule { } }); + // multi-threaded causes issues system!( "arrow_block_hit", world, &mut EventQueue, ) - .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); @@ -288,8 +288,9 @@ impl Module for BowModule { .projectile .entity_view(world) .get::<(&mut Position, &mut Velocity)>(|(position, velocity)| { + debug!("Arrow hit block at {:?}", event.collision.point); velocity.0 = Vec3::ZERO; - **position = event.collision.normal; + **position = event.collision.point; }); } }); From 877962b776ab769c522e1d40a65ec33cc178b588 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Sat, 21 Dec 2024 20:33:49 +0100 Subject: [PATCH 17/20] ray changes DDA-> Amanadites and woo algorithim --- crates/geometry/src/ray.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/geometry/src/ray.rs b/crates/geometry/src/ray.rs index 6d46acb8..3f97c19f 100644 --- a/crates/geometry/src/ray.rs +++ b/crates/geometry/src/ray.rs @@ -56,16 +56,16 @@ impl Ray { self.origin + self.direction * t } - /// Efficiently traverse through grid cells that the ray intersects using an optimized DDA algorithm. + /// Efficiently traverse through grid cells that the ray intersects using the Amanatides and Woo algorithm. /// Returns an iterator over the grid cells ([`IVec3`]) that the ray passes through. pub fn voxel_traversal(&self, bounds_min: IVec3, bounds_max: IVec3) -> VoxelTraversal { let current_pos = self.origin.as_ivec3(); // Determine stepping direction for each axis let step = IVec3::new( - self.direction.x.signum() as i32, - self.direction.y.signum() as i32, - self.direction.z.signum() as i32, + if self.direction.x > 0.0 { 1 } else { -1 }, + if self.direction.y > 0.0 { 1 } else { -1 }, + if self.direction.z > 0.0 { 1 } else { -1 }, ); // Calculate distance to next voxel boundary for each axis From 201f6ddebbe4a68492467da41d648dc77d2e5a47 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Sun, 22 Dec 2024 01:32:59 +0100 Subject: [PATCH 18/20] Arrows Collisions Complete. No more crashing i think! :) --- .../hyperion/src/egress/sync_entity_state.rs | 10 +++--- crates/hyperion/src/simulation/blocks/mod.rs | 21 ++----------- crates/hyperion/src/simulation/mod.rs | 12 +++++++ crates/hyperion/src/spatial/mod.rs | 4 +++ events/tag/src/command/raycast.rs | 2 +- events/tag/src/module/bow.rs | 31 +++---------------- 6 files changed, 31 insertions(+), 49 deletions(-) diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index 7ceb672a..cc36a0f1 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -17,7 +17,7 @@ use crate::{ Prev, net::{Compose, ConnectionId, DataBundle}, simulation::{ - Pitch, Position, Velocity, Xp, Yaw, + Owner, Pitch, Position, Velocity, Xp, Yaw, animation::ActiveAnimation, blocks::Blocks, entity_kind::EntityKind, @@ -407,12 +407,13 @@ impl Module for EntityStateSyncModule { world, &mut Position, &mut Velocity, + &Owner, ?&ConnectionId ) .multi_threaded() .kind::() .with_enum_wildcard::() - .each_iter(|it, row, (position, velocity, connection_id)| { + .each_iter(|it, row, (position, velocity, owner, connection_id)| { if let Some(_connection_id) = connection_id { return; } @@ -430,7 +431,8 @@ impl Module for EntityStateSyncModule { let ray = geometry::ray::Ray::new(center, velocity.0) * distance; - let Some(collision) = get_first_collision(ray, &world) else { + let Some(collision) = + get_first_collision(ray, &world, Some(owner.entity)) else { // Drag (0.99 / 20.0) // 1.0 - (0.99 / 20.0) * 0.05 velocity.0 *= 0.997_525; @@ -458,7 +460,7 @@ impl Module for EntityStateSyncModule { }, &world )); - } + }, Either::Right(collision) => { // send event world.get::<&mut Events>(|events| events.push( diff --git a/crates/hyperion/src/simulation/blocks/mod.rs b/crates/hyperion/src/simulation/blocks/mod.rs index cbea6ae2..2c0e2b3e 100644 --- a/crates/hyperion/src/simulation/blocks/mod.rs +++ b/crates/hyperion/src/simulation/blocks/mod.rs @@ -128,38 +128,23 @@ impl Blocks { // Use voxel traversal to efficiently walk through blocks for cell in ray.voxel_traversal(bounds_min, bounds_max) { if let Some(block) = self.get_block(cell) { - debug!("Checking block at {:?}", cell); let origin = cell.as_vec3(); // Check collision with block shapes let collision = block .collision_shapes() .map(|shape| { - let aabb = - geometry::aabb::Aabb::new(shape.min().as_vec3(), shape.max().as_vec3()); - debug!("Checking shape AABB: {:?}", aabb); - aabb - }) - .map(|shape| { - let translated_shape = shape + origin; - debug!("Translated shape AABB: {:?}", translated_shape); - translated_shape - }) - .filter_map(|shape| { - let intersection = shape.intersect_ray(&ray); - debug!("Intersection result: {:?}", intersection); - intersection + geometry::aabb::Aabb::new(shape.min().as_vec3(), shape.max().as_vec3()) }) + .map(|shape| shape + origin) + .filter_map(|shape| shape.intersect_ray(&ray)) .min(); - debug!("Final collision result: {:?}", collision); if let Some(distance) = collision { let distance = distance.into_inner(); let collision_point = ray.origin() + ray.direction() * distance; let collision_normal = (collision_point - origin).normalize(); - debug!("Collision point: {:?}", collision_point); - return Some(RayCollision { distance, location: cell, diff --git a/crates/hyperion/src/simulation/mod.rs b/crates/hyperion/src/simulation/mod.rs index 20e49c68..cf812296 100644 --- a/crates/hyperion/src/simulation/mod.rs +++ b/crates/hyperion/src/simulation/mod.rs @@ -355,6 +355,17 @@ impl Default for RunningSpeed { } } +#[derive(Component)] +pub struct Owner { + pub entity: Entity, +} + +impl Owner { + pub fn new(entity: Entity) -> Self { + Self { entity } + } +} + /// If the entity can be targeted by non-player entities. #[derive(Component)] pub struct AiTargetable; @@ -586,6 +597,7 @@ impl Module for SimModule { world.component::(); world.component::(); world.component::(); + world.component::(); world.component::().meta(); diff --git a/crates/hyperion/src/spatial/mod.rs b/crates/hyperion/src/spatial/mod.rs index bc994c8a..aafee1fc 100644 --- a/crates/hyperion/src/spatial/mod.rs +++ b/crates/hyperion/src/spatial/mod.rs @@ -32,11 +32,15 @@ pub struct SpatialIndex { pub fn get_first_collision( ray: Ray, world: &World, + owner: Option, ) -> Option, RayCollision>> { // Check for collisions with entities let entity = world.get::<&SpatialIndex>(|index| index.first_ray_collision(ray, world)); let block = world.get::<&Blocks>(|blocks| blocks.first_collision(ray)); + // make sure the entity is not the owner + let entity = entity.filter(|(entity, _)| owner.map_or(true, |owner| *entity != owner)); + // check which one is closest to the Ray don't forget to account for entity size entity.map_or(block.map(Either::Right), |(entity, _)| { let entity_data = entity.get::<(&Position, &EntitySize)>(|(position, size)| { diff --git a/events/tag/src/command/raycast.rs b/events/tag/src/command/raycast.rs index 284f1c54..27e02f99 100644 --- a/events/tag/src/command/raycast.rs +++ b/events/tag/src/command/raycast.rs @@ -62,7 +62,7 @@ impl MinecraftCommand for RaycastCommand { debug!("ray = {ray:?}"); - let result = get_first_collision(ray, &world); + let result = get_first_collision(ray, &world, Some(caller)); match result { Some(Either::Left(entity)) => { diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 9a33d3ac..082d8f24 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -6,17 +6,9 @@ use flecs_ecs::{ }; use glam::I16Vec2; use hyperion::{ - ItemKind, ItemStack, - glam::Vec3, - net::Compose, - simulation::{ - Pitch, Player, Position, Spawn, Uuid, Velocity, Yaw, - entity_kind::EntityKind, - event, get_direction_from_rotation, - metadata::living_entity::{ArrowsInEntity, HandStates}, - }, - storage::{EventQueue, Events}, - valence_protocol::packets::play, + glam::Vec3, net::Compose, simulation::{ + entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::{ArrowsInEntity, HandStates}, Owner, Pitch, Player, Position, Spawn, Uuid, Velocity, Yaw + }, storage::{EventQueue, Events}, valence_protocol::packets::play, ItemKind, ItemStack }; use hyperion_inventory::PlayerInventory; use hyperion_utils::EntityExt; @@ -26,17 +18,6 @@ use valence_protocol::VarInt; #[derive(Component)] pub struct BowModule; -#[derive(Component)] -pub struct Owner { - entity: Entity, -} - -impl Owner { - pub fn new(entity: Entity) -> Self { - Self { entity } - } -} - #[derive(Component)] pub struct LastFireTime { pub time: SystemTime, @@ -83,7 +64,6 @@ impl BowCharging { impl Module for BowModule { fn module(world: &World) { - world.component::(); world.component::(); world.component::(); @@ -217,6 +197,7 @@ impl Module for BowModule { &Compose($), &mut EventQueue, ) + .multi_threaded() .singleton() .kind::() .each_iter(move |it, _, (compose, event_queue)| { @@ -229,9 +210,6 @@ impl Module for BowModule { .projectile .entity_view(world) .get::<(&Velocity, &Owner)>(|(velocity, owner)| { - if owner.entity == event.client { - return (0.0, owner.entity, I16Vec2::ZERO); - } let chunck_pos = event .client .entity_view(world) @@ -279,6 +257,7 @@ impl Module for BowModule { world, &mut EventQueue, ) + .multi_threaded() .kind::() .each_iter(move |it, _, event_queue| { let _system = it.system(); From 4d59a8b07543e7e06e69f4b85f696c63c64588f9 Mon Sep 17 00:00:00 2001 From: e1pupper Date: Sun, 22 Dec 2024 14:21:56 +0100 Subject: [PATCH 19/20] test updates. --- crates/hyperion/tests/collision.rs | 100 +++++++++++++++++++++++++++++ crates/hyperion/tests/entity.rs | 6 +- 2 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 crates/hyperion/tests/collision.rs diff --git a/crates/hyperion/tests/collision.rs b/crates/hyperion/tests/collision.rs new file mode 100644 index 00000000..b5914218 --- /dev/null +++ b/crates/hyperion/tests/collision.rs @@ -0,0 +1,100 @@ +#![feature(assert_matches)] +#![allow( + clippy::print_stdout, + reason = "the purpose of not having printing to stdout is so that tracing is used properly \ + for the core libraries. These are tests, so it doesn't matter" +)] + +use std::{assert_matches::assert_matches, collections::HashSet}; + +use approx::assert_relative_eq; +use flecs_ecs::{core::{ + flecs, Entity, EntityView, EntityViewGet, QueryBuilderImpl, SystemAPI, World, WorldGet +}, macros::system}; +use geometry::{aabb::Aabb, ray::Ray}; +use glam::{IVec3, Vec3}; +use hyperion::{ + runtime::AsyncRuntime, simulation::{ + blocks::Blocks, entity_kind::EntityKind, event, EntitySize, Owner, Pitch, Position, Spawn, Velocity, Yaw + }, spatial::{self, Spatial, SpatialIndex, SpatialModule}, storage::EventQueue, HyperionCore +}; +use spatial::get_first_collision; + +#[test] +fn test_get_first_collision() { + let world = World::new(); + world.import::(); + world.import::(); + world.import::(); + world.get::<&AsyncRuntime>(|runtime| { + const URL: &str = "https://github.com/andrewgazelka/maps/raw/main/GenMap.tar.gz"; + + let f = hyperion_utils::cached_save(&world, URL); + + let save = runtime.block_on(f).unwrap_or_else(|e| { + panic!("failed to download map {URL}: {e}"); + }); + + world.set(Blocks::new(&world, &save).unwrap()); + }); + + // Make all entities have Spatial component so they are spatially indexed + world + .observer::() + .with_enum_wildcard::() + .each_entity(|entity, ()| { + entity.add::(); + }); + + // Create a player entity + let player = world + .entity_named("test_player") + .add_enum(EntityKind::Player) + .set(EntitySize::default()) + .set(Position::new(0.0, 21.0, 0.0)) + .set(Yaw::new(0.0)) + .set(Pitch::new(90.0)); + + // Function to spawn arrows at different angles + fn spawn_arrow(world: &World, position: Vec3, direction: Vec3) -> EntityView<'_> { + world + .entity() + .add_enum(EntityKind::Arrow) + .set(Velocity::new(direction.x, direction.y, direction.z)) + .set(Position::new(position.x, position.y, position.z)) + } + + // Spawn arrows at different angles + let arrow_velocities = [ + Vec3::new(0.0, -1.0, 0.0), + // Vec3::new(1.0, 0.0, 0.0), + ]; + + let arrows: Vec> = arrow_velocities + .iter() + .map(|velocity| { + spawn_arrow(&world, Vec3::new(0.0, 21.0, 0.0), *velocity).set(Owner::new(*player)) + }) + .collect(); + + // Progress the world to ensure that the index is updated + world.progress(); + + // Get all entities with Position and Velocity components + arrows.iter().for_each(|arrow| { + arrow.get::<(&Position, &Velocity)>(|(position, velocity)| { + println!("position: {position:?}"); + println!("velocity: {velocity:?}"); + }); + }); + + world.progress(); + + // Get all entities with Position and Velocity components + arrows.iter().for_each(|arrow| { + arrow.get::<(&Position, &Velocity)>(|(position, velocity)| { + println!("position: {position:?}"); + println!("velocity: {velocity:?}"); + }); + }); +} diff --git a/crates/hyperion/tests/entity.rs b/crates/hyperion/tests/entity.rs index 66976b34..bc767e21 100644 --- a/crates/hyperion/tests/entity.rs +++ b/crates/hyperion/tests/entity.rs @@ -45,8 +45,8 @@ fn arrow() { world.progress(); arrow.get::<&Position>(|position| { - // since velocity.y is 1.0, the arrow should be at y = 20.0 + 1.0 - assert_eq!(*position, Position::new(0.0, 21.0, 0.0)); + // since velocity.y is 1.0, the arrow should be at y = 20.0 + (1.0 * drag - gravity) = 20.947525 + assert_eq!(*position, Position::new(0.0, 20.947_525, 0.0)); }); world.progress(); @@ -55,6 +55,6 @@ fn arrow() { // gravity! drag! this is what was returned from the test but I am unsure if it actually // what we should be getting // todo: make a bunch more tests and compare to the vanilla velocity and positions - assert_eq!(*position, Position::new(0.0, 21.947_525, 0.0)); + assert_eq!(*position, Position::new(0.0, 21.842_704_875_625, 0.0)); }); } From 5cd21a7632220b99dca2d65d683ca6ecf56398ce Mon Sep 17 00:00:00 2001 From: Andrew Gazelka Date: Sun, 22 Dec 2024 12:19:50 -0800 Subject: [PATCH 20/20] update --- Cargo.lock | 1 + crates/geometry/src/ray.rs | 12 +- crates/hyperion/Cargo.toml | 1 + .../hyperion/src/egress/sync_entity_state.rs | 284 +++++++++--------- crates/hyperion/tests/collision.rs | 32 +- crates/hyperion/tests/entity.rs | 2 +- events/tag/src/module/bow.rs | 14 +- 7 files changed, 175 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c38a8d1..d6b612c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2812,6 +2812,7 @@ dependencies = [ "humantime", "hyperion-crafting", "hyperion-event-macros", + "hyperion-genmap", "hyperion-inventory", "hyperion-nerd-font", "hyperion-palette", diff --git a/crates/geometry/src/ray.rs b/crates/geometry/src/ray.rs index 3f97c19f..ececa797 100644 --- a/crates/geometry/src/ray.rs +++ b/crates/geometry/src/ray.rs @@ -148,14 +148,12 @@ impl Iterator for VoxelTraversal { self.current_pos.z += self.step.z; self.t_max.z += self.t_delta.z; } + } else if self.t_max.y < self.t_max.z { + self.current_pos.y += self.step.y; + self.t_max.y += self.t_delta.y; } else { - if self.t_max.y < self.t_max.z { - self.current_pos.y += self.step.y; - self.t_max.y += self.t_delta.y; - } else { - self.current_pos.z += self.step.z; - self.t_max.z += self.t_delta.z; - } + self.current_pos.z += self.step.z; + self.t_max.z += self.t_delta.z; } Some(current) diff --git a/crates/hyperion/Cargo.toml b/crates/hyperion/Cargo.toml index f54fce0a..ce98531c 100644 --- a/crates/hyperion/Cargo.toml +++ b/crates/hyperion/Cargo.toml @@ -77,6 +77,7 @@ ordered-float = { workspace = true } approx = { workspace = true } divan = { workspace = true } fastrand = { workspace = true } +hyperion-genmap = { workspace = true } [lints] workspace = true diff --git a/crates/hyperion/src/egress/sync_entity_state.rs b/crates/hyperion/src/egress/sync_entity_state.rs index cc36a0f1..1cf33d9d 100644 --- a/crates/hyperion/src/egress/sync_entity_state.rs +++ b/crates/hyperion/src/egress/sync_entity_state.rs @@ -297,69 +297,88 @@ impl Module for EntityStateSyncModule { &Yaw, &Pitch, ) - .multi_threaded() - .kind::() - .each_iter( - |it, - row, - ( - compose, - prev_position, - prev_yaw, - prev_pitch, - position, - velocity, - yaw, - pitch, - )| { - // if io.is_none() { - // return; - // } - - let world = it.system().world(); - let system = it.system(); - let entity = it.entity(row); - let entity_id = VarInt(entity.minecraft_id()); - - let chunk_pos = position.to_chunk(); - - let position_delta = **position - **prev_position; - let needs_teleport = position_delta.abs().max_element() >= 8.0; - let changed_position = **position != **prev_position; - - let look_changed = (**yaw - **prev_yaw).abs() >= 0.01 || (**pitch - **prev_pitch).abs() >= 0.01; + .multi_threaded() + .kind::() + .each_iter( + |it, + row, + ( + compose, + prev_position, + prev_yaw, + prev_pitch, + position, + velocity, + yaw, + pitch, + )| { + // if io.is_none() { + // return; + // } + + let world = it.system().world(); + let system = it.system(); + let entity = it.entity(row); + let entity_id = VarInt(entity.minecraft_id()); + + let chunk_pos = position.to_chunk(); + + let position_delta = **position - **prev_position; + let needs_teleport = position_delta.abs().max_element() >= 8.0; + let changed_position = **position != **prev_position; + + let look_changed = (**yaw - **prev_yaw).abs() >= 0.01 || (**pitch - **prev_pitch).abs() >= 0.01; + + let mut bundle = DataBundle::new(compose, system); + + world.get::<&mut Blocks>(|blocks| { + let grounded = is_grounded(position, blocks); + + if changed_position && !needs_teleport && look_changed { + let packet = play::RotateAndMoveRelativeS2c { + entity_id, + #[allow(clippy::cast_possible_truncation)] + delta: (position_delta * 4096.0).to_array().map(|x| x as i16), + yaw: ByteAngle::from_degrees(**yaw), + pitch: ByteAngle::from_degrees(**pitch), + on_ground: grounded, + }; - let mut bundle = DataBundle::new(compose, system); + bundle.add_packet(&packet).unwrap(); + } else { + if changed_position && !needs_teleport { + let packet = play::MoveRelativeS2c { + entity_id, + #[allow(clippy::cast_possible_truncation)] + delta: (position_delta * 4096.0).to_array().map(|x| x as i16), + on_ground: grounded, + }; - world.get::<&mut Blocks>(|blocks| { - let grounded = is_grounded(position, blocks); + bundle.add_packet(&packet).unwrap(); + } - if changed_position && !needs_teleport && look_changed { - let packet = play::RotateAndMoveRelativeS2c { - entity_id, - #[allow(clippy::cast_possible_truncation)] - delta: (position_delta * 4096.0).to_array().map(|x| x as i16), - yaw: ByteAngle::from_degrees(**yaw), - pitch: ByteAngle::from_degrees(**pitch), - on_ground: grounded, - }; + if look_changed { + let packet = play::RotateS2c { + entity_id, + yaw: ByteAngle::from_degrees(**yaw), + pitch: ByteAngle::from_degrees(**pitch), + on_ground: grounded, + }; - bundle.add_packet(&packet).unwrap(); - } else { - if changed_position && !needs_teleport { - let packet = play::MoveRelativeS2c { + bundle.add_packet(&packet).unwrap(); + } + let packet = play::EntitySetHeadYawS2c { entity_id, - #[allow(clippy::cast_possible_truncation)] - delta: (position_delta * 4096.0).to_array().map(|x| x as i16), - on_ground: grounded, + head_yaw: ByteAngle::from_degrees(**yaw), }; bundle.add_packet(&packet).unwrap(); } - if look_changed { - let packet = play::RotateS2c { + if needs_teleport { + let packet = play::EntityPositionS2c { entity_id, + position: position.as_dvec3(), yaw: ByteAngle::from_degrees(**yaw), pitch: ByteAngle::from_degrees(**pitch), on_ground: grounded, @@ -367,40 +386,20 @@ impl Module for EntityStateSyncModule { bundle.add_packet(&packet).unwrap(); } - let packet = play::EntitySetHeadYawS2c { - entity_id, - head_yaw: ByteAngle::from_degrees(**yaw), - }; - - bundle.add_packet(&packet).unwrap(); - } + }); - if needs_teleport { - let packet = play::EntityPositionS2c { + if velocity.0 != Vec3::ZERO { + let packet = play::EntityVelocityUpdateS2c { entity_id, - position: position.as_dvec3(), - yaw: ByteAngle::from_degrees(**yaw), - pitch: ByteAngle::from_degrees(**pitch), - on_ground: grounded, + velocity: velocity.to_packet_units(), }; bundle.add_packet(&packet).unwrap(); } - }); - if velocity.0 != Vec3::ZERO { - - let packet = play::EntityVelocityUpdateS2c { - entity_id, - velocity: velocity.to_packet_units(), - }; - - bundle.add_packet(&packet).unwrap(); - } - - bundle.broadcast_local(chunk_pos).unwrap(); - }, - ); + bundle.broadcast_local(chunk_pos).unwrap(); + }, + ); system!( "update_projectile_positions", @@ -410,81 +409,80 @@ impl Module for EntityStateSyncModule { &Owner, ?&ConnectionId ) - .multi_threaded() - .kind::() - .with_enum_wildcard::() - .each_iter(|it, row, (position, velocity, owner, connection_id)| { - if let Some(_connection_id) = connection_id { - return; - } - - let system = it.system(); - let world = system.world(); - let _entity = it.entity(row); + .multi_threaded() + .kind::() + .with_enum_wildcard::() + .each_iter(|it, row, (position, velocity, owner, connection_id)| { + if let Some(_connection_id) = connection_id { + return; + } - if velocity.0 != Vec3::ZERO { - // let (new_yaw, new_pitch) = get_rotation_from_velocity(velocity.0); - let center = **position; + let system = it.system(); + let world = system.world(); + let _entity = it.entity(row); - // getting max distance - let distance = velocity.0.length(); + if velocity.0 != Vec3::ZERO { + // let (new_yaw, new_pitch) = get_rotation_from_velocity(velocity.0); + let center = **position; - let ray = geometry::ray::Ray::new(center, velocity.0) * distance; + // getting max distance + let distance = velocity.0.length(); - let Some(collision) = - get_first_collision(ray, &world, Some(owner.entity)) else { - // Drag (0.99 / 20.0) - // 1.0 - (0.99 / 20.0) * 0.05 - velocity.0 *= 0.997_525; + let ray = geometry::ray::Ray::new(center, velocity.0) * distance; - // Gravity (20 MPSS) - velocity.0.y -= 0.05; + let Some(collision) = get_first_collision(ray, &world, Some(owner.entity)) else { + // Drag (0.99 / 20.0) + // 1.0 - (0.99 / 20.0) * 0.05 + velocity.0 *= 0.997_525; - // Terminal Velocity max (100.0) - velocity.0 = velocity.0.clamp_length_max(100.0); + // Gravity (20 MPSS) + velocity.0.y -= 0.05; - position.x += velocity.0.x; - position.y += velocity.0.y; - position.z += velocity.0.z; - return; - }; - - match collision { - Either::Left(entity) => { - let entity = entity.entity_view(world); - // send event - world.get::<&mut Events>(|events| events.push( - event::ProjectileEntityEvent { - client: *entity, - projectile: *_entity, - }, - &world - )); - }, - Either::Right(collision) => { - // send event - world.get::<&mut Events>(|events| events.push( - event::ProjectileBlockEvent { - collision: collision, - projectile: *_entity, - }, - &world - )); - } - } + // Terminal Velocity max (100.0) + velocity.0 = velocity.0.clamp_length_max(100.0); - /* debug!("collision = {collision:?}"); + position.x += velocity.0.x; + position.y += velocity.0.y; + position.z += velocity.0.z; + return; + }; - velocity.0 = Vec3::ZERO; */ + match collision { + Either::Left(entity) => { + let entity = entity.entity_view(world); + // send event + world.get::<&mut Events>(|events| events.push( + event::ProjectileEntityEvent { + client: *entity, + projectile: *_entity, + }, + &world, + )); + } + Either::Right(collision) => { + // send event + world.get::<&mut Events>(|events| events.push( + event::ProjectileBlockEvent { + collision: collision, + projectile: *_entity, + }, + &world, + )); + } + } - /* // Set arrow position to the collision location - **position = collision.normal; + /* debug!("collision = {collision:?}"); + + velocity.0 = Vec3::ZERO; */ - blocks - .set_block(collision.location, BlockState::DIRT) - .unwrap(); */ - } - }); + /* // Set arrow position to the collision location + **position = collision.normal; + + blocks + .set_block(collision.location, BlockState::DIRT) + .unwrap(); */ + } + }); track_previous::(world); track_previous::(world); diff --git a/crates/hyperion/tests/collision.rs b/crates/hyperion/tests/collision.rs index b5914218..f60533af 100644 --- a/crates/hyperion/tests/collision.rs +++ b/crates/hyperion/tests/collision.rs @@ -8,15 +8,23 @@ use std::{assert_matches::assert_matches, collections::HashSet}; use approx::assert_relative_eq; -use flecs_ecs::{core::{ - flecs, Entity, EntityView, EntityViewGet, QueryBuilderImpl, SystemAPI, World, WorldGet -}, macros::system}; +use flecs_ecs::{ + core::{ + Entity, EntityView, EntityViewGet, QueryBuilderImpl, SystemAPI, World, WorldGet, flecs, + }, + macros::system, +}; use geometry::{aabb::Aabb, ray::Ray}; use glam::{IVec3, Vec3}; use hyperion::{ - runtime::AsyncRuntime, simulation::{ - blocks::Blocks, entity_kind::EntityKind, event, EntitySize, Owner, Pitch, Position, Spawn, Velocity, Yaw - }, spatial::{self, Spatial, SpatialIndex, SpatialModule}, storage::EventQueue, HyperionCore + HyperionCore, + runtime::AsyncRuntime, + simulation::{ + EntitySize, Owner, Pitch, Position, Spawn, Velocity, Yaw, blocks::Blocks, + entity_kind::EntityKind, event, + }, + spatial::{self, Spatial, SpatialIndex, SpatialModule}, + storage::EventQueue, }; use spatial::get_first_collision; @@ -26,17 +34,7 @@ fn test_get_first_collision() { world.import::(); world.import::(); world.import::(); - world.get::<&AsyncRuntime>(|runtime| { - const URL: &str = "https://github.com/andrewgazelka/maps/raw/main/GenMap.tar.gz"; - - let f = hyperion_utils::cached_save(&world, URL); - - let save = runtime.block_on(f).unwrap_or_else(|e| { - panic!("failed to download map {URL}: {e}"); - }); - - world.set(Blocks::new(&world, &save).unwrap()); - }); + world.import::(); // Make all entities have Spatial component so they are spatially indexed world diff --git a/crates/hyperion/tests/entity.rs b/crates/hyperion/tests/entity.rs index bc767e21..7f958e42 100644 --- a/crates/hyperion/tests/entity.rs +++ b/crates/hyperion/tests/entity.rs @@ -45,7 +45,7 @@ fn arrow() { world.progress(); arrow.get::<&Position>(|position| { - // since velocity.y is 1.0, the arrow should be at y = 20.0 + (1.0 * drag - gravity) = 20.947525 + // since velocity.y is 1.0, the arrow should be at y = 20.0 + (1.0 * drag - gravity) = 20.947525 assert_eq!(*position, Position::new(0.0, 20.947_525, 0.0)); }); diff --git a/events/tag/src/module/bow.rs b/events/tag/src/module/bow.rs index 082d8f24..ed29e1e2 100644 --- a/events/tag/src/module/bow.rs +++ b/events/tag/src/module/bow.rs @@ -6,9 +6,17 @@ use flecs_ecs::{ }; use glam::I16Vec2; use hyperion::{ - glam::Vec3, net::Compose, simulation::{ - entity_kind::EntityKind, event, get_direction_from_rotation, metadata::living_entity::{ArrowsInEntity, HandStates}, Owner, Pitch, Player, Position, Spawn, Uuid, Velocity, Yaw - }, storage::{EventQueue, Events}, valence_protocol::packets::play, ItemKind, ItemStack + ItemKind, ItemStack, + glam::Vec3, + net::Compose, + simulation::{ + Owner, Pitch, Player, Position, Spawn, Uuid, Velocity, Yaw, + entity_kind::EntityKind, + event, get_direction_from_rotation, + metadata::living_entity::{ArrowsInEntity, HandStates}, + }, + storage::{EventQueue, Events}, + valence_protocol::packets::play, }; use hyperion_inventory::PlayerInventory; use hyperion_utils::EntityExt;