diff --git a/Cargo.lock b/Cargo.lock index 58814f54..fe9182eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1762,6 +1762,18 @@ dependencies = [ "valence_protocol", ] +[[package]] +name = "hyperion-item" +version = "0.1.0" +dependencies = [ + "bytemuck", + "derive_more", + "flecs_ecs", + "hyperion", + "hyperion-inventory", + "valence_protocol", +] + [[package]] name = "hyperion-minecraft-proto" version = "0.1.0" @@ -1830,9 +1842,12 @@ dependencies = [ name = "hyperion-rank-tree" version = "0.1.0" dependencies = [ + "bytemuck", "clap", + "flecs_ecs", "hyperion", "hyperion-inventory", + "hyperion-item", "toml", "valence_protocol", ] @@ -2290,16 +2305,6 @@ dependencies = [ "rawpointer", ] -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - [[package]] name = "memchr" version = "2.7.4" @@ -4124,7 +4129,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", - "md-5", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index e834e3ff..1ab480e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,4 @@ +[profile] [profile.release-debug] debug = true inherits = 'release' @@ -30,6 +31,7 @@ members = [ 'crates/hyperion-clap', 'crates/hyperion-command', 'crates/hyperion-rank-tree', + 'crates/hyperion-item', ] resolver = '2' @@ -49,6 +51,7 @@ convert_case = '0.6.0' criterion = '0.5.1' derive-build = '0.1.1' directories = '5.0.1' +dotenvy = '0.15.7' enumset = '1.1.5' fastrand = '2.1.0' futures-util = '0.3.31' @@ -56,7 +59,6 @@ glam = '0.29.0' heapless = '0.8.0' heed = '0.20.5' hex = '0.4.3' -indexmap = { version = '2.6.0', features = ['rayon'] } itertools = '0.13.0' kanal = '0.1.0-pre8' libc = '0.2.155' @@ -141,6 +143,9 @@ path = 'crates/hyperion-event-macros' [workspace.dependencies.hyperion-inventory] path = 'crates/hyperion-inventory' +[workspace.dependencies.hyperion-item] +path = 'crates/hyperion-item' + [workspace.dependencies.hyperion-nerd-font] path = 'crates/hyperion-nerd-font' @@ -165,6 +170,10 @@ path = 'crates/hyperion-text' [workspace.dependencies.hyperion-utils] path = 'crates/hyperion-utils' +[workspace.dependencies.indexmap] +features = ['rayon'] +version = '2.6.0' + [workspace.dependencies.ndarray] features = ['blas'] version = '0.16.1' diff --git a/crates/bvh-region/Cargo.toml b/crates/bvh-region/Cargo.toml index 8ede5c7e..881e148b 100644 --- a/crates/bvh-region/Cargo.toml +++ b/crates/bvh-region/Cargo.toml @@ -19,20 +19,20 @@ plotters = {workspace = true, features = [ "image" ], optional = true} plotters-bitmap = {workspace = true, optional = true} -serde = {workspace = true, features = ["derive"]} -arrayvec.workspace = true -fastrand.workspace = true -itertools.workspace = true -ordered-float.workspace = true -rayon.workspace = true -tracing.workspace = true +serde = {workspace = true} +arrayvec = {workspace = true} +fastrand = {workspace = true} +itertools = {workspace = true} +ordered-float = {workspace = true} +rayon = {workspace = true} +tracing = {workspace = true} [dev-dependencies] -criterion.workspace = true -divan.workspace = true -rand.workspace = true -tango-bench.workspace = true -tracing-subscriber.workspace = true +criterion = {workspace = true} +divan = {workspace = true} +rand = {workspace = true} +tango-bench = {workspace = true} +tracing-subscriber = {workspace = true} [features] default = [] diff --git a/crates/hyperion-clap/Cargo.toml b/crates/hyperion-clap/Cargo.toml index fd180ca8..ce9ab55d 100644 --- a/crates/hyperion-clap/Cargo.toml +++ b/crates/hyperion-clap/Cargo.toml @@ -9,12 +9,12 @@ readme = "README.md" publish = false [dependencies] -clap = { version = "4.5.20", features = ["derive"] } -flecs_ecs.workspace = true -hyperion-command.workspace = true -tracing.workspace = true -hyperion.workspace = true -valence_protocol.workspace = true +clap ={ workspace = true } +flecs_ecs = { workspace = true } +hyperion-command = { workspace = true } +tracing = { workspace = true } +hyperion = { workspace = true } +valence_protocol = { workspace = true } [lints] workspace = true diff --git a/crates/hyperion-command/Cargo.toml b/crates/hyperion-command/Cargo.toml index 78b15862..57be032b 100644 --- a/crates/hyperion-command/Cargo.toml +++ b/crates/hyperion-command/Cargo.toml @@ -9,11 +9,11 @@ readme = "README.md" publish = false [dependencies] -flecs_ecs.workspace = true -hyperion.workspace = true -gxhash.workspace = true -tracing.workspace = true -indexmap.workspace = true +flecs_ecs = {workspace = true} +hyperion = {workspace = true} +gxhash = {workspace = true} +tracing = {workspace = true} +indexmap = {workspace = true} [lints] workspace = true diff --git a/crates/hyperion-crafting/Cargo.toml b/crates/hyperion-crafting/Cargo.toml index 49c9cbdf..3632f4a2 100644 --- a/crates/hyperion-crafting/Cargo.toml +++ b/crates/hyperion-crafting/Cargo.toml @@ -1,11 +1,11 @@ cargo-features = ["edition2024"] [dependencies] -anyhow.workspace = true -derive-build.workspace = true -flecs_ecs.workspace = true -slotmap.workspace = true -valence_protocol.workspace = true +anyhow = { workspace = true } +derive-build = { workspace = true } +flecs_ecs = { workspace = true } +slotmap = { workspace = true } +valence_protocol = { workspace = true } [lints] workspace = true diff --git a/crates/hyperion-event-macros/src/lib.rs b/crates/hyperion-event-macros/src/lib.rs index 7d896ecc..c7335ddb 100644 --- a/crates/hyperion-event-macros/src/lib.rs +++ b/crates/hyperion-event-macros/src/lib.rs @@ -178,6 +178,12 @@ impl EventsInput { fn generate(&self) -> proc_macro2::TokenStream { // Generate all fields and initializers let fields = self.events.iter().map(EventType::generate_field); + + let field_idents = self.events.iter().map(|event| { + let field_name = event.ident.to_string().to_case(Case::Snake); + format_ident!("{field_name}") + }); + let initializers = self.events.iter().map(EventType::generate_initializer); // Generate all trait implementations @@ -197,6 +203,15 @@ impl EventsInput { #(#initializers)* } } + + pub fn clear(&mut self) { + #( + let ptr = self.#field_idents.0; + let ptr = ptr.cast_mut(); + let ptr = unsafe { &mut *ptr }; + ptr.clear(); + )* + } } }; diff --git a/crates/hyperion-inventory/Cargo.toml b/crates/hyperion-inventory/Cargo.toml index 64a0f34a..740732da 100644 --- a/crates/hyperion-inventory/Cargo.toml +++ b/crates/hyperion-inventory/Cargo.toml @@ -1,11 +1,11 @@ cargo-features = ["edition2024"] [dependencies] -flecs_ecs.workspace = true -hyperion-crafting.workspace = true -roaring.workspace = true -snafu.workspace = true -valence_protocol.workspace = true +hyperion-crafting = {workspace = true} +roaring = {workspace = true} +snafu = {workspace = true} +valence_protocol = {workspace = true} +flecs_ecs = {workspace = true} [lints] workspace = true diff --git a/crates/hyperion-inventory/src/lib.rs b/crates/hyperion-inventory/src/lib.rs index f23ae163..0c21a8db 100644 --- a/crates/hyperion-inventory/src/lib.rs +++ b/crates/hyperion-inventory/src/lib.rs @@ -1,6 +1,6 @@ use std::cmp::min; -use flecs_ecs::macros::Component; +use flecs_ecs::{core::World, macros::Component, prelude::Module}; use roaring::RoaringBitmap; use valence_protocol::{ItemKind, ItemStack}; @@ -483,3 +483,12 @@ pub const OFFHAND_SLOT: u16 = 45; // assert_eq!(inventory.get(37).unwrap().count, 63); // } // } + +#[derive(Component)] +pub struct InventoryModule; + +impl Module for InventoryModule { + fn module(world: &World) { + world.component::(); + } +} diff --git a/crates/hyperion-item/.gitignore b/crates/hyperion-item/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/crates/hyperion-item/.gitignore @@ -0,0 +1 @@ +/target diff --git a/crates/hyperion-item/Cargo.toml b/crates/hyperion-item/Cargo.toml new file mode 100644 index 00000000..dd1005a1 --- /dev/null +++ b/crates/hyperion-item/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "hyperion-item" +version = "0.1.0" +edition = "2021" +authors = ["Andrew Gazelka "] +readme = "README.md" +publish = false + +[dependencies] +flecs_ecs = { workspace = true } +bytemuck = "1.19.0" +valence_protocol = { workspace = true } +hyperion = { workspace = true } +hyperion-inventory = { workspace = true } +derive_more = { workspace = true } + +[lints] +workspace = true diff --git a/crates/hyperion-item/README.md b/crates/hyperion-item/README.md new file mode 100644 index 00000000..ab228fbd --- /dev/null +++ b/crates/hyperion-item/README.md @@ -0,0 +1 @@ +# hyperion-item \ No newline at end of file diff --git a/crates/hyperion-rank-tree/src/util.rs b/crates/hyperion-item/src/builder.rs similarity index 90% rename from crates/hyperion-rank-tree/src/util.rs rename to crates/hyperion-item/src/builder.rs index 9cc82006..20caf808 100644 --- a/crates/hyperion-rank-tree/src/util.rs +++ b/crates/hyperion-item/src/builder.rs @@ -1,3 +1,4 @@ +use flecs_ecs::core::Entity; use valence_protocol::{nbt, nbt::Value, ItemKind, ItemStack}; /// A builder for creating Minecraft items with NBT data @@ -97,6 +98,17 @@ impl ItemBuilder { self } + pub fn handler(mut self, handler: Entity) -> Self { + let nbt = self.nbt.get_or_insert_with(nbt::Compound::new); + let id = handler.0; + + // we are explicitly casting to i64 because although sign might be lost, when we read it back, + // we will revert it back to a u64. + let id: i64 = bytemuck::cast(id); + nbt.insert("Handler", Value::Long(id)); + self + } + pub fn glowing(mut self) -> Self { let nbt = self.nbt.get_or_insert_with(nbt::Compound::new); nbt.insert( @@ -122,6 +134,7 @@ impl ItemBuilder { self } + #[must_use] pub fn build(self) -> ItemStack { ItemStack::new(self.kind, self.count, self.nbt) } diff --git a/crates/hyperion-item/src/lib.rs b/crates/hyperion-item/src/lib.rs new file mode 100644 index 00000000..be906fd4 --- /dev/null +++ b/crates/hyperion-item/src/lib.rs @@ -0,0 +1,59 @@ +use derive_more::{Constructor, Deref, DerefMut}; +use flecs_ecs::{ + core::{EntityViewGet, QueryBuilderImpl, SystemAPI, TermBuilderImpl, World, WorldGet}, + macros::Component, + prelude::Module, +}; +use hyperion::storage::{EventHandler, EventHandlers, GlobalEventHandlers}; +use valence_protocol::{nbt, Hand}; + +pub mod builder; + +#[derive(Component)] +pub struct ItemModule; + +#[derive(Component, Constructor, Deref, DerefMut)] +pub struct Handler { + on_click: EventHandler, +} + +impl Module for ItemModule { + fn module(world: &World) { + world.import::(); + world.component::(); + + world.get::<&mut GlobalEventHandlers>(|handlers| { + handlers.click.register(|query, hand| { + let world = query.world; + let inventory = &mut *query.inventory; + + let stack = inventory.get_cursor(); + + if stack.is_empty() { + return; + } + + let Some(nbt) = stack.nbt.as_ref() else { + return; + }; + + let Some(handler) = nbt.get("Handler") else { + return; + }; + + let nbt::Value::Long(id) = handler else { + return; + }; + + let id: u64 = bytemuck::cast(*id); + + let handler = world.entity_from_id(id); + + handler.try_get::<&Handler>(|handler| { + let on_interact = &handler.on_click; + on_interact.trigger(query, hand); + }); + }); + }); + } +} diff --git a/crates/hyperion-permission/Cargo.toml b/crates/hyperion-permission/Cargo.toml index 684e6711..e36c455a 100644 --- a/crates/hyperion-permission/Cargo.toml +++ b/crates/hyperion-permission/Cargo.toml @@ -1,15 +1,15 @@ cargo-features = ["edition2024"] [dependencies] -anyhow.workspace = true -clap.workspace = true -flecs_ecs.workspace = true -heed.workspace = true -hyperion.workspace = true -num-derive.workspace = true -num-traits.workspace = true -tracing.workspace = true -uuid.workspace = true +anyhow = {workspace = true} +clap = {workspace = true} +flecs_ecs = {workspace = true} +heed = {workspace = true} +hyperion = {workspace = true} +num-derive = {workspace = true} +num-traits = {workspace = true} +tracing = {workspace = true} +uuid = {workspace = true} [lints] workspace = true diff --git a/crates/hyperion-proto/Cargo.toml b/crates/hyperion-proto/Cargo.toml index 508ac67a..0ad7e7a9 100644 --- a/crates/hyperion-proto/Cargo.toml +++ b/crates/hyperion-proto/Cargo.toml @@ -3,7 +3,7 @@ cargo-features = ["edition2024"] [build-dependencies] [dependencies] -rkyv.workspace = true +rkyv = {workspace = true} [lints] workspace = true diff --git a/crates/hyperion-proxy/Cargo.toml b/crates/hyperion-proxy/Cargo.toml index d9e356e6..60044231 100644 --- a/crates/hyperion-proxy/Cargo.toml +++ b/crates/hyperion-proxy/Cargo.toml @@ -1,24 +1,24 @@ cargo-features = ["edition2024"] [dependencies] -colored.workspace = true -kanal.workspace = true -papaya.workspace = true -rkyv.workspace = true -rustc-hash.workspace = true +colored = {workspace = true} +kanal = {workspace = true} +papaya = {workspace = true} +rkyv = {workspace = true} +rustc-hash = {workspace = true} tokio = {workspace = true, features = ["full", "tracing"]} tokio-util = {workspace = true, features = ["full"]} -anyhow.workspace = true -bvh.workspace = true -bytes.workspace = true -clap.workspace = true -glam.workspace = true -heapless.workspace = true -hyperion-proto.workspace = true -more-asserts.workspace = true -slotmap.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true +anyhow = {workspace = true} +bvh = {workspace = true} +bytes = {workspace = true} +clap = {workspace = true} +glam = {workspace = true} +heapless = {workspace = true} +hyperion-proto = {workspace = true} +more-asserts = {workspace = true} +slotmap = {workspace = true} +tracing = {workspace = true} +tracing-subscriber = {workspace = true} [lints] workspace = true diff --git a/crates/hyperion-rank-tree/Cargo.toml b/crates/hyperion-rank-tree/Cargo.toml index a0af7720..c4667c68 100644 --- a/crates/hyperion-rank-tree/Cargo.toml +++ b/crates/hyperion-rank-tree/Cargo.toml @@ -7,11 +7,14 @@ readme = "README.md" publish = false [dependencies] -hyperion-inventory.workspace = true -hyperion.workspace = true -toml.workspace = true -valence_protocol.workspace = true -clap.workspace = true +hyperion-inventory = { workspace = true } +hyperion = { workspace = true } +toml = { workspace = true } +valence_protocol = { workspace = true } +clap = { workspace = true } +flecs_ecs = { workspace = true } +bytemuck = { workspace = true } +hyperion-item.workspace = true [lints] workspace = true diff --git a/crates/hyperion-rank-tree/src/inventory.rs b/crates/hyperion-rank-tree/src/inventory.rs index 764c0e1b..676e7147 100644 --- a/crates/hyperion-rank-tree/src/inventory.rs +++ b/crates/hyperion-rank-tree/src/inventory.rs @@ -1,10 +1,9 @@ +use flecs_ecs::core::{World, WorldGet}; use hyperion_inventory::PlayerInventory; +use hyperion_item::builder::{AttackDamage, ItemBuilder}; use valence_protocol::ItemKind; -use crate::{ - util::{AttackDamage, ItemBuilder}, - Rank, Team, -}; +use crate::{Handles, Rank, Team}; impl Team { pub const fn build_item(self) -> ItemBuilder { @@ -19,7 +18,7 @@ impl Team { } impl Rank { - pub fn apply_inventory(self, team: Team, inventory: &mut PlayerInventory) { + pub fn apply_inventory(self, team: Team, inventory: &mut PlayerInventory, world: &World) { const MAIN_SLOT: u16 = 0; const PICKAXE_SLOT: u16 = 1; const BUILD_SLOT: u16 = 2; @@ -32,10 +31,17 @@ impl Rank { let upgrades = ["Speed", "Vision", "Health", "Armor", "Damage"]; - for (i, upgrade) in upgrades.into_iter().enumerate() { - let slot = u16::try_from(i).unwrap() + UPGRADE_START_SLOT; - inventory.set_hotbar(slot, upgrade_not_available.clone().name(upgrade).build()); - } + world.get::<&Handles>(|handles| { + for (i, upgrade) in upgrades.into_iter().enumerate() { + let slot = u16::try_from(i).unwrap() + UPGRADE_START_SLOT; + let item = upgrade_not_available + .clone() + .name(upgrade) + .handler(handles.speed) + .build(); + inventory.set_hotbar(slot, item); + } + }); let default_pickaxe = ItemBuilder::new(ItemKind::WoodenPickaxe).build(); inventory.set_hotbar(PICKAXE_SLOT, default_pickaxe); diff --git a/crates/hyperion-rank-tree/src/lib.rs b/crates/hyperion-rank-tree/src/lib.rs index 6f643d68..f5cc736a 100644 --- a/crates/hyperion-rank-tree/src/lib.rs +++ b/crates/hyperion-rank-tree/src/lib.rs @@ -1,11 +1,16 @@ use clap::ValueEnum; +use flecs_ecs::{ + core::{Entity, IdOperations, World, WorldGet}, + macros::Component, + prelude::Module, +}; +use hyperion::{net::Compose, storage::EventHandler}; +use valence_protocol::Hand; pub mod inventory; pub mod skin; -mod util; - -#[derive(Copy, Clone, Debug, ValueEnum, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, ValueEnum, PartialEq, Eq, Component)] #[repr(C)] pub enum Rank { /// ![Widget Example](https://i.imgur.com/pW7v0Xn.png) @@ -29,3 +34,28 @@ pub enum Team { White, Blue, } + +#[derive(Component)] +pub struct RankTree; + +#[derive(Component)] +pub struct Handles { + pub speed: Entity, +} + +impl Module for RankTree { + fn module(world: &World) { + world.import::(); + world.component::(); + world.component::(); + + let handler = EventHandler::new(|query, hand| { + let cursor = query.inventory.get_cursor(); + println!("clicked {cursor:?}"); + }); + + let speed = world.entity().set(hyperion_item::Handler::new(handler)); + + world.set(Handles { speed: speed.id() }); + } +} diff --git a/crates/hyperion-utils/Cargo.toml b/crates/hyperion-utils/Cargo.toml index ba7d4030..b6ae0695 100644 --- a/crates/hyperion-utils/Cargo.toml +++ b/crates/hyperion-utils/Cargo.toml @@ -1,18 +1,18 @@ cargo-features = ["edition2024"] [dependencies] -flecs_ecs.workspace = true -anyhow.workspace = true -directories.workspace = true -sha2.workspace = true -hex.workspace = true -tracing.workspace = true -reqwest.workspace = true -flate2.workspace = true -tar.workspace = true -tokio-util.workspace = true -tokio.workspace = true -futures-util.workspace = true +flecs_ecs = {workspace = true} +anyhow = {workspace = true} +directories = {workspace = true} +sha2 = {workspace = true} +hex = {workspace = true} +tracing = {workspace = true} +reqwest = {workspace = true} +flate2 = {workspace = true} +tar = {workspace = true} +tokio-util = {workspace = true} +tokio = {workspace = true} +futures-util = {workspace = true} [lints] workspace = true diff --git a/crates/hyperion/Cargo.toml b/crates/hyperion/Cargo.toml index b054c3c0..6e00645d 100644 --- a/crates/hyperion/Cargo.toml +++ b/crates/hyperion/Cargo.toml @@ -12,67 +12,67 @@ name = "atomic" colored = "2.1.0" flate2 = {workspace = true, features = ["zlib-ng"]} glam = {workspace = true, features = ["serde"]} -rkyv.workspace = true +rkyv = {workspace = true} roaring = {workspace = true, features = ["simd"]} serde = {workspace = true, features = ["derive"]} tokio = {workspace = true, features = ["full", "tracing"]} tracing = {workspace = true} -tracing-tracy.workspace = true -uuid = {workspace = true, features = ["v3"]} -anyhow.workspace = true -base64.workspace = true -bitfield-struct.workspace = true -bitvec.workspace = true -bumpalo.workspace = true -bvh-region.workspace = true -bytemuck.workspace = true -byteorder.workspace = true -bytes.workspace = true -derive_more.workspace = true -enumset.workspace = true -fastrand.workspace = true -flecs_ecs.workspace = true -heapless.workspace = true -heed.workspace = true -hyperion-crafting.workspace = true -hyperion-event-macros.workspace = true -hyperion-inventory.workspace = true -hyperion-nerd-font.workspace = true -hyperion-palette.workspace = true -hyperion-proto.workspace = true -hyperion-text.workspace = true -hyperion-utils.workspace = true -indexmap.workspace = true -itertools.workspace = true -kanal.workspace = true -libc.workspace = true -libdeflater.workspace = true -memmap2.workspace = true -more-asserts.workspace = true -ndarray.workspace = true -no_denormals.workspace = true -once_cell.workspace = true -ouroboros.workspace = true -parking_lot.workspace = true -rayon.workspace = true -reqwest.workspace = true -rustc-hash.workspace = true -serde_json.workspace = true -sha2.workspace = true -thiserror.workspace = true -toml.workspace = true -valence_anvil.workspace = true -valence_generated.workspace = true -valence_ident.workspace = true -valence_nbt.workspace = true -valence_protocol.workspace = true -valence_registry.workspace = true -valence_server.workspace = true -valence_text.workspace = true +tracing-tracy = {workspace = true} +uuid = {workspace = true} +anyhow = {workspace = true} +base64 = {workspace = true} +bitfield-struct = {workspace = true} +bitvec = {workspace = true} +bumpalo = {workspace = true} +bvh-region = {workspace = true} +bytemuck = {workspace = true} +byteorder = {workspace = true} +bytes = {workspace = true} +derive_more = {workspace = true} +enumset = {workspace = true} +fastrand = {workspace = true} +flecs_ecs = {workspace = true} +heapless = {workspace = true} +heed = {workspace = true} +hyperion-crafting = {workspace = true} +hyperion-event-macros = {workspace = true} +hyperion-inventory = {workspace = true} +hyperion-nerd-font = {workspace = true} +hyperion-palette = {workspace = true} +hyperion-proto = {workspace = true} +hyperion-text = {workspace = true} +hyperion-utils = {workspace = true} +indexmap = {workspace = true} +itertools = {workspace = true} +kanal = {workspace = true} +libc = {workspace = true} +libdeflater = {workspace = true} +memmap2 = {workspace = true} +more-asserts = {workspace = true} +ndarray = {workspace = true} +no_denormals = {workspace = true} +once_cell = {workspace = true} +ouroboros = {workspace = true} +parking_lot = {workspace = true} +rayon = {workspace = true} +reqwest = {workspace = true} +rustc-hash = {workspace = true} +serde_json = {workspace = true} +sha2 = {workspace = true} +thiserror = {workspace = true} +toml = {workspace = true} +valence_anvil = {workspace = true} +valence_generated = {workspace = true} +valence_ident = {workspace = true} +valence_nbt = {workspace = true} +valence_protocol = {workspace = true} +valence_registry = {workspace = true} +valence_server = {workspace = true} +valence_text = {workspace = true} [dev-dependencies] -divan.workspace = true -fastrand.workspace = true +divan = {workspace = true} +fastrand = {workspace = true} [lints] workspace = true diff --git a/crates/hyperion/src/egress/player_join/mod.rs b/crates/hyperion/src/egress/player_join/mod.rs index 85605c2c..31924b54 100644 --- a/crates/hyperion/src/egress/player_join/mod.rs +++ b/crates/hyperion/src/egress/player_join/mod.rs @@ -31,7 +31,7 @@ use crate::{ ingress::PendingRemove, net::{Compose, DataBundle, NetworkStreamRef}, simulation::{ - Comms, InGameName, Position, Uuid, Yaw, + Comms, Name, Position, Uuid, Yaw, command::{Command, ROOT_COMMAND, get_command_packet}, metadata::{EntityFlags, MetadataBuilder}, skin::PlayerSkin, @@ -61,7 +61,7 @@ pub fn player_join_world( root_command: Entity, query: &Query<( &Uuid, - &InGameName, + &Name, &Position, &Yaw, &Pitch, @@ -522,7 +522,7 @@ impl Module for PlayerJoinModule { fn module(world: &World) { let query = world.new_query::<( &Uuid, - &InGameName, + &Name, &Position, &Yaw, &Pitch, @@ -597,38 +597,33 @@ impl Module for PlayerJoinModule { let entity = world.entity_from_id(entity); - entity.get::<( - &Uuid, - &InGameName, - &Position, - &Yaw, - &Pitch, - &NetworkStreamRef, - )>(|(uuid, name, position, yaw, pitch, &stream_id)| { - let query = &query; - let query = &query.0; - - // if we get an error joining, we should kick the player - if let Err(e) = player_join_world( - &entity, - compose, - uuid.0, - name, - stream_id, - position, - yaw, - pitch, - &world, - &skin, - system_id, - root_command, - query, - crafting_registry, - config, - ) { - entity.set(PendingRemove::new(e.to_string())); - }; - }); + entity.get::<(&Uuid, &Name, &Position, &Yaw, &Pitch, &NetworkStreamRef)>( + |(uuid, name, position, yaw, pitch, &stream_id)| { + let query = &query; + let query = &query.0; + + // if we get an error joining, we should kick the player + if let Err(e) = player_join_world( + &entity, + compose, + uuid.0, + name, + stream_id, + position, + yaw, + pitch, + &world, + &skin, + system_id, + root_command, + query, + crafting_registry, + config, + ) { + entity.set(PendingRemove::new(e.to_string())); + }; + }, + ); let entity = world.entity_from_id(entity); entity.set(skin); diff --git a/crates/hyperion/src/ingress/mod.rs b/crates/hyperion/src/ingress/mod.rs index 13378d4e..15bdce3d 100644 --- a/crates/hyperion/src/ingress/mod.rs +++ b/crates/hyperion/src/ingress/mod.rs @@ -25,8 +25,8 @@ use crate::{ runtime::AsyncRuntime, simulation::{ AiTargetable, ChunkPosition, Comms, ConfirmBlockSequences, EntityReaction, EntitySize, - Health, IgnMap, ImmuneStatus, InGameName, PacketState, Pitch, Player, Position, - StreamLookup, Uuid, Xp, Yaw, + Health, IgnMap, ImmuneStatus, Name, PacketState, Pitch, Player, Position, StreamLookup, + Uuid, Xp, Yaw, animation::ActiveAnimation, blocks::Blocks, handlers::PacketSwitchQuery, @@ -90,7 +90,6 @@ fn process_login( compose: &Compose, entity: &EntityView<'_>, system_id: SystemId, - handlers: &GlobalEventHandlers, ign_map: &IgnMap, ) -> anyhow::Result<()> { debug_assert!( @@ -105,13 +104,11 @@ fn process_login( let username = username.0; - let mut player_join = PlayerJoinServer { + let player_join = PlayerJoinServer { username: username.to_string(), entity: entity.id(), }; - handlers.join_server.trigger_all(world, &mut player_join); - let username = player_join.username.as_str(); let global = compose.global(); @@ -164,7 +161,7 @@ fn process_login( ign_map.insert(username.clone(), entity.id(), world); entity - .set(InGameName::from(username)) + .set(Name::from(username)) .add::() .set(ImmuneStatus::default()) .set(Uuid::from(uuid)) @@ -475,7 +472,7 @@ impl Module for IngressModule { &mut hyperion_inventory::PlayerInventory, &mut ActiveAnimation, &hyperion_crafting::CraftingRegistry($), - &IgnMap($) + &IgnMap($), ) .kind::() .multi_threaded() @@ -560,7 +557,6 @@ impl Module for IngressModule { compose, &entity, system_id, - handlers, ign_map, ) { error!("failed to process login packet"); @@ -614,6 +610,7 @@ impl Module for IngressModule { inventory, animation, crafting_registry, + handlers, }; // info_span!("ingress", ign = name).in_scope(|| { diff --git a/crates/hyperion/src/lib.rs b/crates/hyperion/src/lib.rs index 648d0670..0277fe57 100644 --- a/crates/hyperion/src/lib.rs +++ b/crates/hyperion/src/lib.rs @@ -20,20 +20,6 @@ #![feature(split_array)] #![feature(never_type)] #![feature(duration_constructors)] -// todo: deny more and completely fix panics -// #![deny( -// clippy::expect_used, -// clippy::get_unwrap, -// clippy::indexing_slicing, -// clippy::missing_assert_message, -// clippy::panic, -// clippy::string_slice, -// clippy::todo, -// clippy::unimplemented, -// clippy::unwrap_in_result, -// clippy::unwrap_used, -// clippy::allow_attributes -// )] pub const NUM_THREADS: usize = 8; pub const CHUNK_HEIGHT_SPAN: u32 = 384; // 512; // usually 384 @@ -58,7 +44,7 @@ pub use uuid; // todo: slowly move more and more things to arbitrary module // and then eventually do not re-export valence_protocol pub use valence_protocol; -use valence_protocol::{CompressionThreshold, Encode, Packet}; +use valence_protocol::{CompressionThreshold, Encode, Hand, Packet}; pub use valence_protocol::{ ItemKind, ItemStack, Particle, block::{BlockKind, BlockState}, @@ -82,6 +68,7 @@ use crate::{ runtime::Tasks, simulation::{ EgressComm, EntitySize, IgnMap, PacketState, Player, + handlers::PacketSwitchQuery, metadata::{EntityFlags, Pose}, }, util::mojang::ApiProvider, @@ -149,6 +136,11 @@ pub fn adjust_file_descriptor_limits(recommended_min: u64) -> std::io::Result<() Ok(()) } +#[derive(Component)] +pub struct ClickEventHandlers { + fns: Vec, Hand)>, +} + /// The central [`Hyperion`] struct which owns and manages the entire server. pub struct Hyperion; diff --git a/crates/hyperion/src/simulation/event.rs b/crates/hyperion/src/simulation/event.rs index b0a18da1..4d14af9f 100644 --- a/crates/hyperion/src/simulation/event.rs +++ b/crates/hyperion/src/simulation/event.rs @@ -15,6 +15,13 @@ pub struct ItemDropEvent { pub location: Vec3, } +#[derive(Component, Default, Debug)] +pub struct ItemInteract { + pub entity: Entity, + pub hand: Hand, + pub sequence: i32, +} + #[derive(Debug)] pub struct ChatMessage<'a> { pub msg: &'a str, diff --git a/crates/hyperion/src/simulation/handlers.rs b/crates/hyperion/src/simulation/handlers.rs index a5480086..e8a80f88 100644 --- a/crates/hyperion/src/simulation/handlers.rs +++ b/crates/hyperion/src/simulation/handlers.rs @@ -9,6 +9,7 @@ use glam::{IVec3, Vec3}; use hyperion_utils::EntityExt; use tracing::{info, instrument, trace, warn}; use valence_generated::block::{BlockKind, BlockState, PropName}; +use valence_nbt::Value; use valence_protocol::{ Decode, Hand, ItemStack, Packet, VarInt, packets::play::{ @@ -20,7 +21,7 @@ use valence_protocol::{ use valence_text::IntoText; use super::{ - ConfirmBlockSequences, EntitySize, Position, + ConfirmBlockSequences, EntitySize, Name, Position, animation::{self, ActiveAnimation}, block_bounds, blocks::Blocks, @@ -29,7 +30,7 @@ use super::{ use crate::{ net::{Compose, NetworkStreamRef, decoder::BorrowedPacketFrame}, simulation::{Pitch, Yaw, aabb, event, event::PluginMessage}, - storage::Events, + storage::{Events, GlobalEventHandlers}, system_registry::SystemId, }; @@ -219,6 +220,8 @@ fn chat_command(mut data: &'static [u8], query: &PacketSwitchQuery<'_>) -> anyho fn hand_swing(mut data: &[u8], query: &mut PacketSwitchQuery<'_>) -> anyhow::Result<()> { let packet = play::HandSwingC2s::decode(&mut data)?; + println!("hand swing"); + match packet.hand { Hand::Main => { query.animation.push(animation::Kind::SwingMainArm); @@ -254,9 +257,10 @@ fn player_interact_entity(mut data: &[u8], query: &PacketSwitchQuery<'_>) -> any Ok(()) } -// + pub struct PacketSwitchQuery<'a> { pub id: Entity, + pub handlers: &'a GlobalEventHandlers, pub view: EntityView<'a>, pub compose: &'a Compose, pub io_ref: NetworkStreamRef, @@ -321,20 +325,25 @@ fn client_command(mut data: &[u8], query: &mut PacketSwitchQuery<'_>) -> anyhow: Ok(()) } -// // starting to wind up bow -// pub fn player_interact_item( -// mut data: &[u8], -// query: &PacketSwitchQuery<'_>, -// world: &'static World, -// ) -> anyhow::Result<()> { -// let _packet = play::PlayerInteractItemC2s::decode(&mut data)?; -// -// let id = query.id; -// -// world.send_to(id, event::ItemInteract); -// -// Ok(()) -// } + +/// Handles player interaction with items in hand +/// +/// Common uses: +/// - Starting to wind up a bow for shooting arrows +/// - Using consumable items like food or potions +/// - Throwing items like snowballs or ender pearls +/// - Using tools/items with special right-click actions (e.g. fishing rods, shields) +/// - Activating items with duration effects (e.g. chorus fruit teleport) +pub fn player_interact_item( + mut data: &'static [u8], + query: &mut PacketSwitchQuery<'_>, +) -> anyhow::Result<()> { + let packet = play::PlayerInteractItemC2s::decode(&mut data)?; + + query.handlers.click.trigger_all(query, &packet.hand); + + Ok(()) +} pub fn player_interact_block( mut data: &[u8], @@ -510,6 +519,7 @@ pub fn custom_payload( Ok(()) } +// keywords: inventory fn click_slot(mut data: &'static [u8], query: &PacketSwitchQuery<'_>) -> anyhow::Result<()> { let pkt = play::ClickSlotC2s::decode(&mut data)?; @@ -601,6 +611,7 @@ pub fn packet_switch( play::PlayerActionC2s::ID => player_action(data, query)?, play::PlayerInteractBlockC2s::ID => player_interact_block(data, query)?, play::PlayerInteractEntityC2s::ID => player_interact_entity(data, query)?, + play::PlayerInteractItemC2s::ID => player_interact_item(data, query)?, play::PositionAndOnGroundC2s::ID => position_and_on_ground(query, data)?, play::UpdateSelectedSlotC2s::ID => update_selected_slot(data, query)?, _ => trace!("unknown packet id: 0x{:02X}", packet_id), @@ -608,177 +619,3 @@ pub fn packet_switch( Ok(()) } - -// for inventory events -// fn inventory_action( -// mut data: &[u8], -// world: &'static World, -// query: &PacketSwitchQuery<'_>, -// ) -> anyhow::Result<()> { -// let packet = play::ClickSlotC2s::decode(&mut data)?; -// -// let play::ClickSlotC2s { -// window_id, -// // todo what is that for? -// something important? -// slot_idx, -// button, -// mode, -// slot_changes, -// carried_item, -// .. -// } = packet; -// -// info!("slot changes: {:?}", slot_changes); -// -// // todo support other windows like chests, etc -// if window_id != 0 { -// warn!("unsupported window id from client: {}", window_id); -// return Ok(()); -// }; -// -// let click_type = match mode { -// ClickMode::Click if slot_changes.len() == 1 => { -// let change = slot_changes.iter().next(); -// -// let Some(_) = change else { -// // todo error -// warn!("unexpected empty slot change"); -// return Ok(()); -// }; -// -// match button { -// 0 => event::ClickType::LeftClick { -// slot: slot_idx, -// // slot_change: change, -// }, -// 1 => event::ClickType::RightClick { -// slot: slot_idx, -// // slot_change: change, -// }, -// _ => { -// // Button no supported for click -// // todo error -// warn!("unexpected button for click: {}", button); -// return Ok(()); -// } -// } -// } -// ClickMode::ShiftClick if slot_changes.len() == 2 => { -// // Shift right click is identical behavior to shift left click -// match button { -// 0 => event::ClickType::ShiftLeftClick { -// slot: slot_idx, -// // slot_changes: change, -// }, -// 1 => event::ClickType::ShiftRightClick { -// slot: slot_idx, -// // slot_changes: change, -// }, -// _ => { -// // Button no supported for shift click -// // todo error -// warn!("unexpected button for shift click: {}", button); -// return Ok(()); -// } -// } -// } -// ClickMode::Hotbar if slot_changes.len() == 2 => { -// match button { -// // calculate real index -// 0..=8 => event::ClickType::HotbarKeyPress { -// button: button + 36, -// slot: slot_idx, -// // slot_changes: change, -// }, -// 40 => event::ClickType::OffHandSwap { -// slot: slot_idx, -// // slot_changes: change, -// }, -// _ => { -// // Button no supported for hotbar -// // todo error -// warn!("unexpected button for hotbar: {button}"); -// return Ok(()); -// } -// } -// } -// ClickMode::CreativeMiddleClick => event::ClickType::CreativeMiddleClick { slot: slot_idx }, -// ClickMode::DropKey if slot_changes.len() == 1 => { -// match button { -// 0 => event::ClickType::QDrop { -// slot: slot_idx, -// // slot_change: change, -// }, -// 1 => event::ClickType::QControlDrop { -// slot: slot_idx, -// // slot_change: change, -// }, -// _ => { -// // Button no supported for drop -// // todo error -// warn!("unexpected button for drop: {}", button); -// return Ok(()); -// } -// } -// } -// ClickMode::Drag => { -// match button { -// 0 => event::ClickType::StartLeftMouseDrag, -// 4 => event::ClickType::StartRightMouseDrag, -// 8 => event::ClickType::StartMiddleMouseDrag, -// 1 => event::ClickType::AddSlotLeftDrag { slot: slot_idx }, -// 5 => event::ClickType::AddSlotRightDrag { slot: slot_idx }, -// 9 => event::ClickType::AddSlotMiddleDrag { slot: slot_idx }, -// 2 => event::ClickType::EndLeftMouseDrag { -// //slot_changes: slot_changes.iter().cloned().collect(), -// }, -// 6 => event::ClickType::EndRightMouseDrag { -// //slot_changes: slot_changes.iter().cloned().collect(), -// }, -// 10 => event::ClickType::EndMiddleMouseDrag, -// _ => { -// // Button no supported for drag -// // todo error -// warn!("unexpected button for drag: {}", button); -// return Ok(()); -// } -// } -// } -// ClickMode::DoubleClick => { -// match button { -// 0 => event::ClickType::DoubleClick { -// slot: slot_idx, -// // slot_changes: slot_changes.iter().cloned().collect(), -// }, -// 1 => event::ClickType::DoubleClickReverseOrder { -// slot: slot_idx, -// // slot_changes: slot_changes.iter().cloned().collect(), -// }, -// _ => { -// // Button no supported for double click -// // todo error -// warn!("unexpected button for double click: {}", button); -// return Ok(()); -// } -// } -// } -// _ => { -// // todo error -// warn!("unexpected click mode or slot change: {:?}", mode); -// return Ok(()); -// } -// }; -// -// let id = query.id; -// -// let event = event::ClickEvent { -// click_type, -// carried_item, -// slot_changes: slot_changes.iter().cloned().collect(), -// }; -// -// world.send_to(id, event); -// -// Ok(()) -// } diff --git a/crates/hyperion/src/simulation/mod.rs b/crates/hyperion/src/simulation/mod.rs index 8c1ae5ea..5c091393 100644 --- a/crates/hyperion/src/simulation/mod.rs +++ b/crates/hyperion/src/simulation/mod.rs @@ -123,7 +123,7 @@ impl DeferredMap { /// todo: fix the meta #[derive(Component, Deref, From, Display, Debug)] #[meta] -pub struct InGameName(Arc); +pub struct Name(Arc); #[derive(Component, Deref, DerefMut, From, Debug, Default)] pub struct IgnMap(DeferredMap, Entity>); @@ -611,8 +611,8 @@ impl Module for SimModule { world.component::(); - world.component::(); - component!(world, InGameName).opaque_func(meta_ser_stringify_type_display::); + world.component::(); + component!(world, Name).opaque_func(meta_ser_stringify_type_display::); world.component::(); world.component::().meta(); diff --git a/crates/hyperion/src/storage/event/queue/mod.rs b/crates/hyperion/src/storage/event/queue/mod.rs index 20c1e042..7bf9aee3 100644 --- a/crates/hyperion/src/storage/event/queue/mod.rs +++ b/crates/hyperion/src/storage/event/queue/mod.rs @@ -45,6 +45,7 @@ fn register_and_pointer>( // Create the Events struct define_events! { + event::ItemInteract, event::SetSkin, event::AttackEntity, event::ChatMessage<'static>, diff --git a/crates/hyperion/src/storage/event/sync.rs b/crates/hyperion/src/storage/event/sync.rs index c1ce3359..a62dd2e3 100644 --- a/crates/hyperion/src/storage/event/sync.rs +++ b/crates/hyperion/src/storage/event/sync.rs @@ -2,16 +2,33 @@ use flecs_ecs::{ core::{Entity, World}, macros::Component, }; +use valence_protocol::Hand; -type EventFn = dyn Fn(&World, &mut T) -> O + Send + Sync + 'static; +use crate::simulation::handlers::PacketSwitchQuery; + +type EventFn = fn(&mut PacketSwitchQuery<'_>, &T); #[derive(Component, Default)] pub struct GlobalEventHandlers { - pub join_server: EventHandlers, + pub click: EventHandlers, } pub struct EventHandlers { - handlers: Vec>>, + handlers: Vec>, +} + +pub struct EventHandler { + handler: EventFn, +} + +impl EventHandler { + pub fn new(handler: EventFn) -> Self { + Self { handler } + } + + pub fn trigger(&self, world: &mut PacketSwitchQuery<'_>, event: &T) { + (self.handler)(world, event); + } } impl Default for EventHandlers { @@ -23,14 +40,13 @@ impl Default for EventHandlers { } impl EventHandlers { - pub fn trigger_all(&self, world: &World, event: &mut T) { + pub fn trigger_all(&self, world: &mut PacketSwitchQuery<'_>, event: &T) { for handler in &self.handlers { handler(world, event); } } - pub fn register(&mut self, handler: impl Fn(&World, &mut T) + Send + Sync + 'static) { - let handler = Box::new(handler); + pub fn register(&mut self, handler: EventFn) { self.handlers.push(handler); } } diff --git a/crates/hyperion/src/storage/thread_local.rs b/crates/hyperion/src/storage/thread_local.rs index 56f1c922..14991092 100644 --- a/crates/hyperion/src/storage/thread_local.rs +++ b/crates/hyperion/src/storage/thread_local.rs @@ -85,6 +85,13 @@ impl ThreadLocalVec { .map(|x| x.len()) .sum() } + + pub fn clear(&mut self) { + self.inner + .iter_mut() + .map(SyncUnsafeCell::get_mut) + .for_each(Vec::clear); + } } /// Structure of arrays diff --git a/events/proof-of-concept/Cargo.toml b/events/proof-of-concept/Cargo.toml index b0f28db7..9cdecc44 100644 --- a/events/proof-of-concept/Cargo.toml +++ b/events/proof-of-concept/Cargo.toml @@ -1,28 +1,28 @@ cargo-features = ["edition2024"] [dependencies] -anyhow.workspace = true -clap = {workspace = true, features = ["derive"]} -compact_str.workspace = true -dotenvy = "0.15.7" -fastrand.workspace = true -flecs_ecs.workspace = true -hyperion-clap.workspace = true -hyperion-inventory.workspace = true -hyperion-permission.workspace = true -hyperion-scheduled.workspace = true -hyperion-text.workspace = true -hyperion-utils.workspace = true -hyperion-rank-tree.workspace = true -hyperion.workspace = true -roaring.workspace = true -rustc-hash.workspace = true -tracing-subscriber.workspace = true -tracing-tracy.workspace = true -tracing.workspace = true -rayon.workspace = true -gxhash.workspace = true -derive_more.workspace = true +anyhow = { workspace = true } +clap = { workspace = true } +compact_str = { workspace = true } +dotenvy = { workspace = true } +fastrand = { workspace = true } +flecs_ecs = { workspace = true } +hyperion-clap = { workspace = true } +hyperion-inventory = { workspace = true } +hyperion-permission = { workspace = true } +hyperion-scheduled = { workspace = true } +hyperion-text = { workspace = true } +hyperion-utils = { workspace = true } +hyperion-rank-tree = { workspace = true } +hyperion = { workspace = true } +roaring = { workspace = true } +rustc-hash = { workspace = true } +tracing-subscriber = { workspace = true } +tracing-tracy = { workspace = true } +tracing = { workspace = true } +rayon = { workspace = true } +gxhash = { workspace = true } +derive_more = { workspace = true } [dev-dependencies] tracing = {workspace = true, features = ["release_max_level_info"]} diff --git a/events/proof-of-concept/src/command/rank.rs b/events/proof-of-concept/src/command/rank.rs index 9a369850..a4b13e8f 100644 --- a/events/proof-of-concept/src/command/rank.rs +++ b/events/proof-of-concept/src/command/rank.rs @@ -42,7 +42,7 @@ impl MinecraftCommand for RankCommand { )>(|(stream, uuid, inventory)| { inventory.clear(); - rank.apply_inventory(team, inventory); + rank.apply_inventory(team, inventory, world); let minecraft_id = caller.minecraft_id(); let mut bundle = DataBundle::new(compose); diff --git a/events/proof-of-concept/src/lib.rs b/events/proof-of-concept/src/lib.rs index 22b42c2a..87dfc00e 100644 --- a/events/proof-of-concept/src/lib.rs +++ b/events/proof-of-concept/src/lib.rs @@ -43,6 +43,7 @@ struct OreVeins { impl Module for ProofOfConceptModule { fn module(world: &World) { world.component::(); + world.import::(); world.component::(); world.set(OreVeins::default()); diff --git a/events/proof-of-concept/src/module/chat.rs b/events/proof-of-concept/src/module/chat.rs index 424df05d..9f39fcf1 100644 --- a/events/proof-of-concept/src/module/chat.rs +++ b/events/proof-of-concept/src/module/chat.rs @@ -5,7 +5,7 @@ use flecs_ecs::{ }; use hyperion::{ net::NetworkStreamRef, - simulation::{InGameName, Player, Position, event}, + simulation::{Name, Player, Position, event}, storage::EventQueue, system_registry::SystemId, valence_protocol::{packets::play, text::IntoText}, @@ -53,7 +53,7 @@ impl Module for ChatModule { // Check cooldown // todo: try_get if entity is dead/not found what will happen? - by.get::<(&InGameName, &Position, &mut ChatCooldown, &NetworkStreamRef)>(|(name, position, cooldown, io)| { + by.get::<(&Name, &Position, &mut ChatCooldown, &NetworkStreamRef)>(|(name, position, cooldown, io)| { // Check if player is still on cooldown if cooldown.expires > current_tick { let remaining_ticks = cooldown.expires - current_tick; diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index f661e7c7..00000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "nightly-2024-11-11" -components = ["rustfmt", "clippy", "rustc-codegen-cranelift-preview"] -profile = "minimal"