diff --git a/Cargo.toml b/Cargo.toml index b6ee776b..d234a39c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,8 @@ members = [ "minecraft-protocol", "minecraft-protocol-derive", "minecraft-server", - "minecraft-entities", - "minecraft-positions", - "tags-macros" + "minecraft-entities-derive", + "minecraft-positions" ] workspace.resolver = "2" diff --git a/minecraft-entities-derive/Cargo.toml b/minecraft-entities-derive/Cargo.toml index a65a0c22..c6922f18 100644 --- a/minecraft-entities-derive/Cargo.toml +++ b/minecraft-entities-derive/Cargo.toml @@ -8,3 +8,6 @@ proc-macro = true [dependencies] convert_case = "0.6" +proc-macro-error = "1.0.4" +quote = "1.0" +proc-macro2 = "1.0" diff --git a/minecraft-entities-derive/examples/main.rs b/minecraft-entities-derive/examples/main.rs new file mode 100644 index 00000000..c20ef6f4 --- /dev/null +++ b/minecraft-entities-derive/examples/main.rs @@ -0,0 +1,133 @@ +use minecraft_entities_derive::*; + +use std::{pin::Pin, future::Future, sync::{Mutex, Arc}}; +type CallBack = fn(O) -> Pin + Sync + Send>>; +type CallBack1 = fn(O, I) -> Pin + Sync + Send>>; +type CallBack2 = fn(O, I, J) -> Pin + Sync + Send>>; +type Eid = u32; + +trait TryAsEntityRef { + fn try_as_entity_ref(&self) -> Option<&T>; + fn try_as_entity_mut(&mut self) -> Option<&mut T>; +} + +enum AnyEntity { + Entity(Entity), + Animal(Animal), + Cow(Cow), +} + +pub struct Handler { + uuid: Eid, + world: Arc>, + entity: std::marker::PhantomData, +} + +impl Handler { + fn assume(uuid: Eid, world: Arc>) -> Self { + Self { + uuid, + world, + entity: std::marker::PhantomData, + } + } + + fn assume_other(self) -> Handler { + Handler { + uuid: self.uuid, + world: self.world, + entity: std::marker::PhantomData, + } + } +} + +// Entity + +#[MinecraftEntity( + inheritable, + ancestors { }, + descendants { Animal... }, + defines { + on_moved(self, from: f32, to: f32); + on_spawned(self); + } +)] +pub struct Entity { + +} + +impl Handler { + async fn on_moved(self, from: f32, to: f32) { + println!("Entity moved from {} to {}", from, to); + } + + async fn on_spawned(self) { + println!("Entity spawned"); + } +} + +// Animal + +#[MinecraftEntity( + inheritable, + ancestors { Entity }, + descendants { Cow }, + defines { + Entity.on_spawned(self); + on_hit(self, damage: usize); + on_jump(self); + } +)] +pub struct Animal { + entity: Entity, +} + +impl Handler { + async fn on_hit(self, damage: usize) { + println!("Animal hit with {} damage", damage); + } + + async fn on_jump(self) { + println!("Animal jumped"); + } + + async fn on_spawned(self) { + println!("Animal spawned"); + } +} + +// Cow + +#[MinecraftEntity( + ancestors { Animal, Entity }, + defines { + Entity.on_spawned(self); + Animal.on_hit(self, damage: usize); + on_milked(self); + } +)] +pub struct Cow { + animal: Animal, +} + +impl Handler { + async fn on_milked(self) { + println!("Cow milked"); + } + + async fn on_hit(self, damage: usize) { + println!("Cow hit with {} damage", damage); + } + + async fn on_spawned(self) { + println!("Cow spawned"); + } +} + +fn main() { +} + +#[test] +fn test() { + +} diff --git a/minecraft-entities-derive/src/lib.rs b/minecraft-entities-derive/src/lib.rs index 2996c77b..73aee6aa 100644 --- a/minecraft-entities-derive/src/lib.rs +++ b/minecraft-entities-derive/src/lib.rs @@ -1,7 +1,9 @@ use std::collections::HashMap; -use proc_macro::{TokenStream, TokenTree, Ident, Group}; +use proc_macro2::{TokenStream, TokenTree, Ident, Group, Delimiter}; use convert_case::{Case, Casing}; +use proc_macro_error::*; +use quote::quote; fn replace_idents(token: &mut TokenTree, to_replace: &HashMap<&'static str, Ident>) { match token { @@ -22,95 +24,208 @@ fn replace_idents(token: &mut TokenTree, to_replace: &HashMap<&'static str, Iden } } +#[allow(non_snake_case)] #[proc_macro_attribute] -pub fn inherit(attr: TokenStream, item: TokenStream) -> TokenStream { - // List inherited items - let mut inherited = Vec::new(); - let mut attrs = attr.into_iter(); - loop { - match attrs.next() { - Some(TokenTree::Ident(ident)) => inherited.push(ident), - Some(other) => panic!("unexpected token: {:?}", other), - None => break, - } - - match attrs.next() { - Some(TokenTree::Punct(punct)) => { - if punct.as_char() != ',' { - panic!("unexpected punct: {:?}", punct); - } - }, - Some(other) => panic!("unexpected token: {:?}", other), - None => break, - } - } - let parent = inherited.remove(0); +#[proc_macro_error] +pub fn MinecraftEntity(attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let attr = TokenStream::from(attr.clone()); + let item = TokenStream::from(item.clone()); + let mut ancestors = Vec::new(); + let mut descendants = Vec::new(); + let mut wildcard_descendants = Vec::new(); + let mut inheritable = false; + let mut defines = Vec::new(); + let mut codes = Vec::new(); + // Get struct name let mut items = item.clone().into_iter(); match items.next() { - Some(TokenTree::Ident(ident)) if ident.to_string() == "pub" => (), + Some(TokenTree::Ident(ident)) if ident == "pub" => (), Some(TokenTree::Punct(punct)) if punct.as_char() == '#' => { items.next(); match items.next() { - Some(TokenTree::Ident(ident)) if ident.to_string() == "pub" => (), - Some(other) => panic!("expected struct to be public, found {:?}", other), + Some(TokenTree::Ident(ident)) if ident == "pub" => (), + Some(other) => abort!(other.span(), "expected struct to be public"), None => panic!("expected public struct, found nothing"), } } - Some(other) => panic!("expected struct to be public, found {:?}", other), + Some(other) => abort!(other.span(), "expected struct to be public"), None => panic!("expected public struct, found nothing"), } match items.next() { - Some(TokenTree::Ident(ident)) if ident.to_string() == "struct" => (), - Some(other) => panic!("expected struct, found {:?}", other), + Some(TokenTree::Ident(ident)) if ident == "struct" => (), + Some(other) => abort!(other.span(), "expected struct, found {:?}"), None => panic!("expected struct, found nothing"), } let struct_name = match items.next() { Some(TokenTree::Ident(ident)) => ident, - Some(other) => panic!("expected struct name, found {:?}", other), + Some(other) => abort!(other.span(), "expected struct name, found {:?}"), None => panic!("expected struct name, found nothing"), }; - let mut codes = Vec::new(); - - // Generate code for parent - let code: TokenStream = r#" - impl ParentDescendant for This { - fn get_parent(&self) -> &Parent { &self.parent } - fn get_parent_mut(&mut self) -> &mut Parent { &mut self.parent } + // Parse attributes + let mut attrs = attr.into_iter().peekable(); + while let Some(ident) = attrs.next() { + let TokenTree::Ident(ident) = ident else { abort!(ident.span(), "expected ident") }; + match ident.to_string().as_str() { + "ancestors" => { + let Some(token_tree) = attrs.next() else { abort!(ident.span(), "expected group after parents") }; + let TokenTree::Group(group) = token_tree else { abort!(token_tree.span(), "expected group") }; + let mut group_attrs = group.stream().into_iter().peekable(); + while let Some(ident) = group_attrs.next() { + let TokenTree::Ident(ident) = ident else { abort!(ident.span(), "expected ident") }; + ancestors.push(ident); + if matches!(group_attrs.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ',') { + group_attrs.next(); + } + } + } + "descendants" => { + let Some(token_tree) = attrs.next() else { abort!(ident.span(), "expected group after parents") }; + let TokenTree::Group(group) = token_tree else { abort!(token_tree.span(), "expected group") }; + let mut group_attrs = group.stream().into_iter().peekable(); + while let Some(ident) = group_attrs.next() { + let TokenTree::Ident(ident) = ident else { abort!(ident.span(), "expected ident") }; + if matches!(group_attrs.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == '.') { + let dot = group_attrs.next().unwrap(); + if !matches!(group_attrs.next(), Some(TokenTree::Punct(punct)) if punct.as_char() == '.') { + abort!(dot.span(), "this dot needs to come with two other dots"); + } + if !matches!(group_attrs.next(), Some(TokenTree::Punct(punct)) if punct.as_char() == '.') { + abort!(dot.span(), "this dot needs to come with two other dots"); + } + wildcard_descendants.push(ident); + } else { + descendants.push(ident); + } + if matches!(group_attrs.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ',') { + group_attrs.next(); + } + } + } + "inheritable" => inheritable = true, + "defines" => { + let Some(token_tree) = attrs.next() else { abort!(ident.span(), "expected group after") }; + let TokenTree::Group(group) = token_tree else { abort!(token_tree.span(), "expected group") }; + let mut group_attrs = group.stream().into_iter().peekable(); + while group_attrs.peek().is_some() { + let TokenTree::Ident(mut method) = group_attrs.next().unwrap() else { abort!(ident.span(), "expected ident") }; + let mut ty = None; + if matches!(group_attrs.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == '.') { + let point = group_attrs.next().unwrap(); + let TokenTree::Ident(ident) = group_attrs.next().unwrap() else { abort!(point.span(), "expected method name") }; + ty = Some(method); + method = ident; + } + let Some(group) = group_attrs.next() else { abort!(method.span(), "expected group after method name") }; + let TokenTree::Group(params) = group else { abort!(group.span(), "expected group") }; + if params.delimiter() != Delimiter::Parenthesis { + abort!(params.span(), "expected parenthesis"); + } + let mut params = params.stream().into_iter().peekable(); + if matches!(params.peek(), Some(TokenTree::Ident(ident)) if ident == "self") { + params.next(); + if matches!(params.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ',') { + params.next(); + } + } else { + abort!(params.peek().unwrap().span(), "expected self as first parameter"); + } + let mut args: Vec<(Ident, TokenStream)> = Vec::new(); + while params.peek().is_some() { + let TokenTree::Ident(name) = params.next().unwrap() else { abort!(params.peek().unwrap().span(), "expected ident") }; + if !matches!(params.next(), Some(TokenTree::Punct(punct)) if punct.as_char() == ':') { + abort!(name.span(), "expected colon after name"); + } + let mut ty = TokenStream::new(); + while params.peek().is_some() && !matches!(params.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ',') { + ty.extend(params.next()); + } + params.next(); + args.push((name, ty)); + } + defines.push((ty.unwrap_or_else(|| struct_name.clone()), method, args)); + if matches!(group_attrs.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ';') { + group_attrs.next(); + } + } + } + other => abort!(ident.span(), "unrecognized identifier {}", other), } - "#.parse().unwrap(); + if matches!(attrs.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ',') { + attrs.next(); + } + } + let mut hierarchy = ancestors.clone(); + hierarchy.insert(0, struct_name.clone()); + let mut to_replace = HashMap::new(); - to_replace.insert("ParentDescendant", Ident::new(&format!("{}Descendant", parent), parent.span())); - to_replace.insert("Parent", parent.clone()); - to_replace.insert("This", struct_name.clone()); - to_replace.insert("get_parent", Ident::new(&format!("get_{}", parent.to_string().to_case(Case::Snake)), parent.span())); - to_replace.insert("get_parent_mut", Ident::new(&format!("get_{}_mut", parent.to_string().to_case(Case::Snake)), parent.span())); - to_replace.insert("parent", Ident::new(&parent.to_string().to_case(Case::Snake), parent.span())); - let mut code = code.clone().into_iter().collect::>(); - for element in &mut code { - replace_idents(element, &to_replace); + let this = struct_name.clone(); + let this_snake = Ident::new(&struct_name.to_string().to_case(Case::Snake), struct_name.span()); + let this_test = Ident::new(&format!("test_entity_{}", struct_name.to_string().to_case(Case::Snake)), struct_name.span()); + to_replace.insert("This", this.clone()); + to_replace.insert("ThisDescendant", Ident::new(&format!("{}Descendant", struct_name), struct_name.span())); + to_replace.insert("ThisMethods", Ident::new(&format!("{}Methods", struct_name), struct_name.span())); + to_replace.insert("ThisExt", Ident::new(&format!("{}Ext", struct_name), struct_name.span())); + to_replace.insert("get_this", Ident::new(&format!("get_{}", struct_name.to_string().to_case(Case::Snake)), struct_name.span())); + to_replace.insert("get_this_mut", Ident::new(&format!("get_{}_mut", struct_name.to_string().to_case(Case::Snake)), struct_name.span())); + + if !ancestors.is_empty() { + // Generate code for parent + let parent = ancestors.remove(0); + let parent_snake = Ident::new(&parent.to_string().to_case(Case::Snake), parent.span()); + let code: TokenStream = r#" + #[automatically_derived] + impl ParentDescendant for This { + fn get_parent(&self) -> &Parent { &self.parent } + fn get_parent_mut(&mut self) -> &mut Parent { &mut self.parent } + } + "#.parse().unwrap(); + to_replace.insert("ParentDescendant", Ident::new(&format!("{}Descendant", parent), parent.span())); + to_replace.insert("Parent", parent.clone()); + to_replace.insert("get_parent", Ident::new(&format!("get_{}", parent.to_string().to_case(Case::Snake)), parent.span())); + to_replace.insert("get_parent_mut", Ident::new(&format!("get_{}_mut", parent.to_string().to_case(Case::Snake)), parent.span())); + to_replace.insert("parent", Ident::new(&parent.to_string().to_case(Case::Snake), parent.span())); + let mut code = code.clone().into_iter().collect::>(); + for element in &mut code { + replace_idents(element, &to_replace); + } + let code: TokenStream = code.into_iter().collect(); + codes.push(code); + + let code = quote! { + impl From<#this> for AnyEntity { + fn from(val: #this) -> Self { + AnyEntity::#this(val) + } + } + + #[cfg(test)] + #[automatically_derived] + #[test] + fn #this_test() { + let #this_snake: AnyEntity = AnyEntity::#this(<#this as std::default::Default>::default()); + let #parent_snake: Option<&#parent> = #this_snake.try_as_entity_ref(); + assert!(#parent_snake.is_some(), "Please add {} to {} list", stringify!(#this), stringify!(#parent)); + } + }; + codes.push(code); } - let code: TokenStream = code.into_iter().collect(); - println!("{}", code); - codes.push(code); // Generate code for higher inheritance levels let code: TokenStream = r#" + #[automatically_derived] impl InheritedDescendant for This { fn get_inherited(&self) -> &Inherited { self.parent.get_inherited() } fn get_inherited_mut(&mut self) -> &mut Inherited { self.parent.get_inherited_mut() } } "#.parse().unwrap(); - for inherited in inherited { - let mut to_replace = HashMap::new(); + for inherited in &ancestors { to_replace.insert("InheritedDescendant", Ident::new(&format!("{}Descendant", inherited), inherited.span())); to_replace.insert("Inherited", inherited.clone()); - to_replace.insert("This", struct_name.clone()); to_replace.insert("get_inherited", Ident::new(&format!("get_{}", inherited.to_string().to_case(Case::Snake)), inherited.span())); to_replace.insert("get_inherited_mut", Ident::new(&format!("get_{}_mut", inherited.to_string().to_case(Case::Snake)), inherited.span())); - to_replace.insert("parent", Ident::new(&parent.to_string().to_case(Case::Snake), parent.span())); let mut code = code.clone().into_iter().collect::>(); for element in &mut code { @@ -120,69 +235,268 @@ pub fn inherit(attr: TokenStream, item: TokenStream) -> TokenStream { codes.push(code); } - // Generate final code - let mut final_code = item; - for code in codes { - final_code.extend(code); + if inheritable { + // Generate descendant trait + let code: TokenStream = r#" + #[automatically_derived] + pub trait ThisDescendant { + fn get_this(&self) -> &This; + fn get_this_mut(&mut self) -> &mut This; + } + + #[automatically_derived] + impl ThisDescendant for This { + fn get_this(&self) -> &This { self } + fn get_this_mut(&mut self) -> &mut This { self } + } + "#.parse().unwrap(); + let mut code = code.clone().into_iter().collect::>(); + for element in &mut code { + replace_idents(element, &to_replace); + } + let code: TokenStream = code.into_iter().collect(); + codes.push(code); } - final_code -} -#[proc_macro_attribute] -pub fn inheritable(_attr: TokenStream, item: TokenStream) -> TokenStream { - println!("{:?}", item); - - // Get struct name - let mut items = item.clone().into_iter(); - match items.next() { - Some(TokenTree::Ident(ident)) if ident.to_string() == "pub" => (), - Some(TokenTree::Punct(punct)) if punct.as_char() == '#' => { - items.next(); - match items.next() { - Some(TokenTree::Ident(ident)) if ident.to_string() == "pub" => (), - Some(other) => panic!("expected struct to be public, found {:?}", other), - None => panic!("expected public struct, found nothing"), - } + // Generate ext trait + let code: TokenStream = r#" + #[automatically_derived] + pub trait ThisExt: Sized + Into> { + fn methods() -> &'static ThisMethods; } - Some(other) => panic!("expected struct to be public, found {:?}", other), - None => panic!("expected public struct, found nothing"), + "#.parse().unwrap(); + let mut code = code.clone().into_iter().collect::>(); + for element in &mut code { + replace_idents(element, &to_replace); } - match items.next() { - Some(TokenTree::Ident(ident)) if ident.to_string() == "struct" => (), - Some(other) => panic!("expected struct, found {:?}", other), - None => panic!("expected struct, found nothing"), + let mut inner_codes = TokenStream::new(); + for (_, method, args) in defines.iter().filter(|(ty, _, _)| ty == &struct_name) { + let inner_code: TokenStream = match args.len() { + 0 => String::from(r#" + fn method(self) -> Pin + Sync + Send>> {{ + (Self::methods().method)(self.into()) + }} + "#), + 1 => format!(r#" + fn method(self, arg1: {}) -> Pin + Sync + Send>> {{ + (Self::methods().method)(self.into(), arg1) + }} + "#, args[0].1), + 2 => format!(r#" + fn method(self, arg1: {}, arg2: {}) -> Pin + Sync + Send>> {{ + (Self::methods().method)(self.into(), arg1, arg2) + }} + "#, args[0].1, args[1].1), + 3 => format!(r#" + fn method(self, arg1: {}, arg2: {}, arg3: {}) -> Pin + Sync + Send>> {{ + (Self::methods().method)(self.into(), arg1, arg2, arg3) + }} + "#, args[0].1, args[1].1, args[2].1), + 4 => format!(r#" + fn method(self, arg1: {}, arg2: {}, arg3: {}, arg4: {}) -> Pin + Sync + Send>> {{ + (Self::methods().method)(self.into(), arg1, arg2, arg3, arg4) + }} + "#, args[0].1, args[1].1, args[2].1, args[3].1), + _ => abort!(method.span(), "too many arguments"), + }.parse().unwrap(); + to_replace.insert("method", method.clone()); + let mut inner_code = inner_code.clone().into_iter().collect::>(); + for element in &mut inner_code { + replace_idents(element, &to_replace); + } + let inner_code: TokenStream = inner_code.into_iter().collect(); + inner_codes.extend(inner_code); } - let struct_name = match items.next() { - Some(TokenTree::Ident(ident)) => ident, - Some(other) => panic!("expected struct name, found {:?}", other), - None => panic!("expected struct name, found nothing"), - }; + let TokenTree::Group(ref mut group) = code.last_mut().unwrap() else {unreachable!()}; + *group = Group::new(group.delimiter(), group.stream().into_iter().chain(inner_codes.into_iter()).collect()); + let code: TokenStream = code.into_iter().collect(); + codes.push(code); - // Generate implementation + // Generate methods struct let code: TokenStream = r#" - pub trait ThisDescendant { - fn get_this(&self) -> &This; - fn get_this_mut(&mut self) -> &mut This; - } - - impl ThisDescendant for This { - fn get_this(&self) -> &This { self } - fn get_this_mut(&mut self) -> &mut This { self } + #[automatically_derived] + pub struct ThisMethods { + } "#.parse().unwrap(); - let mut to_replace = HashMap::new(); - to_replace.insert("This", struct_name.clone()); - to_replace.insert("ThisDescendant", Ident::new(&format!("{}Descendant", struct_name), struct_name.span())); - to_replace.insert("get_this", Ident::new(&format!("get_{}", struct_name.to_string().to_case(Case::Snake)), struct_name.span())); - to_replace.insert("get_this_mut", Ident::new(&format!("get_{}_mut", struct_name.to_string().to_case(Case::Snake)), struct_name.span())); let mut code = code.clone().into_iter().collect::>(); for element in &mut code { replace_idents(element, &to_replace); } + let mut inner_codes = TokenStream::new(); + for (_, method, args) in defines.iter().filter(|(ty, _, _)| ty == &struct_name) { + let inner_code: TokenStream = match args.len() { + 0 => String::from(r#"pub method: CallBack>,"#), + 1 => format!(r#"pub method: CallBack1, {}>,"#, args[0].1), + 2 => format!(r#"pub method: CallBack2, {}, {}>,"#, args[0].1, args[1].1), + 3 => format!(r#"pub method: CallBack3, {}, {}, {}>,"#, args[0].1, args[1].1, args[2].1), + 4 => format!(r#"pub method: CallBack4, {}, {}, {}, {}>,"#, args[0].1, args[1].1, args[2].1, args[3].1), + _ => abort!(method.span(), "too many arguments"), + }.parse().unwrap(); + to_replace.insert("method", method.clone()); + let mut inner_code = inner_code.clone().into_iter().collect::>(); + for element in &mut inner_code { + replace_idents(element, &to_replace); + } + let inner_code: TokenStream = inner_code.into_iter().collect(); + inner_codes.extend(inner_code); + } + let TokenTree::Group(ref mut group) = code.last_mut().unwrap() else {unreachable!()}; + *group = Group::new(group.delimiter(), group.stream().into_iter().chain(inner_codes.into_iter()).collect()); let code: TokenStream = code.into_iter().collect(); + codes.push(code); + + // Generate default for methods struct + let mut mod_codes = Vec::new(); + for ascendant in hierarchy.iter().peekable() { + let code: TokenStream = r#" + pub const ASCENDANT_METHODS_FOR_THIS: &AscendantMethods = &AscendantMethods { + + }; + "#.parse().unwrap(); + to_replace.insert("ASCENDANT_METHODS_FOR_THIS", Ident::new(&format!("{}_METHODS_FOR_{}", ascendant.to_string().to_case(Case::ScreamingSnake), struct_name.to_string().to_case(Case::ScreamingSnake)), ascendant.span())); + to_replace.insert("Ascendant", ascendant.clone()); + to_replace.insert("AscendantMethods", Ident::new(&format!("{}Methods", ascendant), ascendant.span())); + let mut code = code.clone().into_iter().collect::>(); + for element in &mut code { + replace_idents(element, &to_replace); + } + let mut inner_codes = TokenStream::new(); + for (_, method, args) in defines.iter().filter(|(ty, _, _)| ty == ascendant) { + let inner_code: TokenStream = match args.len() { + 0 => r#"method: |s| Box::pin(s.assume_other::().method()),"#, + 1 => r#"method: |s, arg1| Box::pin(s.assume_other::().method(arg1)),"#, + 2 => r#"method: |s, arg1, arg2| Box::pin(s.assume_other::().method(arg1, arg2)),"#, + 3 => r#"method: |s, arg1, arg2, arg3| Box::pin(s.assume_other::().method(arg1, arg2, arg3)),"#, + 4 => r#"method: |s, arg1, arg2, arg3, arg4| Box::pin(s.assume_other::().method(arg1, arg2, arg3, arg4)),"#, + _ => abort!(method.span(), "too many arguments"), + }.parse().unwrap(); + to_replace.insert("method", method.clone()); + for (i, (name, _)) in args.iter().enumerate() { + to_replace.insert(["arg1", "arg2", "arg3", "arg4"][i], name.clone()); + } + let mut inner_code = inner_code.clone().into_iter().collect::>(); + for element in &mut inner_code { + replace_idents(element, &to_replace); + } + let inner_code: TokenStream = inner_code.into_iter().collect(); + inner_codes.extend(inner_code); + } + let i = code.len() - 2; + let TokenTree::Group(ref mut group) = code.get_mut(i).unwrap() else {unreachable!()}; + *group = Group::new(group.delimiter(), group.stream().into_iter().chain(inner_codes.into_iter()).collect()); + + if ascendant != &struct_name { + let inner_code: TokenStream = r#"..*ASCENDANT_METHODS_FOR_PARENT"#.parse().unwrap(); + to_replace.insert("ASCENDANT_METHODS_FOR_PARENT", Ident::new(&format!("{}_METHODS_FOR_{}", ascendant.to_string().to_case(Case::ScreamingSnake), hierarchy[1].to_string().to_case(Case::ScreamingSnake)), ascendant.span())); + let mut inner_code = inner_code.clone().into_iter().collect::>(); + for element in &mut inner_code { + replace_idents(element, &to_replace); + } + let inner_code: TokenStream = inner_code.into_iter().collect(); + let TokenTree::Group(ref mut group) = code.get_mut(i).unwrap() else {unreachable!()}; + *group = Group::new(group.delimiter(), group.stream().into_iter().chain(inner_code.into_iter()).collect()); + } + + let code: TokenStream = code.into_iter().collect(); + mod_codes.push(code); + } + let mod_codes: TokenStream = mod_codes.into_iter().collect(); + let mod_code: TokenStream = format!(r#" + #[allow(clippy::needless_update)] + #[automatically_derived] + pub mod {struct_name}_methods {{ + use super::{{ {} }}; + }} + "#, hierarchy.iter().map(|i| format!("{i}, {i}Methods")).chain(hierarchy.iter().skip(1).map(|i| format!("{i}_methods::*"))).collect::>().join(","), + ).parse().unwrap(); + let mut mod_code = mod_code.into_iter().collect::>(); + let TokenTree::Group(ref mut group) = mod_code.last_mut().unwrap() else {unreachable!()}; + *group = Group::new(group.delimiter(), group.stream().into_iter().chain(mod_codes.into_iter()).collect()); + codes.push(mod_code.into_iter().collect()); + + // Implement ext traits + for ascendant in hierarchy.iter().peekable() { + let code: TokenStream = format!(r#" + #[automatically_derived] + impl AscendantExt for Handler {{ + fn methods() -> &'static AscendantMethods {{ + {struct_name}_methods::ASCENDANT_METHODS_FOR_THIS + }} + }} + "#).parse().unwrap(); + to_replace.insert("ASCENDANT_METHODS_FOR_THIS", Ident::new(&format!("{}_METHODS_FOR_{}", ascendant.to_string().to_case(Case::ScreamingSnake), struct_name.to_string().to_case(Case::ScreamingSnake)), ascendant.span())); + to_replace.insert("Ascendant", ascendant.clone()); + to_replace.insert("AscendantExt", Ident::new(&format!("{}Ext", ascendant), ascendant.span())); + to_replace.insert("AscendantMethods", Ident::new(&format!("{}Methods", ascendant), ascendant.span())); + let mut code = code.clone().into_iter().collect::>(); + for element in &mut code { + replace_idents(element, &to_replace); + } + let code: TokenStream = code.into_iter().collect(); + codes.push(code); + } + + // Implement conversion traits + for ascendant in hierarchy.iter().skip(1) { + let code: TokenStream = r#" + #[automatically_derived] + impl From> for Handler { + fn from(val: Handler) -> Self { + val.assume_other() + } + } + "#.parse().unwrap(); + to_replace.insert("Ascendant", ascendant.clone()); + let mut code = code.clone().into_iter().collect::>(); + for element in &mut code { + replace_idents(element, &to_replace); + } + let code: TokenStream = code.into_iter().collect(); + codes.push(code); + } + + // Implement TryAsEntityRef + let descendants_snake = descendants.iter().map(|i| Ident::new(i.to_string().to_case(Case::Snake).as_str(), i.span())).collect::>(); + let wildcard_descendants_snake = wildcard_descendants.iter().map(|i| Ident::new(i.to_string().to_case(Case::Snake).as_str(), i.span())).collect::>(); + let code = quote! { + #[automatically_derived] + impl TryAsEntityRef<#this> for AnyEntity { + fn try_as_entity_ref(&self) -> Option<&#this> { + match self { + AnyEntity::#this(#this_snake) => return Some(#this_snake), + #( AnyEntity::#descendants(#descendants_snake) => return Some(&#descendants_snake.#this_snake), )* + _ => (), + } + #( + if let Some(#wildcard_descendants_snake) = >::try_as_entity_ref(self) { + return Some(&#wildcard_descendants_snake.#this_snake) + } + )* + None + } + + fn try_as_entity_mut(&mut self) -> Option<&mut #this> { + match self { + AnyEntity::#this(#this_snake) => return Some(#this_snake), + #( AnyEntity::#descendants(#descendants_snake) => return Some(&mut #descendants_snake.#this_snake), )* + _ => (), + } + #( + if >::try_as_entity_ref(self).is_some() { + return >::try_as_entity_mut(self).map(|#wildcard_descendants_snake| &mut #wildcard_descendants_snake.#this_snake) + } + )* + None + } + } + }; + codes.push(code); - // Assemble final code + // Generate final code let mut final_code = item; - final_code.extend(code); - final_code + for code in codes { + final_code.extend(code); + } + proc_macro::TokenStream::from(final_code) } diff --git a/minecraft-entities/Cargo.toml b/minecraft-entities/Cargo.toml deleted file mode 100644 index 4d13a269..00000000 --- a/minecraft-entities/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "minecraft-entities" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -minecraft-protocol = { path = "../minecraft-protocol" } -minecraft-positions = { path = "../minecraft-positions" } -minecraft-entities-derive = { path="../minecraft-entities-derive" } diff --git a/minecraft-entities/src/animals/chicken.rs b/minecraft-entities/src/animals/chicken.rs deleted file mode 100644 index 4ecb19a6..00000000 --- a/minecraft-entities/src/animals/chicken.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Chicken { - pub animal: Animal, -} diff --git a/minecraft-entities/src/animals/cow.rs b/minecraft-entities/src/animals/cow.rs deleted file mode 100644 index c89b8f81..00000000 --- a/minecraft-entities/src/animals/cow.rs +++ /dev/null @@ -1,16 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Cow { - pub animal: Animal, -} - -#[derive(Default)] -#[inherit(Cow, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] - -pub struct Mooshroom { - pub cow: Cow, - pub variant: u8, // In the doc it is a string -} diff --git a/minecraft-entities/src/animals/donkey.rs b/minecraft-entities/src/animals/donkey.rs deleted file mode 100644 index 0e734fb3..00000000 --- a/minecraft-entities/src/animals/donkey.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inherit(ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Donkey { - pub chested_horse: ChestedHorse, -} diff --git a/minecraft-entities/src/animals/fishes.rs b/minecraft-entities/src/animals/fishes.rs deleted file mode 100644 index 1e38a964..00000000 --- a/minecraft-entities/src/animals/fishes.rs +++ /dev/null @@ -1,42 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct AbstractFish { - pub water_animal: WaterAnimal, - pub from_bucket: bool, -} - - -#[derive(Default)] -#[inherit(AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Cod { - pub abstract_fish: AbstractFish, -} - -#[derive(Default)] -#[inherit(AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct PufferFish { - pub abstract_fish: AbstractFish, - pub puff_state: usize, -} - -#[derive(Default)] -#[inherit(AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Salmon { - pub abstract_fish: AbstractFish, -} - -#[derive(Default)] -#[inherit(AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct TropicalFish { - pub abstract_fish: AbstractFish, - pub variant: usize, -} - -#[derive(Default)] -#[inherit(AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Tadpole { - pub abstract_fish: AbstractFish, -} diff --git a/minecraft-entities/src/animals/hoglin.rs b/minecraft-entities/src/animals/hoglin.rs deleted file mode 100644 index dfa35664..00000000 --- a/minecraft-entities/src/animals/hoglin.rs +++ /dev/null @@ -1,9 +0,0 @@ -use minecraft_protocol::packets::UUID; -use super::*; - -#[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Hoglin { - pub animal: Animal, - pub is_immune: bool, -} diff --git a/minecraft-entities/src/animals/horses.rs b/minecraft-entities/src/animals/horses.rs deleted file mode 100644 index fdaab191..00000000 --- a/minecraft-entities/src/animals/horses.rs +++ /dev/null @@ -1,51 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct AbstractHorse { - pub animal: Animal, - pub mask: u8, -} - -#[derive(Default)] -#[inherit(AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Horse { - pub abstract_horse: AbstractHorse, - pub variant: usize, -} - -#[derive(Default)] -#[inherit(AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct ZombieHorse { - pub abstract_horse: AbstractHorse, -} - -#[derive(Default)] -#[inherit(AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct SkeletonHorse { - pub abstract_horse: AbstractHorse, -} - -#[derive(Default)] -#[inherit(AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Camel { - pub abstract_horse: AbstractHorse, - pub is_dashing: bool, - pub last_pose_change_tick: usize, -} - -#[derive(Default)] -#[inheritable] -#[inherit(AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct ChestedHorse { - pub abstract_horse: AbstractHorse, - pub has_chest: bool, -} - -#[derive(Default)] -#[inheritable] -#[inherit(ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Mule { - pub chested_horse: ChestedHorse, -} diff --git a/minecraft-entities/src/animals/llama.rs b/minecraft-entities/src/animals/llama.rs deleted file mode 100644 index 3e814e75..00000000 --- a/minecraft-entities/src/animals/llama.rs +++ /dev/null @@ -1,36 +0,0 @@ -use super::*; - -#[inheritable] -#[inherit(ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Llama { - pub chested_horse: ChestedHorse, - /// Strength (number of columns of 3 slots in the llama's inventory once a chest is equipped) - pub stength: u8, - /// Carpet color (a dye color, or -1 if no carpet equipped) - pub carpet_color: i16, - pub variant: u8, -} - -impl Default for Llama { - fn default() -> Self { - Self { - chested_horse: ChestedHorse::default(), - stength: 0, - carpet_color: -1, - variant: 0, - } - } -} - -#[derive(Default)] -#[inherit(Llama, ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct TraderLlama { - pub llama: Llama, -} - - -#[derive(Default)] -#[inherit(Entity)] -pub struct LlamaSpit { - pub entity: Entity, -} diff --git a/minecraft-entities/src/animals/ocelot.rs b/minecraft-entities/src/animals/ocelot.rs deleted file mode 100644 index 8fdf3bfb..00000000 --- a/minecraft-entities/src/animals/ocelot.rs +++ /dev/null @@ -1,9 +0,0 @@ -use minecraft_protocol::packets::UUID; -use super::*; - -#[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Ocelot { - pub animal: Animal, - pub is_trusting: bool, -} diff --git a/minecraft-entities/src/animals/water_animal.rs b/minecraft-entities/src/animals/water_animal.rs deleted file mode 100644 index 22783dbe..00000000 --- a/minecraft-entities/src/animals/water_animal.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(PathfinderMob, Mob, LivingEntity, Entity)] -pub struct WaterAnimal { - pub pathfinder_mob: PathfinderMob, -} - -#[derive(Default)] -#[inherit(WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Dolphin { - pub water_animal: WaterAnimal, - pub treasure_position: Option, - pub has_fish: bool, - pub moisture_level: usize, -} diff --git a/minecraft-entities/src/lib.rs b/minecraft-entities/src/lib.rs deleted file mode 100644 index 97591a3f..00000000 --- a/minecraft-entities/src/lib.rs +++ /dev/null @@ -1,410 +0,0 @@ -#![allow(clippy::derivable_impls)] - -mod entity; -pub use entity::*; -mod thrown_item_projectile; -pub use thrown_item_projectile::*; -mod arrow; -pub use arrow::*; -mod boat; -pub use boat::*; -mod living_entity; -pub use living_entity::*; -mod player; -pub use player::*; -mod mobs; -pub use mobs::*; -mod interaction; -pub use interaction::*; -mod animals; -pub use animals::*; -mod display; -pub use display::*; -mod shulker; -pub use shulker::*; -mod monsters; -pub use monsters::*; -mod block; -pub use block::*; -mod particles; -pub use particles::*; -mod fire_entities; -pub use fire_entities::*; -mod item; -pub use item::*; - -pub(crate) use minecraft_positions::*; -pub(crate) use minecraft_entities_derive::{inherit, inheritable}; -pub(crate) use minecraft_protocol::{ - components::{ - entity::Pose, - slots::{Slot, SlotItem, Hand} - }, - ids::{items::Item, block_states::BlockWithState}, - nbt::NbtTag, - packets::UUID -}; - -pub type Eid = u32; - -pub enum AnyEntity { - Entity(Entity), - Display(Display), - BlockDisplay(BlockDisplay), - ItemDisplay(ItemDisplay), - TextDisplay(TextDisplay), - ThrownItemProjectile(ThrownItemProjectile), - ThrownEgg(ThrownEgg), - ThrownEnderPearl(ThrownEnderPearl), - ThrownExperienceBottle(ThrownExperienceBottle), - ThrownPotion(ThrownPotion), - Snowball(Snowball), - AbstractArrow(AbstractArrow), - Arrow(Arrow), - SpectralArrow(SpectralArrow), - ThrownTrident(ThrownTrident), - Boat(Boat), - ChestBoat(ChestBoat), - LivingEntity(LivingEntity), - Player(Player), - Mob(Mob), - AmbientCreature(AmbientCreature), - Bat(Bat), - PathfinderMob(PathfinderMob), - WaterAnimal(WaterAnimal), - Squid(Squid), - AgeableMob(AgeableMob), - Animal(Animal), - Sniffer(Sniffer), - AbstractHorse(AbstractHorse), - Horse(Horse), - ZombieHorse(ZombieHorse), - SkeletonHorse(SkeletonHorse), - Camel(Camel), - ChestedHorse(ChestedHorse), - Donkey(Donkey), - Llama(Llama), - TraderLlama(TraderLlama), - Mule(Mule), - Axolotl(Axolotl), - Bee(Bee), - Fox(Fox), - Frog(Frog), - Ocelot(Ocelot), - Panda(Panda), - Pig(Pig), - Rabbit(Rabbit), - Turtle(Turtle), - PolarBear(PolarBear), - Chicken(Chicken), - Cow(Cow), - Hoglin(Hoglin), - Mooshroom(Mooshroom), - Sheep(Sheep), - Strider(Strider), - TameableAnimal(TameableAnimal), - Cat(Cat), - Wolf(Wolf), - Parrot(Parrot), - AbstractVillager(AbstractVillager), - Villager(Villager), - WanderingTrader(WanderingTrader), - AbstractGolem(AbstractGolem), - IronGolem(IronGolem), - SnowGolem(SnowGolem), - Shulker(Shulker), - Monster(Monster), - BasePiglin(BasePiglin), - Piglin(Piglin), - PiglinBrute(PiglinBrute), - Blaze(Blaze), - Creeper(Creeper), - Endermite(Endermite), - Giant(Giant), - Goat(Goat), - Guardian(Guardian), - ElderGuardian(ElderGuardian), - Silverfish(Silverfish), - Raider(Raider), - AbstractIllager(AbstractIllager), - Vindicator(Vindicator), - Pillager(Pillager), - SpellcasterIllager(SpellcasterIllager), - Evoker(Evoker), - Illusioner(Illusioner), - Ravager(Ravager), - Witch(Witch), - EvokerFangs(EvokerFangs), - Vex(Vex), - Skeleton(Skeleton), - AbstractSkeleton(AbstractSkeleton), - WitherSkeleton(WitherSkeleton), - Stray(Stray), - Spider(Spider), - Warden(Warden), - Wither(Wither), - Zoglin(Zoglin), - Zombie(Zombie), - ZombieVillager(ZombieVillager), - Husk(Husk), - Drowned(Drowned), - ZombifiedPiglin(ZombifiedPiglin), - Enderman(Enderman), - EnderDragon(EnderDragon), - Flying(Flying), - Ghast(Ghast), - Phantom(Phantom), - Slime(Slime), - LlamaSpit(LlamaSpit), - EyeOfEnder(EyeOfEnder), - FallingBlock(FallingBlock), - AreaEffectCloud(AreaEffectCloud), - FishingHook(FishingHook), - EndCrystal(EndCrystal), - DragonFireball(DragonFireball), - SmallFireball(SmallFireball), - Fireball(Fireball), - WitherSkull(WitherSkull), - FireworkRocket(FireworkRocket), - ItemFrame(ItemFrame), - GlowingItemFrame(GlowingItemFrame), - Painting(Painting), - ItemEntity(ItemEntity), - ArmorStand(ArmorStand), - Dolphin(Dolphin), - AbstractFish(AbstractFish), - Cod(Cod), - PufferFish(PufferFish), - Salmon(Salmon), - TropicalFish(TropicalFish), - Tadpole(Tadpole), -} - -#[allow(clippy::single_match)] -impl AnyEntity { - pub fn as_entity(&self) -> &Entity { - match self { - AnyEntity::Entity(entity) => entity, - AnyEntity::Display(display) => display.get_entity(), - AnyEntity::BlockDisplay(block_display) => block_display.get_entity(), - AnyEntity::ItemDisplay(item_display) => item_display.get_entity(), - AnyEntity::TextDisplay(text_display) => text_display.get_entity(), - AnyEntity::ThrownItemProjectile(throw_item_projectile) => throw_item_projectile.get_entity(), - AnyEntity::ThrownEgg(throw_egg) => throw_egg.get_entity(), - AnyEntity::ThrownEnderPearl(throw_ender_pearl) => throw_ender_pearl.get_entity(), - AnyEntity::ThrownExperienceBottle(throw_experience_bottle) => throw_experience_bottle.get_entity(), - AnyEntity::ThrownPotion(throw_potion) => throw_potion.get_entity(), - AnyEntity::Snowball(snowball) => snowball.get_entity(), - AnyEntity::AbstractArrow(abstract_arrow) => abstract_arrow.get_entity(), - AnyEntity::Arrow(arrow) => arrow.get_entity(), - AnyEntity::SpectralArrow(spectral_arrow) => spectral_arrow.get_entity(), - AnyEntity::ThrownTrident(throw_trident) => throw_trident.get_entity(), - AnyEntity::Boat(boat) => boat.get_entity(), - AnyEntity::ChestBoat(chest_boat) => chest_boat.get_entity(), - AnyEntity::LivingEntity(living_entity) => living_entity.get_entity(), - AnyEntity::Player(player) => player.get_entity(), - AnyEntity::Mob(mob) => mob.get_entity(), - AnyEntity::AmbientCreature(ambient_creature) => ambient_creature.get_entity(), - AnyEntity::Bat(bat) => bat.get_entity(), - AnyEntity::PathfinderMob(pathfinder_mob) => pathfinder_mob.get_entity(), - AnyEntity::WaterAnimal(water_animal) => water_animal.get_entity(), - AnyEntity::Squid(squid) => squid.get_entity(), - AnyEntity::AgeableMob(ageable_mob) => ageable_mob.get_entity(), - AnyEntity::Animal(animal) => animal.get_entity(), - AnyEntity::Sniffer(sniffer) => sniffer.get_entity(), - AnyEntity::AbstractHorse(abstract_horse) => abstract_horse.get_entity(), - AnyEntity::ZombieHorse(zombie_horse) => zombie_horse.get_entity(), - AnyEntity::Horse(horse) => horse.get_entity(), - AnyEntity::SkeletonHorse(skeleton_horse) => skeleton_horse.get_entity(), - AnyEntity::Camel(camel) => camel.get_entity(), - AnyEntity::ChestedHorse(chested_horse) => chested_horse.get_entity(), - AnyEntity::Donkey(donkey) => donkey.get_entity(), - AnyEntity::Llama(llama) => llama.get_entity(), - AnyEntity::TraderLlama(trader_llama) => trader_llama.get_entity(), - AnyEntity::Mule(mule) => mule.get_entity(), - AnyEntity::Axolotl(axolotl) => axolotl.get_entity(), - AnyEntity::Bee(bee) => bee.get_entity(), - AnyEntity::Fox(fox) => fox.get_entity(), - AnyEntity::Frog(frog) => frog.get_entity(), - AnyEntity::Ocelot(ocelot) => ocelot.get_entity(), - AnyEntity::Panda(panda) => panda.get_entity(), - AnyEntity::Pig(pig) => pig.get_entity(), - AnyEntity::Rabbit(rabbit) => rabbit.get_entity(), - AnyEntity::Turtle(turtle) => turtle.get_entity(), - AnyEntity::PolarBear(polar_bear) => polar_bear.get_entity(), - AnyEntity::Chicken(chicken) => chicken.get_entity(), - AnyEntity::Cow(cow) => cow.get_entity(), - AnyEntity::Hoglin(hoglin) => hoglin.get_entity(), - AnyEntity::Mooshroom(mooshroom) => mooshroom.get_entity(), - AnyEntity::Sheep(sheep) => sheep.get_entity(), - AnyEntity::Strider(strider) => strider.get_entity(), - AnyEntity::TameableAnimal(tameable_animal) => tameable_animal.get_entity(), - AnyEntity::Cat(cat) => cat.get_entity(), - AnyEntity::Wolf(wolf) => wolf.get_entity(), - AnyEntity::Parrot(parrot) => parrot.get_entity(), - AnyEntity::AbstractVillager(abstract_villager) => abstract_villager.get_entity(), - AnyEntity::Villager(villager) => villager.get_entity(), - AnyEntity::WanderingTrader(wandering_trader) => wandering_trader.get_entity(), - AnyEntity::AbstractGolem(abstract_golem) => abstract_golem.get_entity(), - AnyEntity::IronGolem(iron_golem) => iron_golem.get_entity(), - AnyEntity::SnowGolem(snow_golem) => snow_golem.get_entity(), - AnyEntity::Shulker(shulker) => shulker.get_entity(), - AnyEntity::Monster(monster) => monster.get_entity(), - AnyEntity::BasePiglin(base_piglin) => base_piglin.get_entity(), - AnyEntity::Piglin(piglin) => piglin.get_entity(), - AnyEntity::PiglinBrute(piglin_brute) => piglin_brute.get_entity(), - AnyEntity::Blaze(blaze) => blaze.get_entity(), - AnyEntity::Creeper(creeper) => creeper.get_entity(), - AnyEntity::Endermite(endermite) => endermite.get_entity(), - AnyEntity::Giant(giant) => giant.get_entity(), - AnyEntity::Goat(goat) => goat.get_entity(), - AnyEntity::Guardian(guardian) => guardian.get_entity(), - AnyEntity::ElderGuardian(elder_guardian) => elder_guardian.get_entity(), - AnyEntity::Silverfish(silverfish) => silverfish.get_entity(), - AnyEntity::Raider(raider) => raider.get_entity(), - AnyEntity::AbstractIllager(abstract_illager) => abstract_illager.get_entity(), - AnyEntity::Vindicator(vindicator) => vindicator.get_entity(), - AnyEntity::Pillager(pillager) => pillager.get_entity(), - AnyEntity::SpellcasterIllager(spellcaster_illager) => spellcaster_illager.get_entity(), - AnyEntity::Evoker(evoker) => evoker.get_entity(), - AnyEntity::Illusioner(illusioner) => illusioner.get_entity(), - AnyEntity::Ravager(ravager) => ravager.get_entity(), - AnyEntity::Witch(witch) => witch.get_entity(), - AnyEntity::EvokerFangs(evoker_fangs) => evoker_fangs.get_entity(), - AnyEntity::Vex(vex) => vex.get_entity(), - AnyEntity::Skeleton(skeleton) => skeleton.get_entity(), - AnyEntity::AbstractSkeleton(abstract_skeleton) => abstract_skeleton.get_entity(), - AnyEntity::WitherSkeleton(wither_skeleton) => wither_skeleton.get_entity(), - AnyEntity::Stray(stray) => stray.get_entity(), - AnyEntity::Spider(spider) => spider.get_entity(), - AnyEntity::Warden(warden) => warden.get_entity(), - AnyEntity::Wither(wither) => wither.get_entity(), - AnyEntity::Zoglin(zoglin) => zoglin.get_entity(), - AnyEntity::Zombie(zombie) => zombie.get_entity(), - AnyEntity::ZombieVillager(zombie_villager) => zombie_villager.get_entity(), - AnyEntity::Husk(husk) => husk.get_entity(), - AnyEntity::Drowned(drowned) => drowned.get_entity(), - AnyEntity::ZombifiedPiglin(zombified_piglin) => zombified_piglin.get_entity(), - AnyEntity::Enderman(enderman) => enderman.get_entity(), - AnyEntity::EnderDragon(ender_dragon) => ender_dragon.get_entity(), - AnyEntity::Flying(flying) => flying.get_entity(), - AnyEntity::Ghast(ghast) => ghast.get_entity(), - AnyEntity::Phantom(phantom) => phantom.get_entity(), - AnyEntity::Slime(slime) => slime.get_entity(), - AnyEntity::LlamaSpit(llama_spit) => llama_spit.get_entity(), - AnyEntity::EyeOfEnder(eye_of_ender) => eye_of_ender.get_entity(), - AnyEntity::FallingBlock(falling_block) => falling_block.get_entity(), - AnyEntity::AreaEffectCloud(area_effect_cloud) => area_effect_cloud.get_entity(), - AnyEntity::FishingHook(fishing_hook) => fishing_hook.get_entity(), - AnyEntity::EndCrystal(end_crystal) => end_crystal.get_entity(), - AnyEntity::DragonFireball(dragon_fireball) => dragon_fireball.get_entity(), - AnyEntity::SmallFireball(small_fireball) => small_fireball.get_entity(), - AnyEntity::Fireball(fireball) => fireball.get_entity(), - AnyEntity::WitherSkull(wither_skull) => wither_skull.get_entity(), - AnyEntity::FireworkRocket(firework_rocket) => firework_rocket.get_entity(), - AnyEntity::ItemFrame(item_frame) => item_frame.get_entity(), - AnyEntity::GlowingItemFrame(glowing_item_frame) => glowing_item_frame.get_entity(), - AnyEntity::Painting(painting) => painting.get_entity(), - AnyEntity::ItemEntity(item_entity) => item_entity.get_entity(), - AnyEntity::ArmorStand(armor_stand) => armor_stand.get_entity(), - AnyEntity::Dolphin(dolphin) => dolphin.get_entity(), - AnyEntity::AbstractFish(abstract_fish) => abstract_fish.get_entity(), - AnyEntity::Cod(cod) => cod.get_entity(), - AnyEntity::PufferFish(pufferfish) => pufferfish.get_entity(), - AnyEntity::Salmon(salmon) => salmon.get_entity(), - AnyEntity::TropicalFish(tropical_fish) => tropical_fish.get_entity(), - AnyEntity::Tadpole(tadpole) => tadpole.get_entity(), - } - } - - pub fn as_display(&self) -> Option<&Display> { - match self { - AnyEntity::Display(display) => Some(display), - AnyEntity::BlockDisplay(block_display) => Some(&block_display.display), - AnyEntity::ItemDisplay(item_display) => Some(&item_display.display), - AnyEntity::TextDisplay(text_display) => Some(&text_display.display), - _ => None, - } - } - - pub fn as_thrown_item_projectile(&self) -> Option<&ThrownItemProjectile> { - match self { - AnyEntity::ThrownItemProjectile(throw_item_projectile) => Some(throw_item_projectile), - AnyEntity::ThrownEgg(throw_egg) => Some(&throw_egg.thrown_item_projectile), - AnyEntity::ThrownEnderPearl(throw_ender_pearl) => Some(&throw_ender_pearl.thrown_item_projectile), - AnyEntity::ThrownExperienceBottle(throw_experience_bottle) => Some(&throw_experience_bottle.thrown_item_projectile), - AnyEntity::ThrownPotion(throw_potion) => Some(&throw_potion.thrown_item_projectile), - AnyEntity::Snowball(snowball) => Some(&snowball.thrown_item_projectile), - _ => None, - } - } - - pub fn as_abstract_arrow(&self) -> Option<&AbstractArrow> { - match self { - AnyEntity::AbstractArrow(abstract_arrow) => Some(abstract_arrow), - AnyEntity::Arrow(arrow) => Some(&arrow.abstract_arrow), - AnyEntity::SpectralArrow(spectral_arrow) => Some(&spectral_arrow.abstract_arrow), - AnyEntity::ThrownTrident(throw_trident) => Some(&throw_trident.abstract_arrow), - _ => None, - } - } - - pub fn as_boat(&self) -> Option<&Boat> { - match self { - AnyEntity::Boat(boat) => Some(boat), - AnyEntity::ChestBoat(chest_boat) => Some(&chest_boat.boat), - _ => None, - } - } - - pub fn as_living_entity(&self) -> Option<&LivingEntity> { - match self { - AnyEntity::LivingEntity(living_entity) => return Some(living_entity), - AnyEntity::Player(player) => return Some(&player.living_entity), - _ => (), - }; - if let Some(mob) = self.as_mob() { - return Some(&mob.living_entity); - } - None - } - - pub fn as_mob(&self) -> Option<&Mob> { - match self { - AnyEntity::Mob(mob) => return Some(mob), - AnyEntity::PathfinderMob(pathfinder_mob) => return Some(&pathfinder_mob.mob), - _ => (), - }; - if let Some(ambient_creature) = self.as_ambient_creature() { - return Some(&ambient_creature.mob); - } - None - } - - pub fn as_ambient_creature(&self) -> Option<&AmbientCreature> { - match self { - AnyEntity::AmbientCreature(ambient_creature) => Some(ambient_creature), - AnyEntity::Bat(bat) => Some(&bat.ambient_creature), - _ => None, - } - } - - pub fn as_pathfinder_mob(&self) -> Option<&PathfinderMob> { - match self { - AnyEntity::PathfinderMob(pathfinder_mob) => return Some(pathfinder_mob), - _ => (), - } - if let Some(water_animal) = self.as_water_animal() { - return Some(&water_animal.pathfinder_mob); - } - None - } - - pub fn as_water_animal(&self) -> Option<&WaterAnimal> { - match self { - AnyEntity::WaterAnimal(water_animal) => Some(water_animal), - AnyEntity::Squid(squid) => Some(&squid.water_animal), - _ => None, - } - } -} diff --git a/minecraft-entities/src/mobs/flying.rs b/minecraft-entities/src/mobs/flying.rs deleted file mode 100644 index 5d72ef48..00000000 --- a/minecraft-entities/src/mobs/flying.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(Mob, LivingEntity, Entity)] -pub struct Flying { - pub mob: Mob, -} - -#[derive(Default)] -#[inherit(Flying, Mob, LivingEntity, Entity)] -pub struct Ghast { - pub flying: Flying, - pub is_attacking: bool, -} - -#[derive(Default)] -#[inherit(Flying, Mob, LivingEntity, Entity)] -pub struct Phantom { - pub flying: Flying, - pub size: usize, -} diff --git a/minecraft-entities/src/mobs/golems.rs b/minecraft-entities/src/mobs/golems.rs deleted file mode 100644 index cd2b3934..00000000 --- a/minecraft-entities/src/mobs/golems.rs +++ /dev/null @@ -1,23 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(PathfinderMob, Mob, LivingEntity, Entity)] -pub struct AbstractGolem { - pub pathfinder_mob: PathfinderMob, -} - -#[derive(Default)] -#[inherit(AbstractGolem, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct IronGolem { - pub abstract_golem: AbstractGolem, - pub is_player_created: bool, -} - -#[derive(Default)] -#[inherit(AbstractGolem, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct SnowGolem { - pub abstract_golem: AbstractGolem, - pub has_pumpkin_hat: bool, -} - diff --git a/minecraft-entities/src/mobs/squid.rs b/minecraft-entities/src/mobs/squid.rs deleted file mode 100644 index 2afeb889..00000000 --- a/minecraft-entities/src/mobs/squid.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inherit(WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Squid { - pub water_animal: WaterAnimal, -} diff --git a/minecraft-entities/src/mobs/villagers.rs b/minecraft-entities/src/mobs/villagers.rs deleted file mode 100644 index 696971f4..00000000 --- a/minecraft-entities/src/mobs/villagers.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct AbstractVillager { - pub ageable_mob: AgeableMob, - pub head_shake_timer: u32, -} - -#[derive(Default)] -#[inherit(AbstractVillager, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Villager { - pub abstract_villager: AbstractVillager, - pub villager_data: Vec, -} - -#[derive(Default)] -#[inherit(AbstractVillager, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct WanderingTrader { - pub abstract_villager: AbstractVillager, -} diff --git a/minecraft-entities/src/monsters/base_piglin.rs b/minecraft-entities/src/monsters/base_piglin.rs deleted file mode 100644 index b72ef2e7..00000000 --- a/minecraft-entities/src/monsters/base_piglin.rs +++ /dev/null @@ -1,9 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct BasePiglin { - pub monster: Monster, - pub is_immune: bool, -} diff --git a/minecraft-entities/src/monsters/endermite.rs b/minecraft-entities/src/monsters/endermite.rs deleted file mode 100644 index b5da89cc..00000000 --- a/minecraft-entities/src/monsters/endermite.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Endermite { - pub monster: Monster, -} diff --git a/minecraft-entities/src/monsters/giant.rs b/minecraft-entities/src/monsters/giant.rs deleted file mode 100644 index f773f6a1..00000000 --- a/minecraft-entities/src/monsters/giant.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Giant { - pub monster: Monster, -} diff --git a/minecraft-entities/src/monsters/guardian.rs b/minecraft-entities/src/monsters/guardian.rs deleted file mode 100644 index f408a98f..00000000 --- a/minecraft-entities/src/monsters/guardian.rs +++ /dev/null @@ -1,18 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Guardian { - pub monster: Monster, - pub is_retracting_spikes: bool, - pub target_eid: Eid, -} - -#[derive(Default)] -#[inheritable] -#[inherit(Guardian, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct ElderGuardian { - pub guardian: Guardian, -} - diff --git a/minecraft-entities/src/monsters/piglin.rs b/minecraft-entities/src/monsters/piglin.rs deleted file mode 100644 index b5e97192..00000000 --- a/minecraft-entities/src/monsters/piglin.rs +++ /dev/null @@ -1,16 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inherit(BasePiglin, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Piglin { - pub base_piglin: BasePiglin, - pub is_baby: bool, - pub is_charging_crossbow: bool, - pub is_dancing: bool, -} - -#[derive(Default)] -#[inherit(BasePiglin, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct PiglinBrute { - pub base_piglin: BasePiglin, -} diff --git a/minecraft-entities/src/monsters/raider.rs b/minecraft-entities/src/monsters/raider.rs deleted file mode 100644 index 6999f62a..00000000 --- a/minecraft-entities/src/monsters/raider.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Raider { - pub monster: Monster, - pub is_celebrating: bool, -} - -#[derive(Default)] -#[inheritable] -#[inherit(Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct AbstractIllager { - pub raider: Raider, -} - -#[derive(Default)] -#[inherit(AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Vindicator { - pub abstract_illager: AbstractIllager, -} - -#[derive(Default)] -#[inherit(AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Pillager { - pub abstract_illager: AbstractIllager, - pub is_charging: bool, -} diff --git a/minecraft-entities/src/monsters/silverfish.rs b/minecraft-entities/src/monsters/silverfish.rs deleted file mode 100644 index 59be1aec..00000000 --- a/minecraft-entities/src/monsters/silverfish.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Silverfish { - pub monster: Monster, -} diff --git a/minecraft-entities/src/monsters/skeleton.rs b/minecraft-entities/src/monsters/skeleton.rs deleted file mode 100644 index c9acc7d9..00000000 --- a/minecraft-entities/src/monsters/skeleton.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct AbstractSkeleton { - pub monster: Monster, -} - -#[derive(Default)] -#[inherit(AbstractSkeleton, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Skeleton { - pub abstract_skeleton: AbstractSkeleton, -} - -#[derive(Default)] -#[inherit(AbstractSkeleton, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct WitherSkeleton { - pub abstract_skeleton: AbstractSkeleton, -} - -#[derive(Default)] -#[inherit(AbstractSkeleton, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Stray { - pub abstract_skeleton: AbstractSkeleton, -} - -#[derive(Default)] -#[inherit(Entity)] -pub struct WitherSkull { - pub entity: Entity, - pub is_invulnerable: bool, -} diff --git a/minecraft-entities/src/monsters/spellcaster_illager.rs b/minecraft-entities/src/monsters/spellcaster_illager.rs deleted file mode 100644 index b04aba35..00000000 --- a/minecraft-entities/src/monsters/spellcaster_illager.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct SpellcasterIllager { - pub abstract_illager: AbstractIllager, - pub spell: u8, -} - -#[derive(Default)] -#[inherit(SpellcasterIllager, AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Evoker { - pub spellcaster_illager: SpellcasterIllager, -} - -#[derive(Default)] -#[inherit(Entity)] -pub struct EvokerFangs { - pub entity: Entity, -} - -#[derive(Default)] -#[inherit(SpellcasterIllager, AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Illusioner { - pub spellcaster_illager: SpellcasterIllager, -} - -#[derive(Default)] -#[inherit(SpellcasterIllager, AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Ravager { - pub spellcaster_illager: SpellcasterIllager, -} diff --git a/minecraft-entities/src/monsters/spider.rs b/minecraft-entities/src/monsters/spider.rs deleted file mode 100644 index f77f5f11..00000000 --- a/minecraft-entities/src/monsters/spider.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Spider { - pub monster: Monster, - pub is_climbing_mask: u8, -} diff --git a/minecraft-entities/src/monsters/vex.rs b/minecraft-entities/src/monsters/vex.rs deleted file mode 100644 index f8f63b08..00000000 --- a/minecraft-entities/src/monsters/vex.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Vex { - pub monster: Monster, -} diff --git a/minecraft-entities/src/monsters/witch.rs b/minecraft-entities/src/monsters/witch.rs deleted file mode 100644 index 9b3d2076..00000000 --- a/minecraft-entities/src/monsters/witch.rs +++ /dev/null @@ -1,8 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inherit(Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Witch { - pub raider: Raider, - pub is_drinking_potion: bool, -} diff --git a/minecraft-entities/src/monsters/wither.rs b/minecraft-entities/src/monsters/wither.rs deleted file mode 100644 index 89c219ae..00000000 --- a/minecraft-entities/src/monsters/wither.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::*; - -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Wither { - pub monster: Monster, - pub center_head_target: Option, - pub left_head_target: Option, - pub right_head: Option, - pub invulnerable_time: usize, -} diff --git a/minecraft-entities/src/monsters/zombies.rs b/minecraft-entities/src/monsters/zombies.rs deleted file mode 100644 index b8153662..00000000 --- a/minecraft-entities/src/monsters/zombies.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::*; - -#[derive(Default)] -#[inheritable] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Zombie { - pub monster: Monster, - pub is_baby: bool, - pub unused: isize, - pub is_becoming_drowned: bool, -} - -#[derive(Default)] -#[inherit(Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct ZombieVillager { - pub zombie: Zombie, - pub is_converting: bool, - pub villager_data: Vec, -} - -#[derive(Default)] -#[inherit(Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Husk { - pub zombie: Zombie, -} - -#[derive(Default)] -#[inherit(Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct Drowned { - pub zombie: Zombie, -} - -#[derive(Default)] -#[inherit(Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity)] -pub struct ZombifiedPiglin { - pub zombie: Zombie, -} diff --git a/minecraft-positions/src/lib.rs b/minecraft-positions/src/lib.rs index 3d3f0ce8..e67cc314 100644 --- a/minecraft-positions/src/lib.rs +++ b/minecraft-positions/src/lib.rs @@ -105,6 +105,13 @@ impl Position { cz: (self.z.floor() as i32).div_euclid(16), } } + + pub fn chunk_column(&self) -> ChunkColumnPosition { + ChunkColumnPosition { + cx: (self.x.floor() as i32).div_euclid(16), + cz: (self.z.floor() as i32).div_euclid(16), + } + } } impl std::ops::Add for Position { diff --git a/minecraft-server/Cargo.toml b/minecraft-server/Cargo.toml index 4a329bf8..2c37ee16 100644 --- a/minecraft-server/Cargo.toml +++ b/minecraft-server/Cargo.toml @@ -11,7 +11,6 @@ log = "0.4.20" tokio = { version = "1.33.0", features = ["full"] } futures = "0.3.29" minecraft-protocol = { path="../minecraft-protocol" } -minecraft-entities = { path="../minecraft-entities" } minecraft-positions = { path="../minecraft-positions" } -tags-macros = { path = "../tags-macros"} +minecraft-entities-derive = { path="../minecraft-entities-derive" } rand = "0.8.4" diff --git a/minecraft-server/src/ecs/components/health.rs b/minecraft-server/src/ecs/components/health.rs deleted file mode 100644 index fc7fb617..00000000 --- a/minecraft-server/src/ecs/components/health.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::prelude::*; - -#[derive(Clone)] -pub struct HealthComponent { - pub health: f32, - pub max_health: f32, -} - -impl HealthComponent { - /// Returns true if the entity is dead.s - pub async fn is_dead(&self) -> bool { - self.health <= 0.0 - } - - pub async fn get_health(&self) -> f32{ - self.health - } - - /// Heals the entity by the given amount. - pub async fn heal(&mut self, amount: f32) { - self.health = (self.health + amount).min(self.max_health); - } - - /// Fully heals the entity. - pub async fn full_heal(&mut self) { - self.health = self.max_health; - } - - /// Damages the entity by the given amount. - pub async fn damage(&mut self, amount: f32) -> Option<()> { - self.health -= amount; - self.health = self.health.max(0.0); - Some(()) - } -} - -impl Entities { - /// Set the health of an entity. - pub async fn set_health(&self, id: Eid, health: HealthComponent) -> Option<()> { - //let mut health_components = self.health_components.write().await; - //health_components.insert(id, health); - //Some(()) - unimplemented!() - } - - /// Get the health of an entity. - pub async fn get_health(&self, id: Eid) -> Option { - //self.health_components.read().await.get(&id).cloned() - unimplemented!() - } -} diff --git a/minecraft-server/src/ecs/components/mod.rs b/minecraft-server/src/ecs/components/mod.rs deleted file mode 100644 index d26fd92f..00000000 --- a/minecraft-server/src/ecs/components/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub use super::*; - -pub mod health; -pub mod positions; - -pub use health::HealthComponent; -pub use positions::PositionComponent; - -#[derive(Clone, Eq, Hash, PartialEq)] -pub enum Component { - Health, - Position, -} diff --git a/minecraft-server/src/ecs/components/positions.rs b/minecraft-server/src/ecs/components/positions.rs deleted file mode 100644 index 17a377eb..00000000 --- a/minecraft-server/src/ecs/components/positions.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -use crate::prelude::*; - -#[derive(Clone)] -pub struct PositionComponent { - position: Position, -} - -impl Entities { - pub async fn get_position(&self, id: Eid) -> Option { - // we don't check if the entity has a position component because the hashmap will return None if the entity doesn't have it - //self.position_components.read().await.get(&id).cloned() - unimplemented!() - } - - pub async fn set_position(&self, id: Eid, position: PositionComponent) -> Option<()> { -// let new_chunk = position.get_chunk(); -// let mut position_components = self.position_components.write().await; -// let old_chunk = position_components.get(&id).cloned(); -// -// // Update the position component -// let pos = position_components.get_mut(&id)?; -// *pos = position; -// drop(position_components); -// -// if let Some(old_chunk) = old_chunk { -// let old_chunk = old_chunk.get_chunk(); -// if old_chunk != new_chunk { -// // Remove the entity from the old chunk -// let mut chunks = self.chunks.write().await; -// let old_chunk_entities = chunks.get_mut(&old_chunk)?; -// old_chunk_entities.remove(&id); -// -// // Add the entity to the new chunk -// let new_chunk_entities = chunks.get_mut(&new_chunk)?; -// new_chunk_entities.insert(id); -// -// } -// } else { -// // Add the entity to the new chunk -// let mut chunks = self.chunks.write().await; -// let new_chunk_entities = chunks.get_mut(&new_chunk)?; -// new_chunk_entities.insert(id); -// } -// -// Some(()) - unimplemented!() - } -} - -impl PositionComponent { - pub async fn set_position(&mut self, position: Position) -> Option<()> { - self.position = position; - Some(()) - } - - pub fn get_position(&self) -> Position { - self.position.clone() - } - - pub fn move_entity(&mut self, delta: Position) { - self.position += delta; - } - - pub fn get_chunk(&self) -> ChunkPosition { - self.position.chunk() - } -} diff --git a/minecraft-server/src/ecs/entities.rs b/minecraft-server/src/ecs/entities.rs deleted file mode 100644 index c71c6447..00000000 --- a/minecraft-server/src/ecs/entities.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::{prelude::*, entities::AnyEntity}; -use minecraft_protocol::packets::UUID; -use super::tags::Tag; - -pub struct Entities { - pub entities: RwLock>, - - /// A hashmap of chunk positions to get a list of entities in a chunk - pub chunks: RwLock>>, - pub uuids: RwLock>, - - // TODO: pub entities_by_tag: RwLock>>, -} - -impl Entities { - pub fn new() -> Entities { - Entities { - entities: RwLock::new(HashMap::new()), - chunks: RwLock::new(HashMap::new()), - uuids: RwLock::new(HashMap::new()), - } - } - - /// Observe an entity through a closure - pub async fn observe_entity(&self, eid: Eid, observer: impl FnOnce(&AnyEntity) -> R) -> Option { - self.entities.read().await.get(&eid).map(observer) - } - - /// Mutate an entity through a closure - pub async fn mutate_entity(&self, eid: Eid, mutator: impl FnOnce(&mut AnyEntity) -> R) -> Option { - let mut entities = self.entities.write().await; - - if let Some(entity) = entities.get_mut(&eid) { - let prev_position = entity.as_entity().position.clone(); - let r = mutator(entity); - if prev_position != entity.as_entity().position { - let old_chunk = prev_position.chunk(); - let new_chunk = entity.as_entity().position.chunk(); - drop(entities); - let mut chunks = self.chunks.write().await; - chunks.get_mut(&old_chunk).unwrap().remove(&eid); - chunks.get_mut(&new_chunk).unwrap().insert(eid); - } - Some(r) - } else { - None - } - } - - /// Remove an entity - pub async fn remove_entity(&self, eid: Eid) -> Option { - self.entities.write().await.remove(&eid) - } -} diff --git a/minecraft-server/src/ecs/mod.rs b/minecraft-server/src/ecs/mod.rs deleted file mode 100644 index 1ea05e63..00000000 --- a/minecraft-server/src/ecs/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod entities; -pub mod tags; -pub mod components; - -pub use entities::*; -pub use tags::*; -pub use components::*; diff --git a/minecraft-server/src/ecs/tags.rs b/minecraft-server/src/ecs/tags.rs deleted file mode 100644 index 5cf8afb6..00000000 --- a/minecraft-server/src/ecs/tags.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::ecs::components::*; -use tags_macros::tags; - -tags! { - Player { - Position, - Health, - } - Enemy { - Health - } -} diff --git a/minecraft-entities/src/animals/axolotl.rs b/minecraft-server/src/entities/animals/axolotl.rs similarity index 61% rename from minecraft-entities/src/animals/axolotl.rs rename to minecraft-server/src/entities/animals/axolotl.rs index 876bc08a..233c93af 100644 --- a/minecraft-entities/src/animals/axolotl.rs +++ b/minecraft-server/src/entities/animals/axolotl.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Axolotl { pub animal: Animal, pub variant: u8, diff --git a/minecraft-entities/src/animals/bee.rs b/minecraft-server/src/entities/animals/bee.rs similarity index 54% rename from minecraft-entities/src/animals/bee.rs rename to minecraft-server/src/entities/animals/bee.rs index 4f6e32dc..de1ee9d4 100644 --- a/minecraft-entities/src/animals/bee.rs +++ b/minecraft-server/src/entities/animals/bee.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Bee { pub animal: Animal, pub flag: u8, diff --git a/minecraft-entities/src/animals/cat.rs b/minecraft-server/src/entities/animals/cat.rs similarity index 78% rename from minecraft-entities/src/animals/cat.rs rename to minecraft-server/src/entities/animals/cat.rs index 8e111c4f..215b65b9 100644 --- a/minecraft-entities/src/animals/cat.rs +++ b/minecraft-server/src/entities/animals/cat.rs @@ -1,6 +1,8 @@ use super::*; -#[inherit(TameableAnimal, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { TameableAnimal, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Cat { pub tameable_animal: TameableAnimal, pub variant: u8, diff --git a/minecraft-server/src/entities/animals/chicken.rs b/minecraft-server/src/entities/animals/chicken.rs new file mode 100644 index 00000000..84d4fae0 --- /dev/null +++ b/minecraft-server/src/entities/animals/chicken.rs @@ -0,0 +1,9 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Chicken { + pub animal: Animal, +} diff --git a/minecraft-server/src/entities/animals/cow.rs b/minecraft-server/src/entities/animals/cow.rs new file mode 100644 index 00000000..e13915e0 --- /dev/null +++ b/minecraft-server/src/entities/animals/cow.rs @@ -0,0 +1,20 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Mooshroom }, +)] +pub struct Cow { + pub animal: Animal, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Cow, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Mooshroom { + pub cow: Cow, + pub variant: u8, // In the doc it is a string +} diff --git a/minecraft-entities/src/animals/fox.rs b/minecraft-server/src/entities/animals/fox.rs similarity index 68% rename from minecraft-entities/src/animals/fox.rs rename to minecraft-server/src/entities/animals/fox.rs index 678e0fc5..45bcbe5b 100644 --- a/minecraft-entities/src/animals/fox.rs +++ b/minecraft-server/src/entities/animals/fox.rs @@ -2,7 +2,9 @@ use minecraft_protocol::packets::UUID; use super::*; #[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Fox { pub animal: Animal, pub variant: u8, diff --git a/minecraft-entities/src/animals/frog.rs b/minecraft-server/src/entities/animals/frog.rs similarity index 55% rename from minecraft-entities/src/animals/frog.rs rename to minecraft-server/src/entities/animals/frog.rs index 63115498..6fcd5aef 100644 --- a/minecraft-entities/src/animals/frog.rs +++ b/minecraft-server/src/entities/animals/frog.rs @@ -1,8 +1,9 @@ -use minecraft_protocol::packets::UUID; use super::*; #[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Frog { pub animal: Animal, pub variant: u8, diff --git a/minecraft-entities/src/animals/goat.rs b/minecraft-server/src/entities/animals/goat.rs similarity index 78% rename from minecraft-entities/src/animals/goat.rs rename to minecraft-server/src/entities/animals/goat.rs index bcacdb3c..b944fb2b 100644 --- a/minecraft-entities/src/animals/goat.rs +++ b/minecraft-server/src/entities/animals/goat.rs @@ -1,6 +1,8 @@ use super::*; -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Goat { pub animal: Animal, pub is_screaming: bool, diff --git a/minecraft-server/src/entities/animals/hoglin.rs b/minecraft-server/src/entities/animals/hoglin.rs new file mode 100644 index 00000000..8b4b17b9 --- /dev/null +++ b/minecraft-server/src/entities/animals/hoglin.rs @@ -0,0 +1,10 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Hoglin { + pub animal: Animal, + pub is_immune: bool, +} diff --git a/minecraft-server/src/entities/animals/horses.rs b/minecraft-server/src/entities/animals/horses.rs new file mode 100644 index 00000000..af9172f6 --- /dev/null +++ b/minecraft-server/src/entities/animals/horses.rs @@ -0,0 +1,116 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Horse, ZombieHorse, SkeletonHorse, Camel, ChestedHorse... }, +)] +pub struct AbstractHorse { + pub animal: Animal, + pub mask: u8, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Horse { + pub abstract_horse: AbstractHorse, + pub variant: usize, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct ZombieHorse { + pub abstract_horse: AbstractHorse, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct SkeletonHorse { + pub abstract_horse: AbstractHorse, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Camel { + pub abstract_horse: AbstractHorse, + pub is_dashing: bool, + pub last_pose_change_tick: usize, +} + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Mule, Donkey, Llama... }, +)] +pub struct ChestedHorse { + pub abstract_horse: AbstractHorse, + pub has_chest: bool, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Mule { + pub chested_horse: ChestedHorse, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Donkey { + pub chested_horse: ChestedHorse, +} + +#[MinecraftEntity( + inheritable, + ancestors { ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { TraderLlama }, +)] +pub struct Llama { + pub chested_horse: ChestedHorse, + /// Strength (number of columns of 3 slots in the llama's inventory once a chest is equipped) + pub stength: u8, + /// Carpet color (a dye color, or -1 if no carpet equipped) + pub carpet_color: i16, + pub variant: u8, +} + +impl Default for Llama { + fn default() -> Self { + Self { + chested_horse: ChestedHorse::default(), + stength: 0, + carpet_color: -1, + variant: 0, + } + } +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Llama, ChestedHorse, AbstractHorse, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct TraderLlama { + pub llama: Llama, +} + + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Entity }, +)] +pub struct LlamaSpit { + pub entity: Entity, +} diff --git a/minecraft-entities/src/animals/mod.rs b/minecraft-server/src/entities/animals/mod.rs similarity index 66% rename from minecraft-entities/src/animals/mod.rs rename to minecraft-server/src/entities/animals/mod.rs index 53f385f5..8576968c 100644 --- a/minecraft-entities/src/animals/mod.rs +++ b/minecraft-server/src/entities/animals/mod.rs @@ -4,10 +4,6 @@ mod sniffer; pub use sniffer::*; mod horses; pub use horses::*; -mod donkey; -pub use donkey::*; -mod llama; -pub use llama::*; mod axolotl; pub use axolotl::*; mod bee; @@ -46,21 +42,25 @@ mod parrot; pub use parrot::*; mod goat; pub use goat::*; -mod fishes; -pub use fishes::*; mod water_animal; pub use water_animal::*; #[derive(Default)] -#[inheritable] -#[inherit(AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + inheritable, + ancestors { AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Axolotl, Bee, Chicken, Cow, Fox, Frog, Goat, Hoglin, Ocelot, Panda, Pig, PolarBear, Rabbit, Sheep, Sniffer, Strider, Turtle, AbstractHorse..., TameableAnimal... }, +)] pub struct Animal { pub ageable_mob: AgeableMob, } #[derive(Default)] -#[inheritable] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + inheritable, + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Cat, Parrot, Wolf } +)] pub struct TameableAnimal { pub animal: Animal, pub action_mask: u8, diff --git a/minecraft-server/src/entities/animals/ocelot.rs b/minecraft-server/src/entities/animals/ocelot.rs new file mode 100644 index 00000000..39550542 --- /dev/null +++ b/minecraft-server/src/entities/animals/ocelot.rs @@ -0,0 +1,10 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Ocelot { + pub animal: Animal, + pub is_trusting: bool, +} diff --git a/minecraft-entities/src/animals/panda.rs b/minecraft-server/src/entities/animals/panda.rs similarity index 67% rename from minecraft-entities/src/animals/panda.rs rename to minecraft-server/src/entities/animals/panda.rs index eaf8f55b..e38a256f 100644 --- a/minecraft-entities/src/animals/panda.rs +++ b/minecraft-server/src/entities/animals/panda.rs @@ -1,8 +1,9 @@ -use minecraft_protocol::packets::UUID; use super::*; #[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Panda { pub animal: Animal, pub breed_timer: u16, diff --git a/minecraft-entities/src/animals/parrot.rs b/minecraft-server/src/entities/animals/parrot.rs similarity index 50% rename from minecraft-entities/src/animals/parrot.rs rename to minecraft-server/src/entities/animals/parrot.rs index 86aebd5b..0fa54605 100644 --- a/minecraft-entities/src/animals/parrot.rs +++ b/minecraft-server/src/entities/animals/parrot.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(TameableAnimal, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { TameableAnimal, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Parrot { pub tameable_animal: TameableAnimal, pub variant: u8, diff --git a/minecraft-entities/src/animals/pig.rs b/minecraft-server/src/entities/animals/pig.rs similarity index 55% rename from minecraft-entities/src/animals/pig.rs rename to minecraft-server/src/entities/animals/pig.rs index 2f209af3..921f2121 100644 --- a/minecraft-entities/src/animals/pig.rs +++ b/minecraft-server/src/entities/animals/pig.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Pig { pub animal: Animal, pub has_saddle: bool, diff --git a/minecraft-entities/src/animals/polar_bear.rs b/minecraft-server/src/entities/animals/polar_bear.rs similarity index 51% rename from minecraft-entities/src/animals/polar_bear.rs rename to minecraft-server/src/entities/animals/polar_bear.rs index 23c933d8..95e0aefa 100644 --- a/minecraft-entities/src/animals/polar_bear.rs +++ b/minecraft-server/src/entities/animals/polar_bear.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct PolarBear { pub animal: Animal, pub is_standing: bool, diff --git a/minecraft-entities/src/animals/rabbit.rs b/minecraft-server/src/entities/animals/rabbit.rs similarity index 50% rename from minecraft-entities/src/animals/rabbit.rs rename to minecraft-server/src/entities/animals/rabbit.rs index 2d39d9c1..0173be5f 100644 --- a/minecraft-entities/src/animals/rabbit.rs +++ b/minecraft-server/src/entities/animals/rabbit.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Rabbit { pub animal: Animal, pub variant: u16, diff --git a/minecraft-entities/src/animals/sheep.rs b/minecraft-server/src/entities/animals/sheep.rs similarity index 50% rename from minecraft-entities/src/animals/sheep.rs rename to minecraft-server/src/entities/animals/sheep.rs index ef16d152..53fa1ebf 100644 --- a/minecraft-entities/src/animals/sheep.rs +++ b/minecraft-server/src/entities/animals/sheep.rs @@ -1,8 +1,9 @@ use super::*; #[derive(Default)] -#[inheritable] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Sheep { pub animal: Animal, pub mask_style: u8, diff --git a/minecraft-entities/src/animals/sniffer.rs b/minecraft-server/src/entities/animals/sniffer.rs similarity index 58% rename from minecraft-entities/src/animals/sniffer.rs rename to minecraft-server/src/entities/animals/sniffer.rs index e34235a0..20f54d7d 100644 --- a/minecraft-entities/src/animals/sniffer.rs +++ b/minecraft-server/src/entities/animals/sniffer.rs @@ -1,8 +1,9 @@ use super::*; #[derive(Default)] -#[inheritable] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Sniffer { pub animal: Animal, pub sniffer_state: u8, diff --git a/minecraft-entities/src/animals/strider.rs b/minecraft-server/src/entities/animals/strider.rs similarity index 60% rename from minecraft-entities/src/animals/strider.rs rename to minecraft-server/src/entities/animals/strider.rs index 91f95a5c..176c4381 100644 --- a/minecraft-entities/src/animals/strider.rs +++ b/minecraft-server/src/entities/animals/strider.rs @@ -1,8 +1,9 @@ use super::*; #[derive(Default)] -#[inheritable] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Strider { pub animal: Animal, pub boost_time: u16, diff --git a/minecraft-entities/src/animals/turtle.rs b/minecraft-server/src/entities/animals/turtle.rs similarity index 70% rename from minecraft-entities/src/animals/turtle.rs rename to minecraft-server/src/entities/animals/turtle.rs index 75fb2028..72e17a47 100644 --- a/minecraft-entities/src/animals/turtle.rs +++ b/minecraft-server/src/entities/animals/turtle.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Turtle { pub animal: Animal, pub block_position: BlockPosition, diff --git a/minecraft-server/src/entities/animals/water_animal.rs b/minecraft-server/src/entities/animals/water_animal.rs new file mode 100644 index 00000000..2073ac7f --- /dev/null +++ b/minecraft-server/src/entities/animals/water_animal.rs @@ -0,0 +1,83 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Dolphin, Squid, AbstractFish... }, +)] +pub struct WaterAnimal { + pub pathfinder_mob: PathfinderMob, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Dolphin { + pub water_animal: WaterAnimal, + pub treasure_position: Option, + pub has_fish: bool, + pub moisture_level: usize, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Squid { + pub water_animal: WaterAnimal, +} + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Cod, Pufferfish, Salmon, TropicalFish, Tadpole... }, +)] +pub struct AbstractFish { + pub water_animal: WaterAnimal, + pub from_bucket: bool, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Cod { + pub abstract_fish: AbstractFish, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Pufferfish { + pub abstract_fish: AbstractFish, + pub puff_state: usize, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Salmon { + pub abstract_fish: AbstractFish, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct TropicalFish { + pub abstract_fish: AbstractFish, + pub variant: usize, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractFish, WaterAnimal, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Tadpole { + pub abstract_fish: AbstractFish, +} diff --git a/minecraft-entities/src/animals/wolf.rs b/minecraft-server/src/entities/animals/wolf.rs similarity index 75% rename from minecraft-entities/src/animals/wolf.rs rename to minecraft-server/src/entities/animals/wolf.rs index 9fc0a3a4..00b634a4 100644 --- a/minecraft-entities/src/animals/wolf.rs +++ b/minecraft-server/src/entities/animals/wolf.rs @@ -1,6 +1,8 @@ use super::*; -#[inherit(TameableAnimal, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { TameableAnimal, Animal, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Wolf { pub tameable_animal: TameableAnimal, pub is_begging: bool, diff --git a/minecraft-entities/src/arrow.rs b/minecraft-server/src/entities/arrow.rs similarity index 81% rename from minecraft-entities/src/arrow.rs rename to minecraft-server/src/entities/arrow.rs index b3636c95..7a8367f2 100644 --- a/minecraft-entities/src/arrow.rs +++ b/minecraft-server/src/entities/arrow.rs @@ -1,7 +1,10 @@ use super::*; -#[inheritable] -#[inherit(Entity)] +#[MinecraftEntity( + inheritable, + ancestors { Entity }, + descendants { Arrow, SpectralArrow, ThrownTrident }, +)] pub struct AbstractArrow { pub entity: Entity, pub is_critical: bool, @@ -20,7 +23,9 @@ impl Default for AbstractArrow { } } -#[inherit(AbstractArrow, Entity)] +#[MinecraftEntity( + ancestors { AbstractArrow, Entity }, +)] pub struct Arrow { pub abstract_arrow: AbstractArrow, pub color: isize, @@ -35,7 +40,9 @@ impl Default for Arrow { } } -#[inherit(AbstractArrow, Entity)] +#[MinecraftEntity( + ancestors { AbstractArrow, Entity }, +)] pub struct SpectralArrow { pub abstract_arrow: AbstractArrow, pub loyalty_level: isize, @@ -52,7 +59,9 @@ impl Default for SpectralArrow { } } -#[inherit(AbstractArrow, Entity)] +#[MinecraftEntity( + ancestors { AbstractArrow, Entity }, +)] pub struct ThrownTrident { pub abstract_arrow: AbstractArrow, pub loyalty_level: isize, diff --git a/minecraft-entities/src/block.rs b/minecraft-server/src/entities/block.rs similarity index 72% rename from minecraft-entities/src/block.rs rename to minecraft-server/src/entities/block.rs index 4be25b34..26d7f1b7 100644 --- a/minecraft-entities/src/block.rs +++ b/minecraft-server/src/entities/block.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct FallingBlock { pub entity: Entity, pub spawn_position: BlockPosition, diff --git a/minecraft-entities/src/boat.rs b/minecraft-server/src/entities/boat.rs similarity index 83% rename from minecraft-entities/src/boat.rs rename to minecraft-server/src/entities/boat.rs index 4cd33694..53288360 100644 --- a/minecraft-entities/src/boat.rs +++ b/minecraft-server/src/entities/boat.rs @@ -1,7 +1,10 @@ use super::*; -#[inheritable] -#[inherit(Entity)] +#[MinecraftEntity( + inheritable, + ancestors { Entity }, + descendants { ChestBoat }, +)] pub struct Boat { pub entity: Entity, pub time_since_last_hit: usize, @@ -30,7 +33,9 @@ impl Default for Boat { } #[derive(Default)] -#[inherit(Boat, Entity)] +#[MinecraftEntity( + ancestors { Boat, Entity }, +)] pub struct ChestBoat { pub boat: Boat, } diff --git a/minecraft-entities/src/display.rs b/minecraft-server/src/entities/display.rs similarity index 89% rename from minecraft-entities/src/display.rs rename to minecraft-server/src/entities/display.rs index 12ee1515..e5f2527f 100644 --- a/minecraft-entities/src/display.rs +++ b/minecraft-server/src/entities/display.rs @@ -1,8 +1,11 @@ use super::*; use minecraft_protocol::components::paintings::Painting as PaintingType; -#[inherit(Entity)] -#[inheritable] +#[MinecraftEntity( + inheritable, + ancestors { Entity }, + descendants { BlockDisplay, ItemDisplay, TextDisplay }, +)] pub struct Display { pub entity: Entity, pub interpolation_delay: u32, @@ -51,7 +54,9 @@ impl Default for Display { } } -#[inherit(Display, Entity)] +#[MinecraftEntity( + ancestors { Display, Entity }, +)] pub struct BlockDisplay { pub display: Display, pub block: BlockWithState, @@ -66,7 +71,9 @@ impl Default for BlockDisplay { } } -#[inherit(Display, Entity)] +#[MinecraftEntity( + ancestors { Display, Entity }, +)] pub struct ItemDisplay { pub display: Display, pub item: Slot, @@ -83,7 +90,9 @@ impl Default for ItemDisplay { } } -#[inherit(Display, Entity)] +#[MinecraftEntity( + ancestors { Display, Entity }, +)] pub struct TextDisplay { pub display: Display, pub text: String, @@ -113,7 +122,9 @@ impl Default for TextDisplay { } #[derive(Default)] -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct Painting { pub entity: Entity, pub painting_type: PaintingType, diff --git a/minecraft-entities/src/entity.rs b/minecraft-server/src/entities/entity.rs similarity index 55% rename from minecraft-entities/src/entity.rs rename to minecraft-server/src/entities/entity.rs index e9b62b43..59da82ac 100644 --- a/minecraft-entities/src/entity.rs +++ b/minecraft-server/src/entities/entity.rs @@ -1,8 +1,17 @@ use super::*; -#[inheritable] +#[MinecraftEntity( + inheritable, + descendants { AbstractArrow..., Boat..., Display, FallingBlock, LlamaSpit, Painting, DragonFireball, Fireball..., FireworkRocket, SmallFireball, Interaction..., ItemEntity, ItemFrame..., LivingEntity... EndCrystal, EvokerFangs, WitherSkull, AreaEffectCloud, FishingHook, EyeOfEnder, ThrownItemProjectile... }, + defines { + init(self, server_msg_rcvr: BroadcastReceiver); + } +)] pub struct Entity { pub position: Position, + pub velocity: Translation, + pub pitch: f32, + pub yaw: f32, pub is_on_fire: bool, pub is_crouching: bool, pub is_sprinting: bool, @@ -19,10 +28,19 @@ pub struct Entity { pub ticks_frozen: u32, } +impl Handler { + pub async fn init(self, server_msg_rcvr: BroadcastReceiver) { + self.insert_task("newton", tokio::spawn(newton_task(self.clone(), server_msg_rcvr))).await; + } +} + impl Default for Entity { fn default() -> Self { Entity { position: Position { x: 0.0, y: 0.0, z: 0.0 }, + velocity: Translation { x: 0.0, y: 0.0, z: 0.0 }, + pitch: 0.0, + yaw: 0.0, is_on_fire: false, is_crouching: false, is_sprinting: false, diff --git a/minecraft-entities/src/fire_entities.rs b/minecraft-server/src/entities/fire_entities.rs similarity index 68% rename from minecraft-entities/src/fire_entities.rs rename to minecraft-server/src/entities/fire_entities.rs index 5525f929..4342a970 100644 --- a/minecraft-entities/src/fire_entities.rs +++ b/minecraft-server/src/entities/fire_entities.rs @@ -1,27 +1,35 @@ use super::*; #[derive(Default)] -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct DragonFireball { pub entity: Entity, } #[derive(Default)] -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct SmallFireball { pub entity: Entity, pub item: Slot, } #[derive(Default)] -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct Fireball { pub entity: Entity, pub item: Slot, } #[derive(Default)] -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct FireworkRocket { pub entity: Entity, pub item: Slot, diff --git a/minecraft-entities/src/interaction.rs b/minecraft-server/src/entities/interaction.rs similarity index 87% rename from minecraft-entities/src/interaction.rs rename to minecraft-server/src/entities/interaction.rs index 5ceaeaa9..c33c1eb8 100644 --- a/minecraft-entities/src/interaction.rs +++ b/minecraft-server/src/entities/interaction.rs @@ -1,6 +1,8 @@ use super::*; -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct Interaction { pub entity: Entity, pub width: f32, diff --git a/minecraft-entities/src/item.rs b/minecraft-server/src/entities/item.rs similarity index 58% rename from minecraft-entities/src/item.rs rename to minecraft-server/src/entities/item.rs index b80e3b29..3d4eede4 100644 --- a/minecraft-entities/src/item.rs +++ b/minecraft-server/src/entities/item.rs @@ -1,8 +1,11 @@ use super::*; #[derive(Default)] -#[inheritable] -#[inherit(Entity)] +#[MinecraftEntity( + inheritable, + ancestors { Entity }, + descendants { GlowingItemFrame }, +)] pub struct ItemFrame { pub entity: Entity, pub item: Slot, @@ -10,13 +13,17 @@ pub struct ItemFrame { } #[derive(Default)] -#[inherit(ItemFrame, Entity)] +#[MinecraftEntity( + ancestors { ItemFrame, Entity }, +)] pub struct GlowingItemFrame { pub item_frame: ItemFrame, } #[derive(Default)] -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct ItemEntity { pub entity: Entity, pub item: Slot, diff --git a/minecraft-entities/src/living_entity.rs b/minecraft-server/src/entities/living_entity.rs similarity index 89% rename from minecraft-entities/src/living_entity.rs rename to minecraft-server/src/entities/living_entity.rs index 41cb45f8..d0d06d33 100644 --- a/minecraft-entities/src/living_entity.rs +++ b/minecraft-server/src/entities/living_entity.rs @@ -1,9 +1,13 @@ use super::*; -#[inheritable] -#[inherit(Entity)] +#[MinecraftEntity( + inheritable, + ancestors { Entity }, + descendants { Player, ArmorStand, Mob... }, +)] pub struct LivingEntity { pub entity: Entity, + pub head_yaw: f32, pub is_hand_active: bool, pub active_hand: Hand, pub is_riptide_spinning: bool, @@ -19,6 +23,7 @@ impl Default for LivingEntity { fn default() -> Self { LivingEntity { entity: Entity::default(), + head_yaw: 0.0, is_hand_active: false, active_hand: Hand::MainHand, is_riptide_spinning: false, @@ -32,7 +37,9 @@ impl Default for LivingEntity { } } -#[inherit(LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { LivingEntity, Entity }, +)] pub struct ArmorStand { pub living_entity: LivingEntity, pub apparence_mask: u8, diff --git a/minecraft-entities/src/mobs/bat.rs b/minecraft-server/src/entities/mobs/bat.rs similarity index 59% rename from minecraft-entities/src/mobs/bat.rs rename to minecraft-server/src/entities/mobs/bat.rs index bb16f7e7..f4a4ecc6 100644 --- a/minecraft-entities/src/mobs/bat.rs +++ b/minecraft-server/src/entities/mobs/bat.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(AmbientCreature, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { AmbientCreature, Mob, LivingEntity, Entity }, +)] pub struct Bat { pub ambient_creature: AmbientCreature, pub is_hanging: bool, diff --git a/minecraft-entities/src/mobs/ender_dragon.rs b/minecraft-server/src/entities/mobs/ender_dragon.rs similarity index 82% rename from minecraft-entities/src/mobs/ender_dragon.rs rename to minecraft-server/src/entities/mobs/ender_dragon.rs index 73163c98..04a200b6 100644 --- a/minecraft-entities/src/mobs/ender_dragon.rs +++ b/minecraft-server/src/entities/mobs/ender_dragon.rs @@ -1,6 +1,8 @@ use super::*; -#[inherit(Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Mob, LivingEntity, Entity }, +)] pub struct EnderDragon { pub mob: Mob, pub phase: usize, @@ -15,7 +17,9 @@ impl Default for EnderDragon { } } -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct EndCrystal { pub entity: Entity, pub block_position: Option, diff --git a/minecraft-server/src/entities/mobs/flying.rs b/minecraft-server/src/entities/mobs/flying.rs new file mode 100644 index 00000000..c516c278 --- /dev/null +++ b/minecraft-server/src/entities/mobs/flying.rs @@ -0,0 +1,29 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { Mob, LivingEntity, Entity }, + descendants { Ghast, Phantom }, +)] +pub struct Flying { + pub mob: Mob, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Flying, Mob, LivingEntity, Entity }, +)] +pub struct Ghast { + pub flying: Flying, + pub is_attacking: bool, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Flying, Mob, LivingEntity, Entity }, +)] +pub struct Phantom { + pub flying: Flying, + pub size: usize, +} diff --git a/minecraft-server/src/entities/mobs/golems.rs b/minecraft-server/src/entities/mobs/golems.rs new file mode 100644 index 00000000..fa283721 --- /dev/null +++ b/minecraft-server/src/entities/mobs/golems.rs @@ -0,0 +1,29 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { PathfinderMob, Mob, LivingEntity, Entity }, + descendants { IronGolem, SnowGolem, Shulker }, +)] +pub struct AbstractGolem { + pub pathfinder_mob: PathfinderMob, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractGolem, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct IronGolem { + pub abstract_golem: AbstractGolem, + pub is_player_created: bool, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractGolem, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct SnowGolem { + pub abstract_golem: AbstractGolem, + pub has_pumpkin_hat: bool, +} diff --git a/minecraft-entities/src/mobs/mod.rs b/minecraft-server/src/entities/mobs/mod.rs similarity index 50% rename from minecraft-entities/src/mobs/mod.rs rename to minecraft-server/src/entities/mobs/mod.rs index a6c30697..e350db03 100644 --- a/minecraft-entities/src/mobs/mod.rs +++ b/minecraft-server/src/entities/mobs/mod.rs @@ -1,7 +1,5 @@ use super::*; -mod squid; -pub use squid::*; mod villagers; pub use villagers::*; mod golems; @@ -16,8 +14,11 @@ mod bat; pub use bat::*; #[derive(Default)] -#[inheritable] -#[inherit(LivingEntity, Entity)] +#[MinecraftEntity( + inheritable, + ancestors { LivingEntity, Entity }, + descendants { AmbientCreature..., PathfinderMob..., EnderDragon, Flying..., Slime }, +)] pub struct Mob { pub living_entity: LivingEntity, pub no_ai: bool, @@ -26,22 +27,31 @@ pub struct Mob { } #[derive(Default)] -#[inheritable] -#[inherit(Mob, LivingEntity, Entity)] +#[MinecraftEntity( + inheritable, + ancestors { Mob, LivingEntity, Entity }, + descendants { Bat }, +)] pub struct AmbientCreature { pub mob: Mob, } #[derive(Default)] -#[inheritable] -#[inherit(Mob, LivingEntity, Entity)] +#[MinecraftEntity( + inheritable, + ancestors { Mob, LivingEntity, Entity }, + descendants { WaterAnimal..., AgeableMob..., Monster..., AbstractGolem... }, +)] pub struct PathfinderMob { pub mob: Mob, } #[derive(Default)] -#[inheritable] -#[inherit(PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + inheritable, + ancestors { PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Animal..., AbstractVillager... }, +)] pub struct AgeableMob { pub pathfinder_mob: PathfinderMob, pub is_baby: bool, diff --git a/minecraft-entities/src/mobs/slime.rs b/minecraft-server/src/entities/mobs/slime.rs similarity index 58% rename from minecraft-entities/src/mobs/slime.rs rename to minecraft-server/src/entities/mobs/slime.rs index 6fadb29b..8383ca50 100644 --- a/minecraft-entities/src/mobs/slime.rs +++ b/minecraft-server/src/entities/mobs/slime.rs @@ -1,8 +1,9 @@ use super::*; #[derive(Default)] -#[inheritable] -#[inherit(Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Mob, LivingEntity, Entity }, +)] pub struct Slime { pub mob: Mob, pub size: usize, diff --git a/minecraft-server/src/entities/mobs/villagers.rs b/minecraft-server/src/entities/mobs/villagers.rs new file mode 100644 index 00000000..ca8a2e14 --- /dev/null +++ b/minecraft-server/src/entities/mobs/villagers.rs @@ -0,0 +1,29 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Villager, WanderingTrader }, +)] +pub struct AbstractVillager { + pub ageable_mob: AgeableMob, + pub head_shake_timer: u32, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractVillager, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Villager { + pub abstract_villager: AbstractVillager, + pub villager_data: Vec, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractVillager, AgeableMob, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct WanderingTrader { + pub abstract_villager: AbstractVillager, +} diff --git a/minecraft-server/src/entities/mod.rs b/minecraft-server/src/entities/mod.rs new file mode 100644 index 00000000..909251c0 --- /dev/null +++ b/minecraft-server/src/entities/mod.rs @@ -0,0 +1,404 @@ +#![allow(clippy::derivable_impls)] + +mod entity; +pub use entity::*; +mod thrown_item_projectile; +pub use thrown_item_projectile::*; +mod arrow; +pub use arrow::*; +mod boat; +pub use boat::*; +mod living_entity; +pub use living_entity::*; +mod player; +pub use player::*; +mod mobs; +pub use mobs::*; +mod interaction; +pub use interaction::*; +mod animals; +pub use animals::*; +mod display; +pub use display::*; +mod shulker; +pub use shulker::*; +mod monsters; +pub use monsters::*; +mod block; +pub use block::*; +mod particles; +pub use particles::*; +mod fire_entities; +pub use fire_entities::*; +mod item; +pub use item::*; + +mod tasks; +pub use tasks::*; + +pub use minecraft_positions::*; +pub use minecraft_entities_derive::MinecraftEntity; +pub use minecraft_protocol::{ + components::{ + entity::Pose, + slots::{Slot, SlotItem, Hand} + }, + ids::{items::Item, block_states::BlockWithState}, + nbt::NbtTag, + packets::UUID +}; +pub use crate::prelude::*; +use std::{pin::Pin, future::Future}; + +pub type Eid = u32; + +#[allow(dead_code)] +type CallBack = fn(O) -> Pin + Sync + Send>>; +#[allow(dead_code)] +type CallBack1 = fn(O, I) -> Pin + Sync + Send>>; +#[allow(dead_code)] +type CallBack2 = fn(O, I, J) -> Pin + Sync + Send>>; +#[allow(dead_code)] +type CallBack3 = fn(O, I, J) -> Pin + Sync + Send>>; +#[allow(dead_code)] +type CallBack4 = fn(O, I, J) -> Pin + Sync + Send>>; + +pub trait TryAsEntityRef { + fn try_as_entity_ref(&self) -> Option<&T>; + fn try_as_entity_mut(&mut self) -> Option<&mut T>; +} + +pub struct Handler where AnyEntity: TryAsEntityRef { + pub eid: Eid, + pub world: &'static World, + entity: std::marker::PhantomData, +} + +impl Clone for Handler where AnyEntity: TryAsEntityRef { + fn clone(&self) -> Self { + Self { + eid: self.eid, + world: self.world, + entity: std::marker::PhantomData, + } + } +} + +impl Handler where AnyEntity: TryAsEntityRef { + pub fn assume(id: Eid, world: &'static World) -> Self { + Self { + eid: id, + world, + entity: std::marker::PhantomData, + } + } + + fn assume_other(self) -> Handler where AnyEntity: TryAsEntityRef { + Handler { + eid: self.eid, + world: self.world, + entity: std::marker::PhantomData, + } + } + + pub async fn observe(&self, observer: impl FnOnce(&T) -> R) -> Option { + self.world.observe_entity(self.eid, move |entity| { + observer(entity.try_as_entity_ref().expect("Called observe on the wrong entity")) + }).await + } + + pub async fn observe_any(&self, observer: impl FnOnce(&AnyEntity) -> R) -> Option { + self.world.observe_entity(self.eid, observer).await + } + + pub async fn mutate(&self, mutator: impl FnOnce(&mut T) -> (R, EntityChanges)) -> Option { + self.world.mutate_entity(self.eid, move |entity| { + mutator(entity.try_as_entity_mut().expect("Called mutate on the wrong entity")) + }).await + } + + pub async fn mutate_any(&self, mutator: impl FnOnce(&mut AnyEntity) -> (R, EntityChanges)) -> Option { + self.world.mutate_entity(self.eid, mutator).await + } +} + +pub enum AnyEntity { + Entity(Entity), + Interaction(Interaction), + Display(Display), + BlockDisplay(BlockDisplay), + ItemDisplay(ItemDisplay), + TextDisplay(TextDisplay), + ThrownItemProjectile(ThrownItemProjectile), + ThrownEgg(ThrownEgg), + ThrownEnderPearl(ThrownEnderPearl), + ThrownExperienceBottle(ThrownExperienceBottle), + ThrownPotion(ThrownPotion), + Snowball(Snowball), + AbstractArrow(AbstractArrow), + Arrow(Arrow), + SpectralArrow(SpectralArrow), + ThrownTrident(ThrownTrident), + Boat(Boat), + ChestBoat(ChestBoat), + LivingEntity(LivingEntity), + Player(Player), + Mob(Mob), + AmbientCreature(AmbientCreature), + Bat(Bat), + PathfinderMob(PathfinderMob), + WaterAnimal(WaterAnimal), + Squid(Squid), + AgeableMob(AgeableMob), + Animal(Animal), + Sniffer(Sniffer), + AbstractHorse(AbstractHorse), + Horse(Horse), + ZombieHorse(ZombieHorse), + SkeletonHorse(SkeletonHorse), + Camel(Camel), + ChestedHorse(ChestedHorse), + Donkey(Donkey), + Llama(Llama), + TraderLlama(TraderLlama), + Mule(Mule), + Axolotl(Axolotl), + Bee(Bee), + Fox(Fox), + Frog(Frog), + Ocelot(Ocelot), + Panda(Panda), + Pig(Pig), + Rabbit(Rabbit), + Turtle(Turtle), + PolarBear(PolarBear), + Chicken(Chicken), + Cow(Cow), + Hoglin(Hoglin), + Mooshroom(Mooshroom), + Sheep(Sheep), + Strider(Strider), + TameableAnimal(TameableAnimal), + Cat(Cat), + Wolf(Wolf), + Parrot(Parrot), + AbstractVillager(AbstractVillager), + Villager(Villager), + WanderingTrader(WanderingTrader), + AbstractGolem(AbstractGolem), + IronGolem(IronGolem), + SnowGolem(SnowGolem), + Shulker(Shulker), + Monster(Monster), + BasePiglin(BasePiglin), + Piglin(Piglin), + PiglinBrute(PiglinBrute), + Blaze(Blaze), + Creeper(Creeper), + Endermite(Endermite), + Giant(Giant), + Goat(Goat), + Guardian(Guardian), + ElderGuardian(ElderGuardian), + Silverfish(Silverfish), + Raider(Raider), + AbstractIllager(AbstractIllager), + Vindicator(Vindicator), + Pillager(Pillager), + SpellcasterIllager(SpellcasterIllager), + Evoker(Evoker), + Illusioner(Illusioner), + Ravager(Ravager), + Witch(Witch), + EvokerFangs(EvokerFangs), + Vex(Vex), + Skeleton(Skeleton), + AbstractSkeleton(AbstractSkeleton), + WitherSkeleton(WitherSkeleton), + Stray(Stray), + Spider(Spider), + Warden(Warden), + Wither(Wither), + Zoglin(Zoglin), + Zombie(Zombie), + ZombieVillager(ZombieVillager), + Husk(Husk), + Drowned(Drowned), + ZombifiedPiglin(ZombifiedPiglin), + Enderman(Enderman), + EnderDragon(EnderDragon), + Flying(Flying), + Ghast(Ghast), + Phantom(Phantom), + Slime(Slime), + LlamaSpit(LlamaSpit), + EyeOfEnder(EyeOfEnder), + FallingBlock(FallingBlock), + AreaEffectCloud(AreaEffectCloud), + FishingHook(FishingHook), + EndCrystal(EndCrystal), + DragonFireball(DragonFireball), + SmallFireball(SmallFireball), + Fireball(Fireball), + WitherSkull(WitherSkull), + FireworkRocket(FireworkRocket), + ItemFrame(ItemFrame), + GlowingItemFrame(GlowingItemFrame), + Painting(Painting), + ItemEntity(ItemEntity), + ArmorStand(ArmorStand), + Dolphin(Dolphin), + AbstractFish(AbstractFish), + Cod(Cod), + Pufferfish(Pufferfish), + Salmon(Salmon), + TropicalFish(TropicalFish), + Tadpole(Tadpole), +} + +impl AnyEntity { + pub fn as_entity(&self) -> &Entity { + self.try_as_entity_ref().unwrap() + } + + pub fn as_other(&self) -> Option<&O> where AnyEntity: TryAsEntityRef { + self.try_as_entity_ref() + } + + pub fn to_network(&self) -> Option { + use minecraft_protocol::ids::entities::Entity::*; + match self { + AnyEntity::Entity(_) => None, + AnyEntity::Interaction(_) => Some(Interaction), + AnyEntity::Display(_) => None, + AnyEntity::BlockDisplay(_) => Some(BlockDisplay), + AnyEntity::ItemDisplay(_) => Some(ItemDisplay), + AnyEntity::TextDisplay(_) => Some(TextDisplay), + AnyEntity::ThrownItemProjectile(_) => None, + AnyEntity::ThrownEgg(_) => Some(Egg), + AnyEntity::ThrownEnderPearl(_) => Some(EnderPearl), + AnyEntity::ThrownExperienceBottle(_) => Some(ExperienceBottle), + AnyEntity::ThrownPotion(_) => Some(Potion), + AnyEntity::Snowball(_) => Some(Snowball), + AnyEntity::AbstractArrow(_) => Some(Arrow), // Default to arrow + AnyEntity::Arrow(_) => Some(Arrow), + AnyEntity::SpectralArrow(_) => Some(SpectralArrow), + AnyEntity::ThrownTrident(_) => Some(Trident), + AnyEntity::Boat(_) => Some(Boat), + AnyEntity::ChestBoat(_) => Some(ChestBoat), + AnyEntity::LivingEntity(_) => None, + AnyEntity::Player(_) => Some(Player), + AnyEntity::Mob(_) => None, + AnyEntity::AmbientCreature(_) => None, + AnyEntity::Bat(_) => Some(Bat), + AnyEntity::PathfinderMob(_) => None, + AnyEntity::WaterAnimal(_) => None, + AnyEntity::Squid(_) => Some(Squid), + AnyEntity::AgeableMob(_) => None, + AnyEntity::Animal(_) => None, + AnyEntity::Sniffer(_) => Some(Sniffer), + AnyEntity::AbstractHorse(_) => None, + AnyEntity::Horse(_) => Some(Horse), + AnyEntity::ZombieHorse(_) => Some(ZombieHorse), + AnyEntity::SkeletonHorse(_) => Some(SkeletonHorse), + AnyEntity::Camel(_) => Some(Camel), + AnyEntity::ChestedHorse(_) => None, + AnyEntity::Donkey(_) => Some(Donkey), + AnyEntity::Llama(_) => Some(Llama), + AnyEntity::TraderLlama(_) => Some(TraderLlama), + AnyEntity::Mule(_) => Some(Mule), + AnyEntity::Axolotl(_) => Some(Axolotl), + AnyEntity::Bee(_) => Some(Bee), + AnyEntity::Fox(_) => Some(Fox), + AnyEntity::Frog(_) => Some(Frog), + AnyEntity::Ocelot(_) => Some(Ocelot), + AnyEntity::Panda(_) => Some(Panda), + AnyEntity::Pig(_) => Some(Pig), + AnyEntity::Rabbit(_) => Some(Rabbit), + AnyEntity::Turtle(_) => Some(Turtle), + AnyEntity::PolarBear(_) => Some(PolarBear), + AnyEntity::Chicken(_) => Some(Chicken), + AnyEntity::Cow(_) => Some(Cow), + AnyEntity::Hoglin(_) => Some(Hoglin), + AnyEntity::Mooshroom(_) => Some(Mooshroom), + AnyEntity::Sheep(_) => Some(Sheep), + AnyEntity::Strider(_) => Some(Strider), + AnyEntity::TameableAnimal(_) => None, + AnyEntity::Cat(_) => Some(Cat), + AnyEntity::Wolf(_) => Some(Wolf), + AnyEntity::Parrot(_) => Some(Parrot), + AnyEntity::AbstractVillager(_) => None, + AnyEntity::Villager(_) => Some(Villager), + AnyEntity::WanderingTrader(_) => Some(WanderingTrader), + AnyEntity::AbstractGolem(_) => None, + AnyEntity::IronGolem(_) => Some(IronGolem), + AnyEntity::SnowGolem(_) => Some(SnowGolem), + AnyEntity::Shulker(_) => Some(Shulker), + AnyEntity::Monster(_) => None, + AnyEntity::BasePiglin(_) => None, + AnyEntity::Piglin(_) => Some(Piglin), + AnyEntity::PiglinBrute(_) => Some(PiglinBrute), + AnyEntity::Blaze(_) => Some(Blaze), + AnyEntity::Creeper(_) => Some(Creeper), + AnyEntity::Endermite(_) => Some(Endermite), + AnyEntity::Giant(_) => Some(Giant), + AnyEntity::Goat(_) => Some(Goat), + AnyEntity::Guardian(_) => Some(Guardian), + AnyEntity::ElderGuardian(_) => Some(ElderGuardian), + AnyEntity::Silverfish(_) => Some(Silverfish), + AnyEntity::Raider(_) => None, + AnyEntity::AbstractIllager(_) => None, + AnyEntity::Vindicator(_) => Some(Vindicator), + AnyEntity::Pillager(_) => Some(Pillager), + AnyEntity::SpellcasterIllager(_) => None, + AnyEntity::Evoker(_) => Some(Evoker), + AnyEntity::Illusioner(_) => Some(Illusioner), + AnyEntity::Ravager(_) => Some(Ravager), + AnyEntity::Witch(_) => Some(Witch), + AnyEntity::EvokerFangs(_) => Some(EvokerFangs), + AnyEntity::Vex(_) => Some(Vex), + AnyEntity::Skeleton(_) => Some(Skeleton), + AnyEntity::AbstractSkeleton(_) => None, + AnyEntity::WitherSkeleton(_) => Some(WitherSkeleton), + AnyEntity::Stray(_) => Some(Stray), + AnyEntity::Spider(_) => Some(Spider), + AnyEntity::Warden(_) => Some(Warden), + AnyEntity::Wither(_) => Some(Wither), + AnyEntity::Zoglin(_) => Some(Zoglin), + AnyEntity::Zombie(_) => Some(Zombie), + AnyEntity::ZombieVillager(_) => Some(ZombieVillager), + AnyEntity::Husk(_) => Some(Husk), + AnyEntity::Drowned(_) => Some(Drowned), + AnyEntity::ZombifiedPiglin(_) => Some(ZombifiedPiglin), + AnyEntity::Enderman(_) => Some(Enderman), + AnyEntity::EnderDragon(_) => Some(EnderDragon), + AnyEntity::Flying(_) => None, + AnyEntity::Ghast(_) => Some(Ghast), + AnyEntity::Phantom(_) => Some(Phantom), + AnyEntity::Slime(_) => Some(Slime), + AnyEntity::LlamaSpit(_) => Some(LlamaSpit), + AnyEntity::EyeOfEnder(_) => Some(EyeOfEnder), + AnyEntity::FallingBlock(_) => Some(FallingBlock), + AnyEntity::AreaEffectCloud(_) => Some(AreaEffectCloud), + AnyEntity::FishingHook(_) => Some(FishingBobber), + AnyEntity::EndCrystal(_) => Some(EndCrystal), + AnyEntity::DragonFireball(_) => Some(DragonFireball), + AnyEntity::SmallFireball(_) => Some(SmallFireball), + AnyEntity::Fireball(_) => Some(Fireball), + AnyEntity::WitherSkull(_) => Some(WitherSkull), + AnyEntity::FireworkRocket(_) => Some(FireworkRocket), + AnyEntity::ItemFrame(_) => Some(ItemFrame), + AnyEntity::GlowingItemFrame(_) => Some(GlowItemFrame), + AnyEntity::Painting(_) => Some(Painting), + AnyEntity::ItemEntity(_) => Some(Item), + AnyEntity::ArmorStand(_) => Some(ArmorStand), + AnyEntity::Dolphin(_) => Some(Dolphin), + AnyEntity::AbstractFish(_) => None, + AnyEntity::Cod(_) => Some(Cod), + AnyEntity::Pufferfish(_) => Some(Pufferfish), + AnyEntity::Salmon(_) => Some(Salmon), + AnyEntity::TropicalFish(_) => Some(TropicalFish), + AnyEntity::Tadpole(_) => Some(Tadpole), + } + } +} diff --git a/minecraft-entities/src/monsters/blaze.rs b/minecraft-server/src/entities/monsters/blaze.rs similarity index 54% rename from minecraft-entities/src/monsters/blaze.rs rename to minecraft-server/src/entities/monsters/blaze.rs index 94b9a585..bdac2f22 100644 --- a/minecraft-entities/src/monsters/blaze.rs +++ b/minecraft-server/src/entities/monsters/blaze.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Blaze { pub monster: Monster, pub is_on_fire: bool, diff --git a/minecraft-entities/src/monsters/creeper.rs b/minecraft-server/src/entities/monsters/creeper.rs similarity index 79% rename from minecraft-entities/src/monsters/creeper.rs rename to minecraft-server/src/entities/monsters/creeper.rs index f9ef9f1a..92ee9cf5 100644 --- a/minecraft-entities/src/monsters/creeper.rs +++ b/minecraft-server/src/entities/monsters/creeper.rs @@ -1,6 +1,8 @@ use super::*; -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Creeper { pub monster: Monster, pub state: i8, diff --git a/minecraft-entities/src/monsters/enderman.rs b/minecraft-server/src/entities/monsters/enderman.rs similarity index 64% rename from minecraft-entities/src/monsters/enderman.rs rename to minecraft-server/src/entities/monsters/enderman.rs index c14ecdb3..e0e2d109 100644 --- a/minecraft-entities/src/monsters/enderman.rs +++ b/minecraft-server/src/entities/monsters/enderman.rs @@ -2,7 +2,10 @@ use minecraft_protocol::ids::blocks::Block; use super::*; -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] +#[derive(Default)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Enderman { pub monster: Monster, pub block_id: Option, diff --git a/minecraft-server/src/entities/monsters/endermite.rs b/minecraft-server/src/entities/monsters/endermite.rs new file mode 100644 index 00000000..aec4e303 --- /dev/null +++ b/minecraft-server/src/entities/monsters/endermite.rs @@ -0,0 +1,9 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Endermite { + pub monster: Monster, +} diff --git a/minecraft-server/src/entities/monsters/giant.rs b/minecraft-server/src/entities/monsters/giant.rs new file mode 100644 index 00000000..c7fbe40c --- /dev/null +++ b/minecraft-server/src/entities/monsters/giant.rs @@ -0,0 +1,9 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Giant { + pub monster: Monster, +} diff --git a/minecraft-server/src/entities/monsters/guardian.rs b/minecraft-server/src/entities/monsters/guardian.rs new file mode 100644 index 00000000..0f831530 --- /dev/null +++ b/minecraft-server/src/entities/monsters/guardian.rs @@ -0,0 +1,22 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { ElderGuardian }, +)] +pub struct Guardian { + pub monster: Monster, + pub is_retracting_spikes: bool, + pub target_eid: Eid, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Guardian, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct ElderGuardian { + pub guardian: Guardian, +} + diff --git a/minecraft-entities/src/monsters/mod.rs b/minecraft-server/src/entities/monsters/mod.rs similarity index 67% rename from minecraft-entities/src/monsters/mod.rs rename to minecraft-server/src/entities/monsters/mod.rs index b6e28897..80ea6cdc 100644 --- a/minecraft-entities/src/monsters/mod.rs +++ b/minecraft-server/src/entities/monsters/mod.rs @@ -1,7 +1,5 @@ -use super::*; +pub use super::*; -mod base_piglin; -pub use base_piglin::*; mod piglin; pub use piglin::*; mod blaze; @@ -18,10 +16,6 @@ mod silverfish; pub use silverfish::*; mod raider; pub use raider::*; -mod spellcaster_illager; -pub use spellcaster_illager::*; -mod witch; -pub use witch::*; mod vex; pub use vex::*; mod skeleton; @@ -40,8 +34,11 @@ mod enderman; pub use enderman::*; #[derive(Default)] -#[inheritable] -#[inherit(PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + inheritable, + ancestors { PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Blaze, Creeper, Enderman, Endermite, Giant, Raider, Silverfish, Spider, Vex, Warden, Wither, Zoglin, Zombie, AbstractSkeleton...,BasePiglin..., Guardian... }, +)] pub struct Monster { pub pathfinder_mob: PathfinderMob, } diff --git a/minecraft-server/src/entities/monsters/piglin.rs b/minecraft-server/src/entities/monsters/piglin.rs new file mode 100644 index 00000000..75e9594e --- /dev/null +++ b/minecraft-server/src/entities/monsters/piglin.rs @@ -0,0 +1,31 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Piglin, PiglinBrute }, +)] +pub struct BasePiglin { + pub monster: Monster, + pub is_immune: bool, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { BasePiglin, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Piglin { + pub base_piglin: BasePiglin, + pub is_baby: bool, + pub is_charging_crossbow: bool, + pub is_dancing: bool, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { BasePiglin, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct PiglinBrute { + pub base_piglin: BasePiglin, +} diff --git a/minecraft-server/src/entities/monsters/raider.rs b/minecraft-server/src/entities/monsters/raider.rs new file mode 100644 index 00000000..34e5bffd --- /dev/null +++ b/minecraft-server/src/entities/monsters/raider.rs @@ -0,0 +1,91 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Witch, AbstractIllager... }, +)] +pub struct Raider { + pub monster: Monster, + pub is_celebrating: bool, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Witch { + pub raider: Raider, + pub is_drinking_potion: bool, +} + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Vindicator, Pillager, SpellcasterIllager... }, +)] +pub struct AbstractIllager { + pub raider: Raider, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractIllager, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Vindicator { + pub abstract_illager: AbstractIllager, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractIllager, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Pillager { + pub abstract_illager: AbstractIllager, + pub is_charging: bool, +} + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Illusioner, Ravager, Evoker }, +)] +pub struct SpellcasterIllager { + pub abstract_illager: AbstractIllager, + pub spell: u8, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { SpellcasterIllager, AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Illusioner { + pub spellcaster_illager: SpellcasterIllager, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { SpellcasterIllager, AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Ravager { + pub spellcaster_illager: SpellcasterIllager, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { SpellcasterIllager, AbstractIllager, Raider, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Evoker { + pub spellcaster_illager: SpellcasterIllager, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Entity }, +)] +pub struct EvokerFangs { + pub entity: Entity, +} diff --git a/minecraft-server/src/entities/monsters/silverfish.rs b/minecraft-server/src/entities/monsters/silverfish.rs new file mode 100644 index 00000000..a8578abb --- /dev/null +++ b/minecraft-server/src/entities/monsters/silverfish.rs @@ -0,0 +1,9 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Silverfish { + pub monster: Monster, +} diff --git a/minecraft-server/src/entities/monsters/skeleton.rs b/minecraft-server/src/entities/monsters/skeleton.rs new file mode 100644 index 00000000..c5ea250f --- /dev/null +++ b/minecraft-server/src/entities/monsters/skeleton.rs @@ -0,0 +1,35 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { Skeleton, WitherSkeleton, Stray }, +)] +pub struct AbstractSkeleton { + pub monster: Monster, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractSkeleton, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Skeleton { + pub abstract_skeleton: AbstractSkeleton, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractSkeleton, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct WitherSkeleton { + pub abstract_skeleton: AbstractSkeleton, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { AbstractSkeleton, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Stray { + pub abstract_skeleton: AbstractSkeleton, +} diff --git a/minecraft-server/src/entities/monsters/spider.rs b/minecraft-server/src/entities/monsters/spider.rs new file mode 100644 index 00000000..d62fc4d7 --- /dev/null +++ b/minecraft-server/src/entities/monsters/spider.rs @@ -0,0 +1,10 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Spider { + pub monster: Monster, + pub is_climbing_mask: u8, +} diff --git a/minecraft-server/src/entities/monsters/vex.rs b/minecraft-server/src/entities/monsters/vex.rs new file mode 100644 index 00000000..54c0198d --- /dev/null +++ b/minecraft-server/src/entities/monsters/vex.rs @@ -0,0 +1,9 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Vex { + pub monster: Monster, +} diff --git a/minecraft-entities/src/monsters/warden.rs b/minecraft-server/src/entities/monsters/warden.rs similarity index 54% rename from minecraft-entities/src/monsters/warden.rs rename to minecraft-server/src/entities/monsters/warden.rs index ad99afaa..906fc877 100644 --- a/minecraft-entities/src/monsters/warden.rs +++ b/minecraft-server/src/entities/monsters/warden.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Warden { pub monster: Monster, pub anger_level: usize, diff --git a/minecraft-server/src/entities/monsters/wither.rs b/minecraft-server/src/entities/monsters/wither.rs new file mode 100644 index 00000000..0edc9c8c --- /dev/null +++ b/minecraft-server/src/entities/monsters/wither.rs @@ -0,0 +1,22 @@ +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Wither { + pub monster: Monster, + pub center_head_target: Option, + pub left_head_target: Option, + pub right_head: Option, + pub invulnerable_time: usize, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Entity }, +)] +pub struct WitherSkull { + pub entity: Entity, + pub is_invulnerable: bool, +} diff --git a/minecraft-entities/src/monsters/zoglin.rs b/minecraft-server/src/entities/monsters/zoglin.rs similarity index 53% rename from minecraft-entities/src/monsters/zoglin.rs rename to minecraft-server/src/entities/monsters/zoglin.rs index a59b7365..d05a01ac 100644 --- a/minecraft-entities/src/monsters/zoglin.rs +++ b/minecraft-server/src/entities/monsters/zoglin.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(Monster, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Zoglin { pub monster: Monster, pub is_baby: bool, diff --git a/minecraft-server/src/entities/monsters/zombies.rs b/minecraft-server/src/entities/monsters/zombies.rs new file mode 100644 index 00000000..45306920 --- /dev/null +++ b/minecraft-server/src/entities/monsters/zombies.rs @@ -0,0 +1,103 @@ +use minecraft_protocol::network; + +use super::*; + +#[derive(Default)] +#[MinecraftEntity( + inheritable, + ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity }, + descendants { ZombieVillager, Husk, Drowned, ZombifiedPiglin }, + defines { + Entity.init(self, server_msg_rcvr: BroadcastReceiver); + } +)] +pub struct Zombie { + pub monster: Monster, + pub is_baby: bool, + pub unused: isize, + pub is_becoming_drowned: bool, +} + +impl Handler { + pub async fn init(self, server_msg_rcvr: BroadcastReceiver) { + self.insert_task("newton", tokio::spawn(newton_task(self.clone(), server_msg_rcvr.resubscribe()))).await; + self.insert_task("zombie-ai", tokio::spawn(zombie_ai_task(self.clone(), server_msg_rcvr))).await; + } +} + +pub async fn zombie_ai_task(h: Handler, mut server_msg_rcvr: BroadcastReceiver) where AnyEntity: TryAsEntityRef { + loop { + let msg = server_msg_rcvr.recv().await.unwrap(); + + if !matches!(&msg, &ServerMessage::Tick) { + continue; + } + + let self_position = h.observe(|e| e.get_entity().position.clone()).await.unwrap(); + let chunk = self_position.chunk_column(); + let player_positions = h.world.observe_entities(chunk, |entity| { + let network_entity = entity.to_network().unwrap(); + TryAsEntityRef::::try_as_entity_ref(entity).map(|player| { + (player.get_entity().position.clone(), network_entity) + }) + }).await; + + let Some((target_position, network_entity)) = player_positions.get(0) else { continue }; + let target_object = CollisionShape { + x1: target_position.x - network_entity.width() as f64 / 2.0, + y1: target_position.y, + z1: target_position.z - network_entity.width() as f64 / 2.0, + x2: target_position.x + network_entity.width() as f64 / 2.0, + y2: target_position.y + network_entity.height() as f64, + z2: target_position.z + network_entity.width() as f64 / 2.0, + }; + + let mut translation = Translation { + x: target_position.x - self_position.x, + y: target_position.y - self_position.y, + z: target_position.z - self_position.z, + }; + translation.set_norm(0.23000000417232513); + + let authorized_translation = h.world.try_move(&target_object, &translation).await; + + h.mutate(|e| { + e.get_entity_mut().position += authorized_translation; + ((), EntityChanges::position()) + }).await; + } +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct ZombieVillager { + pub zombie: Zombie, + pub is_converting: bool, + pub villager_data: Vec, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Husk { + pub zombie: Zombie, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct Drowned { + pub zombie: Zombie, +} + +#[derive(Default)] +#[MinecraftEntity( + ancestors { Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity }, +)] +pub struct ZombifiedPiglin { + pub zombie: Zombie, +} diff --git a/minecraft-entities/src/particles.rs b/minecraft-server/src/entities/particles.rs similarity index 86% rename from minecraft-entities/src/particles.rs rename to minecraft-server/src/entities/particles.rs index 479f1fbc..8f28cbe7 100644 --- a/minecraft-entities/src/particles.rs +++ b/minecraft-server/src/entities/particles.rs @@ -1,8 +1,9 @@ use minecraft_protocol::components::particle::Particle; - use super::*; -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct AreaEffectCloud { pub entity: Entity, pub radius: f32, @@ -24,7 +25,9 @@ impl Default for AreaEffectCloud { } #[derive(Default)] -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct FishingHook{ pub entity: Entity, pub hooked_entity: Option, diff --git a/minecraft-entities/src/player.rs b/minecraft-server/src/entities/player.rs similarity index 74% rename from minecraft-entities/src/player.rs rename to minecraft-server/src/entities/player.rs index fbd2b690..b1fe0cac 100644 --- a/minecraft-entities/src/player.rs +++ b/minecraft-server/src/entities/player.rs @@ -1,6 +1,11 @@ use super::*; -#[inherit(LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { LivingEntity, Entity }, + defines { + Entity.init(self, server_msg_rcvr: BroadcastReceiver); + } +)] pub struct Player { pub living_entity: LivingEntity, pub additional_hearts: f32, @@ -17,6 +22,12 @@ pub struct Player { pub right_shoulder_entity: NbtTag, } +impl Handler { + pub async fn init(self, server_msg_rcvr: BroadcastReceiver) { + //self.insert_task("newton", tokio::spawn(newton_task(self.clone(), server_msg_rcvr))).await; + } +} + impl Default for Player { fn default() -> Self { Player { diff --git a/minecraft-entities/src/shulker.rs b/minecraft-server/src/entities/shulker.rs similarity index 68% rename from minecraft-entities/src/shulker.rs rename to minecraft-server/src/entities/shulker.rs index c26c56f4..6cc7093e 100644 --- a/minecraft-entities/src/shulker.rs +++ b/minecraft-server/src/entities/shulker.rs @@ -1,7 +1,9 @@ use super::*; #[derive(Default)] -#[inherit(AbstractGolem, PathfinderMob, Mob, LivingEntity, Entity)] +#[MinecraftEntity( + ancestors { AbstractGolem, PathfinderMob, Mob, LivingEntity, Entity }, +)] pub struct Shulker { pub abstract_golem: AbstractGolem, pub attach_face: u8, diff --git a/minecraft-server/src/entities/tasks/mod.rs b/minecraft-server/src/entities/tasks/mod.rs new file mode 100644 index 00000000..bc48155f --- /dev/null +++ b/minecraft-server/src/entities/tasks/mod.rs @@ -0,0 +1,4 @@ +pub use super::*; + +mod newton; +pub use newton::*; diff --git a/minecraft-server/src/entities/tasks/newton.rs b/minecraft-server/src/entities/tasks/newton.rs new file mode 100644 index 00000000..21d981e8 --- /dev/null +++ b/minecraft-server/src/entities/tasks/newton.rs @@ -0,0 +1,73 @@ +use crate::CollisionShape; + +use super::*; + +pub async fn newton_task(h: Handler, mut server_msg_rcvr: BroadcastReceiver) where AnyEntity: TryAsEntityRef { + loop { + let msg = server_msg_rcvr.recv().await.unwrap(); + + if !matches!(&msg, &ServerMessage::Tick) { + continue; + } + + // Get data from entity + let Some((mut position, mut velocity, network_entity)) = h.observe_any(|any_entity| { + let entity = any_entity.as_entity(); + let network_entity = any_entity.to_network(); + (entity.position.clone(), entity.velocity.clone(), network_entity) + }).await else { return; }; + + let (width, height) = match network_entity { + Some(network_entity) => (network_entity.width() as f64, network_entity.height() as f64), + None => { + warn!("Entity {} has no network entity", h.eid); // TODO(perf): Make gravity verify that the entity has bounding boxes at the start + return; + } + }; + + // Apply velocity and collisions + let mut changes = EntityChanges::nothing(); + let mut new_velocity = velocity.clone(); + new_velocity.y -= 9.81/20.0; + let bounding_box = CollisionShape { + x1: position.x - width/2.0, + y1: position.y, + z1: position.z - width/2.0, + x2: position.x + width/2.0, + y2: position.y + height, + z2: position.z + width/2.0, + }; + let new_velocity = h.world.try_move(&bounding_box, &new_velocity).await; + if velocity.x != new_velocity.x { + velocity.x = 0.0; + changes += EntityChanges::velocity(); + } + if velocity.y != new_velocity.y { + velocity.y = 0.0; + changes += EntityChanges::velocity(); + } + if velocity.z != new_velocity.z { + velocity.z = 0.0; + changes += EntityChanges::velocity(); + } + if !new_velocity.is_zero() { + changes += EntityChanges::position(); + position += new_velocity; + } + + // TODO(feat): Apply air resistance to x and z velocity + // Keep in mind that velocity shouldn't flicker when constantly kept up by another task but slowed down in this task + + // Mutate entity + // TODO(correctness): Before modifying entity values, we should ensure the original values we based the changes on are still the same + if changes.nothing_changed() { + continue; + } + h.mutate(|entity| { + let entity = entity.get_entity_mut(); + entity.velocity = velocity; + entity.position = position; + ((), changes) + }).await; + } +} diff --git a/minecraft-entities/src/thrown_item_projectile.rs b/minecraft-server/src/entities/thrown_item_projectile.rs similarity index 83% rename from minecraft-entities/src/thrown_item_projectile.rs rename to minecraft-server/src/entities/thrown_item_projectile.rs index f45fb5b3..e07e3c8e 100644 --- a/minecraft-entities/src/thrown_item_projectile.rs +++ b/minecraft-server/src/entities/thrown_item_projectile.rs @@ -1,7 +1,10 @@ use super::*; -#[inheritable] -#[inherit(Entity)] +#[MinecraftEntity( + inheritable, + ancestors { Entity }, + descendants { ThrownEgg, ThrownEnderPearl, ThrownExperienceBottle, ThrownPotion, Snowball }, +)] pub struct ThrownItemProjectile { pub entity: Entity, pub item: Slot, @@ -16,7 +19,9 @@ impl Default for ThrownItemProjectile { } } -#[inherit(ThrownItemProjectile, Entity)] +#[MinecraftEntity( + ancestors { ThrownItemProjectile, Entity }, +)] pub struct ThrownEgg { pub thrown_item_projectile: ThrownItemProjectile, } @@ -36,7 +41,9 @@ impl Default for ThrownEgg { } } -#[inherit(ThrownItemProjectile, Entity)] +#[MinecraftEntity( + ancestors { ThrownItemProjectile, Entity }, +)] pub struct ThrownEnderPearl { pub thrown_item_projectile: ThrownItemProjectile, } @@ -56,7 +63,9 @@ impl Default for ThrownEnderPearl { } } -#[inherit(ThrownItemProjectile, Entity)] +#[MinecraftEntity( + ancestors { ThrownItemProjectile, Entity }, +)] pub struct ThrownExperienceBottle { pub thrown_item_projectile: ThrownItemProjectile, } @@ -76,7 +85,9 @@ impl Default for ThrownExperienceBottle { } } -#[inherit(ThrownItemProjectile, Entity)] +#[MinecraftEntity( + ancestors { ThrownItemProjectile, Entity }, +)] pub struct ThrownPotion { pub thrown_item_projectile: ThrownItemProjectile, } @@ -96,7 +107,9 @@ impl Default for ThrownPotion { } } -#[inherit(ThrownItemProjectile, Entity)] +#[MinecraftEntity( + ancestors { ThrownItemProjectile, Entity }, +)] pub struct Snowball { pub thrown_item_projectile: ThrownItemProjectile, } @@ -117,7 +130,9 @@ impl Default for Snowball { } #[derive(Default)] -#[inherit(Entity)] +#[MinecraftEntity( + ancestors { Entity }, +)] pub struct EyeOfEnder { pub entity: Entity, pub item: Slot, diff --git a/minecraft-server/src/main.rs b/minecraft-server/src/main.rs index 4f554561..9667ad48 100644 --- a/minecraft-server/src/main.rs +++ b/minecraft-server/src/main.rs @@ -3,48 +3,11 @@ mod player_handler; mod server_behavior; mod prelude; -mod ecs; mod world; +mod entities; use crate::prelude::*; -struct Map { - -} - -fn min(a: f32, b: f32, c: f32) -> f32 { - fn min2(a: f32, b: f32) -> f32 { - if a < b { - a - } else { - b - } - } - min2(min2(a, b), c) -} - -fn ray_cast(position: (f32, f32, f32), movement: (f32, f32, f32)) -> Vec<(isize, isize, isize)> { - let final_position = ((position.0+movement.0) as isize, (position.1+movement.1) as isize, (position.2+movement.2) as isize); - let mut result = Vec::new(); - let mut next_position = position; - result.push((next_position.0 as isize, next_position.1 as isize, next_position.2 as isize)); - while result.last().unwrap() != &final_position { - let next_x = if movement.0 > 0.0 { next_position.0.floor()+1.0 } else { next_position.0.floor()-1.0 }; - let next_y = if movement.1 > 0.0 { next_position.1.floor()+1.0 } else { next_position.1.floor()-1.0 }; - let next_z = if movement.2 > 0.0 { next_position.2.floor()+1.0 } else { next_position.2.floor()-1.0 }; - let x_dist = (next_x - next_position.0).abs(); - let y_dist = (next_y - next_position.1).abs(); - let z_dist = (next_z - next_position.2).abs(); - let x_time = x_dist / movement.0.abs(); - let y_time = y_dist / movement.1.abs(); - let z_time = z_dist / movement.2.abs(); - let time = min(x_time, y_time, z_time); - println!("pos{next_position:?} dist({x_dist}, {y_dist}, {z_dist}) time({x_time}, {y_time}, {z_time}) time({time})"); - next_position = (next_position.0 + movement.0 * time, next_position.1 + movement.1 * time, next_position.2 + movement.2 * time); - result.push((next_position.0 as isize, next_position.1 as isize, next_position.2 as isize)); - } - result -} struct ServerFuture { server: ServerBehavior, diff --git a/minecraft-server/src/player_handler/connect.rs b/minecraft-server/src/player_handler/connect.rs index cdf4b170..84d99f66 100644 --- a/minecraft-server/src/player_handler/connect.rs +++ b/minecraft-server/src/player_handler/connect.rs @@ -4,7 +4,7 @@ pub async fn handle_connection( mut stream: TcpStream, addr: SocketAddr, server_msg_rcvr: BroadcastReceiver, - world: Arc, + world: &'static World, ) -> Result<(), ()> { // Receive handshake let packet = receive_packet(&mut stream).await?; @@ -12,9 +12,9 @@ pub async fn handle_connection( match next_state { ConnectionState::Login => { let player_info = login(&mut stream, addr).await?; - let (player_info, change_receiver) = handshake(&mut stream, player_info, Arc::clone(&world)).await?; + let (player_info, change_receiver) = handshake(&mut stream, player_info, world).await?; let uuid = player_info.uuid; - let r = handle_player(stream, player_info, server_msg_rcvr, Arc::clone(&world), change_receiver).await; + let r = handle_player(stream, player_info, server_msg_rcvr, world, change_receiver).await; world.remove_loader(uuid).await; r }, diff --git a/minecraft-server/src/player_handler/handshake.rs b/minecraft-server/src/player_handler/handshake.rs index 086869a6..442ebe93 100644 --- a/minecraft-server/src/player_handler/handshake.rs +++ b/minecraft-server/src/player_handler/handshake.rs @@ -14,7 +14,7 @@ pub struct PlayerInfo { pub(super) allow_server_listing: bool, } -pub async fn handshake(stream: &mut TcpStream, logged_in_player_info: LoggedInPlayerInfo, world: Arc) -> Result<(PlayerInfo, MpscReceiver), ()> { +pub async fn handshake(stream: &mut TcpStream, logged_in_player_info: LoggedInPlayerInfo, world: &'static World) -> Result<(PlayerInfo, MpscReceiver), ()> { // Receive client informations let packet = receive_packet(stream).await?; debug!("Packet received"); diff --git a/minecraft-server/src/player_handler/play.rs b/minecraft-server/src/player_handler/play.rs index 52b5bf74..0e80f42a 100644 --- a/minecraft-server/src/player_handler/play.rs +++ b/minecraft-server/src/player_handler/play.rs @@ -1,7 +1,8 @@ use super::*; struct PlayerHandler { - world: Arc, + eid: Eid, + world: &'static World, game_mode: Gamemode, info: PlayerInfo, position: Position, @@ -10,6 +11,9 @@ struct PlayerHandler { on_ground: bool, packet_sender: MpscSender>, + // TODO: make this a hashmap + entity_prev_positions: HashMap, + render_distance: i32, loaded_chunks: HashSet, center_chunk: ChunkPosition, @@ -30,16 +34,62 @@ impl PlayerHandler { } } - async fn on_block_change(&mut self, position: BlockPosition, block: BlockWithState) { + async fn on_block_changed(&mut self, position: BlockPosition, block: BlockWithState) { self.send_packet(PlayClientbound::BlockUpdate { location: position.into(), block_state: block, }).await; } + async fn on_entity_spawned(&mut self, eid: Eid, uuid: UUID, ty: NetworkEntity, position: Position, pitch: f32, yaw: f32, head_yaw: f32, data: u32, velocity: Translation, metadata: ()) { + self.entity_prev_positions.insert(eid, position.clone()); + self.send_packet(PlayClientbound::SpawnEntity { + id: VarInt(eid as i32), + uuid, + entity_type: ty, + x: position.x, + y: position.y, + z: position.z, + pitch: (pitch * (256.0 / 360.0)) as u8, + yaw: (yaw * (256.0 / 360.0)) as u8, + head_yaw: (head_yaw * (256.0 / 360.0)) as u8, + data: VarInt(data as i32), + velocity_x: (velocity.x * 8000.0) as i16, + velocity_y: (velocity.y * 8000.0) as i16, + velocity_z: (velocity.z * 8000.0) as i16, + }).await; + } + + async fn on_entity_moved(&mut self, eid: Eid, position: Position) { + let prev_position = self.entity_prev_positions.insert(eid, position.clone()).unwrap_or_else(|| position.clone()); + self.send_packet(PlayClientbound::UpdateEntityPosition { + entity_id: VarInt(eid as i32), + delta_x: (position.x * 4096.0 - prev_position.x * 4096.0) as i16, + delta_y: (position.y * 4096.0 - prev_position.y * 4096.0) as i16, + delta_z: (position.z * 4096.0 - prev_position.z * 4096.0) as i16, + on_ground: true, // TODO + }).await; + } + + async fn on_entity_velocity_changes(&mut self, eid: Eid, velocity: Translation) { + self.send_packet(PlayClientbound::SetEntityVelocity { + entity_id: VarInt(eid as i32), + velocity_x: (velocity.x * 8000.0) as i16, + velocity_y: (velocity.y * 8000.0) as i16, + velocity_z: (velocity.z * 8000.0) as i16, + }).await; + } + async fn on_move(&mut self) { let new_center_chunk = self.position.chunk(); + // Tell the ECS about the changes + self.world.mutate_entity(self.eid, |entity| { + let entity: &mut Entity = entity.try_as_entity_mut().unwrap(); // Cannot fail + entity.position = self.position.clone(); + ((), EntityChanges::position()) + }).await; + // Tell the client which chunk he is in if new_center_chunk == self.center_chunk { return }; self.send_packet(PlayClientbound::SetCenterChunk { chunk_x: VarInt(new_center_chunk.cx), chunk_z: VarInt(new_center_chunk.cz) }).await; @@ -150,15 +200,26 @@ impl PlayerHandler { self.world.set_block(location.into(), BlockWithState::Air).await; } } + ChatMessage { message, .. } => { + if message == "summon" { + let mut zombie = Zombie::default(); + let mut position = self.position.clone(); + position.y += 20.0; + zombie.get_entity_mut().position = position; + self.world.spawn_entity::(AnyEntity::Zombie(zombie)).await; + } + } packet => warn!("Unsupported packet received: {packet:?}"), } } } -pub async fn handle_player(stream: TcpStream, player_info: PlayerInfo, mut server_msg_rcvr: BroadcastReceiver, world: Arc, mut change_receiver: MpscReceiver) -> Result<(), ()> { +pub async fn handle_player(stream: TcpStream, player_info: PlayerInfo, mut server_msg_rcvr: BroadcastReceiver, world: &'static World, mut change_receiver: MpscReceiver) -> Result<(), ()> { let (packet_sender, mut packet_receiver) = mpsc_channel(100); + let eid = world.spawn_entity::(AnyEntity::Player(Player::default())).await; let mut handler = PlayerHandler { + eid, world, game_mode: Gamemode::Creative, position: Position { x: 0.0, y: 60.0, z: 0.0 }, @@ -167,6 +228,8 @@ pub async fn handle_player(stream: TcpStream, player_info: PlayerInfo, mut serve on_ground: false, packet_sender, + entity_prev_positions: HashMap::new(), + center_chunk: ChunkPosition { cx: 0, cy: 11, cz: 0 }, render_distance: player_info.render_distance.clamp(4, 15) as i32, loaded_chunks: HashSet::new(), @@ -174,6 +237,8 @@ pub async fn handle_player(stream: TcpStream, player_info: PlayerInfo, mut serve info: player_info, }; + // TODO: player should load existing entities + for cx in -3..=3 { for cz in -3..=3 { handler.loaded_chunks.insert(ChunkColumnPosition { cx, cz }); @@ -225,7 +290,13 @@ pub async fn handle_player(stream: TcpStream, player_info: PlayerInfo, mut serve receive_change_fut = Box::pin(change_receiver.recv().fuse()); match change { - WorldChange::BlockChange(position, block) => handler.on_block_change(position, block).await, + WorldChange::Block(position, block) => handler.on_block_changed(position, block).await, + WorldChange::EntitySpawned { eid, uuid: uid, ty, position, pitch, yaw, head_yaw, data, velocity, metadata } => handler.on_entity_spawned(eid, uid, ty, position, pitch, yaw, head_yaw, data, velocity, metadata).await, + WorldChange::EntityDispawned { eid } => todo!(), + WorldChange::EntityMetadata { eid, metadata } => todo!(), + WorldChange::EntityPosition { eid, position } => handler.on_entity_moved(eid, position).await, + WorldChange::EntityVelocity { eid, velocity } => handler.on_entity_velocity_changes(eid, velocity).await, + WorldChange::EntityPitch { eid, pitch, yaw, head_yaw } => todo!(), } }, Event::Message(Err(recv_error)) => { diff --git a/minecraft-server/src/prelude.rs b/minecraft-server/src/prelude.rs index 12714819..67738215 100644 --- a/minecraft-server/src/prelude.rs +++ b/minecraft-server/src/prelude.rs @@ -1,4 +1,4 @@ -pub use crate::{ecs::*, player_handler::*, server_behavior::*, world::*}; +pub use crate::{entities::*, player_handler::*, server_behavior::*, world::*}; pub use futures::FutureExt; pub use log::{debug, error, info, trace, warn}; pub use minecraft_protocol::{ @@ -22,7 +22,10 @@ pub use minecraft_protocol::{ status::{ClientboundPacket as StatusClientbound, ServerboundPacket as StatusServerbound}, Array, ConnectionState, Map, RawBytes, VarInt, VarLong, UUID, Position as NetworkPosition }, - ids::block_states::BlockWithState, + ids::{ + block_states::BlockWithState, + entities::Entity as NetworkEntity, + }, MinecraftPacketPart, }; pub use std::{ @@ -54,6 +57,5 @@ pub use tokio::{ }, }; pub use minecraft_positions::*; -pub use minecraft_entities::*; pub const MAX_PLAYERS: usize = 1001; diff --git a/minecraft-server/src/server_behavior.rs b/minecraft-server/src/server_behavior.rs index 14bb2585..25fea9e0 100644 --- a/minecraft-server/src/server_behavior.rs +++ b/minecraft-server/src/server_behavior.rs @@ -10,7 +10,7 @@ pub enum ServerMessage { } pub struct ServerBehavior { - world: Arc, + world: &'static World, player_handlers: Vec, listener: TcpListener, message_receiver: BroadcastReceiver, @@ -31,7 +31,7 @@ impl ServerBehavior { }); ServerBehavior { - world: Arc::new(World::new()), + world: Box::leak(Box::new(World::new(receiver.resubscribe()))), listener, player_handlers: Vec::new(), message_receiver: receiver, @@ -49,7 +49,7 @@ impl ServerBehavior { } else { debug!("Accepted connection from: {addr}"); let server_msg_rcvr = self.message_receiver.resubscribe(); - self.player_handlers.push(Box::pin(handle_connection(stream, addr, server_msg_rcvr, Arc::clone(&self.world)))); + self.player_handlers.push(Box::pin(handle_connection(stream, addr, server_msg_rcvr, self.world))); } } Ready(Err(e)) => error!("Failed to accept connection: {e}"), diff --git a/minecraft-server/src/world/change.rs b/minecraft-server/src/world/change.rs new file mode 100644 index 00000000..a14f736d --- /dev/null +++ b/minecraft-server/src/world/change.rs @@ -0,0 +1,101 @@ +use crate::prelude::*; + +#[derive(Debug, Clone)] +pub enum WorldChange { + Block(BlockPosition, BlockWithState), + EntitySpawned { + eid: Eid, + uuid: UUID, + ty: NetworkEntity, + position: Position, + pitch: f32, + yaw: f32, + head_yaw: f32, + data: u32, + velocity: Translation, + metadata: (), + }, + EntityDispawned { + eid: Eid, + }, + EntityMetadata { + eid: Eid, + metadata: (), + }, + EntityPosition { + eid: Eid, + position: Position, + }, + EntityVelocity { + eid: Eid, + velocity: Translation, + }, + EntityPitch { + eid: Eid, + pitch: f32, + yaw: f32, + head_yaw: f32, + }, +} + +pub struct EntityChanges(u8); + +impl EntityChanges { + pub const fn other() -> EntityChanges { + EntityChanges(0) + } + + pub const fn nothing() -> EntityChanges { + EntityChanges(0) + } + + pub const fn position() -> EntityChanges { + EntityChanges(1) + } + + pub const fn velocity() -> EntityChanges { + EntityChanges(1 << 1) + } + + pub const fn pitch() -> EntityChanges { + EntityChanges(1 << 2) + } + + pub const fn metadata() -> EntityChanges { + EntityChanges(1 << 3) + } + + pub const fn nothing_changed(&self) -> bool { + self.0 == 0 + } + + pub const fn position_changed(&self) -> bool { + self.0 & 1 != 0 + } + + pub const fn velocity_changed(&self) -> bool { + self.0 & (1 << 1) != 0 + } + + pub const fn pitch_changed(&self) -> bool { + self.0 & (1 << 2) != 0 + } + + pub const fn metadata_changed(&self) -> bool { + self.0 & (1 << 3) != 0 + } +} + +impl std::ops::Add for EntityChanges { + type Output = EntityChanges; + + fn add(self, rhs: EntityChanges) -> EntityChanges { + EntityChanges(self.0 | rhs.0) + } +} + +impl std::ops::AddAssign for EntityChanges { + fn add_assign(&mut self, rhs: EntityChanges) { + self.0 |= rhs.0; + } +} diff --git a/minecraft-server/src/world/change_event.rs b/minecraft-server/src/world/change_event.rs deleted file mode 100644 index 6ac08fcf..00000000 --- a/minecraft-server/src/world/change_event.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::prelude::*; - -#[derive(Debug, Clone)] -pub enum WorldChange { - BlockChange(BlockPosition, BlockWithState), -} diff --git a/minecraft-server/src/world/collisions.rs b/minecraft-server/src/world/collisions.rs new file mode 100644 index 00000000..723684a9 --- /dev/null +++ b/minecraft-server/src/world/collisions.rs @@ -0,0 +1,451 @@ +use super::*; + +/// Returns minimum of two floats, but not NaN +fn min2(a: f64, b: f64) -> f64 { + if a > b { + b + } else { + a + } +} + +/// Returns minimum of three floats +fn min(a: f64, b: f64, c: f64) -> f64 { + min2(min2(a, b), c) +} + +/// An object in space +#[derive(Debug, Clone, PartialEq)] +pub struct CollisionShape { + pub x1: f64, + pub y1: f64, + pub z1: f64, + pub x2: f64, + pub y2: f64, + pub z2: f64, +} + +impl CollisionShape { + const fn points(&self) -> PointIter { + PointIter { + shape: self, + index: 0, + } + } + + // TODO(perf): Return an iterator yielding blocks instead of a vec of blocks + pub fn containing_blocks(&self) -> Vec { + let mut result = Vec::new(); + for x in self.x1.floor() as i32..=self.x2.floor() as i32 { + for y in self.y1.floor() as i32..=self.y2.floor() as i32 { + for z in self.z1.floor() as i32..=self.z2.floor() as i32 { + let block = BlockPosition { x, y, z }; + result.push(block); + } + } + } + result + } +} + +/// A point in space +pub struct Point { + x: f64, + y: f64, + z: f64, +} + +impl Point { + /// Returns true if the point is inside the shape + fn is_inside(&self, shape: &CollisionShape) -> bool { + (shape.x1..=shape.x2).contains(&self.x) && (shape.y1..=shape.y2).contains(&self.y) && (shape.z1..=shape.z2).contains(&self.z) + } + + /// Returns the proportion of the translation that can be applied without absorbing `point` inside `shape` on the x axis + fn collide_x(&self, shape: &CollisionShape, translation: &Translation) -> f64 { + if translation.x == 0.0 { + return 1.0; + } + let x = if translation.x < 0.0 { shape.x1 } else { shape.x2 }; + let translated_ratio = (self.x - x) / translation.x; + if translated_ratio >= 1.0 { + return 1.0; + } else if translated_ratio <= 0.0 { + return 0.0; + } + let translated_y1 = shape.y1 + translation.y * translated_ratio; + let translated_y2 = shape.y2 + translation.y * translated_ratio; + let translated_z1 = shape.z1 + translation.z * translated_ratio; + let translated_z2 = shape.z2 + translation.z * translated_ratio; + if (translated_y1..=translated_y2).contains(&self.y) && (translated_z1..=translated_z2).contains(&self.z) { + translated_ratio + } else { + 1.0 + } + } + + /// Returns the proportion of the translation that can be applied without absorbing `point` inside `shape` on the y axis + fn collide_y(&self, shape: &CollisionShape, translation: &Translation) -> f64 { + if translation.y == 0.0 { + return 1.0; + } + let y = if translation.y < 0.0 { shape.y1 } else { shape.y2 }; + let translated_ratio = (self.y - y) / translation.y; + if translated_ratio >= 1.0 { + return 1.0; + } else if translated_ratio <= 0.0 { + return 0.0; + } + let translated_x1 = shape.x1 + translation.x * translated_ratio; + let translated_x2 = shape.x2 + translation.x * translated_ratio; + let translated_z1 = shape.z1 + translation.z * translated_ratio; + let translated_z2 = shape.z2 + translation.z * translated_ratio; + if (translated_x1..=translated_x2).contains(&self.x) && (translated_z1..=translated_z2).contains(&self.z) { + translated_ratio + } else { + 1.0 + } + } + + /// Returns the proportion of the translation that can be applied without absorbing `point` inside `shape` on the z axis + fn collide_z(&self, shape: &CollisionShape, translation: &Translation) -> f64 { + if translation.z == 0.0 { + return 1.0; + } + let z = if translation.z < 0.0 { shape.z1 } else { shape.z2 }; + let translated_ratio = (self.z - z) / translation.z; + if translated_ratio >= 1.0 { + return 1.0; + } else if translated_ratio <= 0.0 { + return 0.0; + } + let translated_x1 = shape.x1 + translation.x * translated_ratio; + let translated_x2 = shape.x2 + translation.x * translated_ratio; + let translated_y1 = shape.y1 + translation.y * translated_ratio; + let translated_y2 = shape.y2 + translation.y * translated_ratio; + if (translated_x1..=translated_x2).contains(&self.x) && (translated_y1..=translated_y2).contains(&self.y) { + translated_ratio + } else { + 1.0 + } + } + + /// Returns the proportion of the translation that can be applied without absorbing `point` inside `shape` + fn collide(&self, shape: &CollisionShape, translation: &Translation) -> f64 { + min( + self.collide_x(shape, translation), + self.collide_y(shape, translation), + self.collide_z(shape, translation) + ) + } +} + +/// An iterator over the 8 corners of a [CollisionShape] +pub struct PointIter<'a> { + shape: &'a CollisionShape, + index: usize, +} + +impl<'a> Iterator for PointIter<'a> { + type Item = Point; + + fn next(&mut self) -> Option { + if self.index < 8 { + let result = Point { + x: if self.index & 1 == 0 { self.shape.x1 } else { self.shape.x2 }, + y: if self.index & 2 == 0 { self.shape.y1 } else { self.shape.y2 }, + z: if self.index & 4 == 0 { self.shape.z1 } else { self.shape.z2 }, + }; + self.index += 1; + Some(result) + } else { + None + } + } +} + +/// Vector describing a movement +#[derive(Debug, Clone, PartialEq)] +pub struct Translation { + pub x: f64, + pub y: f64, + pub z: f64, +} + +impl Translation { + /// Cuts the translation just enough so that the shape doesn't collide with the obstacle + fn prevent_collision(&mut self, object: &CollisionShape, obstacle: &CollisionShape) { + let mut limit = 1.0; + + for point in obstacle.points() { + limit = min2(limit, point.collide(object, self)); + if limit == 0.0 { + break; + } + } + + self.x *= limit; + self.y *= limit; + self.z *= limit; + } + + pub fn norm(&self) -> f64 { + (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() + } + + /// Keep direction but change norm + pub fn set_norm(&mut self, norm: f64) { + let current_norm = self.norm(); + if current_norm == 0.0 { + return; + } + self.x *= norm / current_norm; + self.y *= norm / current_norm; + self.z *= norm / current_norm; + } + + pub fn is_zero(&self) -> bool { + self.x == 0.0 && self.y == 0.0 && self.z == 0.0 + } + + // TODO: turn CollisionShape.fragment into an iterator + pub fn fragment(self, position: &CollisionShape) -> Vec { + let mut result = Vec::new(); + let mut fragmented = Translation { x: 0.0, y: 0.0, z: 0.0 }; + //let mut current_position = position.clone(); + //result.extend(position.containing_blocks().into_iter()); + while fragmented.norm() < self.norm() { + let x_dist = if self.x > 0.0 { + let next_x = (position.x2 + fragmented.x).floor()+1.0; + (next_x - (position.x2 + fragmented.x)).abs() + } else { + let next_x = (position.x1 + fragmented.x).ceil()-1.0; + (next_x - (position.x1 + fragmented.x)).abs() + }; + let y_dist = if self.y > 0.0 { + let next_y = (position.y2 + fragmented.y).floor()+1.0; + (next_y - (position.y2 + fragmented.y)).abs() + } else { + let next_y = (position.y1 + fragmented.y).ceil()-1.0; + (next_y - (position.y1 + fragmented.y)).abs() + }; + let z_dist = if self.z > 0.0 { + let next_z = (position.z2 + fragmented.z).floor()+1.0; + (next_z - (position.z2 + fragmented.z)).abs() + } else { + let next_z = (position.z1 + fragmented.z).ceil()-1.0; + (next_z - (position.z1 + fragmented.z)).abs() + }; + let x_time = x_dist / self.x.abs(); + let y_time = y_dist / self.y.abs(); + let z_time = z_dist / self.z.abs(); + let time = min(x_time, y_time, z_time); + //println!("pos{fragmented:?} dist({x_dist}, {y_dist}, {z_dist}) time({x_time}, {y_time}, {z_time}) time({time})"); + let mini_translation = self.clone() * time; + fragmented += &mini_translation; + result.push(mini_translation); + } + // Last one might be too long + if let Some(last) = result.pop() { + let final_position = position.clone() + self; + let previous_fragmented = fragmented.clone() - last; + let previous_position = position.clone() + previous_fragmented; + let difference = Translation { + x: final_position.x1 - previous_position.x1, + y: final_position.y1 - previous_position.y1, + z: final_position.z1 - previous_position.z1, + }; + result.push(difference); + } + result + } +} + +impl std::ops::Add for Translation { + type Output = Translation; + + fn add(self, rhs: Translation) -> Self::Output { + Translation { + x: self.x + rhs.x, + y: self.y + rhs.y, + z: self.z + rhs.z, + } + } +} + +impl std::ops::AddAssign<&Translation> for Translation { + fn add_assign(&mut self, rhs: &Translation) { + self.x += rhs.x; + self.y += rhs.y; + self.z += rhs.z; + } +} + +impl std::ops::Sub for Translation { + type Output = Translation; + + fn sub(self, rhs: Translation) -> Self::Output { + Translation { + x: self.x - rhs.x, + y: self.y - rhs.y, + z: self.z - rhs.z, + } + } +} + +impl std::ops::Add for CollisionShape { + type Output = CollisionShape; + + fn add(self, rhs: Translation) -> Self::Output { + CollisionShape { + x1: self.x1 + rhs.x, + y1: self.y1 + rhs.y, + z1: self.z1 + rhs.z, + x2: self.x2 + rhs.x, + y2: self.y2 + rhs.y, + z2: self.z2 + rhs.z, + } + } +} + +impl std::ops::Add<&Translation> for CollisionShape { + type Output = CollisionShape; + + fn add(self, rhs: &Translation) -> Self::Output { + CollisionShape { + x1: self.x1 + rhs.x, + y1: self.y1 + rhs.y, + z1: self.z1 + rhs.z, + x2: self.x2 + rhs.x, + y2: self.y2 + rhs.y, + z2: self.z2 + rhs.z, + } + } +} + +impl std::ops::AddAssign<&Translation> for CollisionShape { + fn add_assign(&mut self, rhs: &Translation) { + self.x1 += rhs.x; + self.y1 += rhs.y; + self.z1 += rhs.z; + self.x2 += rhs.x; + self.y2 += rhs.y; + self.z2 += rhs.z; + } +} + +impl std::ops::AddAssign for Position { + fn add_assign(&mut self, rhs: Translation) { + self.x += rhs.x; + self.y += rhs.y; + self.z += rhs.z; + } +} + +impl std::ops::Mul for Translation { + type Output = Translation; + + fn mul(self, rhs: f64) -> Self::Output { + Translation { + x: self.x * rhs, + y: self.y * rhs, + z: self.z * rhs, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + let shape1 = CollisionShape { + x1: 0.0, + y1: 0.0, + z1: 0.0, + x2: 1.0, + y2: 1.0, + z2: 1.0, + }; + + // Boxes are just next to each other and pushing against each other + let shape2 = shape1.clone() + Translation { x: 1.0, y: 0.0, z: 0.0 }; + let mut translation = Translation { x: -1.0, y: 0.0, z: 0.0 }; + translation.prevent_collision(&shape2, &shape1); + assert_eq!(translation, Translation { x: 0.0, y: 0.0, z: 0.0 }); + + // Boxes are one block away but one comes and pushes the other + let shape2 = shape1.clone() + Translation { x: 2.0, y: 0.0, z: 0.0 }; + let mut translation = Translation { x: -2.0, y: 0.0, z: 0.0 }; + translation.prevent_collision(&shape2, &shape1); + assert_eq!(translation, Translation { x: -1.0, y: 0.0, z: 0.0 }); + + // The other way around + let shape2 = shape1.clone() + Translation { x: -2.0, y: 0.0, z: 0.0 }; + let mut translation = Translation { x: 2.0, y: 0.0, z: 0.0 }; + translation.prevent_collision(&shape2, &shape1); + assert_eq!(translation, Translation { x: 1.0, y: 0.0, z: 0.0 }); + + // From top + let shape2 = shape1.clone() + Translation { x: 0.0, y: 2.0, z: 0.0 }; + let mut translation = Translation { x: 0.0, y: -2.0, z: 0.0 }; + translation.prevent_collision(&shape2, &shape1); + assert_eq!(translation, Translation { x: 0.0, y: -1.0, z: 0.0 }); + + // On last axis + let shape2 = shape1.clone() + Translation { x: 0.0, y: 0.0, z: 2.0 }; + let mut translation = Translation { x: 0.0, y: 0.0, z: -2.0 }; + translation.prevent_collision(&shape2, &shape1); + assert_eq!(translation, Translation { x: 0.0, y: 0.0, z: -1.0 }); + + // Colliding on corner + let shape2 = shape1.clone() + Translation { x: 2.0, y: 2.0, z: 2.0 }; + let mut translation = Translation { x: -2.0, y: -2.0, z: -2.0 }; + translation.prevent_collision(&shape2, &shape1); + assert_eq!(translation, Translation { x: -1.0, y: -1.0, z: -1.0 }); + + // Colliding with offset on other axis + let shape2 = shape1.clone() + Translation { x: 2.0, y: 0.5, z: 0.0 }; + let mut translation = Translation { x: -2.0, y: 0.0, z: 0.0 }; + translation.prevent_collision(&shape2, &shape1); + assert_eq!(translation, Translation { x: -1.0, y: 0.0, z: 0.0 }); + + // Colliding when already inside + let shape2 = shape1.clone() + Translation { x: 0.5, y: 0.5, z: 0.5 }; + let mut translation = Translation { x: -0.5, y: -0.5, z: -0.5 }; + translation.prevent_collision(&shape2, &shape1); + assert_eq!(translation, Translation { x: 0.0, y: 0.0, z: 0.0 }); + } + + #[test] + fn test_ray_cast() { + let shape = CollisionShape { + x1: 0.0, + y1: 0.0, + z1: 0.0, + x2: 1.0, + y2: 1.0, + z2: 1.0, + }; + + let movement = Translation { x: 3.0, y: 0.0, z: 0.0 }; + let fragments = movement.fragment(&shape); + assert_eq!(fragments, vec![Translation { x: 1.0, y: 0.0, z: 0.0 }; 3]); + + let movement = Translation { x: 2.3, y: 0.0, z: 0.0 }; + let fragments = movement.fragment(&shape); + assert_eq!(fragments, vec![Translation { x: 1.0, y: 0.0, z: 0.0 }, Translation { x: 1.0, y: 0.0, z: 0.0 }, Translation { x: 0.2999999999999998, y: 0.0, z: 0.0 }]); + + let movement = Translation { x: 1.0, y: 0.75, z: 0.0 } * 4.0; + let fragments = movement.fragment(&shape); + assert_eq!(fragments, vec![ + Translation { x: 1.0, y: 0.75, z: 0.0 }, + Translation { x: 0.3333333333333333, y: 0.25, z: 0.0 }, + Translation { x: 0.666666666666667, y: 0.5000000000000002, z: 0.0 }, + Translation { x: 0.6666666666666666, y: 0.5, z: 0.0 }, + Translation { x: 0.3333333333333335, y: 0.2500000000000001, z: 0.0 }, + Translation { x: 1.0, y: 0.75, z: 0.0 }] + ); + } +} diff --git a/minecraft-server/src/world/ecs.rs b/minecraft-server/src/world/ecs.rs new file mode 100644 index 00000000..143ee0f5 --- /dev/null +++ b/minecraft-server/src/world/ecs.rs @@ -0,0 +1,118 @@ +use std::collections::{HashMap, HashSet}; +use crate::*; +use minecraft_protocol::packets::UUID; +use tokio::sync::RwLock; + +pub type EntityTask = Pin + Send + Sync + 'static>>; +pub type EntityTaskHandle = tokio::task::JoinHandle<()>; + +pub struct Entities { + eid_counter: std::sync::atomic::AtomicU32, + uuid_counter: std::sync::atomic::AtomicU64, + pub entities: RwLock>, + + /// A hashmap of chunk positions to get a list of entities in a chunk + pub chunks: RwLock>>, + pub uuids: RwLock>, + pub entity_tasks: RwLock>>, +} + +impl Entities { + pub fn new() -> Entities { + Entities { + eid_counter: std::sync::atomic::AtomicU32::new(0), + uuid_counter: std::sync::atomic::AtomicU64::new(0), + entities: RwLock::new(HashMap::new()), + chunks: RwLock::new(HashMap::new()), + uuids: RwLock::new(HashMap::new()), + entity_tasks: RwLock::new(HashMap::new()), + } + } + + /// Observe an entity through a closure + pub(super) async fn observe_entity(&self, eid: Eid, observer: impl FnOnce(&AnyEntity) -> R) -> Option { + self.entities.read().await.get(&eid).map(observer) + } + + /// Observe entities in a chunk through a closure + /// That closure will be applied to each entity, and the results will be returned in a vector + pub(super) async fn observe_entities(&self, chunk: ChunkColumnPosition, mut observer: impl FnMut(&AnyEntity) -> Option) -> Vec { + let entities = self.chunks.read().await; + let Some(eids) = entities.get(&chunk) else {return Vec::new()}; + let mut results = Vec::with_capacity(eids.len()); + for eid in eids { + if let Some(entity) = self.entities.read().await.get(eid) { + if let Some(r) = observer(entity) { + results.push(r); + } + } + } + results + } + + /// Mutate an entity through a closure + pub(super) async fn mutate_entity(&self, eid: Eid, mutator: impl FnOnce(&mut AnyEntity) -> (R, EntityChanges)) -> Option<(R, EntityChanges)> { + let mut entities = self.entities.write().await; + + if let Some(entity) = entities.get_mut(&eid) { + let prev_position = entity.as_entity().position.clone(); + let r = mutator(entity); + if prev_position != entity.as_entity().position { + let old_chunk = prev_position.chunk_column(); + let new_chunk = entity.as_entity().position.chunk_column(); + drop(entities); + let mut chunks = self.chunks.write().await; + chunks.entry(old_chunk).and_modify(|set| { set.remove(&eid); }); // TODO: ensure it gets removed + chunks.entry(new_chunk).or_insert(HashSet::new()).insert(eid); + } + Some(r) + } else { + None + } + } + + pub(super) async fn spawn_entity(&self, entity: AnyEntity, world: &'static World, receiver: BroadcastReceiver) -> (Eid, UUID) + where AnyEntity: TryAsEntityRef, Handler: EntityExt + { + let eid = self.eid_counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + let uuid = self.uuid_counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u128; + let mut entities = self.entities.write().await; + let mut chunks = self.chunks.write().await; + let mut uuids = self.uuids.write().await; + chunks.entry(entity.as_entity().position.chunk_column()).or_insert(HashSet::new()).insert(eid); + entities.insert(eid, entity); + uuids.insert(uuid, eid); + drop(entities); + drop(chunks); + drop(uuids); + let h = Handler::::assume(eid, world); + h.init(receiver).await; + (eid, uuid) + } + + pub(super) async fn insert_entity_task(&self, eid: Eid, name: &'static str, handle: EntityTaskHandle) { + let mut entity_tasks = self.entity_tasks.write().await; + let old = entity_tasks.entry(eid).or_insert(HashMap::new()).insert(name, handle); + if let Some(old) = old { + old.abort(); + } + } + + /// Remove an entity + pub(super) async fn remove_entity(&self, eid: Eid) -> Option { + let entity = self.entities.write().await.remove(&eid); + let mut chunks = self.chunks.write().await; + chunks.values_mut().for_each(|set| { set.remove(&eid); }); + chunks.retain(|_,v| !v.is_empty()); + drop(chunks); + self.uuids.write().await.retain(|_,v| *v != eid); + self.entity_tasks.write().await.remove(&eid); + entity + } +} + +impl Handler where AnyEntity: TryAsEntityRef { + pub async fn insert_task(&self, name: &'static str, handle: EntityTaskHandle) { + self.world.entities.insert_entity_task(self.eid, name, handle).await; + } +} diff --git a/minecraft-server/src/world/map.rs b/minecraft-server/src/world/map.rs index e3e8f8d9..687769bf 100644 --- a/minecraft-server/src/world/map.rs +++ b/minecraft-server/src/world/map.rs @@ -271,6 +271,25 @@ impl WorldMap { inner_get_block(self, position, block).await; } + pub async fn try_move(&self, object: &CollisionShape, movement: &Translation) -> Translation { + // TODO(perf): Optimize Map.try_move by preventing block double-checking + // Also lock the map only once + let movement_fragments = movement.clone().fragment(object); + let mut validated = Translation{ x: 0.0, y: 0.0, z: 0.0 }; + for fragment in movement_fragments { + let validating = validated.clone() + fragment; + let translated_object = object.clone() + &validating; + for block in translated_object.containing_blocks() { + let block = self.get_block(block).await; + if block.block_id() != 0 { + return validated; + } + } + validated = validating; + } + movement.clone() // Would be more logic if it returned validated, but this way we avoid precision errors + } + pub async fn load(&self, position: ChunkColumnPosition) { let chunk = ChunkColumn::flat(); // TODO: load from disk let shard = position.shard(self.shard_count); @@ -431,4 +450,36 @@ mod tests { } } } + + #[tokio::test] + async fn test_try_move() { + let map = WorldMap::new(1); + map.load(ChunkColumnPosition { cx: 0, cz: 0 }).await; + let bounding_box = CollisionShape { + x1: 0.0, + y1: 0.0, + z1: 0.0, + x2: 1.0, + y2: 1.0, + z2: 1.0, + }; + + // Position on ground and try to go through it + let positionned_box = bounding_box.clone() + &Translation { x: 0.0, y: -3.0*16.0, z: 0.0 }; + let movement = Translation { x: 0.0, y: -10.0, z: 0.0 }; + let movement = map.try_move(&positionned_box, &movement).await; + assert_eq!(movement, Translation { x: 0.0, y: 0.0, z: 0.0 }); // It doesn't get through + + // Place it a little above ground + let positionned_box = bounding_box.clone() + &Translation { x: 0.0, y: -3.0*16.0 + 1.0, z: 0.0 }; + let movement = Translation { x: 0.0, y: -10.0, z: 0.0 }; + let movement = map.try_move(&positionned_box, &movement).await; + assert_eq!(movement, Translation { x: 0.0, y: -1.0, z: 0.0 }); // It falls down but doesn't get through + + // Place it above but not on round coordinates + let positionned_box = bounding_box.clone() + &Translation { x: 0.0, y: -3.0*16.0 + 1.1, z: 0.2 }; + let movement = Translation { x: 2.0, y: -10.0, z: 0.0 }; + let movement = map.try_move(&positionned_box, &movement).await; + assert_eq!(movement, Translation { x: 0.2200000000000003, y: -1.1000000000000014, z: 0.0 }); // It falls down but doesn't get through + } } diff --git a/minecraft-server/src/world/mod.rs b/minecraft-server/src/world/mod.rs index 414ada9f..b7c04ae7 100644 --- a/minecraft-server/src/world/mod.rs +++ b/minecraft-server/src/world/mod.rs @@ -1,11 +1,15 @@ use crate::prelude::*; -mod change_event; -pub use change_event::*; +mod change; +pub use change::*; mod loading_manager; use loading_manager::*; mod map; use map::*; +mod ecs; +use ecs::*; +mod collisions; +pub use collisions::*; /// World is the union of the map and entities. /// World handles loaded chunks and entities. @@ -16,15 +20,17 @@ pub struct World { loading_manager: RwLock, change_senders: RwLock>>, // TODO: Add a way to select events you want to subscribe to + receiver: BroadcastReceiver, } impl World { - pub fn new() -> World { + pub fn new(receiver: BroadcastReceiver) -> World { World { map: WorldMap::new(4), entities: Entities::new(), loading_manager: RwLock::new(WorldLoadingManager::default()), change_senders: RwLock::new(HashMap::new()), + receiver, } } @@ -38,7 +44,11 @@ impl World { pub async fn set_block(&self, position: BlockPosition, block: BlockWithState) { self.map.set_block(position.clone(), block.clone()).await; - self.notify(&position.chunk_column(), WorldChange::BlockChange(position, block)).await; + self.notify(&position.chunk_column(), WorldChange::Block(position, block)).await; + } + + pub async fn try_move(&self, object: &CollisionShape, movement: &Translation) -> Translation { + self.map.try_move(object, movement).await } pub async fn add_loader(&self, uuid: UUID) -> MpscReceiver { @@ -67,6 +77,76 @@ impl World { } } + pub async fn spawn_entity(&'static self, entity: AnyEntity) -> Eid + where AnyEntity: TryAsEntityRef, Handler: EntityExt + { + let position = entity.as_entity().position.clone(); + let velocity = entity.as_entity().velocity.clone(); + let ty = entity.to_network().unwrap(); // TODO: error handling + let pitch = entity.as_entity().pitch; + let yaw = entity.as_entity().yaw; + let head_yaw = entity.as_other::().map(|e| e.head_yaw).unwrap_or(0.0); + let (eid, uuid) = self.entities.spawn_entity::(entity, self, self.receiver.resubscribe()).await; + self.notify(&position.chunk_column(), WorldChange::EntitySpawned { + eid, + uuid, + ty, + position, + pitch, + yaw, + head_yaw, + data: 0, + velocity, + metadata: (), + }).await; + eid + } + + pub async fn observe_entity(&self, eid: Eid, observer: impl FnOnce(&AnyEntity) -> R) -> Option { + self.entities.observe_entity(eid, observer).await + } + + pub async fn observe_entities(&self, chunk: ChunkColumnPosition, observer: impl FnMut(&AnyEntity) -> Option) -> Vec { + self.entities.observe_entities(chunk, observer).await + } + + pub async fn mutate_entity(&self, eid: Eid, mutator: impl FnOnce(&mut AnyEntity) -> (R, EntityChanges)) -> Option { + // TODO change events + match self.entities.mutate_entity(eid, mutator).await { + Some((r, changes)) => { + // TODO: make only one lookup and group into a single message with optional fields + let position = self.entities.observe_entity(eid, |e| e.as_entity().position.clone()).await?; + if changes.position_changed() { + self.notify(&position.chunk_column(), WorldChange::EntityPosition { + eid, + position: position.clone(), + }).await; + } + if changes.velocity_changed() { + let velocity = self.entities.observe_entity(eid, |e| e.as_entity().velocity.clone()).await?; + self.notify(&position.chunk_column(), WorldChange::EntityVelocity { + eid, + velocity, + }).await; + } + if changes.pitch_changed() { + let (pitch, yaw, head_yaw) = self.entities.observe_entity(eid, |e| (e.as_entity().pitch, e.as_entity().yaw, e.as_other::().map(|e| e.head_yaw).unwrap_or(0.0))).await?; + self.notify(&position.chunk_column(), WorldChange::EntityPitch { + eid, + pitch, + yaw, + head_yaw, + }).await; + } + if changes.metadata_changed() { + todo!() + } + Some(r) + }, + None => None, + } + } + async fn notify(&self, position: &ChunkColumnPosition, change: WorldChange) { let loading_manager = self.loading_manager.read().await; let mut senders = self.change_senders.write().await; @@ -86,7 +166,7 @@ mod tests { #[tokio::test] async fn test_world_notifications() { - let world = World::new(); + let world = World::new(broadcast_channel(100).1); let mut receiver1 = world.add_loader(1).await; let mut receiver2 = world.add_loader(2).await; @@ -94,7 +174,7 @@ mod tests { world.update_loaded_chunks(2, vec![ChunkColumnPosition{cx: 1, cz: 1}].into_iter().collect()).await; world.set_block(BlockPosition{x: 1, y: 1, z: 1}, BlockWithState::Air).await; - assert!(matches!(receiver1.try_recv(), Ok(WorldChange::BlockChange(BlockPosition{x: 1, y: 1, z: 1}, BlockWithState::Air)))); + assert!(matches!(receiver1.try_recv(), Ok(WorldChange::Block(BlockPosition{x: 1, y: 1, z: 1}, BlockWithState::Air)))); assert!(matches!(receiver2.try_recv(), Err(TryRecvError::Empty))); } } diff --git a/tags-macros/Cargo.toml b/tags-macros/Cargo.toml deleted file mode 100644 index a35c9fac..00000000 --- a/tags-macros/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "tags-macros" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -proc-macro = true - -[dependencies] -quote = "1.0" -syn = { version = "2.0", features = ["full"] } diff --git a/tags-macros/src/lib.rs b/tags-macros/src/lib.rs deleted file mode 100644 index 40e32b6a..00000000 --- a/tags-macros/src/lib.rs +++ /dev/null @@ -1,136 +0,0 @@ -#[macro_use] -extern crate quote; -extern crate proc_macro; - -use std::collections::HashMap; - -use proc_macro::TokenStream; -use syn::{parse_macro_input, Token, Ident, parse}; - -struct MultipleTags { - tags: Vec<(Ident, Vec)>, - components: HashMap>, -} - -impl parse::Parse for MultipleTags { - fn parse(input: syn::parse::ParseStream) -> parse::Result { - let mut tags = Vec::new(); - let mut components_table: HashMap> = HashMap::new(); - while !input.is_empty() { - let tag_name: Ident = input.parse()?; - let content; - syn::braced!(content in input); - - let components = content - .parse_terminated(Ident::parse, Token![,])?; - for component in components.iter() { - // We add the tag to the component - let tags_of_component = components_table.get_mut(component); - if let Some(tags_of_component) = tags_of_component { - tags_of_component.push(tag_name.clone()); - } else { - components_table.insert(component.clone(), vec![tag_name.clone()]); - } - } - tags.push((tag_name, components.into_iter().collect())); - } - - Ok(Self { tags, components: components_table }) - } -} - - -#[proc_macro] -pub fn tags(input: TokenStream) -> TokenStream { - /* - We have something like that: - tags! { - Player { - HealthComponent, - PositionComponent, - } - } - */ - let items = parse_macro_input!(input as MultipleTags); - - let tags = items.tags.iter().map(|t| t.0.clone()); - - // We write an enum with all tags - let mut output = quote! { - use std::collections::HashSet; - - /// All tags in the game - #[derive(Clone, PartialEq, Eq, Hash)] - pub enum Tag { - #( - #tags, - )* - } - - }; - - let tags_with_components = items.components; - let match_patterns = tags_with_components.iter().map(|(component, tags)| { - let tags = tags.iter().map(|t| { - let tag = Ident::new(&format!("{}", t), t.span()); - quote! { - Tag::#tag - } - }); - let component = Ident::new(&format!("{}", component), component.span()); - quote! { - Component::#component => vec![#(#tags),*], - } - }); - let map_tag_components = items.tags; - let map_tag_components = map_tag_components.iter().map(|(tag, components)| { - let tag = Ident::new(&format!("{}", tag), tag.span()); - let components = components.iter().map(|c| { - let component = Ident::new(&format!("{}", c), c.span()); - quote! { - Component::#component - } - }); - quote! { - Tag::#tag => vec![#(#components),*], - } - }); - // We write a function to get all tags from components - let get_tags_from_components = quote! { - impl Tag { - /// we get all components from a tag - pub fn get_components(&self) -> Vec { - match self { - #(#map_tag_components)* - } - } - - /// we get all tags from a component - pub fn get_tags_from_component(component: Component) -> Vec { - match component { - #(#match_patterns)* - _ => vec![], - } - } - - /// we get all tags that have the components - pub fn get_tags_from_components(components: HashSet) -> HashSet { - let mut tags = HashSet::new(); - for component in components.iter() { - for tag in Tag::get_tags_from_component(component.clone()) { - let components_of_tag = tag.get_components(); - if components_of_tag.iter().all(|c| components.contains(c)) { - tags.insert(tag); - } - } - } - tags - } - } - }; - - - output.extend(get_tags_from_components); - - output.into() -}