Drop item when destroying blocks
Also make EntityType consts
This commit is contained in:
parent
2e2fe83720
commit
8a5a0cdee8
@ -1,9 +1,8 @@
|
||||
use quote::quote;
|
||||
use quote::{format_ident, quote};
|
||||
use std::{env, fs, path::Path, process::Command};
|
||||
|
||||
use heck::ToPascalCase;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use syn::Ident;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
mod biome;
|
||||
mod chunk_status;
|
||||
@ -43,7 +42,7 @@ pub fn array_to_tokenstream(array: Vec<String>) -> TokenStream {
|
||||
let mut variants = TokenStream::new();
|
||||
|
||||
for item in array.iter() {
|
||||
let name = ident(item.to_pascal_case());
|
||||
let name = format_ident!("{}", item.to_pascal_case());
|
||||
variants.extend([quote! {
|
||||
#name,
|
||||
}]);
|
||||
@ -62,12 +61,3 @@ pub fn write_generated_file(content: TokenStream, out_file: &str) {
|
||||
// Doesn't matter if rustfmt is unavailable.
|
||||
let _ = Command::new("rustfmt").arg(path).output();
|
||||
}
|
||||
|
||||
pub fn ident<I: AsRef<str>>(s: I) -> Ident {
|
||||
let s = s.as_ref().trim();
|
||||
|
||||
// Parse the ident from a str. If the string is a Rust keyword, stick an
|
||||
// underscore in front.
|
||||
syn::parse_str::<Ident>(s)
|
||||
.unwrap_or_else(|_| Ident::new(format!("_{s}").as_str(), Span::call_site()))
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
use heck::ToPascalCase;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
use crate::ident;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub(crate) fn build() -> TokenStream {
|
||||
println!("cargo:rerun-if-changed=../assets/chunk_status.json");
|
||||
@ -14,7 +12,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
for status in chunk_status.iter() {
|
||||
let full_name = format!("minecraft:{status}");
|
||||
let name = ident(status.to_pascal_case());
|
||||
let name = format_ident!("{}", status.to_pascal_case());
|
||||
variants.extend([quote! {
|
||||
#[serde(rename = #full_name)]
|
||||
#name,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use heck::{ToPascalCase, ToShoutySnakeCase};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{format_ident, quote};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -29,8 +29,8 @@ pub(crate) fn build() -> TokenStream {
|
||||
let mut enum_variants = Vec::new();
|
||||
|
||||
for (name, entry) in damage_types {
|
||||
let const_ident = crate::ident(name.to_shouty_snake_case());
|
||||
let enum_ident = crate::ident(name.to_pascal_case());
|
||||
let const_ident = format_ident!("{}", name.to_shouty_snake_case());
|
||||
let enum_ident = format_ident!("{}", name.to_pascal_case());
|
||||
|
||||
enum_variants.push(enum_ident.clone());
|
||||
|
||||
@ -58,7 +58,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
let enum_arms = enum_variants.iter().map(|variant| {
|
||||
let const_name = variant.to_string().to_shouty_snake_case();
|
||||
let const_ident = crate::ident(&const_name);
|
||||
let const_ident = format_ident!("{}", &const_name);
|
||||
quote! {
|
||||
DamageType::#variant => &#const_ident,
|
||||
}
|
||||
|
||||
@ -1,66 +1,101 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use heck::ToPascalCase;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use serde::Deserialize;
|
||||
use syn::LitInt;
|
||||
|
||||
use crate::ident;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct JSONStruct {
|
||||
id: u16,
|
||||
pub struct EntityType {
|
||||
pub id: u16,
|
||||
pub max_health: Option<f32>,
|
||||
pub attackable: Option<bool>,
|
||||
pub summonable: bool,
|
||||
pub fire_immune: bool,
|
||||
pub dimension: [f32; 2],
|
||||
pub eye_height: f32,
|
||||
}
|
||||
|
||||
impl ToTokens for EntityType {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let id = LitInt::new(&self.id.to_string(), proc_macro2::Span::call_site());
|
||||
|
||||
let max_health = match self.max_health {
|
||||
Some(mh) => quote! { Some(#mh) },
|
||||
None => quote! { None },
|
||||
};
|
||||
|
||||
let attackable = match self.attackable {
|
||||
Some(a) => quote! { Some(#a) },
|
||||
None => quote! { None },
|
||||
};
|
||||
|
||||
let summonable = self.summonable;
|
||||
let fire_immune = self.fire_immune;
|
||||
let eye_height = self.eye_height;
|
||||
|
||||
let dimension0 = self.dimension[0];
|
||||
let dimension1 = self.dimension[1];
|
||||
|
||||
tokens.extend(quote! {
|
||||
EntityType {
|
||||
id: #id,
|
||||
max_health: #max_health,
|
||||
attackable: #attackable,
|
||||
summonable: #summonable,
|
||||
fire_immune: #fire_immune,
|
||||
dimension: [#dimension0, #dimension1], // Correctly construct the array
|
||||
eye_height: #eye_height,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build() -> TokenStream {
|
||||
println!("cargo:rerun-if-changed=../assets/entities.json");
|
||||
|
||||
let json: HashMap<String, JSONStruct> =
|
||||
let json: HashMap<String, EntityType> =
|
||||
serde_json::from_str(include_str!("../../assets/entities.json"))
|
||||
.expect("Failed to parse sound_category.json");
|
||||
let mut variants = TokenStream::new();
|
||||
.expect("Failed to parse entities.json");
|
||||
|
||||
for (item, id) in json.iter() {
|
||||
let id = id.id as u8;
|
||||
let mut consts = TokenStream::new();
|
||||
let mut type_from_raw_id_arms = TokenStream::new();
|
||||
let mut type_from_name = TokenStream::new();
|
||||
|
||||
for (name, entity) in json.iter() {
|
||||
let id = entity.id as u8;
|
||||
let id_lit = LitInt::new(&id.to_string(), proc_macro2::Span::call_site());
|
||||
let name = ident(item.to_pascal_case());
|
||||
variants.extend([quote! {
|
||||
#name = #id_lit,
|
||||
}]);
|
||||
let upper_name = format_ident!("{}", name.to_uppercase());
|
||||
|
||||
let entity_tokens = entity.to_token_stream();
|
||||
|
||||
consts.extend(quote! {
|
||||
pub const #upper_name: EntityType = #entity_tokens;
|
||||
});
|
||||
|
||||
type_from_raw_id_arms.extend(quote! {
|
||||
#id_lit => Some(Self::#upper_name),
|
||||
});
|
||||
|
||||
type_from_name.extend(quote! {
|
||||
#name => Some(Self::#upper_name),
|
||||
});
|
||||
}
|
||||
|
||||
let type_from_raw_id_arms = json
|
||||
.iter()
|
||||
.map(|sound| {
|
||||
let id = &sound.1.id;
|
||||
let name = ident(sound.0.to_pascal_case());
|
||||
|
||||
quote! {
|
||||
#id => Some(Self::#name),
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
let type_from_name = json
|
||||
.iter()
|
||||
.map(|sound| {
|
||||
let id = &sound.0;
|
||||
let name = ident(sound.0.to_pascal_case());
|
||||
|
||||
quote! {
|
||||
#id => Some(Self::#name),
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
quote! {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum EntityType {
|
||||
#variants
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct EntityType {
|
||||
pub id: u16,
|
||||
pub max_health: Option<f32>,
|
||||
pub attackable: Option<bool>,
|
||||
pub summonable: bool,
|
||||
pub fire_immune: bool,
|
||||
pub dimension: [f32; 2],
|
||||
pub eye_height: f32,
|
||||
}
|
||||
|
||||
impl EntityType {
|
||||
#consts
|
||||
|
||||
pub const fn from_raw(id: u16) -> Option<Self> {
|
||||
match id {
|
||||
#type_from_raw_id_arms
|
||||
|
||||
@ -2,11 +2,9 @@ use std::collections::HashMap;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use pumpkin_util::text::style::Style;
|
||||
use quote::quote;
|
||||
use quote::{format_ident, quote};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ident;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RawChatType {
|
||||
id: u32,
|
||||
@ -37,7 +35,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
for (name, typee) in json.iter() {
|
||||
let i = typee.id;
|
||||
let name = ident(name.to_uppercase());
|
||||
let name = format_ident!("{}", name.to_uppercase());
|
||||
variants.extend([quote! {
|
||||
pub const #name: u32 = #i;
|
||||
}]);
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{format_ident, quote};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::ident;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DoublePerlinNoiseParameters {
|
||||
#[serde(rename = "firstOctave")]
|
||||
@ -23,7 +21,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
for (name, parameter) in json.iter() {
|
||||
let raw_name = format!("minecraft:{name}");
|
||||
let name = ident(name.to_uppercase());
|
||||
let name = format_ident!("{}", name.to_uppercase());
|
||||
let first_octave = parameter.first_octave;
|
||||
let amplitudes = ¶meter.amplitudes;
|
||||
variants.extend([quote! {
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{format_ident, quote};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::ident;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Packets {
|
||||
serverbound: HashMap<String, Vec<String>>,
|
||||
@ -40,7 +38,7 @@ pub(crate) fn parse_packets(packets: HashMap<String, Vec<String>>) -> proc_macro
|
||||
for (id, packet_name) in packet.1.iter().enumerate() {
|
||||
let packet_id = id as i32;
|
||||
let name = format!("{phase}_{packet_name}").to_uppercase();
|
||||
let name = ident(name);
|
||||
let name = format_ident!("{}", name);
|
||||
consts.extend([quote! {
|
||||
pub const #name: i32 = #packet_id;
|
||||
}]);
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use heck::ToPascalCase;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
use crate::{array_to_tokenstream, ident};
|
||||
use crate::array_to_tokenstream;
|
||||
|
||||
pub(crate) fn build() -> TokenStream {
|
||||
println!("cargo:rerun-if-changed=../assets/sounds.json");
|
||||
@ -15,7 +15,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
.iter()
|
||||
.map(|sound| {
|
||||
let id = &sound;
|
||||
let name = ident(sound.to_pascal_case());
|
||||
let name = format_ident!("{}", sound.to_pascal_case());
|
||||
|
||||
quote! {
|
||||
#id => Some(Self::#name),
|
||||
@ -27,7 +27,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
.iter()
|
||||
.map(|sound| {
|
||||
let id = &sound;
|
||||
let name = ident(sound.to_pascal_case());
|
||||
let name = format_ident!("{}", sound.to_pascal_case());
|
||||
|
||||
quote! {
|
||||
Self::#name => #id,
|
||||
|
||||
@ -2,9 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use heck::ToPascalCase;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
use crate::ident;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub(crate) fn build() -> TokenStream {
|
||||
println!("cargo:rerun-if-changed=../assets/world_event.json");
|
||||
@ -15,7 +13,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
let mut variants = TokenStream::new();
|
||||
|
||||
for (event, id) in events.iter() {
|
||||
let name = ident(event.to_pascal_case());
|
||||
let name = format_ident!("{}", event.to_pascal_case());
|
||||
variants.extend([quote! {
|
||||
#name = #id,
|
||||
}]);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use pumpkin_data::packet::clientbound::PLAY_SET_ENTITY_MOTION;
|
||||
use pumpkin_macros::client_packet;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::VarInt;
|
||||
@ -8,18 +9,18 @@ use crate::VarInt;
|
||||
#[client_packet(PLAY_SET_ENTITY_MOTION)]
|
||||
pub struct CEntityVelocity<'a> {
|
||||
entity_id: &'a VarInt,
|
||||
velocity_x: i16,
|
||||
velocity_y: i16,
|
||||
velocity_z: i16,
|
||||
velocity: Vector3<i16>,
|
||||
}
|
||||
|
||||
impl<'a> CEntityVelocity<'a> {
|
||||
pub fn new(entity_id: &'a VarInt, velocity_x: f64, velocity_y: f64, velocity_z: f64) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
velocity_x: (velocity_x.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
velocity_y: (velocity_y.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
velocity_z: (velocity_z.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
velocity: Vector3::new(
|
||||
(velocity_x.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
(velocity_y.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
(velocity_z.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use bytes::BufMut;
|
||||
use pumpkin_data::{packet::clientbound::PLAY_SOUND, sound::SoundCategory};
|
||||
use pumpkin_macros::client_packet;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::{bytebuf::ByteBufMut, ClientPacket, IDOrSoundEvent, SoundEvent, VarInt};
|
||||
|
||||
@ -8,23 +9,18 @@ use crate::{bytebuf::ByteBufMut, ClientPacket, IDOrSoundEvent, SoundEvent, VarIn
|
||||
pub struct CSoundEffect {
|
||||
sound_event: IDOrSoundEvent,
|
||||
sound_category: VarInt,
|
||||
effect_position_x: i32,
|
||||
effect_position_y: i32,
|
||||
effect_position_z: i32,
|
||||
position: Vector3<i32>,
|
||||
volume: f32,
|
||||
pitch: f32,
|
||||
seed: f64,
|
||||
}
|
||||
|
||||
impl CSoundEffect {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
sound_id: VarInt,
|
||||
sound_event: Option<SoundEvent>,
|
||||
sound_category: SoundCategory,
|
||||
effect_position_x: f64,
|
||||
effect_position_y: f64,
|
||||
effect_position_z: f64,
|
||||
position: &Vector3<f64>,
|
||||
volume: f32,
|
||||
pitch: f32,
|
||||
seed: f64,
|
||||
@ -35,9 +31,11 @@ impl CSoundEffect {
|
||||
sound_event,
|
||||
},
|
||||
sound_category: VarInt(sound_category as i32),
|
||||
effect_position_x: (effect_position_x * 8.0) as i32,
|
||||
effect_position_y: (effect_position_y * 8.0) as i32,
|
||||
effect_position_z: (effect_position_z * 8.0) as i32,
|
||||
position: Vector3::new(
|
||||
(position.x * 8.0) as i32,
|
||||
(position.y * 8.0) as i32,
|
||||
(position.z * 8.0) as i32,
|
||||
),
|
||||
volume,
|
||||
pitch,
|
||||
seed,
|
||||
@ -58,9 +56,9 @@ impl ClientPacket for CSoundEffect {
|
||||
}
|
||||
}
|
||||
bytebuf.put_var_int(&self.sound_category);
|
||||
bytebuf.put_i32(self.effect_position_x);
|
||||
bytebuf.put_i32(self.effect_position_y);
|
||||
bytebuf.put_i32(self.effect_position_z);
|
||||
bytebuf.put_i32(self.position.x);
|
||||
bytebuf.put_i32(self.position.y);
|
||||
bytebuf.put_i32(self.position.z);
|
||||
bytebuf.put_f32(self.volume);
|
||||
bytebuf.put_f32(self.pitch);
|
||||
bytebuf.put_f64(self.seed);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use pumpkin_data::packet::clientbound::PLAY_ADD_ENTITY;
|
||||
use pumpkin_macros::client_packet;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::VarInt;
|
||||
@ -11,16 +12,12 @@ pub struct CSpawnEntity {
|
||||
#[serde(with = "uuid::serde::compact")]
|
||||
entity_uuid: uuid::Uuid,
|
||||
typ: VarInt,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
position: Vector3<f64>,
|
||||
pitch: u8, // angle
|
||||
yaw: u8, // angle
|
||||
head_yaw: u8, // angle
|
||||
data: VarInt,
|
||||
velocity_x: i16,
|
||||
velocity_y: i16,
|
||||
velocity_z: i16,
|
||||
velocity: Vector3<i16>,
|
||||
}
|
||||
|
||||
impl CSpawnEntity {
|
||||
@ -29,31 +26,27 @@ impl CSpawnEntity {
|
||||
entity_id: VarInt,
|
||||
entity_uuid: uuid::Uuid,
|
||||
typ: VarInt,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
position: Vector3<f64>,
|
||||
pitch: f32, // angle
|
||||
yaw: f32, // angle
|
||||
head_yaw: f32, // angle
|
||||
data: VarInt,
|
||||
velocity_x: f32,
|
||||
velocity_y: f32,
|
||||
velocity_z: f32,
|
||||
velocity: Vector3<f64>,
|
||||
) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
entity_uuid,
|
||||
typ,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
position,
|
||||
pitch: (pitch * 256.0 / 360.0).floor() as u8,
|
||||
yaw: (yaw * 256.0 / 360.0).floor() as u8,
|
||||
head_yaw: (head_yaw * 256.0 / 360.0).floor() as u8,
|
||||
data,
|
||||
velocity_x: (velocity_x.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
velocity_y: (velocity_y.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
velocity_z: (velocity_z.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
velocity: Vector3::new(
|
||||
(velocity.x.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
(velocity.x.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
(velocity.x.clamp(-3.9, 3.9) * 8000.0) as i16,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,15 +7,15 @@ pub struct BoundingBox {
|
||||
}
|
||||
|
||||
impl BoundingBox {
|
||||
pub fn new_default(size: &BoundingBoxSize) -> Self {
|
||||
pub fn new_default(size: &EntityDimensions) -> Self {
|
||||
Self::new_from_pos(0., 0., 0., size)
|
||||
}
|
||||
|
||||
pub fn new_from_pos(x: f64, y: f64, z: f64, size: &BoundingBoxSize) -> Self {
|
||||
let f = size.width / 2.;
|
||||
pub fn new_from_pos(x: f64, y: f64, z: f64, size: &EntityDimensions) -> Self {
|
||||
let f = size.width as f64 / 2.;
|
||||
Self {
|
||||
min: Vector3::new(x - f, y, z - f),
|
||||
max: Vector3::new(x + f, y + size.height, z + f),
|
||||
max: Vector3::new(x + f, y + size.height as f64, z + f),
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ impl BoundingBox {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct BoundingBoxSize {
|
||||
pub width: f64,
|
||||
pub height: f64,
|
||||
pub struct EntityDimensions {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
@ -1 +0,0 @@
|
||||
pub mod registry;
|
||||
@ -1,35 +0,0 @@
|
||||
use std::{collections::HashMap, sync::LazyLock};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
const ENTITIES_JSON: &str = include_str!("../../../assets/entities.json");
|
||||
|
||||
pub static ENTITIES: LazyLock<HashMap<String, Entity>> = LazyLock::new(|| {
|
||||
serde_json::from_str(ENTITIES_JSON).expect("Could not parse entity.json registry.")
|
||||
});
|
||||
|
||||
pub static ENTITIES_BY_ID: LazyLock<HashMap<String, u16>> = LazyLock::new(|| {
|
||||
let mut map = HashMap::new();
|
||||
for (entity_name, entity) in ENTITIES.iter() {
|
||||
map.insert(entity_name.clone(), entity.id);
|
||||
}
|
||||
map
|
||||
});
|
||||
|
||||
pub fn get_entity_id(name: &str) -> Option<&u16> {
|
||||
ENTITIES_BY_ID.get(&name.replace("minecraft:", ""))
|
||||
}
|
||||
|
||||
pub fn get_entity_by_id<'a>(entity_id: u16) -> Option<&'a Entity> {
|
||||
ENTITIES.values().find(|&entity| entity.id == entity_id)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct Entity {
|
||||
pub id: u16,
|
||||
pub max_health: Option<f32>,
|
||||
pub attackable: Option<bool>,
|
||||
pub summonable: bool,
|
||||
pub fire_immune: bool,
|
||||
pub dimension: [f32; 2],
|
||||
}
|
||||
@ -13,7 +13,6 @@ pub mod chunk;
|
||||
pub mod coordinates;
|
||||
pub mod cylindrical_chunk_iterator;
|
||||
pub mod dimension;
|
||||
pub mod entity;
|
||||
mod generation;
|
||||
pub mod item;
|
||||
pub mod level;
|
||||
|
||||
@ -1,10 +1,19 @@
|
||||
use blocks::chest::ChestBlock;
|
||||
use blocks::furnace::FurnaceBlock;
|
||||
use properties::BlockPropertiesManager;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_world::block::registry::Block;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::block::blocks::crafting_table::CraftingTableBlock;
|
||||
use crate::block::blocks::jukebox::JukeboxBlock;
|
||||
use crate::block::registry::BlockRegistry;
|
||||
use crate::entity::item::ItemEntity;
|
||||
use crate::server::Server;
|
||||
use crate::world::World;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod blocks;
|
||||
@ -24,6 +33,21 @@ pub fn default_registry() -> Arc<BlockRegistry> {
|
||||
Arc::new(manager)
|
||||
}
|
||||
|
||||
pub async fn drop_loot(server: &Server, world: &Arc<World>, block: &Block, pos: &BlockPos) {
|
||||
// TODO: Currently only the item block is droped, We should drop the loop table
|
||||
let height = EntityType::ITEM.dimension[1] / 2.0;
|
||||
let pos = Vector3::new(
|
||||
f64::from(pos.0.x) + 0.5 + rand::thread_rng().gen_range(-0.25..0.25),
|
||||
f64::from(pos.0.y) + 0.5 + rand::thread_rng().gen_range(-0.25..0.25) - f64::from(height),
|
||||
f64::from(pos.0.z) + 0.5 + rand::thread_rng().gen_range(-0.25..0.25),
|
||||
);
|
||||
|
||||
let entity = server.add_entity(pos, EntityType::ITEM, world);
|
||||
let item_entity = Arc::new(ItemEntity::new(entity, &ItemStack::new(1, block.item_id)));
|
||||
world.spawn_entity(item_entity.clone()).await;
|
||||
item_entity.send_meta_packet().await;
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn default_block_properties_manager() -> Arc<BlockPropertiesManager> {
|
||||
let mut manager = BlockPropertiesManager::default();
|
||||
|
||||
@ -41,7 +41,7 @@ impl CommandExecutor for SetblockExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?;
|
||||
@ -67,7 +67,9 @@ impl CommandExecutor for SetblockExecutor {
|
||||
for y in start_y..=end_y {
|
||||
for z in start_z..=end_z {
|
||||
let block_position = BlockPos(Vector3 { x, y, z });
|
||||
world.break_block(&block_position, None).await;
|
||||
world
|
||||
.break_block(server, &block_position, None, false)
|
||||
.await;
|
||||
world.set_block_state(&block_position, block_state_id).await;
|
||||
placed_blocks += 1;
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ impl CommandExecutor for KillExecutor {
|
||||
Some(TextComponent::text(name.clone())),
|
||||
));
|
||||
|
||||
if entity.entity_type == entity::EntityType::Player {
|
||||
if entity.entity_type == entity::EntityType::PLAYER {
|
||||
entity_display = entity_display.click_event(ClickEvent::SuggestCommand(
|
||||
format!("/tell {} ", name.clone()).into(),
|
||||
));
|
||||
|
||||
@ -34,7 +34,7 @@ impl CommandExecutor for SetblockExecutor {
|
||||
async fn execute<'a>(
|
||||
&self,
|
||||
sender: &mut CommandSender<'a>,
|
||||
_server: &crate::server::Server,
|
||||
server: &crate::server::Server,
|
||||
args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?;
|
||||
@ -46,7 +46,7 @@ impl CommandExecutor for SetblockExecutor {
|
||||
|
||||
let success = match mode {
|
||||
Mode::Destroy => {
|
||||
world.break_block(&pos, None).await;
|
||||
world.clone().break_block(server, &pos, None, false).await;
|
||||
world.set_block_state(&pos, block_state_id).await;
|
||||
true
|
||||
}
|
||||
|
||||
@ -43,8 +43,8 @@ fn yaw_pitch_facing_position(
|
||||
let yaw_radians = -direction_vector.x.atan2(direction_vector.z);
|
||||
let pitch_radians = (-direction_vector.y).asin();
|
||||
|
||||
let yaw_degrees = yaw_radians * 180.0 / std::f64::consts::PI;
|
||||
let pitch_degrees = pitch_radians * 180.0 / std::f64::consts::PI;
|
||||
let yaw_degrees = yaw_radians.to_degrees();
|
||||
let pitch_degrees = pitch_radians.to_degrees();
|
||||
|
||||
(yaw_degrees as f32, pitch_degrees as f32)
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ impl CommandSender<'_> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn world(&self) -> Option<&World> {
|
||||
pub fn world(&self) -> Option<&Arc<World>> {
|
||||
match self {
|
||||
// TODO: maybe return first world when console
|
||||
CommandSender::Console | CommandSender::Rcon(..) => None,
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use pumpkin_data::{
|
||||
particle::Particle,
|
||||
sound::{Sound, SoundCategory},
|
||||
@ -74,8 +72,8 @@ pub async fn handle_knockback(
|
||||
let saved_velo = victim_entity.velocity.load();
|
||||
victim_entity.knockback(
|
||||
strength * 0.5,
|
||||
f64::from((yaw * (PI / 180.0)).sin()),
|
||||
f64::from(-(yaw * (PI / 180.0)).cos()),
|
||||
f64::from((yaw.to_radians()).sin()),
|
||||
f64::from(-(yaw.to_radians()).cos()),
|
||||
);
|
||||
|
||||
let entity_id = VarInt(victim_entity.entity_id);
|
||||
@ -98,8 +96,8 @@ pub async fn handle_knockback(
|
||||
|
||||
pub async fn spawn_sweep_particle(attacker_entity: &Entity, world: &World, pos: &Vector3<f64>) {
|
||||
let yaw = attacker_entity.yaw.load();
|
||||
let d = -f64::from((yaw * (PI / 180.0)).sin());
|
||||
let e = f64::from((yaw * (PI / 180.0)).cos());
|
||||
let d = -f64::from((yaw.to_radians()).sin());
|
||||
let e = f64::from((yaw.to_radians()).cos());
|
||||
|
||||
let scale = 0.5;
|
||||
// TODO: use entity height
|
||||
|
||||
@ -64,7 +64,7 @@ pub async fn from_type(
|
||||
};
|
||||
#[expect(clippy::single_match)]
|
||||
match entity_type {
|
||||
EntityType::Zombie => Zombie::make(&mob).await,
|
||||
EntityType::ZOMBIE => Zombie::make(&mob).await,
|
||||
// TODO
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
use core::f32;
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
@ -22,7 +19,7 @@ use pumpkin_protocol::{
|
||||
codec::var_int::VarInt,
|
||||
};
|
||||
use pumpkin_util::math::{
|
||||
boundingbox::{BoundingBox, BoundingBoxSize},
|
||||
boundingbox::{BoundingBox, EntityDimensions},
|
||||
get_section_cord,
|
||||
position::BlockPos,
|
||||
vector2::Vector2,
|
||||
@ -94,7 +91,7 @@ pub struct Entity {
|
||||
/// The bounding box of an entity (hitbox)
|
||||
pub bounding_box: AtomicCell<BoundingBox>,
|
||||
///The size (width and height) of the bounding box
|
||||
pub bounding_box_size: AtomicCell<BoundingBoxSize>,
|
||||
pub bounding_box_size: AtomicCell<EntityDimensions>,
|
||||
/// Whether this entity is invulnerable to all damage
|
||||
pub invulnerable: AtomicBool,
|
||||
/// List of damage types this entity is immune to
|
||||
@ -111,7 +108,7 @@ impl Entity {
|
||||
entity_type: EntityType,
|
||||
standing_eye_height: f32,
|
||||
bounding_box: AtomicCell<BoundingBox>,
|
||||
bounding_box_size: AtomicCell<BoundingBoxSize>,
|
||||
bounding_box_size: AtomicCell<EntityDimensions>,
|
||||
invulnerable: bool,
|
||||
) -> Self {
|
||||
let floor_x = position.x.floor() as i32;
|
||||
@ -187,8 +184,8 @@ impl Entity {
|
||||
/// Returns entity rotation as vector
|
||||
pub fn rotation(&self) -> Vector3<f32> {
|
||||
// Convert degrees to radians if necessary
|
||||
let yaw_rad = self.yaw.load() * (PI / 180.0);
|
||||
let pitch_rad = self.pitch.load() * (PI / 180.0);
|
||||
let yaw_rad = self.yaw.load().to_radians();
|
||||
let pitch_rad = self.pitch.load().to_radians();
|
||||
|
||||
Vector3::new(
|
||||
yaw_rad.cos() * pitch_rad.cos(),
|
||||
@ -260,17 +257,13 @@ impl Entity {
|
||||
CSpawnEntity::new(
|
||||
VarInt(self.entity_id),
|
||||
self.entity_uuid,
|
||||
VarInt((self.entity_type) as i32),
|
||||
entity_loc.x,
|
||||
entity_loc.y,
|
||||
entity_loc.z,
|
||||
VarInt(i32::from(self.entity_type.id)),
|
||||
entity_loc,
|
||||
self.pitch.load(),
|
||||
self.yaw.load(),
|
||||
self.head_yaw.load(), // todo: head_yaw and yaw are swapped, find out why
|
||||
0.into(),
|
||||
entity_vel.x as f32,
|
||||
entity_vel.y as f32,
|
||||
entity_vel.z as f32,
|
||||
entity_vel,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ use pumpkin_protocol::{
|
||||
};
|
||||
use pumpkin_util::{
|
||||
math::{
|
||||
boundingbox::{BoundingBox, BoundingBoxSize},
|
||||
boundingbox::{BoundingBox, EntityDimensions},
|
||||
experience,
|
||||
position::BlockPos,
|
||||
vector2::Vector2,
|
||||
@ -169,9 +169,9 @@ impl Player {
|
||||
|
||||
let gameprofile_clone = gameprofile.clone();
|
||||
let config = client.config.lock().await.clone().unwrap_or_default();
|
||||
let bounding_box_size = BoundingBoxSize {
|
||||
width: 0.6,
|
||||
height: 1.8,
|
||||
let bounding_box_size = EntityDimensions {
|
||||
width: EntityType::PLAYER.dimension[0],
|
||||
height: EntityType::PLAYER.dimension[1],
|
||||
};
|
||||
|
||||
Self {
|
||||
@ -180,8 +180,8 @@ impl Player {
|
||||
player_uuid,
|
||||
world,
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
EntityType::Player,
|
||||
1.62,
|
||||
EntityType::PLAYER,
|
||||
EntityType::PLAYER.eye_height,
|
||||
AtomicCell::new(BoundingBox::new_default(&bounding_box_size)),
|
||||
AtomicCell::new(bounding_box_size),
|
||||
matches!(gamemode, GameMode::Creative | GameMode::Spectator),
|
||||
@ -404,9 +404,7 @@ impl Player {
|
||||
VarInt(i32::from(sound_id)),
|
||||
None,
|
||||
category,
|
||||
position.x,
|
||||
position.y,
|
||||
position.z,
|
||||
position,
|
||||
volume,
|
||||
pitch,
|
||||
seed,
|
||||
@ -791,7 +789,7 @@ impl Player {
|
||||
if let Some(item) = inv.held_item_mut() {
|
||||
let entity = server.add_entity(
|
||||
self.living_entity.entity.pos.load(),
|
||||
EntityType::Item,
|
||||
EntityType::ITEM,
|
||||
self.world(),
|
||||
);
|
||||
let item_entity = Arc::new(ItemEntity::new(entity, &item.clone()));
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::f32::{self, consts::PI};
|
||||
use std::f32::{self};
|
||||
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
@ -8,8 +8,6 @@ pub struct ThrownItemEntity {
|
||||
entity: Entity,
|
||||
}
|
||||
|
||||
const DEG_PER_RAD_F32: f32 = 180.0 / PI;
|
||||
|
||||
impl ThrownItemEntity {
|
||||
pub fn new(entity: Entity, owner: &Entity) -> Self {
|
||||
let mut owner_pos = owner.pos.load();
|
||||
@ -69,8 +67,8 @@ impl ThrownItemEntity {
|
||||
self.entity.velocity.store(velocity);
|
||||
let len = velocity.horizontal_length();
|
||||
self.entity.set_rotation(
|
||||
velocity.x.atan2(velocity.z) as f32 * DEG_PER_RAD_F32,
|
||||
velocity.y.atan2(len) as f32 * DEG_PER_RAD_F32,
|
||||
velocity.x.atan2(velocity.z).to_degrees() as f32,
|
||||
velocity.y.atan2(len).to_degrees() as f32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ impl PumpkinItem for EggItem {
|
||||
)
|
||||
.await;
|
||||
// TODO: Implement eggs the right way, so there is a chance of spawning chickens
|
||||
let entity = server.add_entity(position, EntityType::Egg, world);
|
||||
let entity = server.add_entity(position, EntityType::EGG, world);
|
||||
let snowball = ThrownItemEntity::new(entity, &player.living_entity.entity);
|
||||
let yaw = player.living_entity.entity.yaw.load();
|
||||
let pitch = player.living_entity.entity.pitch.load();
|
||||
|
||||
@ -26,7 +26,7 @@ impl PumpkinItem for SnowBallItem {
|
||||
&position,
|
||||
)
|
||||
.await;
|
||||
let entity = server.add_entity(position, EntityType::Snowball, world);
|
||||
let entity = server.add_entity(position, EntityType::SNOWBALL, world);
|
||||
let snowball = ThrownItemEntity::new(entity, &player.living_entity.entity);
|
||||
let yaw = player.living_entity.entity.yaw.load();
|
||||
let pitch = player.living_entity.entity.pitch.load();
|
||||
|
||||
@ -53,7 +53,6 @@ use pumpkin_world::item::registry::{get_item_by_id, get_name_by_id};
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use pumpkin_world::{
|
||||
block::{registry::get_block_by_item, BlockDirection},
|
||||
entity::registry::get_entity_id,
|
||||
item::registry::get_spawn_egg,
|
||||
};
|
||||
|
||||
@ -820,8 +819,9 @@ impl Player {
|
||||
let world = &entity.world;
|
||||
let block = world.get_block(&location).await;
|
||||
|
||||
world.break_block(&location, Some(self.clone())).await;
|
||||
|
||||
world
|
||||
.break_block(server, &location, Some(self.clone()), false)
|
||||
.await;
|
||||
if let Ok(block) = block {
|
||||
server
|
||||
.block_registry
|
||||
@ -864,7 +864,14 @@ impl Player {
|
||||
let world = &entity.world;
|
||||
let block = world.get_block(&location).await;
|
||||
|
||||
world.break_block(&location, Some(self.clone())).await;
|
||||
world
|
||||
.break_block(
|
||||
server,
|
||||
&location,
|
||||
Some(self.clone()),
|
||||
self.gamemode.load() != GameMode::Creative,
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Ok(block) = block {
|
||||
server
|
||||
@ -1171,7 +1178,7 @@ impl Player {
|
||||
face: &BlockDirection,
|
||||
) -> Result<bool, Box<dyn PumpkinError>> {
|
||||
// checks if spawn egg has a corresponding entity name
|
||||
if let Some(spawn_item_id) = get_entity_id(&item_t) {
|
||||
if let Some(entity_type) = EntityType::from_name(&item_t) {
|
||||
let world_pos = BlockPos(location.0 + face.to_offset());
|
||||
// align position like Vanilla does
|
||||
let pos = Vector3::new(
|
||||
@ -1185,7 +1192,7 @@ impl Player {
|
||||
let world = self.world();
|
||||
// create new mob and uuid based on spawn egg id
|
||||
let mob = mob::from_type(
|
||||
EntityType::from_raw(*spawn_item_id).unwrap(),
|
||||
EntityType::from_raw(entity_type.id).unwrap(),
|
||||
server,
|
||||
pos,
|
||||
world,
|
||||
|
||||
@ -8,14 +8,13 @@ use pumpkin_inventory::{Container, OpenContainer};
|
||||
use pumpkin_protocol::client::login::CEncryptionRequest;
|
||||
use pumpkin_protocol::{client::config::CPluginMessage, ClientPacket};
|
||||
use pumpkin_registry::{DimensionType, Registry};
|
||||
use pumpkin_util::math::boundingbox::{BoundingBox, BoundingBoxSize};
|
||||
use pumpkin_util::math::boundingbox::{BoundingBox, EntityDimensions};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_world::block::registry::Block;
|
||||
use pumpkin_world::dimension::Dimension;
|
||||
use pumpkin_world::entity::registry::get_entity_by_id;
|
||||
use rand::prelude::SliceRandom;
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
@ -221,16 +220,10 @@ impl Server {
|
||||
let entity_id = self.new_entity_id();
|
||||
|
||||
// TODO: this should be resolved to a integer using a macro when calling this function
|
||||
let bounding_box_size = get_entity_by_id(entity_type as u16).map_or(
|
||||
BoundingBoxSize {
|
||||
width: 0.6,
|
||||
height: 1.8,
|
||||
},
|
||||
|entity| BoundingBoxSize {
|
||||
width: f64::from(entity.dimension[0]),
|
||||
height: f64::from(entity.dimension[1]),
|
||||
},
|
||||
);
|
||||
let bounding_box_size = EntityDimensions {
|
||||
width: entity_type.dimension[0],
|
||||
height: entity_type.dimension[1],
|
||||
};
|
||||
|
||||
// TODO: standing eye height should be per mob
|
||||
let new_uuid = uuid::Uuid::new_v4();
|
||||
@ -240,7 +233,7 @@ impl Server {
|
||||
world.clone(),
|
||||
position,
|
||||
entity_type,
|
||||
1.62,
|
||||
entity_type.eye_height,
|
||||
AtomicCell::new(BoundingBox::new_from_pos(
|
||||
position.x,
|
||||
position.y,
|
||||
|
||||
@ -7,6 +7,7 @@ pub mod chunker;
|
||||
pub mod time;
|
||||
|
||||
use crate::{
|
||||
block,
|
||||
command::client_suggestions,
|
||||
entity::{player::Player, Entity, EntityBase, EntityId},
|
||||
error::PumpkinError,
|
||||
@ -423,17 +424,13 @@ impl World {
|
||||
&CSpawnEntity::new(
|
||||
entity_id.into(),
|
||||
gameprofile.id,
|
||||
(EntityType::Player as i32).into(),
|
||||
position.x,
|
||||
position.y,
|
||||
position.z,
|
||||
i32::from(EntityType::PLAYER.id).into(),
|
||||
position,
|
||||
pitch,
|
||||
yaw,
|
||||
yaw,
|
||||
0.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
@ -449,17 +446,13 @@ impl World {
|
||||
.send_packet(&CSpawnEntity::new(
|
||||
existing_player.entity_id().into(),
|
||||
gameprofile.id,
|
||||
(EntityType::Player as i32).into(),
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z,
|
||||
i32::from(EntityType::PLAYER.id).into(),
|
||||
pos,
|
||||
entity.yaw.load(),
|
||||
entity.pitch.load(),
|
||||
entity.head_yaw.load(),
|
||||
0.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
@ -594,17 +587,13 @@ impl World {
|
||||
&CSpawnEntity::new(
|
||||
entity.entity_id.into(),
|
||||
player.gameprofile.id,
|
||||
(EntityType::Player as i32).into(),
|
||||
position.x,
|
||||
position.y,
|
||||
position.z,
|
||||
i32::from(EntityType::PLAYER.id).into(),
|
||||
position,
|
||||
pitch,
|
||||
yaw,
|
||||
yaw,
|
||||
0.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
Vector3::new(0.0, 0.0, 0.0),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
@ -1025,7 +1014,13 @@ impl World {
|
||||
chunk
|
||||
}
|
||||
|
||||
pub async fn break_block(&self, position: &BlockPos, cause: Option<Arc<Player>>) {
|
||||
pub async fn break_block(
|
||||
self: &Arc<Self>,
|
||||
server: &Server,
|
||||
position: &BlockPos,
|
||||
cause: Option<Arc<Player>>,
|
||||
drop: bool,
|
||||
) {
|
||||
let block = self.get_block(position).await.unwrap();
|
||||
let event = BlockBreakEvent::new(cause.clone(), block.clone(), 0, false);
|
||||
|
||||
@ -1045,6 +1040,10 @@ impl World {
|
||||
false,
|
||||
);
|
||||
|
||||
if drop {
|
||||
block::drop_loot(server, self, block, position).await;
|
||||
}
|
||||
|
||||
match cause {
|
||||
Some(player) => {
|
||||
self.broadcast_packet_except(&[player.gameprofile.id], &particles_packet)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user