mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 14:31:30 +11:00
Entity Rework (#294)
## Description Closes #269 Closes #199 - Removes `McEntity` and replaces it with bundles of components, one for each entity type. - Tracked data types are now separate components rather than stuffing everything into a `TrackedData` enum. - Tracked data is now cached in binary form within each entity, eliminating some work when entities enter the view of clients. - Complete redesign of entity code generator. - More docs for some components. - Field bits are moved out of the entity extractor and into the valence entity module. - Moved hitbox code to separate module. - Refactor instance update systems to improve parallelism. ### TODOs - [x] Update examples. - [x] Update `default_event_handler`. - [x] Fix bugs. ## Test Plan Steps: 1. Check out the entity module docs with `cargo d --open`. 2. Run examples.
This commit is contained in:
parent
c9fbd1a24e
commit
4cf6e1a207
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,9 +8,11 @@ Cargo.lock
|
||||||
/extractor/out
|
/extractor/out
|
||||||
/extractor/classes
|
/extractor/classes
|
||||||
/extractor/run
|
/extractor/run
|
||||||
|
/extractor/bin
|
||||||
rust-mc-bot
|
rust-mc-bot
|
||||||
.asset_cache/
|
.asset_cache/
|
||||||
/velocity
|
/velocity
|
||||||
flamegraph*.svg
|
flamegraph*.svg
|
||||||
perf.data
|
perf.data
|
||||||
perf.data.old
|
perf.data.old
|
||||||
|
/graph.gv
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::client::event::default_event_handler;
|
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
|
|
|
@ -12,6 +12,7 @@ build = "build/main.rs"
|
||||||
authors = ["Ryan Johnson <ryanj00a@gmail.com>"]
|
authors = ["Ryan Johnson <ryanj00a@gmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
#bevy_mod_debugdump = "0.7.0"
|
||||||
anyhow = "1.0.65"
|
anyhow = "1.0.65"
|
||||||
arrayvec = "0.7.2"
|
arrayvec = "0.7.2"
|
||||||
async-trait = "0.1.60"
|
async-trait = "0.1.60"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use heck::ToPascalCase;
|
use heck::{ToPascalCase, ToShoutySnakeCase, ToSnakeCase};
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ struct Field {
|
||||||
index: u8,
|
index: u8,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
default_value: Value,
|
default_value: Value,
|
||||||
bits: Vec<Bit>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
@ -75,14 +74,8 @@ struct BlockPos {
|
||||||
z: i32,
|
z: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
|
||||||
struct Bit {
|
|
||||||
name: String,
|
|
||||||
index: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn type_id(&self) -> i32 {
|
pub fn type_id(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
Value::Byte(_) => 0,
|
Value::Byte(_) => 0,
|
||||||
Value::Integer(_) => 1,
|
Value::Integer(_) => 1,
|
||||||
|
@ -117,46 +110,26 @@ impl Value {
|
||||||
Value::Integer(_) => quote!(i32),
|
Value::Integer(_) => quote!(i32),
|
||||||
Value::Long(_) => quote!(i64),
|
Value::Long(_) => quote!(i64),
|
||||||
Value::Float(_) => quote!(f32),
|
Value::Float(_) => quote!(f32),
|
||||||
Value::String(_) => quote!(Box<str>),
|
Value::String(_) => quote!(String),
|
||||||
Value::TextComponent(_) => quote!(Text),
|
Value::TextComponent(_) => quote!(crate::protocol::text::Text),
|
||||||
Value::OptionalTextComponent(_) => quote!(Option<Text>),
|
Value::OptionalTextComponent(_) => quote!(Option<crate::protocol::text::Text>),
|
||||||
Value::ItemStack(_) => quote!(()), // TODO
|
Value::ItemStack(_) => quote!(crate::protocol::item::ItemStack),
|
||||||
Value::Boolean(_) => quote!(bool),
|
Value::Boolean(_) => quote!(bool),
|
||||||
Value::Rotation { .. } => quote!(EulerAngle),
|
Value::Rotation { .. } => quote!(crate::entity::EulerAngle),
|
||||||
Value::BlockPos(_) => quote!(BlockPos),
|
Value::BlockPos(_) => quote!(crate::protocol::block_pos::BlockPos),
|
||||||
Value::OptionalBlockPos(_) => quote!(Option<BlockPos>),
|
Value::OptionalBlockPos(_) => quote!(Option<crate::protocol::block_pos::BlockPos>),
|
||||||
Value::Facing(_) => quote!(Facing),
|
Value::Facing(_) => quote!(crate::protocol::types::Direction),
|
||||||
Value::OptionalUuid(_) => quote!(Option<Uuid>),
|
Value::OptionalUuid(_) => quote!(Option<::uuid::Uuid>),
|
||||||
Value::OptionalBlockState(_) => quote!(BlockState),
|
Value::OptionalBlockState(_) => quote!(crate::protocol::block::BlockState),
|
||||||
Value::NbtCompound(_) => quote!(valence_nbt::Compound),
|
Value::NbtCompound(_) => quote!(crate::nbt::Compound),
|
||||||
Value::Particle(_) => quote!(Particle),
|
Value::Particle(_) => quote!(crate::protocol::packet::s2c::play::particle::Particle),
|
||||||
Value::VillagerData { .. } => quote!(VillagerData),
|
Value::VillagerData { .. } => quote!(crate::entity::VillagerData),
|
||||||
Value::OptionalInt(_) => quote!(OptionalInt),
|
Value::OptionalInt(_) => quote!(Option<i32>),
|
||||||
Value::EntityPose(_) => quote!(Pose),
|
Value::EntityPose(_) => quote!(crate::entity::Pose),
|
||||||
Value::CatVariant(_) => quote!(CatKind),
|
Value::CatVariant(_) => quote!(crate::entity::CatKind),
|
||||||
Value::FrogVariant(_) => quote!(FrogKind),
|
Value::FrogVariant(_) => quote!(crate::entity::FrogKind),
|
||||||
Value::OptionalGlobalPos(_) => quote!(()), // TODO
|
Value::OptionalGlobalPos(_) => quote!(()), // TODO
|
||||||
Value::PaintingVariant(_) => quote!(PaintingKind),
|
Value::PaintingVariant(_) => quote!(crate::entity::PaintingKind),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getter_return_type(&self) -> TokenStream {
|
|
||||||
match self {
|
|
||||||
Value::String(_) => quote!(&str),
|
|
||||||
Value::TextComponent(_) => quote!(&Text),
|
|
||||||
Value::OptionalTextComponent(_) => quote!(Option<&Text>),
|
|
||||||
Value::NbtCompound(_) => quote!(&valence_nbt::Compound),
|
|
||||||
_ => self.field_type(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getter_return_expr(&self, field_name: &Ident) -> TokenStream {
|
|
||||||
match self {
|
|
||||||
Value::String(_) | Value::TextComponent(_) | Value::NbtCompound(_) => {
|
|
||||||
quote!(&self.#field_name)
|
|
||||||
}
|
|
||||||
Value::OptionalTextComponent(_) => quote!(self.#field_name.as_ref()),
|
|
||||||
_ => quote!(self.#field_name),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,35 +139,53 @@ impl Value {
|
||||||
Value::Integer(i) => quote!(#i),
|
Value::Integer(i) => quote!(#i),
|
||||||
Value::Long(l) => quote!(#l),
|
Value::Long(l) => quote!(#l),
|
||||||
Value::Float(f) => quote!(#f),
|
Value::Float(f) => quote!(#f),
|
||||||
Value::String(s) => quote!(#s.to_owned().into_boxed_str()),
|
Value::String(s) => quote!(#s.to_owned()),
|
||||||
Value::TextComponent(_) => quote!(Text::default()), // TODO
|
Value::TextComponent(txt) => {
|
||||||
|
assert!(txt.is_empty());
|
||||||
|
quote!(crate::protocol::text::Text::default())
|
||||||
|
}
|
||||||
Value::OptionalTextComponent(t) => {
|
Value::OptionalTextComponent(t) => {
|
||||||
assert!(t.is_none());
|
assert!(t.is_none());
|
||||||
quote!(None)
|
quote!(None)
|
||||||
}
|
}
|
||||||
Value::ItemStack(_) => quote!(()), // TODO
|
Value::ItemStack(stack) => {
|
||||||
|
assert_eq!(stack, "1 air");
|
||||||
|
quote!(crate::protocol::item::ItemStack::default())
|
||||||
|
}
|
||||||
Value::Boolean(b) => quote!(#b),
|
Value::Boolean(b) => quote!(#b),
|
||||||
Value::Rotation { pitch, yaw, roll } => quote! {
|
Value::Rotation { pitch, yaw, roll } => quote! {
|
||||||
EulerAngle {
|
crate::entity::EulerAngle {
|
||||||
pitch: #pitch,
|
pitch: #pitch,
|
||||||
yaw: #yaw,
|
yaw: #yaw,
|
||||||
roll: #roll,
|
roll: #roll,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Value::BlockPos(BlockPos { x, y, z }) => {
|
Value::BlockPos(BlockPos { x, y, z }) => {
|
||||||
quote!(BlockPos { x: #x, y: #y, z: #z })
|
quote!(crate::protocol::block_pos::BlockPos { x: #x, y: #y, z: #z })
|
||||||
|
}
|
||||||
|
Value::OptionalBlockPos(pos) => {
|
||||||
|
assert!(pos.is_none());
|
||||||
|
quote!(None)
|
||||||
}
|
}
|
||||||
Value::OptionalBlockPos(_) => quote!(None), // TODO
|
|
||||||
Value::Facing(f) => {
|
Value::Facing(f) => {
|
||||||
let variant = ident(f.to_pascal_case());
|
let variant = ident(f.to_pascal_case());
|
||||||
quote!(Facing::#variant)
|
quote!(crate::protocol::types::Direction::#variant)
|
||||||
|
}
|
||||||
|
Value::OptionalUuid(uuid) => {
|
||||||
|
assert!(uuid.is_none());
|
||||||
|
quote!(None)
|
||||||
|
}
|
||||||
|
Value::OptionalBlockState(bs) => {
|
||||||
|
assert!(bs.is_none());
|
||||||
|
quote!(crate::protocol::block::BlockState::default())
|
||||||
|
}
|
||||||
|
Value::NbtCompound(s) => {
|
||||||
|
assert_eq!(s, "{}");
|
||||||
|
quote!(crate::nbt::Compound::default())
|
||||||
}
|
}
|
||||||
Value::OptionalUuid(_) => quote!(None), // TODO
|
|
||||||
Value::OptionalBlockState(_) => quote!(BlockState::default()), // TODO
|
|
||||||
Value::NbtCompound(_) => quote!(valence_nbt::Compound::default()), // TODO
|
|
||||||
Value::Particle(p) => {
|
Value::Particle(p) => {
|
||||||
let variant = ident(p.to_pascal_case());
|
let variant = ident(p.to_pascal_case());
|
||||||
quote!(Particle::#variant)
|
quote!(crate::protocol::packet::s2c::play::particle::Particle::#variant)
|
||||||
}
|
}
|
||||||
Value::VillagerData {
|
Value::VillagerData {
|
||||||
typ,
|
typ,
|
||||||
|
@ -203,28 +194,34 @@ impl Value {
|
||||||
} => {
|
} => {
|
||||||
let typ = ident(typ.to_pascal_case());
|
let typ = ident(typ.to_pascal_case());
|
||||||
let profession = ident(profession.to_pascal_case());
|
let profession = ident(profession.to_pascal_case());
|
||||||
quote!(VillagerData::new(VillagerKind::#typ, VillagerProfession::#profession, #level))
|
quote! {
|
||||||
|
crate::entity::VillagerData {
|
||||||
|
kind: crate::entity::VillagerKind::#typ,
|
||||||
|
profession: crate::entity::VillagerProfession::#profession,
|
||||||
|
level: #level,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Value::OptionalInt(i) => {
|
Value::OptionalInt(i) => {
|
||||||
assert!(i.is_none());
|
assert!(i.is_none());
|
||||||
quote!(OptionalInt::default())
|
quote!(None)
|
||||||
}
|
}
|
||||||
Value::EntityPose(p) => {
|
Value::EntityPose(p) => {
|
||||||
let variant = ident(p.to_pascal_case());
|
let variant = ident(p.to_pascal_case());
|
||||||
quote!(Pose::#variant)
|
quote!(crate::entity::Pose::#variant)
|
||||||
}
|
}
|
||||||
Value::CatVariant(c) => {
|
Value::CatVariant(c) => {
|
||||||
let variant = ident(c.to_pascal_case());
|
let variant = ident(c.to_pascal_case());
|
||||||
quote!(CatKind::#variant)
|
quote!(crate::entity::CatKind::#variant)
|
||||||
}
|
}
|
||||||
Value::FrogVariant(f) => {
|
Value::FrogVariant(f) => {
|
||||||
let variant = ident(f.to_pascal_case());
|
let variant = ident(f.to_pascal_case());
|
||||||
quote!(FrogKind::#variant)
|
quote!(crate::entity::FrogKind::#variant)
|
||||||
}
|
}
|
||||||
Value::OptionalGlobalPos(_) => quote!(()),
|
Value::OptionalGlobalPos(_) => quote!(()),
|
||||||
Value::PaintingVariant(p) => {
|
Value::PaintingVariant(p) => {
|
||||||
let variant = ident(p.to_pascal_case());
|
let variant = ident(p.to_pascal_case());
|
||||||
quote!(PaintingKind::#variant)
|
quote!(crate::entity::PaintingKind::#variant)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,7 +229,9 @@ impl Value {
|
||||||
pub fn encodable_expr(&self, self_lvalue: TokenStream) -> TokenStream {
|
pub fn encodable_expr(&self, self_lvalue: TokenStream) -> TokenStream {
|
||||||
match self {
|
match self {
|
||||||
Value::Integer(_) => quote!(VarInt(#self_lvalue)),
|
Value::Integer(_) => quote!(VarInt(#self_lvalue)),
|
||||||
_ => self_lvalue,
|
Value::OptionalInt(_) => quote!(OptionalInt(#self_lvalue)),
|
||||||
|
Value::ItemStack(_) => quote!(Some(&#self_lvalue)),
|
||||||
|
_ => quote!(&#self_lvalue),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,279 +239,320 @@ impl Value {
|
||||||
type Entities = BTreeMap<String, Entity>;
|
type Entities = BTreeMap<String, Entity>;
|
||||||
|
|
||||||
pub fn build() -> anyhow::Result<TokenStream> {
|
pub fn build() -> anyhow::Result<TokenStream> {
|
||||||
let entities =
|
|
||||||
serde_json::from_str::<Entities>(include_str!("../../../extracted/entities.json"))?
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, mut v)| {
|
|
||||||
let strip = |s: String| {
|
|
||||||
if let Some(stripped) = s.strip_suffix("Entity") {
|
|
||||||
if !stripped.is_empty() {
|
|
||||||
return stripped.to_owned();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s
|
|
||||||
};
|
|
||||||
v.parent = v.parent.map(strip);
|
|
||||||
(strip(k), v)
|
|
||||||
})
|
|
||||||
.collect::<Entities>();
|
|
||||||
|
|
||||||
let entity_types =
|
let entity_types =
|
||||||
serde_json::from_str::<EntityData>(include_str!("../../../extracted/entity_data.json"))?
|
serde_json::from_str::<EntityData>(include_str!("../../../extracted/entity_data.json"))?
|
||||||
.types;
|
.types;
|
||||||
|
|
||||||
let concrete_entities = entities
|
let entities: Entities =
|
||||||
.clone()
|
serde_json::from_str::<Entities>(include_str!("../../../extracted/entities.json"))?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(_, v)| v.typ.is_some())
|
.map(|(entity_name, mut entity)| {
|
||||||
.collect::<Entities>();
|
let change_name = |mut name: String| {
|
||||||
|
if let Some(stripped) = name.strip_suffix("Entity") {
|
||||||
let entity_kind_variants = concrete_entities.iter().map(|(name, e)| {
|
if !stripped.is_empty() {
|
||||||
let name = ident(name);
|
name = stripped.into();
|
||||||
let id = entity_types[e.typ.as_ref().unwrap()] as isize;
|
|
||||||
quote! {
|
|
||||||
#name = #id,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let concrete_entity_names = concrete_entities.keys().map(ident).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let concrete_entity_structs = concrete_entities.keys().map(|struct_name| {
|
|
||||||
let fields = collect_all_fields(struct_name, &entities);
|
|
||||||
let struct_name = ident(struct_name);
|
|
||||||
|
|
||||||
let modified_flags_type =
|
|
||||||
ident("u".to_owned() + &fields.len().next_power_of_two().max(8).to_string());
|
|
||||||
|
|
||||||
let struct_fields = fields.iter().map(|&field| {
|
|
||||||
let name = ident(&field.name);
|
|
||||||
let typ = field.default_value.field_type();
|
|
||||||
quote! {
|
|
||||||
#name: #typ,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let field_initializers = fields.iter().map(|&field| {
|
|
||||||
let field_name = ident(&field.name);
|
|
||||||
let init = field.default_value.default_expr();
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#field_name: #init,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let getter_setters = fields.iter().map(|&field| {
|
|
||||||
let field_name = ident(&field.name);
|
|
||||||
let field_type = field.default_value.field_type();
|
|
||||||
let field_index = field.index;
|
|
||||||
|
|
||||||
if !field.bits.is_empty() {
|
|
||||||
field
|
|
||||||
.bits
|
|
||||||
.iter()
|
|
||||||
.map(|bit| {
|
|
||||||
let bit_name = ident(&bit.name);
|
|
||||||
let bit_index = bit.index;
|
|
||||||
let getter_name = ident(format!("get_{}", &bit.name));
|
|
||||||
let setter_name = ident(format!("set_{}", &bit.name));
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
pub fn #getter_name(&self) -> bool {
|
|
||||||
self.#field_name >> #bit_index as #field_type & 1 == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn #setter_name(&mut self, #bit_name: bool) {
|
|
||||||
if self.#getter_name() != #bit_name {
|
|
||||||
self.#field_name =
|
|
||||||
(self.#field_name & !(1 << #bit_index as #field_type))
|
|
||||||
| ((#bit_name as #field_type) << #bit_index);
|
|
||||||
|
|
||||||
self.__modified_flags |= 1 << #field_index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.collect::<TokenStream>()
|
|
||||||
|
name
|
||||||
|
};
|
||||||
|
|
||||||
|
entity.parent = entity.parent.map(change_name);
|
||||||
|
(change_name(entity_name), entity)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut entity_kind_consts = TokenStream::new();
|
||||||
|
let mut entity_kind_fmt_args = TokenStream::new();
|
||||||
|
let mut translation_key_arms = TokenStream::new();
|
||||||
|
let mut modules = TokenStream::new();
|
||||||
|
let mut systems = TokenStream::new();
|
||||||
|
let mut system_names = vec![];
|
||||||
|
|
||||||
|
for (entity_name, entity) in entities.clone() {
|
||||||
|
let entity_name_ident = ident(&entity_name);
|
||||||
|
let shouty_entity_name = entity_name.to_shouty_snake_case();
|
||||||
|
let shouty_entity_name_ident = ident(&shouty_entity_name);
|
||||||
|
let snake_entity_name = entity_name.to_snake_case();
|
||||||
|
let snake_entity_name_ident = ident(&snake_entity_name);
|
||||||
|
|
||||||
|
let mut module_body = TokenStream::new();
|
||||||
|
|
||||||
|
if let Some(parent_name) = entity.parent {
|
||||||
|
let snake_parent_name = parent_name.to_snake_case();
|
||||||
|
|
||||||
|
let module_doc =
|
||||||
|
format!("Parent class: [`{snake_parent_name}`][super::{snake_parent_name}].");
|
||||||
|
|
||||||
|
module_body.extend([quote! {
|
||||||
|
#![doc = #module_doc]
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this a concrete entity type?
|
||||||
|
if let Some(entity_type) = entity.typ {
|
||||||
|
let entity_type_id = entity_types[&entity_type];
|
||||||
|
|
||||||
|
entity_kind_consts.extend([quote! {
|
||||||
|
pub const #shouty_entity_name_ident: EntityKind = EntityKind(#entity_type_id);
|
||||||
|
}]);
|
||||||
|
|
||||||
|
entity_kind_fmt_args.extend([quote! {
|
||||||
|
EntityKind::#shouty_entity_name_ident => write!(f, "{} ({})", #entity_type_id, #shouty_entity_name),
|
||||||
|
}]);
|
||||||
|
|
||||||
|
let translation_key_expr = if let Some(key) = entity.translation_key {
|
||||||
|
quote!(Some(#key))
|
||||||
} else {
|
} else {
|
||||||
let getter_name = ident(format!("get_{}", &field.name));
|
quote!(None)
|
||||||
let setter_name = ident(format!("set_{}", &field.name));
|
};
|
||||||
let getter_return_type = field.default_value.getter_return_type();
|
|
||||||
let getter_return_expr = field.default_value.getter_return_expr(&field_name);
|
|
||||||
|
|
||||||
quote! {
|
translation_key_arms.extend([quote! {
|
||||||
pub fn #getter_name(&self) -> #getter_return_type {
|
EntityKind::#shouty_entity_name_ident => #translation_key_expr,
|
||||||
#getter_return_expr
|
}]);
|
||||||
|
|
||||||
|
// Create bundle type.
|
||||||
|
let mut bundle_fields = TokenStream::new();
|
||||||
|
let mut bundle_init_fields = TokenStream::new();
|
||||||
|
|
||||||
|
for marker_or_field in collect_bundle_fields(&entity_name, &entities) {
|
||||||
|
match marker_or_field {
|
||||||
|
MarkerOrField::Marker { entity_name } => {
|
||||||
|
let snake_entity_name_ident = ident(entity_name.to_snake_case());
|
||||||
|
let pascal_entity_name_ident = ident(entity_name.to_pascal_case());
|
||||||
|
|
||||||
|
bundle_fields.extend([quote! {
|
||||||
|
pub #snake_entity_name_ident: super::#snake_entity_name_ident::#pascal_entity_name_ident,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
bundle_init_fields.extend([quote! {
|
||||||
|
#snake_entity_name_ident: Default::default(),
|
||||||
|
}]);
|
||||||
}
|
}
|
||||||
|
MarkerOrField::Field { entity_name, field } => {
|
||||||
|
let snake_field_name = field.name.to_snake_case();
|
||||||
|
let pascal_field_name = field.name.to_pascal_case();
|
||||||
|
let pascal_field_name_ident = ident(&pascal_field_name);
|
||||||
|
let snake_entity_name = entity_name.to_snake_case();
|
||||||
|
let snake_entity_name_ident = ident(&snake_entity_name);
|
||||||
|
|
||||||
pub fn #setter_name(&mut self, #field_name: impl Into<#field_type>) {
|
let field_name_ident =
|
||||||
let #field_name = #field_name.into();
|
ident(format!("{snake_entity_name}_{snake_field_name}"));
|
||||||
if self.#field_name != #field_name {
|
|
||||||
self.__modified_flags |= 1 << #field_index as #modified_flags_type;
|
bundle_fields.extend([quote! {
|
||||||
self.#field_name = #field_name;
|
pub #field_name_ident: super::#snake_entity_name_ident::#pascal_field_name_ident,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
bundle_init_fields.extend([quote! {
|
||||||
|
#field_name_ident: Default::default(),
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle_fields.extend([quote! {
|
||||||
|
pub kind: super::EntityKind,
|
||||||
|
pub id: super::EntityId,
|
||||||
|
pub uuid: super::UniqueId,
|
||||||
|
pub location: super::Location,
|
||||||
|
pub old_location: super::OldLocation,
|
||||||
|
pub position: super::Position,
|
||||||
|
pub old_position: super::OldPosition,
|
||||||
|
pub look: super::Look,
|
||||||
|
pub head_yaw: super::HeadYaw,
|
||||||
|
pub on_ground: super::OnGround,
|
||||||
|
pub velocity: super::Velocity,
|
||||||
|
pub statuses: super::EntityStatuses,
|
||||||
|
pub animations: super::EntityAnimations,
|
||||||
|
pub object_data: super::ObjectData,
|
||||||
|
pub tracked_data: super::TrackedData,
|
||||||
|
pub packet_byte_range: super::PacketByteRange,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
bundle_init_fields.extend([quote! {
|
||||||
|
kind: super::EntityKind::#shouty_entity_name_ident,
|
||||||
|
id: Default::default(),
|
||||||
|
uuid: Default::default(),
|
||||||
|
location: Default::default(),
|
||||||
|
old_location: Default::default(),
|
||||||
|
position: Default::default(),
|
||||||
|
old_position: Default::default(),
|
||||||
|
look: Default::default(),
|
||||||
|
head_yaw: Default::default(),
|
||||||
|
on_ground: Default::default(),
|
||||||
|
velocity: Default::default(),
|
||||||
|
statuses: Default::default(),
|
||||||
|
animations: Default::default(),
|
||||||
|
object_data: Default::default(),
|
||||||
|
tracked_data: Default::default(),
|
||||||
|
packet_byte_range: Default::default(),
|
||||||
|
}]);
|
||||||
|
|
||||||
|
let bundle_name_ident = ident(format!("{entity_name}Bundle"));
|
||||||
|
let bundle_doc =
|
||||||
|
format!("The bundle of components for spawning `{snake_entity_name}` entities.");
|
||||||
|
|
||||||
|
module_body.extend([quote! {
|
||||||
|
#[doc = #bundle_doc]
|
||||||
|
#[derive(bevy_ecs::bundle::Bundle, Debug)]
|
||||||
|
pub struct #bundle_name_ident {
|
||||||
|
#bundle_fields
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for #bundle_name_ident {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
#bundle_init_fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}]);
|
||||||
});
|
}
|
||||||
|
|
||||||
let initial_tracked_data_stmts = fields.iter().map(|&field| {
|
for field in &entity.fields {
|
||||||
let field_name = ident(&field.name);
|
let pascal_field_name_ident = ident(field.name.to_pascal_case());
|
||||||
let field_index = field.index;
|
let snake_field_name = field.name.to_snake_case();
|
||||||
|
let inner_type = field.default_value.field_type();
|
||||||
let default_expr = field.default_value.default_expr();
|
let default_expr = field.default_value.default_expr();
|
||||||
let type_id = field.default_value.type_id();
|
|
||||||
let encodable = field.default_value.encodable_expr(quote!(self.#field_name));
|
|
||||||
|
|
||||||
quote! {
|
module_body.extend([quote! {
|
||||||
if self.#field_name != (#default_expr) {
|
#[derive(bevy_ecs::component::Component, PartialEq, Clone, Debug)]
|
||||||
data.push(#field_index);
|
pub struct #pascal_field_name_ident(pub #inner_type);
|
||||||
VarInt(#type_id).encode(&mut *data).unwrap();
|
|
||||||
#encodable.encode(&mut *data).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let updated_tracked_data_stmts = fields.iter().map(|&field| {
|
#[allow(clippy::derivable_impls)]
|
||||||
let field_name = ident(&field.name);
|
impl Default for #pascal_field_name_ident {
|
||||||
let field_index = field.index;
|
fn default() -> Self {
|
||||||
let type_id = field.default_value.type_id();
|
Self(#default_expr)
|
||||||
let encodable = field.default_value.encodable_expr(quote!(self.#field_name));
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
if (self.__modified_flags >> #field_index as #modified_flags_type) & 1 == 1 {
|
|
||||||
data.push(#field_index);
|
|
||||||
VarInt(#type_id).encode(&mut *data).unwrap();
|
|
||||||
#encodable.encode(&mut *data).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
pub struct #struct_name {
|
|
||||||
/// Contains a set bit for every modified field.
|
|
||||||
__modified_flags: #modified_flags_type,
|
|
||||||
#(#struct_fields)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl #struct_name {
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
__modified_flags: 0,
|
|
||||||
#(#field_initializers)*
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
pub(crate) fn initial_tracked_data(&self, data: &mut Vec<u8>) {
|
let system_name_ident = ident(format!("update_{snake_entity_name}_{snake_field_name}"));
|
||||||
#(#initial_tracked_data_stmts)*
|
let component_path = quote!(#snake_entity_name_ident::#pascal_field_name_ident);
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn updated_tracked_data(&self, data: &mut Vec<u8>) {
|
system_names.push(quote!(#system_name_ident));
|
||||||
if self.__modified_flags != 0 {
|
|
||||||
#(#updated_tracked_data_stmts)*
|
let data_index = field.index;
|
||||||
|
let data_type = field.default_value.type_id();
|
||||||
|
let encodable_expr = field.default_value.encodable_expr(quote!(value.0));
|
||||||
|
|
||||||
|
systems.extend([quote! {
|
||||||
|
#[allow(clippy::needless_borrow)]
|
||||||
|
fn #system_name_ident(
|
||||||
|
mut query: Query<(&#component_path, &mut TrackedData), Changed<#component_path>>
|
||||||
|
) {
|
||||||
|
for (value, mut tracked_data) in &mut query {
|
||||||
|
if *value == Default::default() {
|
||||||
|
tracked_data.remove_init_value(#data_index);
|
||||||
|
} else {
|
||||||
|
tracked_data.insert_init_value(#data_index, #data_type, #encodable_expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tracked_data.is_added() {
|
||||||
|
tracked_data.append_update_value(#data_index, #data_type, #encodable_expr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn clear_modifications(&mut self) {
|
let marker_doc = format!("Marker component for `{snake_entity_name}` entities.");
|
||||||
self.__modified_flags = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#(#getter_setters)*
|
module_body.extend([quote! {
|
||||||
|
#[doc = #marker_doc]
|
||||||
|
#[derive(bevy_ecs::component::Component, Copy, Clone, Default, Debug)]
|
||||||
|
pub struct #entity_name_ident;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
modules.extend([quote! {
|
||||||
|
#[allow(clippy::module_inception)]
|
||||||
|
pub mod #snake_entity_name_ident {
|
||||||
|
#module_body
|
||||||
}
|
}
|
||||||
}
|
}]);
|
||||||
});
|
}
|
||||||
|
|
||||||
let translation_key_arms = concrete_entities.iter().map(|(k, v)| {
|
|
||||||
let name = ident(k);
|
|
||||||
let key = v
|
|
||||||
.translation_key
|
|
||||||
.as_ref()
|
|
||||||
.expect("translation key should be present for concrete entity");
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
Self::#name => #key,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
/// Contains a variant for each concrete entity type.
|
#modules
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub enum EntityKind {
|
/// Identifies the type of an entity.
|
||||||
#(#entity_kind_variants)*
|
/// As a component, the entity kind should not be modified.
|
||||||
}
|
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
pub struct EntityKind(i32);
|
||||||
|
|
||||||
impl EntityKind {
|
impl EntityKind {
|
||||||
pub fn translation_key(self) -> &'static str {
|
#entity_kind_consts
|
||||||
|
|
||||||
|
pub const fn new(inner: i32) -> Self {
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn get(self) -> i32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn translation_key(self) -> Option<&'static str> {
|
||||||
match self {
|
match self {
|
||||||
#(#translation_key_arms)*
|
#translation_key_arms
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum TrackedData {
|
impl std::fmt::Debug for EntityKind {
|
||||||
#(#concrete_entity_names(#concrete_entity_names),)*
|
#[allow(clippy::write_literal)]
|
||||||
}
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
impl TrackedData {
|
#entity_kind_fmt_args
|
||||||
pub(super) fn new(kind: EntityKind) -> Self {
|
EntityKind(other) => write!(f, "{other}"),
|
||||||
match kind {
|
|
||||||
#(EntityKind::#concrete_entity_names => Self::#concrete_entity_names(#concrete_entity_names::new()),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn kind(&self) -> EntityKind {
|
|
||||||
match self {
|
|
||||||
#(Self::#concrete_entity_names(_) => EntityKind::#concrete_entity_names,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn write_initial_tracked_data(&self, buf: &mut Vec<u8>) {
|
|
||||||
buf.clear();
|
|
||||||
|
|
||||||
match self {
|
|
||||||
#(Self::#concrete_entity_names(e) => e.initial_tracked_data(buf),)*
|
|
||||||
}
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
buf.push(0xff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn write_updated_tracked_data(&self, buf: &mut Vec<u8>) {
|
|
||||||
buf.clear();
|
|
||||||
|
|
||||||
match self {
|
|
||||||
#(Self::#concrete_entity_names(e) => e.updated_tracked_data(buf),)*
|
|
||||||
}
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
buf.push(0xff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn clear_modifications(&mut self) {
|
|
||||||
match self {
|
|
||||||
#(Self::#concrete_entity_names(e) => e.clear_modifications(),)*
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#(#concrete_entity_structs)*
|
fn add_tracked_data_systems(app: &mut App) {
|
||||||
|
#systems
|
||||||
|
|
||||||
|
#(
|
||||||
|
app.add_system(
|
||||||
|
#system_names.before(WriteUpdatePacketsToInstancesSet).in_base_set(CoreSet::PostUpdate)
|
||||||
|
);
|
||||||
|
)*
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_all_fields<'a>(entity_name: &str, entities: &'a Entities) -> Vec<&'a Field> {
|
enum MarkerOrField<'a> {
|
||||||
fn rec<'a>(entity_name: &str, entities: &'a Entities, fields: &mut Vec<&'a Field>) {
|
Marker {
|
||||||
|
entity_name: &'a str,
|
||||||
|
},
|
||||||
|
Field {
|
||||||
|
entity_name: &'a str,
|
||||||
|
field: &'a Field,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_bundle_fields<'a>(
|
||||||
|
mut entity_name: &'a str,
|
||||||
|
entities: &'a Entities,
|
||||||
|
) -> Vec<MarkerOrField<'a>> {
|
||||||
|
let mut res = vec![];
|
||||||
|
|
||||||
|
loop {
|
||||||
let e = &entities[entity_name];
|
let e = &entities[entity_name];
|
||||||
fields.extend(&e.fields);
|
|
||||||
|
res.push(MarkerOrField::Marker { entity_name });
|
||||||
|
res.extend(
|
||||||
|
e.fields
|
||||||
|
.iter()
|
||||||
|
.map(|field| MarkerOrField::Field { entity_name, field }),
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(parent) = &e.parent {
|
if let Some(parent) = &e.parent {
|
||||||
rec(parent, entities, fields);
|
entity_name = parent;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fields = vec![];
|
res
|
||||||
rec(entity_name, entities, &mut fields);
|
|
||||||
|
|
||||||
fields.sort_by_key(|f| f.index);
|
|
||||||
|
|
||||||
fields
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::client::event::default_event_handler;
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::instance::{Chunk, Instance};
|
use valence::instance::{Chunk, Instance};
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
|
@ -67,26 +67,18 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, unique_id, mut pos, mut loc, mut game_mode) in &mut clients {
|
for (entity, uuid, mut game_mode) in &mut clients {
|
||||||
pos.0 = [0.0, SPAWN_Y as f64 + 1.0, 0.0].into();
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
|
|
||||||
commands
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.entity(entity)
|
location: Location(instances.single()),
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, unique_id.0));
|
position: Position::new([0.0, SPAWN_Y as f64 + 1.0, 0.0]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::client::event::default_event_handler;
|
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
const SPAWN_Y: i32 = 0;
|
const SPAWN_Y: i32 = 0;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::event::{ChatMessage, PlayerInteractBlock};
|
||||||
use valence::client::event::{default_event_handler, ChatMessage, PlayerInteractBlock};
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::nbt::{compound, List};
|
use valence::nbt::{compound, List};
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
use valence::protocol::types::Hand;
|
use valence::protocol::types::Hand;
|
||||||
|
@ -63,29 +64,20 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Position,
|
|
||||||
&mut Look,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut pos, mut look, mut loc, mut game_mode) in &mut clients {
|
for (entity, uuid, mut game_mode) in &mut clients {
|
||||||
pos.set([1.5, FLOOR_Y as f64 + 1.0, 1.5]);
|
|
||||||
look.yaw = -90.0;
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
|
|
||||||
commands
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.entity(entity)
|
location: Location(instances.single()),
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
position: Position::new([1.5, FLOOR_Y as f64 + 1.0, 1.5]),
|
||||||
|
look: Look::new(-90.0, 0.0),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::event::{PlayerInteractBlock, StartDigging, StartSneaking, StopDestroyBlock};
|
||||||
use valence::client::event::{
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
default_event_handler, PlayerInteractBlock, StartDigging, StartSneaking, StopDestroyBlock,
|
use valence::entity::player::PlayerBundle;
|
||||||
};
|
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
use valence::protocol::types::Hand;
|
use valence::protocol::types::Hand;
|
||||||
|
|
||||||
|
@ -50,28 +49,20 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut Client, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Client,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut client, mut pos, mut loc, mut game_mode) in &mut clients {
|
for (entity, uuid, mut client, mut game_mode) in &mut clients {
|
||||||
pos.0 = [0.0, SPAWN_Y as f64 + 1.0, 0.0].into();
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
client.send_message("Welcome to Valence! Build something cool.".italic());
|
client.send_message("Welcome to Valence! Build something cool.".italic());
|
||||||
commands
|
|
||||||
.entity(entity)
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
location: Location(instances.single()),
|
||||||
|
position: Position::new([0.0, SPAWN_Y as f64 + 1.0, 0.0]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +71,7 @@ fn toggle_gamemode_on_sneak(
|
||||||
mut events: EventReader<StartSneaking>,
|
mut events: EventReader<StartSneaking>,
|
||||||
) {
|
) {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
let Ok(mut mode) = clients.get_component_mut::<GameMode>(event.client) else {
|
let Ok(mut mode) = clients.get_mut(event.client) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
*mode = match *mode {
|
*mode = match *mode {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use bevy_app::App;
|
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::event::{ChatMessage, CommandExecution};
|
||||||
use valence::client::event::{default_event_handler, ChatMessage, CommandExecution};
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
const SPAWN_Y: i32 = 64;
|
const SPAWN_Y: i32 = 64;
|
||||||
|
@ -12,7 +12,7 @@ pub fn main() {
|
||||||
tracing_subscriber::fmt().init();
|
tracing_subscriber::fmt().init();
|
||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugin(ServerPlugin::new(()).with_connection_mode(ConnectionMode::Offline))
|
.add_plugin(ServerPlugin::new(()))
|
||||||
.add_startup_system(setup)
|
.add_startup_system(setup)
|
||||||
.add_system(init_clients)
|
.add_system(init_clients)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
|
@ -47,29 +47,20 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut Client, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Client,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut client, mut pos, mut loc, mut game_mode) in &mut clients {
|
for (entity, uuid, mut client, mut game_mode) in &mut clients {
|
||||||
pos.set([0.0, SPAWN_Y as f64 + 1.0, 0.0]);
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Adventure;
|
*game_mode = GameMode::Adventure;
|
||||||
client.send_message("Welcome to Valence! Talk about something.".italic());
|
client.send_message("Welcome to Valence! Talk about something.".italic());
|
||||||
|
|
||||||
commands
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.entity(entity)
|
location: Location(instances.single()),
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
position: Position::new([0.0, SPAWN_Y as f64 + 1.0, 0.0]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::event::{PlayerInteractBlock, StartSneaking};
|
||||||
use valence::client::event::{default_event_handler, PlayerInteractBlock, StartSneaking};
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
const SPAWN_Y: i32 = 64;
|
const SPAWN_Y: i32 = 64;
|
||||||
|
@ -50,26 +51,19 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut pos, mut loc, mut game_mode) in &mut clients {
|
for (entity, uuid, mut game_mode) in &mut clients {
|
||||||
pos.0 = [0.5, SPAWN_Y as f64 + 1.0, 0.5].into();
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
commands
|
|
||||||
.entity(entity)
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
location: Location(instances.single()),
|
||||||
|
position: Position::new([0.5, SPAWN_Y as f64 + 1.0, 0.5]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +72,7 @@ fn toggle_gamemode_on_sneak(
|
||||||
mut events: EventReader<StartSneaking>,
|
mut events: EventReader<StartSneaking>,
|
||||||
) {
|
) {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
let Ok(mut mode) = clients.get_component_mut::<GameMode>(event.client) else {
|
let Ok(mut mode) = clients.get_mut(event.client) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
*mode = match *mode {
|
*mode = match *mode {
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
use bevy_ecs::query::WorldQuery;
|
use bevy_ecs::query::WorldQuery;
|
||||||
use glam::Vec3Swizzles;
|
use glam::Vec3Swizzles;
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::event::{PlayerInteract, StartSprinting, StopSprinting};
|
||||||
use valence::client::event::{
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
default_event_handler, PlayerInteract, StartSprinting, StopSprinting,
|
use valence::entity::player::PlayerBundle;
|
||||||
};
|
use valence::entity::EntityStatuses;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
const SPAWN_Y: i32 = 64;
|
const SPAWN_Y: i32 = 64;
|
||||||
|
@ -67,20 +67,22 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<(Entity, &UniqueId, &mut Position, &mut Location), Added<Client>>,
|
mut clients: Query<(Entity, &UniqueId), Added<Client>>,
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut pos, mut loc) in &mut clients {
|
for (entity, uuid) in &mut clients {
|
||||||
pos.set([0.0, SPAWN_Y as f64, 0.0]);
|
|
||||||
loc.0 = instances.single();
|
|
||||||
|
|
||||||
commands.entity(entity).insert((
|
commands.entity(entity).insert((
|
||||||
CombatState {
|
CombatState {
|
||||||
last_attacked_tick: 0,
|
last_attacked_tick: 0,
|
||||||
has_bonus_knockback: false,
|
has_bonus_knockback: false,
|
||||||
},
|
},
|
||||||
McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0),
|
PlayerBundle {
|
||||||
|
location: Location(instances.single()),
|
||||||
|
position: Position::new([0.5, SPAWN_Y as f64, 0.5]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,11 +93,11 @@ struct CombatQuery {
|
||||||
client: &'static mut Client,
|
client: &'static mut Client,
|
||||||
pos: &'static Position,
|
pos: &'static Position,
|
||||||
state: &'static mut CombatState,
|
state: &'static mut CombatState,
|
||||||
entity: &'static mut McEntity,
|
statuses: &'static mut EntityStatuses,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_combat_events(
|
fn handle_combat_events(
|
||||||
manager: Res<McEntityManager>,
|
manager: Res<EntityManager>,
|
||||||
server: Res<Server>,
|
server: Res<Server>,
|
||||||
mut clients: Query<CombatQuery>,
|
mut clients: Query<CombatQuery>,
|
||||||
mut start_sprinting: EventReader<StartSprinting>,
|
mut start_sprinting: EventReader<StartSprinting>,
|
||||||
|
@ -120,7 +122,7 @@ fn handle_combat_events(
|
||||||
..
|
..
|
||||||
} in interact_with_entity.iter()
|
} in interact_with_entity.iter()
|
||||||
{
|
{
|
||||||
let Some(victim_client) = manager.get_with_protocol_id(entity_id) else {
|
let Some(victim_client) = manager.get_with_id(entity_id) else {
|
||||||
// Attacked entity doesn't exist.
|
// Attacked entity doesn't exist.
|
||||||
continue
|
continue
|
||||||
};
|
};
|
||||||
|
@ -162,9 +164,10 @@ fn handle_combat_events(
|
||||||
victim
|
victim
|
||||||
.client
|
.client
|
||||||
.trigger_status(EntityStatus::DamageFromGenericSource);
|
.trigger_status(EntityStatus::DamageFromGenericSource);
|
||||||
|
|
||||||
victim
|
victim
|
||||||
.entity
|
.statuses
|
||||||
.trigger_status(EntityStatus::DamageFromGenericSource);
|
.trigger(EntityStatus::DamageFromGenericSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::event::{StartDigging, StartSneaking};
|
||||||
use valence::client::event::{default_event_handler, StartDigging, StartSneaking};
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
const BOARD_MIN_X: i32 = -30;
|
const BOARD_MIN_X: i32 = -30;
|
||||||
|
@ -67,23 +68,11 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut Client, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Client,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut client, mut pos, mut loc, mut game_mode) in &mut clients {
|
for (entity, uuid, mut client, mut game_mode) in &mut clients {
|
||||||
pos.0 = SPAWN_POS;
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Survival;
|
*game_mode = GameMode::Survival;
|
||||||
|
|
||||||
client.send_message("Welcome to Conway's game of life in Minecraft!".italic());
|
client.send_message("Welcome to Conway's game of life in Minecraft!".italic());
|
||||||
|
@ -92,9 +81,13 @@ fn init_clients(
|
||||||
life."
|
life."
|
||||||
.italic(),
|
.italic(),
|
||||||
);
|
);
|
||||||
commands
|
|
||||||
.entity(entity)
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
location: Location(instances.single()),
|
||||||
|
position: Position(SPAWN_POS),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
use std::f64::consts::TAU;
|
use std::f64::consts::TAU;
|
||||||
|
|
||||||
use glam::{DQuat, EulerRot};
|
use glam::{DQuat, EulerRot};
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::client::event::default_event_handler;
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
use valence::util::to_yaw_and_pitch;
|
|
||||||
|
type SpherePartBundle = valence::entity::cow::CowBundle;
|
||||||
|
|
||||||
const SPHERE_CENTER: DVec3 = DVec3::new(0.5, SPAWN_POS.y as f64 + 2.0, 0.5);
|
const SPHERE_CENTER: DVec3 = DVec3::new(0.5, SPAWN_POS.y as f64 + 2.0, 0.5);
|
||||||
const SPHERE_AMOUNT: usize = 200;
|
const SPHERE_AMOUNT: usize = 200;
|
||||||
const SPHERE_KIND: EntityKind = EntityKind::Cow;
|
|
||||||
const SPHERE_MIN_RADIUS: f64 = 6.0;
|
const SPHERE_MIN_RADIUS: f64 = 6.0;
|
||||||
const SPHERE_MAX_RADIUS: f64 = 12.0;
|
const SPHERE_MAX_RADIUS: f64 = 12.0;
|
||||||
const SPHERE_FREQ: f64 = 0.5;
|
const SPHERE_FREQ: f64 = 0.5;
|
||||||
|
@ -48,41 +48,42 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
|
|
||||||
let instance_id = commands.spawn(instance).id();
|
let instance_id = commands.spawn(instance).id();
|
||||||
|
|
||||||
commands.spawn_batch(
|
commands.spawn_batch([0; SPHERE_AMOUNT].map(|_| {
|
||||||
[0; SPHERE_AMOUNT].map(|_| (McEntity::new(SPHERE_KIND, instance_id), SpherePart)),
|
(
|
||||||
);
|
SpherePartBundle {
|
||||||
|
location: Location(instance_id),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
SpherePart,
|
||||||
|
)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut pos, mut loc, mut game_mode) in &mut clients {
|
for (entity, uuid, mut game_mode) in &mut clients {
|
||||||
pos.set([
|
|
||||||
SPAWN_POS.x as f64 + 0.5,
|
|
||||||
SPAWN_POS.y as f64 + 1.0,
|
|
||||||
SPAWN_POS.z as f64 + 0.5,
|
|
||||||
]);
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
|
|
||||||
commands
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.entity(entity)
|
location: Location(instances.single()),
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
position: Position::new([
|
||||||
|
SPAWN_POS.x as f64 + 0.5,
|
||||||
|
SPAWN_POS.y as f64 + 1.0,
|
||||||
|
SPAWN_POS.z as f64 + 0.5,
|
||||||
|
]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_sphere(server: Res<Server>, mut parts: Query<&mut McEntity, With<SpherePart>>) {
|
fn update_sphere(
|
||||||
|
server: Res<Server>,
|
||||||
|
mut parts: Query<(&mut Position, &mut Look, &mut HeadYaw), With<SpherePart>>,
|
||||||
|
) {
|
||||||
let time = server.current_tick() as f64 / server.tps() as f64;
|
let time = server.current_tick() as f64 / server.tps() as f64;
|
||||||
|
|
||||||
let rot_angles = DVec3::new(0.2, 0.4, 0.6) * SPHERE_FREQ * time * TAU % TAU;
|
let rot_angles = DVec3::new(0.2, 0.4, 0.6) * SPHERE_FREQ * time * TAU % TAU;
|
||||||
|
@ -94,16 +95,16 @@ fn update_sphere(server: Res<Server>, mut parts: Query<&mut McEntity, With<Spher
|
||||||
((time * SPHERE_FREQ * TAU).sin() + 1.0) / 2.0,
|
((time * SPHERE_FREQ * TAU).sin() + 1.0) / 2.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (mut entity, p) in parts.iter_mut().zip(fibonacci_spiral(SPHERE_AMOUNT)) {
|
for ((mut pos, mut look, mut head_yaw), p) in
|
||||||
|
parts.iter_mut().zip(fibonacci_spiral(SPHERE_AMOUNT))
|
||||||
|
{
|
||||||
debug_assert!(p.is_normalized());
|
debug_assert!(p.is_normalized());
|
||||||
|
|
||||||
let dir = rot * p;
|
let dir = rot * p;
|
||||||
let (yaw, pitch) = to_yaw_and_pitch(dir.as_vec3());
|
|
||||||
|
|
||||||
entity.set_position(SPHERE_CENTER + dir * radius);
|
pos.0 = SPHERE_CENTER + dir * radius;
|
||||||
entity.set_yaw(yaw);
|
look.set_vec(dir.as_vec3());
|
||||||
entity.set_head_yaw(yaw);
|
head_yaw.0 = look.yaw;
|
||||||
entity.set_pitch(pitch);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::event::{PerformRespawn, StartSneaking};
|
||||||
use valence::client::event::{default_event_handler, PerformRespawn, StartSneaking};
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
const SPAWN_Y: i32 = 64;
|
const SPAWN_Y: i32 = 64;
|
||||||
|
@ -42,31 +43,22 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut Client, &mut HasRespawnScreen), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Client,
|
|
||||||
&mut Position,
|
|
||||||
&mut HasRespawnScreen,
|
|
||||||
&mut Location,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut client, mut pos, mut has_respawn_screen, mut loc) in &mut clients {
|
for (entity, uuid, mut client, mut has_respawn_screen) in &mut clients {
|
||||||
pos.set([0.0, SPAWN_Y as f64 + 1.0, 0.0]);
|
|
||||||
has_respawn_screen.0 = true;
|
has_respawn_screen.0 = true;
|
||||||
loc.0 = instances.iter().next().unwrap();
|
|
||||||
client.send_message(
|
client.send_message(
|
||||||
"Welcome to Valence! Sneak to die in the game (but not in real life).".italic(),
|
"Welcome to Valence! Sneak to die in the game (but not in real life).".italic(),
|
||||||
);
|
);
|
||||||
|
|
||||||
commands
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.entity(entity)
|
location: Location(instances.iter().next().unwrap()),
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
position: Position::new([0.0, SPAWN_Y as f64 + 1.0, 0.0]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::event::CommandExecution;
|
||||||
use valence::client::event::{default_event_handler, CommandExecution};
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
const SPAWN_Y: i32 = 64;
|
const SPAWN_Y: i32 = 64;
|
||||||
|
@ -39,30 +40,23 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<
|
||||||
(
|
(Entity, &UniqueId, &mut Client, &mut GameMode, &mut OpLevel),
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Client,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
&mut OpLevel,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
Added<Client>,
|
||||||
>,
|
>,
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut client, mut pos, mut loc, mut game_mode, mut op_level) in &mut clients {
|
for (entity, uuid, mut client, mut game_mode, mut op_level) in &mut clients {
|
||||||
pos.set([0.0, SPAWN_Y as f64 + 1.0, 0.0]);
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
op_level.set(2); // required to use F3+F4, eg /gamemode
|
op_level.set(2); // required to use F3+F4, eg /gamemode
|
||||||
client.send_message("Welcome to Valence! Use F3+F4 to change gamemode.".italic());
|
client.send_message("Welcome to Valence! Use F3+F4 to change gamemode.".italic());
|
||||||
|
|
||||||
commands
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.entity(entity)
|
location: Location(instances.single()),
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
position: Position::new([0.5, SPAWN_Y as f64 + 1.0, 0.5]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::client::event::default_event_handler;
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
use valence::protocol::packet::s2c::play::TitleFadeS2c;
|
use valence::protocol::packet::s2c::play::TitleFadeS2c;
|
||||||
use valence::protocol::sound::Sound;
|
use valence::protocol::sound::Sound;
|
||||||
|
@ -52,23 +52,12 @@ struct GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &mut Client, &UniqueId, &mut IsFlat, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&mut Client,
|
|
||||||
&UniqueId,
|
|
||||||
&mut IsFlat,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
server: Res<Server>,
|
server: Res<Server>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, mut client, uuid, mut is_flat, mut loc, mut game_mode) in clients.iter_mut() {
|
for (entity, mut client, uuid, mut is_flat, mut game_mode) in clients.iter_mut() {
|
||||||
is_flat.0 = true;
|
is_flat.0 = true;
|
||||||
loc.0 = entity;
|
|
||||||
*game_mode = GameMode::Adventure;
|
*game_mode = GameMode::Adventure;
|
||||||
client.send_message("Welcome to epic infinite parkour game!".italic());
|
client.send_message("Welcome to epic infinite parkour game!".italic());
|
||||||
|
|
||||||
|
@ -82,9 +71,13 @@ fn init_clients(
|
||||||
|
|
||||||
let instance = server.new_instance(DimensionId::default());
|
let instance = server.new_instance(DimensionId::default());
|
||||||
|
|
||||||
let mcentity = McEntity::with_uuid(EntityKind::Player, entity, uuid.0);
|
let player = PlayerBundle {
|
||||||
|
location: Location(entity),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
commands.entity(entity).insert((state, instance, mcentity));
|
commands.entity(entity).insert((state, instance, player));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::client::event::default_event_handler;
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
const SPAWN_Y: i32 = 64;
|
const SPAWN_Y: i32 = 64;
|
||||||
|
@ -42,27 +42,19 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut pos, mut loc, mut game_mode) in &mut clients {
|
for (entity, uuid, mut game_mode) in &mut clients {
|
||||||
pos.set([0.5, SPAWN_Y as f64 + 1.0, 0.5]);
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
|
|
||||||
commands
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.entity(entity)
|
location: Location(instances.single()),
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
position: Position::new([0.5, SPAWN_Y as f64 + 1.0, 0.5]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use valence::client::event::default_event_handler;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::player_list::Entry;
|
use valence::player_list::Entry;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::event::{PlayerInteract, ResourcePackStatus, ResourcePackStatusChange};
|
||||||
use valence::client::event::{
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
default_event_handler, PlayerInteract, ResourcePackStatus, ResourcePackStatusChange,
|
use valence::entity::player::PlayerBundle;
|
||||||
};
|
use valence::entity::sheep::SheepBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
use valence::protocol::packet::c2s::play::player_interact::Interaction;
|
use valence::protocol::packet::c2s::play::player_interact::Interaction;
|
||||||
|
|
||||||
|
@ -46,38 +46,31 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
|
|
||||||
let instance_ent = commands.spawn(instance).id();
|
let instance_ent = commands.spawn(instance).id();
|
||||||
|
|
||||||
let mut sheep = McEntity::new(EntityKind::Sheep, instance_ent);
|
commands.spawn(SheepBundle {
|
||||||
sheep.set_position([0.0, SPAWN_Y as f64 + 1.0, 2.0]);
|
location: Location(instance_ent),
|
||||||
sheep.set_yaw(180.0);
|
position: Position::new([0.0, SPAWN_Y as f64 + 1.0, 2.0]),
|
||||||
sheep.set_head_yaw(180.0);
|
look: Look::new(180.0, 0.0),
|
||||||
commands.spawn(sheep);
|
head_yaw: HeadYaw(180.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut Client, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut Client,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut client, mut pos, mut loc, mut game_mode) in &mut clients {
|
for (entity, uuid, mut client, mut game_mode) in &mut clients {
|
||||||
pos.set([0.0, SPAWN_Y as f64 + 1.0, 0.0]);
|
|
||||||
loc.0 = instances.single();
|
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
|
|
||||||
client.send_message("Hit the sheep to prompt for the resource pack.".italic());
|
client.send_message("Hit the sheep to prompt for the resource pack.".italic());
|
||||||
|
|
||||||
commands
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.entity(entity)
|
location: Location(instances.single()),
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
position: Position::new([0.0, SPAWN_Y as f64 + 1.0, 0.0]),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ use std::time::SystemTime;
|
||||||
use flume::{Receiver, Sender};
|
use flume::{Receiver, Sender};
|
||||||
use noise::{NoiseFn, SuperSimplex};
|
use noise::{NoiseFn, SuperSimplex};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::client::event::default_event_handler;
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
|
|
||||||
const SPAWN_POS: DVec3 = DVec3::new(0.0, 200.0, 0.0);
|
const SPAWN_POS: DVec3 = DVec3::new(0.0, 200.0, 0.0);
|
||||||
|
@ -108,31 +108,20 @@ fn setup(mut commands: Commands, server: Res<Server>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &UniqueId, &mut IsFlat, &mut GameMode), Added<Client>>,
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&UniqueId,
|
|
||||||
&mut IsFlat,
|
|
||||||
&mut GameMode,
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, mut is_flat, mut game_mode, mut pos, mut loc) in &mut clients {
|
for (entity, uuid, mut is_flat, mut game_mode) in &mut clients {
|
||||||
let instance = instances.single();
|
|
||||||
|
|
||||||
is_flat.0 = true;
|
is_flat.0 = true;
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
pos.0 = SPAWN_POS;
|
|
||||||
loc.0 = instance;
|
|
||||||
|
|
||||||
commands
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
.entity(entity)
|
location: Location(instances.single()),
|
||||||
.insert(McEntity::with_uuid(EntityKind::Player, loc.0, uuid.0));
|
position: Position(SPAWN_POS),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::client::event::default_event_handler;
|
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
use valence::protocol::translation_key;
|
use valence::protocol::translation_key;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ use glam::{DVec3, Vec3};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use valence_protocol::block_pos::BlockPos;
|
use valence_protocol::block_pos::BlockPos;
|
||||||
|
use valence_protocol::byte_angle::ByteAngle;
|
||||||
use valence_protocol::codec::{PacketDecoder, PacketEncoder};
|
use valence_protocol::codec::{PacketDecoder, PacketEncoder};
|
||||||
use valence_protocol::ident::Ident;
|
use valence_protocol::ident::Ident;
|
||||||
use valence_protocol::item::ItemStack;
|
use valence_protocol::item::ItemStack;
|
||||||
|
@ -20,11 +21,11 @@ use valence_protocol::packet::s2c::play::particle::Particle;
|
||||||
use valence_protocol::packet::s2c::play::player_position_look::Flags as PlayerPositionLookFlags;
|
use valence_protocol::packet::s2c::play::player_position_look::Flags as PlayerPositionLookFlags;
|
||||||
use valence_protocol::packet::s2c::play::{
|
use valence_protocol::packet::s2c::play::{
|
||||||
ChunkLoadDistanceS2c, ChunkRenderDistanceCenterS2c, CustomPayloadS2c, DeathMessageS2c,
|
ChunkLoadDistanceS2c, ChunkRenderDistanceCenterS2c, CustomPayloadS2c, DeathMessageS2c,
|
||||||
DisconnectS2c, EntitiesDestroyS2c, EntityStatusS2c, EntityTrackerUpdateS2c,
|
DisconnectS2c, EntitiesDestroyS2c, EntitySetHeadYawS2c, EntitySpawnS2c, EntityStatusS2c,
|
||||||
EntityVelocityUpdateS2c, GameJoinS2c, GameMessageS2c, GameStateChangeS2c, KeepAliveS2c,
|
EntityTrackerUpdateS2c, EntityVelocityUpdateS2c, ExperienceOrbSpawnS2c, GameJoinS2c,
|
||||||
OverlayMessageS2c, ParticleS2c, PlaySoundS2c, PlayerActionResponseS2c, PlayerPositionLookS2c,
|
GameMessageS2c, GameStateChangeS2c, KeepAliveS2c, OverlayMessageS2c, ParticleS2c, PlaySoundS2c,
|
||||||
PlayerRespawnS2c, PlayerSpawnPositionS2c, ResourcePackSendS2c, SubtitleS2c, TitleFadeS2c,
|
PlayerActionResponseS2c, PlayerPositionLookS2c, PlayerRespawnS2c, PlayerSpawnPositionS2c,
|
||||||
TitleS2c, UnloadChunkS2c,
|
PlayerSpawnS2c, ResourcePackSendS2c, SubtitleS2c, TitleFadeS2c, TitleS2c, UnloadChunkS2c,
|
||||||
};
|
};
|
||||||
use valence_protocol::sound::Sound;
|
use valence_protocol::sound::Sound;
|
||||||
use valence_protocol::text::Text;
|
use valence_protocol::text::Text;
|
||||||
|
@ -37,16 +38,22 @@ use crate::component::{
|
||||||
Properties, UniqueId, Username,
|
Properties, UniqueId, Username,
|
||||||
};
|
};
|
||||||
use crate::dimension::DimensionId;
|
use crate::dimension::DimensionId;
|
||||||
use crate::entity::{velocity_to_packet_units, EntityStatus, McEntity, TrackedData};
|
use crate::entity::{
|
||||||
use crate::instance::{Instance, UpdateInstancesPreClientSet};
|
EntityId, EntityKind, EntityStatus, HeadYaw, ObjectData, TrackedData, Velocity,
|
||||||
|
};
|
||||||
|
use crate::instance::{Instance, WriteUpdatePacketsToInstancesSet};
|
||||||
use crate::inventory::{Inventory, InventoryKind};
|
use crate::inventory::{Inventory, InventoryKind};
|
||||||
use crate::packet::WritePacket;
|
use crate::packet::WritePacket;
|
||||||
use crate::prelude::ScratchBuf;
|
use crate::prelude::ScratchBuf;
|
||||||
use crate::server::{NewClientInfo, Server};
|
use crate::server::{NewClientInfo, Server};
|
||||||
|
use crate::util::velocity_to_packet_units;
|
||||||
use crate::view::{ChunkPos, ChunkView};
|
use crate::view::{ChunkPos, ChunkView};
|
||||||
|
|
||||||
|
mod default_event_handler;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
|
|
||||||
|
pub use default_event_handler::*;
|
||||||
|
|
||||||
/// The bundle of components needed for clients to function. All components are
|
/// The bundle of components needed for clients to function. All components are
|
||||||
/// required unless otherwise stated.
|
/// required unless otherwise stated.
|
||||||
#[derive(Bundle)]
|
#[derive(Bundle)]
|
||||||
|
@ -62,7 +69,7 @@ pub(crate) struct ClientBundle {
|
||||||
old_location: OldLocation,
|
old_location: OldLocation,
|
||||||
position: Position,
|
position: Position,
|
||||||
old_position: OldPosition,
|
old_position: OldPosition,
|
||||||
direction: Look,
|
look: Look,
|
||||||
on_ground: OnGround,
|
on_ground: OnGround,
|
||||||
compass_pos: CompassPos,
|
compass_pos: CompassPos,
|
||||||
game_mode: GameMode,
|
game_mode: GameMode,
|
||||||
|
@ -105,7 +112,7 @@ impl ClientBundle {
|
||||||
old_location: OldLocation::default(),
|
old_location: OldLocation::default(),
|
||||||
position: Position::default(),
|
position: Position::default(),
|
||||||
old_position: OldPosition::default(),
|
old_position: OldPosition::default(),
|
||||||
direction: Look::default(),
|
look: Look::default(),
|
||||||
on_ground: OnGround::default(),
|
on_ground: OnGround::default(),
|
||||||
compass_pos: CompassPos::default(),
|
compass_pos: CompassPos::default(),
|
||||||
game_mode: GameMode::default(),
|
game_mode: GameMode::default(),
|
||||||
|
@ -221,10 +228,10 @@ impl Client {
|
||||||
|
|
||||||
/// Kills the client and shows `message` on the death screen. If an entity
|
/// Kills the client and shows `message` on the death screen. If an entity
|
||||||
/// killed the player, you should supply it as `killer`.
|
/// killed the player, you should supply it as `killer`.
|
||||||
pub fn kill(&mut self, killer: Option<&McEntity>, message: impl Into<Text>) {
|
pub fn kill(&mut self, killer: Option<EntityId>, message: impl Into<Text>) {
|
||||||
self.write_packet(&DeathMessageS2c {
|
self.write_packet(&DeathMessageS2c {
|
||||||
player_id: VarInt(0),
|
player_id: VarInt(0),
|
||||||
entity_id: killer.map_or(-1, |k| k.protocol_id()),
|
entity_id: killer.map(|id| id.get()).unwrap_or(-1),
|
||||||
message: message.into().into(),
|
message: message.into().into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -613,10 +620,10 @@ impl Plugin for ClientPlugin {
|
||||||
(
|
(
|
||||||
initial_join,
|
initial_join,
|
||||||
update_chunk_load_dist,
|
update_chunk_load_dist,
|
||||||
read_data_in_view
|
read_data_in_old_view
|
||||||
.after(UpdateInstancesPreClientSet)
|
.after(WriteUpdatePacketsToInstancesSet)
|
||||||
.after(update_chunk_load_dist),
|
.after(update_chunk_load_dist),
|
||||||
update_view.after(initial_join).after(read_data_in_view),
|
update_view.after(initial_join).after(read_data_in_old_view),
|
||||||
respawn.after(update_view),
|
respawn.after(update_view),
|
||||||
remove_entities.after(update_view),
|
remove_entities.after(update_view),
|
||||||
update_spawn_position.after(update_view),
|
update_spawn_position.after(update_view),
|
||||||
|
@ -624,7 +631,8 @@ impl Plugin for ClientPlugin {
|
||||||
teleport.after(update_view),
|
teleport.after(update_view),
|
||||||
update_game_mode,
|
update_game_mode,
|
||||||
send_keepalive,
|
send_keepalive,
|
||||||
update_tracked_data,
|
update_tracked_data.after(WriteUpdatePacketsToInstancesSet),
|
||||||
|
init_tracked_data.after(WriteUpdatePacketsToInstancesSet),
|
||||||
update_op_level,
|
update_op_level,
|
||||||
acknowledge_player_actions,
|
acknowledge_player_actions,
|
||||||
)
|
)
|
||||||
|
@ -634,7 +642,7 @@ impl Plugin for ClientPlugin {
|
||||||
.configure_set(
|
.configure_set(
|
||||||
FlushPacketsSet
|
FlushPacketsSet
|
||||||
.in_base_set(CoreSet::PostUpdate)
|
.in_base_set(CoreSet::PostUpdate)
|
||||||
.after(UpdateInstancesPreClientSet),
|
.after(WriteUpdatePacketsToInstancesSet),
|
||||||
)
|
)
|
||||||
.add_system(flush_packets.in_set(FlushPacketsSet));
|
.add_system(flush_packets.in_set(FlushPacketsSet));
|
||||||
}
|
}
|
||||||
|
@ -783,20 +791,83 @@ fn update_chunk_load_dist(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_data_in_view(
|
#[derive(WorldQuery)]
|
||||||
|
pub(crate) struct EntityInitQuery {
|
||||||
|
pub entity_id: &'static EntityId,
|
||||||
|
pub uuid: &'static UniqueId,
|
||||||
|
pub kind: &'static EntityKind,
|
||||||
|
pub look: &'static Look,
|
||||||
|
pub head_yaw: &'static HeadYaw,
|
||||||
|
pub on_ground: &'static OnGround,
|
||||||
|
pub object_data: &'static ObjectData,
|
||||||
|
pub velocity: &'static Velocity,
|
||||||
|
pub tracked_data: &'static TrackedData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntityInitQueryItem<'_> {
|
||||||
|
/// Writes the appropriate packets to initialize an entity. This will spawn
|
||||||
|
/// the entity and initialize tracked data.
|
||||||
|
pub fn write_init_packets(&self, pos: DVec3, mut writer: impl WritePacket) {
|
||||||
|
match *self.kind {
|
||||||
|
EntityKind::MARKER => {}
|
||||||
|
EntityKind::EXPERIENCE_ORB => {
|
||||||
|
writer.write_packet(&ExperienceOrbSpawnS2c {
|
||||||
|
entity_id: self.entity_id.get().into(),
|
||||||
|
position: pos.to_array(),
|
||||||
|
count: self.object_data.0 as i16,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
EntityKind::PLAYER => {
|
||||||
|
writer.write_packet(&PlayerSpawnS2c {
|
||||||
|
entity_id: self.entity_id.get().into(),
|
||||||
|
player_uuid: self.uuid.0,
|
||||||
|
position: pos.to_array(),
|
||||||
|
yaw: ByteAngle::from_degrees(self.look.yaw),
|
||||||
|
pitch: ByteAngle::from_degrees(self.look.pitch),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Player spawn packet doesn't include head yaw for some reason.
|
||||||
|
writer.write_packet(&EntitySetHeadYawS2c {
|
||||||
|
entity_id: self.entity_id.get().into(),
|
||||||
|
head_yaw: ByteAngle::from_degrees(self.head_yaw.0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => writer.write_packet(&EntitySpawnS2c {
|
||||||
|
entity_id: self.entity_id.get().into(),
|
||||||
|
object_uuid: self.uuid.0,
|
||||||
|
kind: self.kind.get().into(),
|
||||||
|
position: pos.to_array(),
|
||||||
|
pitch: ByteAngle::from_degrees(self.look.pitch),
|
||||||
|
yaw: ByteAngle::from_degrees(self.look.yaw),
|
||||||
|
head_yaw: ByteAngle::from_degrees(self.head_yaw.0),
|
||||||
|
data: self.object_data.0.into(),
|
||||||
|
velocity: velocity_to_packet_units(self.velocity.0),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(init_data) = self.tracked_data.init_data() {
|
||||||
|
writer.write_packet(&EntityTrackerUpdateS2c {
|
||||||
|
entity_id: self.entity_id.get().into(),
|
||||||
|
metadata: init_data.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_data_in_old_view(
|
||||||
mut clients: Query<(
|
mut clients: Query<(
|
||||||
&mut Client,
|
&mut Client,
|
||||||
&mut ScratchBuf,
|
|
||||||
&mut EntityRemoveBuf,
|
&mut EntityRemoveBuf,
|
||||||
&OldLocation,
|
&OldLocation,
|
||||||
&OldPosition,
|
&OldPosition,
|
||||||
&OldViewDistance,
|
&OldViewDistance,
|
||||||
)>,
|
)>,
|
||||||
instances: Query<&Instance>,
|
instances: Query<&Instance>,
|
||||||
entities: Query<&McEntity>,
|
entities: Query<(EntityInitQuery, &OldPosition)>,
|
||||||
|
entity_ids: Query<&EntityId>,
|
||||||
) {
|
) {
|
||||||
clients.par_iter_mut().for_each_mut(
|
clients.par_iter_mut().for_each_mut(
|
||||||
|(mut client, mut scratch, mut remove_buf, old_loc, old_pos, old_view_dist)| {
|
|(mut client, mut remove_buf, old_loc, old_pos, old_view_dist)| {
|
||||||
let Ok(instance) = instances.get(old_loc.get()) else {
|
let Ok(instance) = instances.get(old_loc.get()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -829,15 +900,12 @@ fn read_data_in_view(
|
||||||
if src_pos.map_or(true, |p| !view.contains(p)) {
|
if src_pos.map_or(true, |p| !view.contains(p)) {
|
||||||
// The incoming entity originated from outside the view distance, so it
|
// The incoming entity originated from outside the view distance, so it
|
||||||
// must be spawned.
|
// must be spawned.
|
||||||
if let Ok(entity) = entities.get(id) {
|
if let Ok((entity, old_pos)) = entities.get(id) {
|
||||||
// Spawn the entity at the old position so that later relative
|
// Notice we are spawning the entity at its old position rather than
|
||||||
// entity movement packets will not
|
// the current position. This is because the client could also
|
||||||
// set the entity to the wrong position.
|
// receive update packets for this entity this tick, which may
|
||||||
entity.write_init_packets(
|
// include a relative entity movement.
|
||||||
&mut client.enc,
|
entity.write_init_packets(old_pos.get(), &mut client.enc);
|
||||||
entity.old_position(),
|
|
||||||
&mut scratch.0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -847,8 +915,8 @@ fn read_data_in_view(
|
||||||
if dest_pos.map_or(true, |p| !view.contains(p)) {
|
if dest_pos.map_or(true, |p| !view.contains(p)) {
|
||||||
// The outgoing entity moved outside the view distance, so it must be
|
// The outgoing entity moved outside the view distance, so it must be
|
||||||
// despawned.
|
// despawned.
|
||||||
if let Ok(entity) = entities.get(id) {
|
if let Ok(entity_id) = entity_ids.get(id) {
|
||||||
remove_buf.push(entity.protocol_id());
|
remove_buf.push(entity_id.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -867,7 +935,7 @@ fn read_data_in_view(
|
||||||
/// client's chunk position.
|
/// client's chunk position.
|
||||||
///
|
///
|
||||||
/// This handles the situation when a client changes instances or chunk
|
/// This handles the situation when a client changes instances or chunk
|
||||||
/// position. It must run after [`read_data_in_view`].
|
/// position. It must run after [`read_data_in_old_view`].
|
||||||
fn update_view(
|
fn update_view(
|
||||||
mut clients: Query<
|
mut clients: Query<
|
||||||
(
|
(
|
||||||
|
@ -884,7 +952,8 @@ fn update_view(
|
||||||
Or<(Changed<Location>, Changed<Position>, Changed<ViewDistance>)>,
|
Or<(Changed<Location>, Changed<Position>, Changed<ViewDistance>)>,
|
||||||
>,
|
>,
|
||||||
instances: Query<&Instance>,
|
instances: Query<&Instance>,
|
||||||
entities: Query<&McEntity>,
|
entities: Query<(EntityInitQuery, &Position)>,
|
||||||
|
entity_ids: Query<&EntityId>,
|
||||||
) {
|
) {
|
||||||
clients.par_iter_mut().for_each_mut(
|
clients.par_iter_mut().for_each_mut(
|
||||||
|(
|
|(
|
||||||
|
@ -930,8 +999,8 @@ fn update_view(
|
||||||
|
|
||||||
// Unload all the entities in the cell.
|
// Unload all the entities in the cell.
|
||||||
for &id in &cell.entities {
|
for &id in &cell.entities {
|
||||||
if let Ok(entity) = entities.get(id) {
|
if let Ok(entity_id) = entity_ids.get(id) {
|
||||||
remove_buf.push(entity.protocol_id());
|
remove_buf.push(entity_id.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -956,12 +1025,8 @@ fn update_view(
|
||||||
|
|
||||||
// Load all the entities in this cell.
|
// Load all the entities in this cell.
|
||||||
for &id in &cell.entities {
|
for &id in &cell.entities {
|
||||||
if let Ok(entity) = entities.get(id) {
|
if let Ok((entity, pos)) = entities.get(id) {
|
||||||
entity.write_init_packets(
|
entity.write_init_packets(pos.get(), &mut client.enc);
|
||||||
&mut client.enc,
|
|
||||||
entity.position(),
|
|
||||||
&mut scratch.0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -988,8 +1053,8 @@ fn update_view(
|
||||||
|
|
||||||
// Unload all the entities in the cell.
|
// Unload all the entities in the cell.
|
||||||
for &id in &cell.entities {
|
for &id in &cell.entities {
|
||||||
if let Ok(entity) = entities.get(id) {
|
if let Ok(entity_id) = entity_ids.get(id) {
|
||||||
remove_buf.push(entity.protocol_id());
|
remove_buf.push(entity_id.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1011,12 +1076,8 @@ fn update_view(
|
||||||
|
|
||||||
// Load all the entities in this cell.
|
// Load all the entities in this cell.
|
||||||
for &id in &cell.entities {
|
for &id in &cell.entities {
|
||||||
if let Ok(entity) = entities.get(id) {
|
if let Ok((entity, pos)) = entities.get(id) {
|
||||||
entity.write_init_packets(
|
entity.write_init_packets(pos.get(), &mut client.enc);
|
||||||
&mut client.enc,
|
|
||||||
entity.position(),
|
|
||||||
&mut scratch.0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1130,21 +1191,24 @@ fn flush_packets(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_tracked_data(mut clients: Query<(&mut Client, &McEntity)>, mut scratch: Local<Vec<u8>>) {
|
fn init_tracked_data(mut clients: Query<(&mut Client, &TrackedData), Added<TrackedData>>) {
|
||||||
for (mut client, entity) in &mut clients {
|
for (mut client, tracked_data) in &mut clients {
|
||||||
if let TrackedData::Player(player) = &entity.data {
|
if let Some(init_data) = tracked_data.init_data() {
|
||||||
scratch.clear();
|
client.write_packet(&EntityTrackerUpdateS2c {
|
||||||
// TODO: should some fields be ignored?
|
entity_id: VarInt(0),
|
||||||
player.updated_tracked_data(&mut scratch);
|
metadata: init_data.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !scratch.is_empty() {
|
fn update_tracked_data(mut clients: Query<(&mut Client, &TrackedData)>) {
|
||||||
scratch.push(0xff);
|
for (mut client, tracked_data) in &mut clients {
|
||||||
|
if let Some(update_data) = tracked_data.update_data() {
|
||||||
client.enc.write_packet(&EntityTrackerUpdateS2c {
|
client.write_packet(&EntityTrackerUpdateS2c {
|
||||||
entity_id: VarInt(0),
|
entity_id: VarInt(0),
|
||||||
metadata: scratch.as_slice().into(),
|
metadata: update_data.into(),
|
||||||
})
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
116
crates/valence/src/client/default_event_handler.rs
Normal file
116
crates/valence/src/client/default_event_handler.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
use bevy_ecs::query::WorldQuery;
|
||||||
|
use valence_protocol::types::Hand;
|
||||||
|
|
||||||
|
use super::event::{
|
||||||
|
ClientSettings, HandSwing, PlayerMove, StartSneaking, StartSprinting, StopSneaking,
|
||||||
|
StopSprinting,
|
||||||
|
};
|
||||||
|
use super::{Client, ViewDistance};
|
||||||
|
use crate::entity::player::PlayerModelParts;
|
||||||
|
use crate::entity::{entity, player, EntityAnimation, EntityAnimations, EntityKind, HeadYaw, Pose};
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(WorldQuery)]
|
||||||
|
#[world_query(mutable)]
|
||||||
|
pub struct DefaultEventHandlerQuery {
|
||||||
|
client: &'static mut Client,
|
||||||
|
view_dist: &'static mut ViewDistance,
|
||||||
|
head_yaw: &'static mut HeadYaw,
|
||||||
|
player_model_parts: Option<&'static mut PlayerModelParts>,
|
||||||
|
pose: &'static mut entity::Pose,
|
||||||
|
flags: &'static mut entity::Flags,
|
||||||
|
animations: Option<&'static mut EntityAnimations>,
|
||||||
|
entity_kind: Option<&'static EntityKind>,
|
||||||
|
main_arm: Option<&'static mut player::MainArm>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The default event handler system which handles client events in a
|
||||||
|
/// reasonable default way.
|
||||||
|
///
|
||||||
|
/// For instance, movement events are handled by changing the entity's
|
||||||
|
/// position/rotation to match the received movement, crouching makes the
|
||||||
|
/// entity crouch, etc.
|
||||||
|
///
|
||||||
|
/// This system's primary purpose is to reduce boilerplate code in the
|
||||||
|
/// examples, but it can be used as a quick way to get started in your own
|
||||||
|
/// code. The precise behavior of this system is left unspecified and
|
||||||
|
/// is subject to change.
|
||||||
|
///
|
||||||
|
/// This system must be scheduled to run in the
|
||||||
|
/// [`EventLoopSchedule`](crate::client::event::EventLoopSchedule). Otherwise,
|
||||||
|
/// it may not function correctly.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn default_event_handler(
|
||||||
|
mut clients: Query<DefaultEventHandlerQuery>,
|
||||||
|
mut update_settings_events: EventReader<ClientSettings>,
|
||||||
|
mut player_move: EventReader<PlayerMove>,
|
||||||
|
mut start_sneaking: EventReader<StartSneaking>,
|
||||||
|
mut stop_sneaking: EventReader<StopSneaking>,
|
||||||
|
mut start_sprinting: EventReader<StartSprinting>,
|
||||||
|
mut stop_sprinting: EventReader<StopSprinting>,
|
||||||
|
mut swing_arm: EventReader<HandSwing>,
|
||||||
|
) {
|
||||||
|
for ClientSettings {
|
||||||
|
client,
|
||||||
|
view_distance,
|
||||||
|
displayed_skin_parts,
|
||||||
|
main_arm,
|
||||||
|
..
|
||||||
|
} in update_settings_events.iter()
|
||||||
|
{
|
||||||
|
if let Ok(mut q) = clients.get_mut(*client) {
|
||||||
|
q.view_dist.0 = *view_distance;
|
||||||
|
|
||||||
|
if let Some(mut parts) = q.player_model_parts {
|
||||||
|
parts.set_if_neq(PlayerModelParts(u8::from(*displayed_skin_parts)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut player_main_arm) = q.main_arm {
|
||||||
|
player_main_arm.0 = *main_arm as _;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for PlayerMove { client, yaw, .. } in player_move.iter() {
|
||||||
|
if let Ok(mut q) = clients.get_mut(*client) {
|
||||||
|
q.head_yaw.set_if_neq(HeadYaw(*yaw));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for StartSneaking { client } in start_sneaking.iter() {
|
||||||
|
if let Ok(mut q) = clients.get_mut(*client) {
|
||||||
|
q.pose.set_if_neq(entity::Pose(Pose::Sneaking));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for StopSneaking { client } in stop_sneaking.iter() {
|
||||||
|
if let Ok(mut q) = clients.get_mut(*client) {
|
||||||
|
q.pose.set_if_neq(entity::Pose(Pose::Standing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for StartSprinting { client } in start_sprinting.iter() {
|
||||||
|
if let Ok(mut q) = clients.get_mut(*client) {
|
||||||
|
q.flags.set_sprinting(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for StopSprinting { client } in stop_sprinting.iter() {
|
||||||
|
if let Ok(mut q) = clients.get_mut(*client) {
|
||||||
|
q.flags.set_sprinting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for HandSwing { client, hand } in swing_arm.iter() {
|
||||||
|
if let Ok(q) = clients.get_mut(*client) {
|
||||||
|
if let (Some(mut animations), Some(&EntityKind::PLAYER)) = (q.animations, q.entity_kind)
|
||||||
|
{
|
||||||
|
animations.trigger(match hand {
|
||||||
|
Hand::Main => EntityAnimation::SwingMainHand,
|
||||||
|
Hand::Off => EntityAnimation::SwingOffHand,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,9 +15,7 @@ use valence_protocol::ident::Ident;
|
||||||
use valence_protocol::item::ItemStack;
|
use valence_protocol::item::ItemStack;
|
||||||
use valence_protocol::packet::c2s::play::click_slot::{ClickMode, Slot};
|
use valence_protocol::packet::c2s::play::click_slot::{ClickMode, Slot};
|
||||||
use valence_protocol::packet::c2s::play::client_command::Action as ClientCommandAction;
|
use valence_protocol::packet::c2s::play::client_command::Action as ClientCommandAction;
|
||||||
use valence_protocol::packet::c2s::play::client_settings::{
|
use valence_protocol::packet::c2s::play::client_settings::{ChatMode, DisplayedSkinParts, MainArm};
|
||||||
ChatMode, DisplayedSkinParts, MainHand,
|
|
||||||
};
|
|
||||||
use valence_protocol::packet::c2s::play::player_action::Action as PlayerAction;
|
use valence_protocol::packet::c2s::play::player_action::Action as PlayerAction;
|
||||||
use valence_protocol::packet::c2s::play::player_interact::Interaction;
|
use valence_protocol::packet::c2s::play::player_interact::Interaction;
|
||||||
use valence_protocol::packet::c2s::play::recipe_category_options::RecipeBookId;
|
use valence_protocol::packet::c2s::play::recipe_category_options::RecipeBookId;
|
||||||
|
@ -30,16 +28,13 @@ use valence_protocol::packet::c2s::play::{
|
||||||
AdvancementTabC2s, ClientStatusC2s, ResourcePackStatusC2s, UpdatePlayerAbilitiesC2s,
|
AdvancementTabC2s, ClientStatusC2s, ResourcePackStatusC2s, UpdatePlayerAbilitiesC2s,
|
||||||
};
|
};
|
||||||
use valence_protocol::packet::C2sPlayPacket;
|
use valence_protocol::packet::C2sPlayPacket;
|
||||||
use valence_protocol::tracked_data::Pose;
|
|
||||||
use valence_protocol::types::{Difficulty, Direction, Hand};
|
use valence_protocol::types::{Difficulty, Direction, Hand};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
CursorItem, KeepaliveState, PlayerActionSequence, PlayerInventoryState, TeleportState,
|
CursorItem, KeepaliveState, PlayerActionSequence, PlayerInventoryState, TeleportState,
|
||||||
ViewDistance,
|
|
||||||
};
|
};
|
||||||
use crate::client::Client;
|
use crate::client::Client;
|
||||||
use crate::component::{Look, OnGround, Ping, Position};
|
use crate::component::{Look, OnGround, Ping, Position};
|
||||||
use crate::entity::{EntityAnimation, EntityKind, McEntity, TrackedData};
|
|
||||||
use crate::inventory::Inventory;
|
use crate::inventory::Inventory;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -98,7 +93,7 @@ pub struct ClientSettings {
|
||||||
/// `true` if the client has chat colors enabled, `false` otherwise.
|
/// `true` if the client has chat colors enabled, `false` otherwise.
|
||||||
pub chat_colors: bool,
|
pub chat_colors: bool,
|
||||||
pub displayed_skin_parts: DisplayedSkinParts,
|
pub displayed_skin_parts: DisplayedSkinParts,
|
||||||
pub main_hand: MainHand,
|
pub main_arm: MainArm,
|
||||||
pub enable_text_filtering: bool,
|
pub enable_text_filtering: bool,
|
||||||
pub allow_server_listings: bool,
|
pub allow_server_listings: bool,
|
||||||
}
|
}
|
||||||
|
@ -829,7 +824,7 @@ fn handle_one_packet(
|
||||||
chat_mode: p.chat_mode,
|
chat_mode: p.chat_mode,
|
||||||
chat_colors: p.chat_colors,
|
chat_colors: p.chat_colors,
|
||||||
displayed_skin_parts: p.displayed_skin_parts,
|
displayed_skin_parts: p.displayed_skin_parts,
|
||||||
main_hand: p.main_hand,
|
main_arm: p.main_arm,
|
||||||
enable_text_filtering: p.enable_text_filtering,
|
enable_text_filtering: p.enable_text_filtering,
|
||||||
allow_server_listings: p.allow_server_listings,
|
allow_server_listings: p.allow_server_listings,
|
||||||
});
|
});
|
||||||
|
@ -1396,117 +1391,3 @@ fn handle_one_packet(
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The default event handler system which handles client events in a
|
|
||||||
/// reasonable default way.
|
|
||||||
///
|
|
||||||
/// For instance, movement events are handled by changing the entity's
|
|
||||||
/// position/rotation to match the received movement, crouching makes the
|
|
||||||
/// entity crouch, etc.
|
|
||||||
///
|
|
||||||
/// This system's primary purpose is to reduce boilerplate code in the
|
|
||||||
/// examples, but it can be used as a quick way to get started in your own
|
|
||||||
/// code. The precise behavior of this system is left unspecified and
|
|
||||||
/// is subject to change.
|
|
||||||
///
|
|
||||||
/// This system must be scheduled to run in the
|
|
||||||
/// [`EventLoopSchedule`]. Otherwise, it may
|
|
||||||
/// not function correctly.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn default_event_handler(
|
|
||||||
mut clients: Query<(&mut Client, Option<&mut McEntity>, &mut ViewDistance)>,
|
|
||||||
mut update_settings: EventReader<ClientSettings>,
|
|
||||||
mut player_move: EventReader<PlayerMove>,
|
|
||||||
mut start_sneaking: EventReader<StartSneaking>,
|
|
||||||
mut stop_sneaking: EventReader<StopSneaking>,
|
|
||||||
mut start_sprinting: EventReader<StartSprinting>,
|
|
||||||
mut stop_sprinting: EventReader<StopSprinting>,
|
|
||||||
mut swing_arm: EventReader<HandSwing>,
|
|
||||||
) {
|
|
||||||
for ClientSettings {
|
|
||||||
client,
|
|
||||||
view_distance,
|
|
||||||
displayed_skin_parts,
|
|
||||||
main_hand,
|
|
||||||
..
|
|
||||||
} in update_settings.iter()
|
|
||||||
{
|
|
||||||
if let Ok((_, mcentity, mut view_dist)) = clients.get_mut(*client) {
|
|
||||||
view_dist.set(*view_distance);
|
|
||||||
|
|
||||||
if let Some(mut entity) = mcentity {
|
|
||||||
if let TrackedData::Player(player) = entity.data_mut() {
|
|
||||||
player.set_cape(displayed_skin_parts.cape());
|
|
||||||
player.set_jacket(displayed_skin_parts.jacket());
|
|
||||||
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
|
||||||
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
|
||||||
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
|
||||||
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
|
||||||
player.set_hat(displayed_skin_parts.hat());
|
|
||||||
player.set_main_arm(*main_hand as u8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for PlayerMove {
|
|
||||||
client,
|
|
||||||
position,
|
|
||||||
yaw,
|
|
||||||
pitch,
|
|
||||||
on_ground,
|
|
||||||
..
|
|
||||||
} in player_move.iter()
|
|
||||||
{
|
|
||||||
if let Ok((_, Some(mut mcentity), _)) = clients.get_mut(*client) {
|
|
||||||
mcentity.set_position(*position);
|
|
||||||
mcentity.set_yaw(*yaw);
|
|
||||||
mcentity.set_head_yaw(*yaw);
|
|
||||||
mcentity.set_pitch(*pitch);
|
|
||||||
mcentity.set_on_ground(*on_ground);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for StartSneaking { client } in start_sneaking.iter() {
|
|
||||||
if let Ok((_, Some(mut entity), _)) = clients.get_mut(*client) {
|
|
||||||
if let TrackedData::Player(player) = entity.data_mut() {
|
|
||||||
player.set_pose(Pose::Sneaking);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for StopSneaking { client } in stop_sneaking.iter() {
|
|
||||||
if let Ok((_, Some(mut entity), _)) = clients.get_mut(*client) {
|
|
||||||
if let TrackedData::Player(player) = entity.data_mut() {
|
|
||||||
player.set_pose(Pose::Standing);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for StartSprinting { client } in start_sprinting.iter() {
|
|
||||||
if let Ok((_, Some(mut entity), _)) = clients.get_mut(*client) {
|
|
||||||
if let TrackedData::Player(player) = entity.data_mut() {
|
|
||||||
player.set_sprinting(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for StopSprinting { client } in stop_sprinting.iter() {
|
|
||||||
if let Ok((_, Some(mut entity), _)) = clients.get_mut(*client) {
|
|
||||||
if let TrackedData::Player(player) = entity.data_mut() {
|
|
||||||
player.set_sprinting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for HandSwing { client, hand } in swing_arm.iter() {
|
|
||||||
if let Ok((_, Some(mut entity), _)) = clients.get_mut(*client) {
|
|
||||||
if entity.kind() == EntityKind::Player {
|
|
||||||
entity.trigger_animation(match hand {
|
|
||||||
Hand::Main => EntityAnimation::SwingMainHand,
|
|
||||||
Hand::Off => EntityAnimation::SwingOffHand,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,12 +10,11 @@ use valence_protocol::types::{GameMode as ProtocolGameMode, Property};
|
||||||
use crate::prelude::FlushPacketsSet;
|
use crate::prelude::FlushPacketsSet;
|
||||||
use crate::util::{from_yaw_and_pitch, to_yaw_and_pitch};
|
use crate::util::{from_yaw_and_pitch, to_yaw_and_pitch};
|
||||||
use crate::view::ChunkPos;
|
use crate::view::ChunkPos;
|
||||||
use crate::NULL_ENTITY;
|
|
||||||
|
|
||||||
/// A [`Component`] for marking entities that should be despawned at the end of
|
/// A [`Component`] for marking entities that should be despawned at the end of
|
||||||
/// the tick.
|
/// the tick.
|
||||||
///
|
///
|
||||||
/// In Valence, some built-in components such as [`McEntity`] are not allowed to
|
/// In Valence, some entities such as [Minecraft entities] are not allowed to
|
||||||
/// be removed from the [`World`] directly. Instead, you must give the entities
|
/// be removed from the [`World`] directly. Instead, you must give the entities
|
||||||
/// you wish to despawn the `Despawned` component. At the end of the tick,
|
/// you wish to despawn the `Despawned` component. At the end of the tick,
|
||||||
/// Valence will despawn all entities with this component for you.
|
/// Valence will despawn all entities with this component for you.
|
||||||
|
@ -23,13 +22,25 @@ use crate::NULL_ENTITY;
|
||||||
/// It is legal to remove components or delete entities that Valence does not
|
/// It is legal to remove components or delete entities that Valence does not
|
||||||
/// know about at any time.
|
/// know about at any time.
|
||||||
///
|
///
|
||||||
/// [`McEntity`]: crate::entity::McEntity
|
/// [Minecraft entities]: crate::entity
|
||||||
#[derive(Component, Copy, Clone, Default, PartialEq, Eq, Debug)]
|
#[derive(Component, Copy, Clone, Default, PartialEq, Eq, Debug)]
|
||||||
pub struct Despawned;
|
pub struct Despawned;
|
||||||
|
|
||||||
#[derive(Component, Default, Clone, PartialEq, Eq, Debug)]
|
/// The universally unique identifier of an entity. Component wrapper for a
|
||||||
|
/// [`Uuid`].
|
||||||
|
///
|
||||||
|
/// This component is expected to remain _unique_ and _constant_ during the
|
||||||
|
/// lifetime of the entity. The [`Default`] impl generates a new random UUID.
|
||||||
|
#[derive(Component, Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct UniqueId(pub Uuid);
|
pub struct UniqueId(pub Uuid);
|
||||||
|
|
||||||
|
/// Generates a new random UUID.
|
||||||
|
impl Default for UniqueId {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Uuid::from_bytes(rand::random()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone, PartialEq, Eq, Debug)]
|
#[derive(Component, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Username(pub String);
|
pub struct Username(pub String);
|
||||||
|
|
||||||
|
@ -39,7 +50,7 @@ impl fmt::Display for Username {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone, PartialEq, Eq, Debug)]
|
#[derive(Component, Clone, PartialEq, Eq, Default, Debug)]
|
||||||
pub struct Properties(pub Vec<Property>);
|
pub struct Properties(pub Vec<Property>);
|
||||||
|
|
||||||
impl Properties {
|
impl Properties {
|
||||||
|
@ -54,7 +65,7 @@ impl Properties {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Copy, Clone, PartialEq, Eq, Debug, Default)]
|
#[derive(Component, Copy, Clone, PartialEq, Eq, Default, Debug)]
|
||||||
|
|
||||||
pub enum GameMode {
|
pub enum GameMode {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -105,10 +116,13 @@ pub struct Location(pub Entity);
|
||||||
|
|
||||||
impl Default for Location {
|
impl Default for Location {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self(NULL_ENTITY)
|
Self(Entity::PLACEHOLDER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The value of [`Location`] from the end of the previous tick.
|
||||||
|
///
|
||||||
|
/// **NOTE**: You should not modify this component after the entity is spawned.
|
||||||
#[derive(Component, Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Component, Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct OldLocation(Entity);
|
pub struct OldLocation(Entity);
|
||||||
|
|
||||||
|
@ -124,7 +138,7 @@ impl OldLocation {
|
||||||
|
|
||||||
impl Default for OldLocation {
|
impl Default for OldLocation {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self(NULL_ENTITY)
|
Self(Entity::PLACEHOLDER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,11 +146,15 @@ impl Default for OldLocation {
|
||||||
pub struct Position(pub DVec3);
|
pub struct Position(pub DVec3);
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
|
pub fn new(pos: impl Into<DVec3>) -> Self {
|
||||||
|
Self(pos.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn chunk_pos(&self) -> ChunkPos {
|
pub fn chunk_pos(&self) -> ChunkPos {
|
||||||
ChunkPos::from_dvec3(self.0)
|
ChunkPos::from_dvec3(self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self) -> DVec3 {
|
pub fn get(self) -> DVec3 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,27 +163,26 @@ impl Position {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The value of [`Location`] from the end of the previous tick.
|
||||||
|
///
|
||||||
|
/// **NOTE**: You should not modify this component after the entity is spawned.
|
||||||
#[derive(Component, Copy, Clone, PartialEq, Default, Debug)]
|
#[derive(Component, Copy, Clone, PartialEq, Default, Debug)]
|
||||||
pub struct OldPosition(DVec3);
|
pub struct OldPosition(DVec3);
|
||||||
|
|
||||||
impl OldPosition {
|
impl OldPosition {
|
||||||
pub fn new(pos: DVec3) -> Self {
|
pub fn new(pos: impl Into<DVec3>) -> Self {
|
||||||
Self(pos)
|
Self(pos.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self) -> DVec3 {
|
pub fn get(self) -> DVec3 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunk_pos(&self) -> ChunkPos {
|
pub fn chunk_pos(self) -> ChunkPos {
|
||||||
ChunkPos::from_dvec3(self.0)
|
ChunkPos::from_dvec3(self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Velocity in m/s.
|
|
||||||
#[derive(Component, Copy, Clone, PartialEq, Default, Debug)]
|
|
||||||
pub struct Velocity(pub Vec3);
|
|
||||||
|
|
||||||
/// Describes the direction an entity is looking using pitch and yaw angles.
|
/// Describes the direction an entity is looking using pitch and yaw angles.
|
||||||
#[derive(Component, Copy, Clone, PartialEq, Default, Debug)]
|
#[derive(Component, Copy, Clone, PartialEq, Default, Debug)]
|
||||||
pub struct Look {
|
pub struct Look {
|
||||||
|
@ -176,6 +193,10 @@ pub struct Look {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Look {
|
impl Look {
|
||||||
|
pub const fn new(yaw: f32, pitch: f32) -> Self {
|
||||||
|
Self { yaw, pitch }
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a normalized direction vector from the yaw and pitch.
|
/// Gets a normalized direction vector from the yaw and pitch.
|
||||||
pub fn vec(&self) -> Vec3 {
|
pub fn vec(&self) -> Vec3 {
|
||||||
from_yaw_and_pitch(self.yaw, self.pitch)
|
from_yaw_and_pitch(self.yaw, self.pitch)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,14 +0,0 @@
|
||||||
//! Contains the [`TrackedData`] enum and the types for each variant.
|
|
||||||
|
|
||||||
// TODO: clean this up.
|
|
||||||
#![allow(clippy::all, missing_docs, trivial_numeric_casts, dead_code)]
|
|
||||||
|
|
||||||
use uuid::Uuid;
|
|
||||||
use valence_protocol::block::BlockState;
|
|
||||||
use valence_protocol::block_pos::BlockPos;
|
|
||||||
use valence_protocol::text::Text;
|
|
||||||
use valence_protocol::tracked_data::*;
|
|
||||||
use valence_protocol::var_int::VarInt;
|
|
||||||
use valence_protocol::Encode;
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/entity.rs"));
|
|
294
crates/valence/src/entity/hitbox.rs
Normal file
294
crates/valence/src/entity/hitbox.rs
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
// TODO: Make a `Hitbox` component and plugin.
|
||||||
|
|
||||||
|
/*
|
||||||
|
/// Returns the hitbox of this entity.
|
||||||
|
///
|
||||||
|
/// The hitbox describes the space that an entity occupies. Clients interact
|
||||||
|
/// with this space to create an [interact event].
|
||||||
|
///
|
||||||
|
/// The hitbox of an entity is determined by its position, entity type, and
|
||||||
|
/// other state specific to that type.
|
||||||
|
///
|
||||||
|
/// [interact event]: crate::client::event::PlayerInteract
|
||||||
|
pub fn hitbox(&self) -> Aabb {
|
||||||
|
fn baby(is_baby: bool, adult_hitbox: [f64; 3]) -> [f64; 3] {
|
||||||
|
if is_baby {
|
||||||
|
adult_hitbox.map(|a| a / 2.0)
|
||||||
|
} else {
|
||||||
|
adult_hitbox
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn item_frame(pos: DVec3, rotation: i32) -> Aabb {
|
||||||
|
let mut center_pos = pos + 0.5;
|
||||||
|
|
||||||
|
match rotation {
|
||||||
|
0 => center_pos.y += 0.46875,
|
||||||
|
1 => center_pos.y -= 0.46875,
|
||||||
|
2 => center_pos.z += 0.46875,
|
||||||
|
3 => center_pos.z -= 0.46875,
|
||||||
|
4 => center_pos.x += 0.46875,
|
||||||
|
5 => center_pos.x -= 0.46875,
|
||||||
|
_ => center_pos.y -= 0.46875,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bounds = DVec3::from(match rotation {
|
||||||
|
0 | 1 => [0.75, 0.0625, 0.75],
|
||||||
|
2 | 3 => [0.75, 0.75, 0.0625],
|
||||||
|
4 | 5 => [0.0625, 0.75, 0.75],
|
||||||
|
_ => [0.75, 0.0625, 0.75],
|
||||||
|
});
|
||||||
|
|
||||||
|
Aabb {
|
||||||
|
min: center_pos - bounds / 2.0,
|
||||||
|
max: center_pos + bounds / 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dimensions = match &self.data {
|
||||||
|
TrackedData::Allay(_) => [0.6, 0.35, 0.6],
|
||||||
|
TrackedData::ChestBoat(_) => [1.375, 0.5625, 1.375],
|
||||||
|
TrackedData::Frog(_) => [0.5, 0.5, 0.5],
|
||||||
|
TrackedData::Tadpole(_) => [0.4, 0.3, 0.4],
|
||||||
|
TrackedData::Warden(e) => match e.get_pose() {
|
||||||
|
Pose::Emerging | Pose::Digging => [0.9, 1.0, 0.9],
|
||||||
|
_ => [0.9, 2.9, 0.9],
|
||||||
|
},
|
||||||
|
TrackedData::AreaEffectCloud(e) => [
|
||||||
|
e.get_radius() as f64 * 2.0,
|
||||||
|
0.5,
|
||||||
|
e.get_radius() as f64 * 2.0,
|
||||||
|
],
|
||||||
|
TrackedData::ArmorStand(e) => {
|
||||||
|
if e.get_marker() {
|
||||||
|
[0.0, 0.0, 0.0]
|
||||||
|
} else if e.get_small() {
|
||||||
|
[0.5, 0.9875, 0.5]
|
||||||
|
} else {
|
||||||
|
[0.5, 1.975, 0.5]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TrackedData::Arrow(_) => [0.5, 0.5, 0.5],
|
||||||
|
TrackedData::Axolotl(_) => [1.3, 0.6, 1.3],
|
||||||
|
TrackedData::Bat(_) => [0.5, 0.9, 0.5],
|
||||||
|
TrackedData::Bee(e) => baby(e.get_child(), [0.7, 0.6, 0.7]),
|
||||||
|
TrackedData::Blaze(_) => [0.6, 1.8, 0.6],
|
||||||
|
TrackedData::Boat(_) => [1.375, 0.5625, 1.375],
|
||||||
|
TrackedData::Camel(e) => baby(e.get_child(), [1.7, 2.375, 1.7]),
|
||||||
|
TrackedData::Cat(_) => [0.6, 0.7, 0.6],
|
||||||
|
TrackedData::CaveSpider(_) => [0.7, 0.5, 0.7],
|
||||||
|
TrackedData::Chicken(e) => baby(e.get_child(), [0.4, 0.7, 0.4]),
|
||||||
|
TrackedData::Cod(_) => [0.5, 0.3, 0.5],
|
||||||
|
TrackedData::Cow(e) => baby(e.get_child(), [0.9, 1.4, 0.9]),
|
||||||
|
TrackedData::Creeper(_) => [0.6, 1.7, 0.6],
|
||||||
|
TrackedData::Dolphin(_) => [0.9, 0.6, 0.9],
|
||||||
|
TrackedData::Donkey(e) => baby(e.get_child(), [1.5, 1.39648, 1.5]),
|
||||||
|
TrackedData::DragonFireball(_) => [1.0, 1.0, 1.0],
|
||||||
|
TrackedData::Drowned(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
|
TrackedData::ElderGuardian(_) => [1.9975, 1.9975, 1.9975],
|
||||||
|
TrackedData::EndCrystal(_) => [2.0, 2.0, 2.0],
|
||||||
|
TrackedData::EnderDragon(_) => [16.0, 8.0, 16.0],
|
||||||
|
TrackedData::Enderman(_) => [0.6, 2.9, 0.6],
|
||||||
|
TrackedData::Endermite(_) => [0.4, 0.3, 0.4],
|
||||||
|
TrackedData::Evoker(_) => [0.6, 1.95, 0.6],
|
||||||
|
TrackedData::EvokerFangs(_) => [0.5, 0.8, 0.5],
|
||||||
|
TrackedData::ExperienceOrb(_) => [0.5, 0.5, 0.5],
|
||||||
|
TrackedData::EyeOfEnder(_) => [0.25, 0.25, 0.25],
|
||||||
|
TrackedData::FallingBlock(_) => [0.98, 0.98, 0.98],
|
||||||
|
TrackedData::FireworkRocket(_) => [0.25, 0.25, 0.25],
|
||||||
|
TrackedData::Fox(e) => baby(e.get_child(), [0.6, 0.7, 0.6]),
|
||||||
|
TrackedData::Ghast(_) => [4.0, 4.0, 4.0],
|
||||||
|
TrackedData::Giant(_) => [3.6, 12.0, 3.6],
|
||||||
|
TrackedData::GlowItemFrame(e) => return item_frame(self.position, e.get_rotation()),
|
||||||
|
TrackedData::GlowSquid(_) => [0.8, 0.8, 0.8],
|
||||||
|
TrackedData::Goat(e) => {
|
||||||
|
if e.get_pose() == Pose::LongJumping {
|
||||||
|
baby(e.get_child(), [0.63, 0.91, 0.63])
|
||||||
|
} else {
|
||||||
|
baby(e.get_child(), [0.9, 1.3, 0.9])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TrackedData::Guardian(_) => [0.85, 0.85, 0.85],
|
||||||
|
TrackedData::Hoglin(e) => baby(e.get_child(), [1.39648, 1.4, 1.39648]),
|
||||||
|
TrackedData::Horse(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]),
|
||||||
|
TrackedData::Husk(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
|
TrackedData::Illusioner(_) => [0.6, 1.95, 0.6],
|
||||||
|
TrackedData::IronGolem(_) => [1.4, 2.7, 1.4],
|
||||||
|
TrackedData::Item(_) => [0.25, 0.25, 0.25],
|
||||||
|
TrackedData::ItemFrame(e) => return item_frame(self.position, e.get_rotation()),
|
||||||
|
TrackedData::Fireball(_) => [1.0, 1.0, 1.0],
|
||||||
|
TrackedData::LeashKnot(_) => [0.375, 0.5, 0.375],
|
||||||
|
TrackedData::Lightning(_) => [0.0, 0.0, 0.0],
|
||||||
|
TrackedData::Llama(e) => baby(e.get_child(), [0.9, 1.87, 0.9]),
|
||||||
|
TrackedData::LlamaSpit(_) => [0.25, 0.25, 0.25],
|
||||||
|
TrackedData::MagmaCube(e) => {
|
||||||
|
let s = 0.5202 * e.get_slime_size() as f64;
|
||||||
|
[s, s, s]
|
||||||
|
}
|
||||||
|
TrackedData::Marker(_) => [0.0, 0.0, 0.0],
|
||||||
|
TrackedData::Minecart(_) => [0.98, 0.7, 0.98],
|
||||||
|
TrackedData::ChestMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
|
TrackedData::CommandBlockMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
|
TrackedData::FurnaceMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
|
TrackedData::HopperMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
|
TrackedData::SpawnerMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
|
TrackedData::TntMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
|
TrackedData::Mule(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]),
|
||||||
|
TrackedData::Mooshroom(e) => baby(e.get_child(), [0.9, 1.4, 0.9]),
|
||||||
|
TrackedData::Ocelot(e) => baby(e.get_child(), [0.6, 0.7, 0.6]),
|
||||||
|
TrackedData::Painting(e) => {
|
||||||
|
let bounds: UVec3 = match e.get_variant() {
|
||||||
|
PaintingKind::Kebab => [1, 1, 1],
|
||||||
|
PaintingKind::Aztec => [1, 1, 1],
|
||||||
|
PaintingKind::Alban => [1, 1, 1],
|
||||||
|
PaintingKind::Aztec2 => [1, 1, 1],
|
||||||
|
PaintingKind::Bomb => [1, 1, 1],
|
||||||
|
PaintingKind::Plant => [1, 1, 1],
|
||||||
|
PaintingKind::Wasteland => [1, 1, 1],
|
||||||
|
PaintingKind::Pool => [2, 1, 2],
|
||||||
|
PaintingKind::Courbet => [2, 1, 2],
|
||||||
|
PaintingKind::Sea => [2, 1, 2],
|
||||||
|
PaintingKind::Sunset => [2, 1, 2],
|
||||||
|
PaintingKind::Creebet => [2, 1, 2],
|
||||||
|
PaintingKind::Wanderer => [1, 2, 1],
|
||||||
|
PaintingKind::Graham => [1, 2, 1],
|
||||||
|
PaintingKind::Match => [2, 2, 2],
|
||||||
|
PaintingKind::Bust => [2, 2, 2],
|
||||||
|
PaintingKind::Stage => [2, 2, 2],
|
||||||
|
PaintingKind::Void => [2, 2, 2],
|
||||||
|
PaintingKind::SkullAndRoses => [2, 2, 2],
|
||||||
|
PaintingKind::Wither => [2, 2, 2],
|
||||||
|
PaintingKind::Fighters => [4, 2, 4],
|
||||||
|
PaintingKind::Pointer => [4, 4, 4],
|
||||||
|
PaintingKind::Pigscene => [4, 4, 4],
|
||||||
|
PaintingKind::BurningSkull => [4, 4, 4],
|
||||||
|
PaintingKind::Skeleton => [4, 3, 4],
|
||||||
|
PaintingKind::Earth => [2, 2, 2],
|
||||||
|
PaintingKind::Wind => [2, 2, 2],
|
||||||
|
PaintingKind::Water => [2, 2, 2],
|
||||||
|
PaintingKind::Fire => [2, 2, 2],
|
||||||
|
PaintingKind::DonkeyKong => [4, 3, 4],
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let mut center_pos = self.position + 0.5;
|
||||||
|
|
||||||
|
let (facing_x, facing_z, cc_facing_x, cc_facing_z) =
|
||||||
|
match ((self.yaw + 45.0).rem_euclid(360.0) / 90.0) as u8 {
|
||||||
|
0 => (0, 1, 1, 0), // South
|
||||||
|
1 => (-1, 0, 0, 1), // West
|
||||||
|
2 => (0, -1, -1, 0), // North
|
||||||
|
_ => (1, 0, 0, -1), // East
|
||||||
|
};
|
||||||
|
|
||||||
|
center_pos.x -= facing_x as f64 * 0.46875;
|
||||||
|
center_pos.z -= facing_z as f64 * 0.46875;
|
||||||
|
|
||||||
|
center_pos.x += cc_facing_x as f64 * if bounds.x % 2 == 0 { 0.5 } else { 0.0 };
|
||||||
|
center_pos.y += if bounds.y % 2 == 0 { 0.5 } else { 0.0 };
|
||||||
|
center_pos.z += cc_facing_z as f64 * if bounds.z % 2 == 0 { 0.5 } else { 0.0 };
|
||||||
|
|
||||||
|
let bounds = match (facing_x, facing_z) {
|
||||||
|
(1, 0) | (-1, 0) => DVec3::new(0.0625, bounds.y as f64, bounds.z as f64),
|
||||||
|
_ => DVec3::new(bounds.x as f64, bounds.y as f64, 0.0625),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Aabb {
|
||||||
|
min: center_pos - bounds / 2.0,
|
||||||
|
max: center_pos + bounds / 2.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
TrackedData::Panda(e) => baby(e.get_child(), [1.3, 1.25, 1.3]),
|
||||||
|
TrackedData::Parrot(_) => [0.5, 0.9, 0.5],
|
||||||
|
TrackedData::Phantom(_) => [0.9, 0.5, 0.9],
|
||||||
|
TrackedData::Pig(e) => baby(e.get_child(), [0.9, 0.9, 0.9]),
|
||||||
|
TrackedData::Piglin(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
|
TrackedData::PiglinBrute(_) => [0.6, 1.95, 0.6],
|
||||||
|
TrackedData::Pillager(_) => [0.6, 1.95, 0.6],
|
||||||
|
TrackedData::PolarBear(e) => baby(e.get_child(), [1.4, 1.4, 1.4]),
|
||||||
|
TrackedData::Tnt(_) => [0.98, 0.98, 0.98],
|
||||||
|
TrackedData::Pufferfish(_) => [0.7, 0.7, 0.7],
|
||||||
|
TrackedData::Rabbit(e) => baby(e.get_child(), [0.4, 0.5, 0.4]),
|
||||||
|
TrackedData::Ravager(_) => [1.95, 2.2, 1.95],
|
||||||
|
TrackedData::Salmon(_) => [0.7, 0.4, 0.7],
|
||||||
|
TrackedData::Sheep(e) => baby(e.get_child(), [0.9, 1.3, 0.9]),
|
||||||
|
TrackedData::Shulker(e) => {
|
||||||
|
const PI: f64 = std::f64::consts::PI;
|
||||||
|
|
||||||
|
let pos = self.position + 0.5;
|
||||||
|
let mut min = pos - 0.5;
|
||||||
|
let mut max = pos + 0.5;
|
||||||
|
|
||||||
|
let peek = 0.5 - f64::cos(e.get_peek_amount() as f64 * 0.01 * PI) * 0.5;
|
||||||
|
|
||||||
|
match e.get_attached_face() {
|
||||||
|
Facing::Down => max.y += peek,
|
||||||
|
Facing::Up => min.y -= peek,
|
||||||
|
Facing::North => max.z += peek,
|
||||||
|
Facing::South => min.z -= peek,
|
||||||
|
Facing::West => max.x += peek,
|
||||||
|
Facing::East => min.x -= peek,
|
||||||
|
}
|
||||||
|
|
||||||
|
return Aabb { min, max };
|
||||||
|
}
|
||||||
|
TrackedData::ShulkerBullet(_) => [0.3125, 0.3125, 0.3125],
|
||||||
|
TrackedData::Silverfish(_) => [0.4, 0.3, 0.4],
|
||||||
|
TrackedData::Skeleton(_) => [0.6, 1.99, 0.6],
|
||||||
|
TrackedData::SkeletonHorse(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]),
|
||||||
|
TrackedData::Slime(e) => {
|
||||||
|
let s = 0.5202 * e.get_slime_size() as f64;
|
||||||
|
[s, s, s]
|
||||||
|
}
|
||||||
|
TrackedData::SmallFireball(_) => [0.3125, 0.3125, 0.3125],
|
||||||
|
TrackedData::SnowGolem(_) => [0.7, 1.9, 0.7],
|
||||||
|
TrackedData::Snowball(_) => [0.25, 0.25, 0.25],
|
||||||
|
TrackedData::SpectralArrow(_) => [0.5, 0.5, 0.5],
|
||||||
|
TrackedData::Spider(_) => [1.4, 0.9, 1.4],
|
||||||
|
TrackedData::Squid(_) => [0.8, 0.8, 0.8],
|
||||||
|
TrackedData::Stray(_) => [0.6, 1.99, 0.6],
|
||||||
|
TrackedData::Strider(e) => baby(e.get_child(), [0.9, 1.7, 0.9]),
|
||||||
|
TrackedData::Egg(_) => [0.25, 0.25, 0.25],
|
||||||
|
TrackedData::EnderPearl(_) => [0.25, 0.25, 0.25],
|
||||||
|
TrackedData::ExperienceBottle(_) => [0.25, 0.25, 0.25],
|
||||||
|
TrackedData::Potion(_) => [0.25, 0.25, 0.25],
|
||||||
|
TrackedData::Trident(_) => [0.5, 0.5, 0.5],
|
||||||
|
TrackedData::TraderLlama(_) => [0.9, 1.87, 0.9],
|
||||||
|
TrackedData::TropicalFish(_) => [0.5, 0.4, 0.5],
|
||||||
|
TrackedData::Turtle(e) => {
|
||||||
|
if e.get_child() {
|
||||||
|
[0.36, 0.12, 0.36]
|
||||||
|
} else {
|
||||||
|
[1.2, 0.4, 1.2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TrackedData::Vex(_) => [0.4, 0.8, 0.4],
|
||||||
|
TrackedData::Villager(e) => baby(e.get_child(), [0.6, 1.95, 0.6]),
|
||||||
|
TrackedData::Vindicator(_) => [0.6, 1.95, 0.6],
|
||||||
|
TrackedData::WanderingTrader(_) => [0.6, 1.95, 0.6],
|
||||||
|
TrackedData::Witch(_) => [0.6, 1.95, 0.6],
|
||||||
|
TrackedData::Wither(_) => [0.9, 3.5, 0.9],
|
||||||
|
TrackedData::WitherSkeleton(_) => [0.7, 2.4, 0.7],
|
||||||
|
TrackedData::WitherSkull(_) => [0.3125, 0.3125, 0.3125],
|
||||||
|
TrackedData::Wolf(e) => baby(e.get_child(), [0.6, 0.85, 0.6]),
|
||||||
|
TrackedData::Zoglin(e) => baby(e.get_baby(), [1.39648, 1.4, 1.39648]),
|
||||||
|
TrackedData::Zombie(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
|
TrackedData::ZombieHorse(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]),
|
||||||
|
TrackedData::ZombieVillager(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
|
TrackedData::ZombifiedPiglin(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
|
TrackedData::Player(e) => match e.get_pose() {
|
||||||
|
Pose::Standing => [0.6, 1.8, 0.6],
|
||||||
|
Pose::Sleeping => [0.2, 0.2, 0.2],
|
||||||
|
Pose::FallFlying => [0.6, 0.6, 0.6],
|
||||||
|
Pose::Swimming => [0.6, 0.6, 0.6],
|
||||||
|
Pose::SpinAttack => [0.6, 0.6, 0.6],
|
||||||
|
Pose::Sneaking => [0.6, 1.5, 0.6],
|
||||||
|
Pose::Dying => [0.2, 0.2, 0.2],
|
||||||
|
_ => [0.6, 1.8, 0.6],
|
||||||
|
},
|
||||||
|
TrackedData::FishingBobber(_) => [0.25, 0.25, 0.25],
|
||||||
|
};
|
||||||
|
|
||||||
|
Aabb::from_bottom_size(self.position, dimensions)
|
||||||
|
}
|
||||||
|
*/
|
|
@ -2,9 +2,11 @@ use std::borrow::Cow;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::iter::FusedIterator;
|
use std::iter::FusedIterator;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use bevy_app::{CoreSet, Plugin};
|
use bevy_app::{CoreSet, Plugin};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
use bevy_ecs::query::WorldQuery;
|
||||||
pub use chunk::{Block, BlockEntity, BlockMut, BlockRef, Chunk};
|
pub use chunk::{Block, BlockEntity, BlockMut, BlockRef, Chunk};
|
||||||
pub use chunk_entry::*;
|
pub use chunk_entry::*;
|
||||||
use glam::{DVec3, Vec3};
|
use glam::{DVec3, Vec3};
|
||||||
|
@ -12,19 +14,29 @@ use num::integer::div_ceil;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use valence_protocol::array::LengthPrefixedArray;
|
use valence_protocol::array::LengthPrefixedArray;
|
||||||
use valence_protocol::block_pos::BlockPos;
|
use valence_protocol::block_pos::BlockPos;
|
||||||
|
use valence_protocol::byte_angle::ByteAngle;
|
||||||
use valence_protocol::packet::s2c::play::particle::Particle;
|
use valence_protocol::packet::s2c::play::particle::Particle;
|
||||||
use valence_protocol::packet::s2c::play::{OverlayMessageS2c, ParticleS2c, PlaySoundS2c};
|
use valence_protocol::packet::s2c::play::{
|
||||||
|
EntityAnimationS2c, EntityPositionS2c, EntitySetHeadYawS2c, EntityStatusS2c,
|
||||||
|
EntityTrackerUpdateS2c, EntityVelocityUpdateS2c, MoveRelativeS2c, OverlayMessageS2c,
|
||||||
|
ParticleS2c, PlaySoundS2c, RotateAndMoveRelativeS2c, RotateS2c,
|
||||||
|
};
|
||||||
use valence_protocol::sound::Sound;
|
use valence_protocol::sound::Sound;
|
||||||
use valence_protocol::text::Text;
|
use valence_protocol::text::Text;
|
||||||
use valence_protocol::types::SoundCategory;
|
use valence_protocol::types::SoundCategory;
|
||||||
|
use valence_protocol::var_int::VarInt;
|
||||||
use valence_protocol::Packet;
|
use valence_protocol::Packet;
|
||||||
|
|
||||||
use crate::component::Despawned;
|
use crate::client::FlushPacketsSet;
|
||||||
|
use crate::component::{Despawned, Location, Look, OldLocation, OldPosition, OnGround, Position};
|
||||||
use crate::dimension::DimensionId;
|
use crate::dimension::DimensionId;
|
||||||
use crate::entity::{InitEntitiesSet, McEntity};
|
use crate::entity::{
|
||||||
|
EntityAnimations, EntityId, EntityKind, EntityStatuses, HeadYaw, InitEntitiesSet,
|
||||||
|
PacketByteRange, TrackedData, Velocity,
|
||||||
|
};
|
||||||
use crate::packet::{PacketWriter, WritePacket};
|
use crate::packet::{PacketWriter, WritePacket};
|
||||||
use crate::prelude::FlushPacketsSet;
|
|
||||||
use crate::server::{Server, SharedServer};
|
use crate::server::{Server, SharedServer};
|
||||||
|
use crate::util::velocity_to_packet_units;
|
||||||
use crate::view::ChunkPos;
|
use crate::view::ChunkPos;
|
||||||
|
|
||||||
mod chunk;
|
mod chunk;
|
||||||
|
@ -438,16 +450,21 @@ impl Instance {
|
||||||
pub(crate) struct InstancePlugin;
|
pub(crate) struct InstancePlugin;
|
||||||
|
|
||||||
#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub(crate) struct UpdateInstancesPreClientSet;
|
pub(crate) struct WriteUpdatePacketsToInstancesSet;
|
||||||
|
|
||||||
impl Plugin for InstancePlugin {
|
impl Plugin for InstancePlugin {
|
||||||
fn build(&self, app: &mut bevy_app::App) {
|
fn build(&self, app: &mut bevy_app::App) {
|
||||||
app.configure_set(
|
app.configure_set(
|
||||||
UpdateInstancesPreClientSet
|
WriteUpdatePacketsToInstancesSet
|
||||||
.after(InitEntitiesSet)
|
.after(InitEntitiesSet)
|
||||||
.in_base_set(CoreSet::PostUpdate),
|
.in_base_set(CoreSet::PostUpdate),
|
||||||
)
|
)
|
||||||
.add_system(update_instances_pre_client.in_set(UpdateInstancesPreClientSet))
|
.add_system(
|
||||||
|
update_entity_cell_positions
|
||||||
|
.before(WriteUpdatePacketsToInstancesSet)
|
||||||
|
.in_base_set(CoreSet::PostUpdate),
|
||||||
|
)
|
||||||
|
.add_system(write_update_packets_to_instances.in_set(WriteUpdatePacketsToInstancesSet))
|
||||||
.add_system(
|
.add_system(
|
||||||
update_instances_post_client
|
update_instances_post_client
|
||||||
.after(FlushPacketsSet)
|
.after(FlushPacketsSet)
|
||||||
|
@ -459,56 +476,63 @@ impl Plugin for InstancePlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_instances_pre_client(
|
/// Handles entities moving from one chunk to another.
|
||||||
|
fn update_entity_cell_positions(
|
||||||
|
entities: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
&Position,
|
||||||
|
&OldPosition,
|
||||||
|
&Location,
|
||||||
|
&OldLocation,
|
||||||
|
Option<&Despawned>,
|
||||||
|
),
|
||||||
|
(With<EntityKind>, Or<(Changed<Position>, With<Despawned>)>),
|
||||||
|
>,
|
||||||
mut instances: Query<&mut Instance>,
|
mut instances: Query<&mut Instance>,
|
||||||
mut entities: Query<(Entity, &mut McEntity, Option<&Despawned>)>,
|
|
||||||
server: Res<Server>,
|
|
||||||
) {
|
) {
|
||||||
for (entity_id, entity, despawned) in &entities {
|
for (entity, pos, old_pos, loc, old_loc, despawned) in &entities {
|
||||||
let pos = ChunkPos::at(entity.position().x, entity.position().z);
|
let pos = ChunkPos::at(pos.0.x, pos.0.z);
|
||||||
let old_pos = ChunkPos::at(entity.old_position().x, entity.old_position().z);
|
let old_pos = ChunkPos::at(old_pos.get().x, old_pos.get().z);
|
||||||
|
|
||||||
let instance = entity.instance();
|
|
||||||
let old_instance = entity.old_instance();
|
|
||||||
|
|
||||||
if despawned.is_some() {
|
if despawned.is_some() {
|
||||||
// Entity was deleted. Remove it from the chunk it was in, if it was in a chunk
|
// Entity was deleted. Remove it from the chunk it was in, if it was in a chunk
|
||||||
// at all.
|
// at all.
|
||||||
if let Ok(mut old_instance) = instances.get_mut(old_instance) {
|
if let Ok(mut old_instance) = instances.get_mut(old_loc.get()) {
|
||||||
if let Some(old_cell) = old_instance.partition.get_mut(&old_pos) {
|
if let Some(old_cell) = old_instance.partition.get_mut(&old_pos) {
|
||||||
if old_cell.entities.remove(&entity_id) {
|
if old_cell.entities.remove(&entity) {
|
||||||
old_cell.outgoing.push((entity_id, None));
|
old_cell.outgoing.push((entity, None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if old_instance != instance {
|
} else if old_loc.get() != loc.0 {
|
||||||
// Entity changed the instance it is in. Remove it from old cell and
|
// Entity changed the instance it is in. Remove it from old cell and
|
||||||
// insert it in the new cell.
|
// insert it in the new cell.
|
||||||
|
|
||||||
// TODO: skip marker entity?
|
// TODO: skip marker entity?
|
||||||
|
|
||||||
if let Ok(mut old_instance) = instances.get_mut(old_instance) {
|
if let Ok(mut old_instance) = instances.get_mut(old_loc.get()) {
|
||||||
if let Some(old_cell) = old_instance.partition.get_mut(&old_pos) {
|
if let Some(old_cell) = old_instance.partition.get_mut(&old_pos) {
|
||||||
if old_cell.entities.remove(&entity_id) {
|
if old_cell.entities.remove(&entity) {
|
||||||
old_cell.outgoing.push((entity_id, None));
|
old_cell.outgoing.push((entity, None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(mut instance) = instances.get_mut(instance) {
|
if let Ok(mut instance) = instances.get_mut(loc.0) {
|
||||||
match instance.partition.entry(pos) {
|
match instance.partition.entry(pos) {
|
||||||
Entry::Occupied(oe) => {
|
Entry::Occupied(oe) => {
|
||||||
let cell = oe.into_mut();
|
let cell = oe.into_mut();
|
||||||
if cell.entities.insert(entity_id) {
|
if cell.entities.insert(entity) {
|
||||||
cell.incoming.push((entity_id, None));
|
cell.incoming.push((entity, None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Entry::Vacant(ve) => {
|
Entry::Vacant(ve) => {
|
||||||
ve.insert(PartitionCell {
|
ve.insert(PartitionCell {
|
||||||
chunk: None,
|
chunk: None,
|
||||||
chunk_removed: false,
|
chunk_removed: false,
|
||||||
entities: BTreeSet::from([entity_id]),
|
entities: BTreeSet::from([entity]),
|
||||||
incoming: vec![(entity_id, None)],
|
incoming: vec![(entity, None)],
|
||||||
outgoing: vec![],
|
outgoing: vec![],
|
||||||
packet_buf: vec![],
|
packet_buf: vec![],
|
||||||
});
|
});
|
||||||
|
@ -521,26 +545,26 @@ fn update_instances_pre_client(
|
||||||
|
|
||||||
// TODO: skip marker entity?
|
// TODO: skip marker entity?
|
||||||
|
|
||||||
if let Ok(mut instance) = instances.get_mut(instance) {
|
if let Ok(mut instance) = instances.get_mut(loc.0) {
|
||||||
if let Some(old_cell) = instance.partition.get_mut(&old_pos) {
|
if let Some(old_cell) = instance.partition.get_mut(&old_pos) {
|
||||||
if old_cell.entities.remove(&entity_id) {
|
if old_cell.entities.remove(&entity) {
|
||||||
old_cell.outgoing.push((entity_id, Some(pos)));
|
old_cell.outgoing.push((entity, Some(pos)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match instance.partition.entry(pos) {
|
match instance.partition.entry(pos) {
|
||||||
Entry::Occupied(oe) => {
|
Entry::Occupied(oe) => {
|
||||||
let cell = oe.into_mut();
|
let cell = oe.into_mut();
|
||||||
if cell.entities.insert(entity_id) {
|
if cell.entities.insert(entity) {
|
||||||
cell.incoming.push((entity_id, Some(old_pos)));
|
cell.incoming.push((entity, Some(old_pos)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Entry::Vacant(ve) => {
|
Entry::Vacant(ve) => {
|
||||||
ve.insert(PartitionCell {
|
ve.insert(PartitionCell {
|
||||||
chunk: None,
|
chunk: None,
|
||||||
chunk_removed: false,
|
chunk_removed: false,
|
||||||
entities: BTreeSet::from([entity_id]),
|
entities: BTreeSet::from([entity]),
|
||||||
incoming: vec![(entity_id, Some(old_pos))],
|
incoming: vec![(entity, Some(old_pos))],
|
||||||
outgoing: vec![],
|
outgoing: vec![],
|
||||||
packet_buf: vec![],
|
packet_buf: vec![],
|
||||||
});
|
});
|
||||||
|
@ -552,7 +576,15 @@ fn update_instances_pre_client(
|
||||||
// we need to do.
|
// we need to do.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes update packets from entities and chunks into each cell's packet
|
||||||
|
/// buffer.
|
||||||
|
fn write_update_packets_to_instances(
|
||||||
|
mut instances: Query<&mut Instance>,
|
||||||
|
mut entities: Query<UpdateEntityQuery, (With<EntityKind>, Without<Despawned>)>,
|
||||||
|
server: Res<Server>,
|
||||||
|
) {
|
||||||
let mut scratch_1 = vec![];
|
let mut scratch_1 = vec![];
|
||||||
let mut scratch_2 = vec![];
|
let mut scratch_2 = vec![];
|
||||||
|
|
||||||
|
@ -574,15 +606,11 @@ fn update_instances_pre_client(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache entity update packets into the packet buffer of this cell.
|
// Cache entity update packets into the packet buffer of this cell.
|
||||||
for &id in &cell.entities {
|
for &entity in &cell.entities {
|
||||||
let (_, mut entity, despawned) = entities
|
let mut entity = entities
|
||||||
.get_mut(id)
|
.get_mut(entity)
|
||||||
.expect("missing entity in partition cell");
|
.expect("missing entity in partition cell");
|
||||||
|
|
||||||
if despawned.is_some() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let start = cell.packet_buf.len();
|
let start = cell.packet_buf.len();
|
||||||
|
|
||||||
let writer = PacketWriter::new(
|
let writer = PacketWriter::new(
|
||||||
|
@ -591,11 +619,121 @@ fn update_instances_pre_client(
|
||||||
&mut scratch_2,
|
&mut scratch_2,
|
||||||
);
|
);
|
||||||
|
|
||||||
entity.write_update_packets(writer, &mut scratch_1);
|
entity.write_update_packets(writer);
|
||||||
|
|
||||||
let end = cell.packet_buf.len();
|
let end = cell.packet_buf.len();
|
||||||
|
|
||||||
entity.self_update_range = start..end;
|
entity.packet_byte_range.0 = start..end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(WorldQuery)]
|
||||||
|
#[world_query(mutable)]
|
||||||
|
struct UpdateEntityQuery {
|
||||||
|
id: &'static EntityId,
|
||||||
|
pos: &'static Position,
|
||||||
|
old_pos: &'static OldPosition,
|
||||||
|
loc: &'static Location,
|
||||||
|
old_loc: &'static OldLocation,
|
||||||
|
look: Ref<'static, Look>,
|
||||||
|
head_yaw: Ref<'static, HeadYaw>,
|
||||||
|
on_ground: &'static OnGround,
|
||||||
|
velocity: Ref<'static, Velocity>,
|
||||||
|
tracked_data: &'static TrackedData,
|
||||||
|
statuses: &'static EntityStatuses,
|
||||||
|
animations: &'static EntityAnimations,
|
||||||
|
packet_byte_range: &'static mut PacketByteRange,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateEntityQueryItem<'_> {
|
||||||
|
fn write_update_packets(&self, mut writer: impl WritePacket) {
|
||||||
|
// TODO: @RJ I saw you're using UpdateEntityPosition and UpdateEntityRotation sometimes. These two packets are actually broken on the client and will erase previous position/rotation https://bugs.mojang.com/browse/MC-255263 -Moulberry
|
||||||
|
|
||||||
|
let entity_id = VarInt(self.id.get());
|
||||||
|
|
||||||
|
let position_delta = self.pos.0 - self.old_pos.get();
|
||||||
|
let needs_teleport = position_delta.abs().max_element() >= 8.0;
|
||||||
|
let changed_position = self.pos.0 != self.old_pos.get();
|
||||||
|
|
||||||
|
if changed_position && !needs_teleport && self.look.is_changed() {
|
||||||
|
writer.write_packet(&RotateAndMoveRelativeS2c {
|
||||||
|
entity_id,
|
||||||
|
delta: (position_delta * 4096.0).to_array().map(|v| v as i16),
|
||||||
|
yaw: ByteAngle::from_degrees(self.look.yaw),
|
||||||
|
pitch: ByteAngle::from_degrees(self.look.pitch),
|
||||||
|
on_ground: self.on_ground.0,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if changed_position && !needs_teleport {
|
||||||
|
writer.write_packet(&MoveRelativeS2c {
|
||||||
|
entity_id,
|
||||||
|
delta: (position_delta * 4096.0).to_array().map(|v| v as i16),
|
||||||
|
on_ground: self.on_ground.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.look.is_changed() {
|
||||||
|
writer.write_packet(&RotateS2c {
|
||||||
|
entity_id,
|
||||||
|
yaw: ByteAngle::from_degrees(self.look.yaw),
|
||||||
|
pitch: ByteAngle::from_degrees(self.look.pitch),
|
||||||
|
on_ground: self.on_ground.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if needs_teleport {
|
||||||
|
writer.write_packet(&EntityPositionS2c {
|
||||||
|
entity_id,
|
||||||
|
position: self.pos.0.to_array(),
|
||||||
|
yaw: ByteAngle::from_degrees(self.look.yaw),
|
||||||
|
pitch: ByteAngle::from_degrees(self.look.pitch),
|
||||||
|
on_ground: self.on_ground.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.velocity.is_changed() {
|
||||||
|
writer.write_packet(&EntityVelocityUpdateS2c {
|
||||||
|
entity_id,
|
||||||
|
velocity: velocity_to_packet_units(self.velocity.0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.head_yaw.is_changed() {
|
||||||
|
writer.write_packet(&EntitySetHeadYawS2c {
|
||||||
|
entity_id,
|
||||||
|
head_yaw: ByteAngle::from_degrees(self.head_yaw.0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(update_data) = self.tracked_data.update_data() {
|
||||||
|
writer.write_packet(&EntityTrackerUpdateS2c {
|
||||||
|
entity_id,
|
||||||
|
metadata: update_data.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.statuses.0 != 0 {
|
||||||
|
for i in 0..mem::size_of_val(self.statuses) {
|
||||||
|
if (self.statuses.0 >> i) & 1 == 1 {
|
||||||
|
writer.write_packet(&EntityStatusS2c {
|
||||||
|
entity_id: entity_id.0,
|
||||||
|
entity_status: i as u8,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.animations.0 != 0 {
|
||||||
|
for i in 0..mem::size_of_val(self.animations) {
|
||||||
|
if (self.animations.0 >> i) & 1 == 1 {
|
||||||
|
writer.write_packet(&EntityAnimationS2c {
|
||||||
|
entity_id,
|
||||||
|
animation: i as u8,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -621,7 +759,7 @@ fn update_instances_post_client(mut instances: Query<&mut Instance>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn check_instance_invariants(instances: Query<&Instance>, entities: Query<&McEntity>) {
|
fn check_instance_invariants(instances: Query<&Instance>, entities: Query<(), With<EntityKind>>) {
|
||||||
for instance in &instances {
|
for instance in &instances {
|
||||||
for (pos, cell) in &instance.partition {
|
for (pos, cell) in &instance.partition {
|
||||||
for &id in &cell.entities {
|
for &id in &cell.entities {
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
)]
|
)]
|
||||||
#![allow(clippy::type_complexity)] // ECS queries are often complicated.
|
#![allow(clippy::type_complexity)] // ECS queries are often complicated.
|
||||||
|
|
||||||
use bevy_ecs::prelude::*;
|
|
||||||
pub use {
|
pub use {
|
||||||
anyhow, async_trait, bevy_app, bevy_ecs, uuid, valence_nbt as nbt, valence_protocol as protocol,
|
anyhow, async_trait, bevy_app, bevy_ecs, uuid, valence_nbt as nbt, valence_protocol as protocol,
|
||||||
};
|
};
|
||||||
|
@ -57,9 +56,7 @@ pub mod prelude {
|
||||||
AsyncCallbacks, ConnectionMode, PlayerSampleEntry, ServerListPing, ServerPlugin,
|
AsyncCallbacks, ConnectionMode, PlayerSampleEntry, ServerListPing, ServerPlugin,
|
||||||
};
|
};
|
||||||
pub use dimension::{Dimension, DimensionId};
|
pub use dimension::{Dimension, DimensionId};
|
||||||
pub use entity::{
|
pub use entity::{EntityAnimation, EntityKind, EntityManager, EntityStatus, HeadYaw};
|
||||||
EntityAnimation, EntityKind, EntityStatus, McEntity, McEntityManager, TrackedData,
|
|
||||||
};
|
|
||||||
pub use glam::DVec3;
|
pub use glam::DVec3;
|
||||||
pub use instance::{Block, BlockMut, BlockRef, Chunk, Instance};
|
pub use instance::{Block, BlockMut, BlockRef, Chunk, Instance};
|
||||||
pub use inventory::{Inventory, InventoryKind, OpenInventory};
|
pub use inventory::{Inventory, InventoryKind, OpenInventory};
|
||||||
|
@ -79,8 +76,3 @@ pub mod prelude {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Let's pretend that [`NULL_ENTITY`] was created by spawning an entity,
|
|
||||||
/// immediately despawning it, and then stealing its [`Entity`] ID. The user
|
|
||||||
/// doesn't need to know about this.
|
|
||||||
const NULL_ENTITY: Entity = Entity::from_bits(u64::MAX);
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
pub use glam::*;
|
pub use glam::*;
|
||||||
|
|
||||||
|
use crate::config::DEFAULT_TPS;
|
||||||
|
|
||||||
/// An axis-aligned bounding box. `min` is expected to be <= `max`
|
/// An axis-aligned bounding box. `min` is expected to be <= `max`
|
||||||
/// componentwise.
|
/// componentwise.
|
||||||
#[derive(Copy, Clone, PartialEq, Default, Debug)]
|
#[derive(Copy, Clone, PartialEq, Default, Debug)]
|
||||||
|
@ -18,6 +20,7 @@ impl Aabb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn from_bottom_size(bottom: impl Into<DVec3>, size: impl Into<DVec3>) -> Self {
|
pub(crate) fn from_bottom_size(bottom: impl Into<DVec3>, size: impl Into<DVec3>) -> Self {
|
||||||
let bottom = bottom.into();
|
let bottom = bottom.into();
|
||||||
let size = size.into();
|
let size = size.into();
|
||||||
|
@ -110,3 +113,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn velocity_to_packet_units(vel: Vec3) -> [i16; 3] {
|
||||||
|
// The saturating casts to i16 are desirable.
|
||||||
|
(8000.0 / DEFAULT_TPS as f32 * vel)
|
||||||
|
.to_array()
|
||||||
|
.map(|v| v as i16)
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ use bevy_ecs::prelude::*;
|
||||||
use valence_protocol::packet::s2c::play::game_state_change::GameEventKind;
|
use valence_protocol::packet::s2c::play::game_state_change::GameEventKind;
|
||||||
use valence_protocol::packet::s2c::play::GameStateChangeS2c;
|
use valence_protocol::packet::s2c::play::GameStateChangeS2c;
|
||||||
|
|
||||||
use crate::instance::UpdateInstancesPreClientSet;
|
use crate::instance::WriteUpdatePacketsToInstancesSet;
|
||||||
use crate::packet::WritePacket;
|
use crate::packet::WritePacket;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
@ -217,16 +217,14 @@ impl Plugin for WeatherPlugin {
|
||||||
app.configure_set(
|
app.configure_set(
|
||||||
UpdateWeatherPerInstanceSet
|
UpdateWeatherPerInstanceSet
|
||||||
.in_base_set(CoreSet::PostUpdate)
|
.in_base_set(CoreSet::PostUpdate)
|
||||||
.before(UpdateInstancesPreClientSet),
|
.before(WriteUpdatePacketsToInstancesSet),
|
||||||
);
|
)
|
||||||
|
.configure_set(
|
||||||
app.configure_set(
|
|
||||||
UpdateWeatherPerClientSet
|
UpdateWeatherPerClientSet
|
||||||
.in_base_set(CoreSet::PostUpdate)
|
.in_base_set(CoreSet::PostUpdate)
|
||||||
.before(FlushPacketsSet),
|
.before(FlushPacketsSet),
|
||||||
);
|
)
|
||||||
|
.add_systems(
|
||||||
app.add_systems(
|
|
||||||
(
|
(
|
||||||
handle_rain_begin_per_instance,
|
handle_rain_begin_per_instance,
|
||||||
handle_rain_change_per_instance,
|
handle_rain_change_per_instance,
|
||||||
|
@ -237,9 +235,8 @@ impl Plugin for WeatherPlugin {
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(UpdateWeatherPerInstanceSet)
|
.in_set(UpdateWeatherPerInstanceSet)
|
||||||
.before(UpdateWeatherPerClientSet),
|
.before(UpdateWeatherPerClientSet),
|
||||||
);
|
)
|
||||||
|
.add_systems(
|
||||||
app.add_systems(
|
|
||||||
(
|
(
|
||||||
handle_rain_begin_per_client,
|
handle_rain_begin_per_client,
|
||||||
handle_rain_change_per_client,
|
handle_rain_change_per_client,
|
||||||
|
@ -249,9 +246,12 @@ impl Plugin for WeatherPlugin {
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(UpdateWeatherPerClientSet),
|
.in_set(UpdateWeatherPerClientSet),
|
||||||
|
)
|
||||||
|
.add_system(
|
||||||
|
handle_weather_for_joined_player
|
||||||
|
.before(UpdateWeatherPerClientSet)
|
||||||
|
.in_base_set(CoreSet::PostUpdate),
|
||||||
);
|
);
|
||||||
|
|
||||||
app.add_system(handle_weather_for_joined_player.before(UpdateWeatherPerClientSet));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ valence_nbt = { version = "0.5.0", path = "../valence_nbt" }
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1.0.68"
|
anyhow = "1.0.68"
|
||||||
bevy_ecs = "0.10"
|
bevy_ecs = "0.10"
|
||||||
clap = "4.1.4"
|
clap = { version = "4.1.4", features = ["derive"] }
|
||||||
criterion = "0.4.0"
|
criterion = "0.4.0"
|
||||||
flume = "0.10.14"
|
flume = "0.10.14"
|
||||||
fs_extra = "1.2.0"
|
fs_extra = "1.2.0"
|
||||||
|
|
|
@ -7,8 +7,8 @@ use clap::Parser;
|
||||||
use flume::{Receiver, Sender};
|
use flume::{Receiver, Sender};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use valence::bevy_app::AppExit;
|
use valence::bevy_app::AppExit;
|
||||||
use valence::client::despawn_disconnected_clients;
|
use valence::client::{default_event_handler, despawn_disconnected_clients};
|
||||||
use valence::client::event::default_event_handler;
|
use valence::entity::player::PlayerBundle;
|
||||||
use valence::prelude::*;
|
use valence::prelude::*;
|
||||||
use valence_anvil::{AnvilChunk, AnvilWorld};
|
use valence_anvil::{AnvilChunk, AnvilWorld};
|
||||||
|
|
||||||
|
@ -90,28 +90,20 @@ fn setup(world: &mut World) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_clients(
|
fn init_clients(
|
||||||
mut clients: Query<
|
mut clients: Query<(Entity, &mut GameMode, &mut IsFlat, &UniqueId), Added<Client>>,
|
||||||
(
|
|
||||||
&mut Position,
|
|
||||||
&mut Location,
|
|
||||||
&mut GameMode,
|
|
||||||
&mut IsFlat,
|
|
||||||
&UniqueId,
|
|
||||||
),
|
|
||||||
Added<Client>,
|
|
||||||
>,
|
|
||||||
instances: Query<Entity, With<Instance>>,
|
instances: Query<Entity, With<Instance>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (mut pos, mut loc, mut game_mode, mut is_flat, uuid) in &mut clients {
|
for (entity, mut game_mode, mut is_flat, uuid) in &mut clients {
|
||||||
let instance = instances.single();
|
|
||||||
|
|
||||||
pos.0 = SPAWN_POS;
|
|
||||||
loc.0 = instance;
|
|
||||||
*game_mode = GameMode::Creative;
|
*game_mode = GameMode::Creative;
|
||||||
is_flat.0 = true;
|
is_flat.0 = true;
|
||||||
|
|
||||||
commands.spawn(McEntity::with_uuid(EntityKind::Player, instance, uuid.0));
|
commands.entity(entity).insert(PlayerBundle {
|
||||||
|
location: Location(instances.single()),
|
||||||
|
position: Position(SPAWN_POS),
|
||||||
|
uuid: *uuid,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -504,7 +504,7 @@ mod tests {
|
||||||
use crate::ident::Ident;
|
use crate::ident::Ident;
|
||||||
use crate::item::{ItemKind, ItemStack};
|
use crate::item::{ItemKind, ItemStack};
|
||||||
use crate::text::{Text, TextFormat};
|
use crate::text::{Text, TextFormat};
|
||||||
use crate::tracked_data::PaintingKind;
|
use crate::types::Hand;
|
||||||
use crate::var_long::VarLong;
|
use crate::var_long::VarLong;
|
||||||
use crate::Decode;
|
use crate::Decode;
|
||||||
|
|
||||||
|
@ -520,7 +520,7 @@ mod tests {
|
||||||
d: f32,
|
d: f32,
|
||||||
e: f64,
|
e: f64,
|
||||||
f: BlockPos,
|
f: BlockPos,
|
||||||
g: PaintingKind,
|
g: Hand,
|
||||||
h: Ident<&'a str>,
|
h: Ident<&'a str>,
|
||||||
i: Option<ItemStack>,
|
i: Option<ItemStack>,
|
||||||
j: Text,
|
j: Text,
|
||||||
|
@ -540,7 +540,7 @@ mod tests {
|
||||||
d: 5.001,
|
d: 5.001,
|
||||||
e: 1e10,
|
e: 1e10,
|
||||||
f: BlockPos::new(1, 2, 3),
|
f: BlockPos::new(1, 2, 3),
|
||||||
g: PaintingKind::DonkeyKong,
|
g: Hand::Off,
|
||||||
h: Ident::new("minecraft:whatever").unwrap(),
|
h: Ident::new("minecraft:whatever").unwrap(),
|
||||||
i: Some(ItemStack::new(ItemKind::WoodenSword, 12, None)),
|
i: Some(ItemStack::new(ItemKind::WoodenSword, 12, None)),
|
||||||
j: "my ".into_text() + "fancy".italic() + " text",
|
j: "my ".into_text() + "fancy".italic() + " text",
|
||||||
|
|
|
@ -40,6 +40,12 @@ impl ItemStack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ItemStack {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(ItemKind::Air, 1, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Encode for Option<ItemStack> {
|
impl Encode for Option<ItemStack> {
|
||||||
fn encode(&self, w: impl Write) -> Result<()> {
|
fn encode(&self, w: impl Write) -> Result<()> {
|
||||||
self.as_ref().encode(w)
|
self.as_ref().encode(w)
|
||||||
|
|
|
@ -94,7 +94,6 @@ pub mod packet;
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
pub mod sound;
|
pub mod sound;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod tracked_data;
|
|
||||||
pub mod translation_key;
|
pub mod translation_key;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod var_int;
|
pub mod var_int;
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub struct ClientSettingsC2s<'a> {
|
||||||
pub chat_mode: ChatMode,
|
pub chat_mode: ChatMode,
|
||||||
pub chat_colors: bool,
|
pub chat_colors: bool,
|
||||||
pub displayed_skin_parts: DisplayedSkinParts,
|
pub displayed_skin_parts: DisplayedSkinParts,
|
||||||
pub main_hand: MainHand,
|
pub main_arm: MainArm,
|
||||||
pub enable_text_filtering: bool,
|
pub enable_text_filtering: bool,
|
||||||
pub allow_server_listings: bool,
|
pub allow_server_listings: bool,
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ pub struct DisplayedSkinParts {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Encode, Decode)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Encode, Decode)]
|
||||||
pub enum MainHand {
|
pub enum MainArm {
|
||||||
Left,
|
Left,
|
||||||
#[default]
|
#[default]
|
||||||
Right,
|
Right,
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub struct ParticleS2c<'a> {
|
||||||
pub count: i32,
|
pub count: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum Particle {
|
pub enum Particle {
|
||||||
AmbientEntityEffect,
|
AmbientEntityEffect,
|
||||||
AngryVillager,
|
AngryVillager,
|
||||||
|
@ -232,6 +232,125 @@ impl Particle {
|
||||||
Particle::Scrape => 91,
|
Particle::Scrape => 91,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decodes the particle assuming the given particle ID.
|
||||||
|
pub fn decode_with_id(particle_id: i32, r: &mut &[u8]) -> anyhow::Result<Self> {
|
||||||
|
Ok(match particle_id {
|
||||||
|
0 => Particle::AmbientEntityEffect,
|
||||||
|
1 => Particle::AngryVillager,
|
||||||
|
2 => Particle::Block(BlockState::decode(r)?),
|
||||||
|
3 => Particle::BlockMarker(BlockState::decode(r)?),
|
||||||
|
4 => Particle::Bubble,
|
||||||
|
5 => Particle::Cloud,
|
||||||
|
6 => Particle::Crit,
|
||||||
|
7 => Particle::DamageIndicator,
|
||||||
|
8 => Particle::DragonBreath,
|
||||||
|
9 => Particle::DrippingLava,
|
||||||
|
10 => Particle::FallingLava,
|
||||||
|
11 => Particle::LandingLava,
|
||||||
|
12 => Particle::DrippingWater,
|
||||||
|
13 => Particle::FallingWater,
|
||||||
|
14 => Particle::Dust {
|
||||||
|
rgb: <[f32; 3]>::decode(r)?,
|
||||||
|
scale: f32::decode(r)?,
|
||||||
|
},
|
||||||
|
15 => Particle::DustColorTransition {
|
||||||
|
from_rgb: <[f32; 3]>::decode(r)?,
|
||||||
|
scale: f32::decode(r)?,
|
||||||
|
to_rgb: <[f32; 3]>::decode(r)?,
|
||||||
|
},
|
||||||
|
16 => Particle::Effect,
|
||||||
|
17 => Particle::ElderGuardian,
|
||||||
|
18 => Particle::EnchantedHit,
|
||||||
|
19 => Particle::Enchant,
|
||||||
|
20 => Particle::EndRod,
|
||||||
|
21 => Particle::EntityEffect,
|
||||||
|
22 => Particle::ExplosionEmitter,
|
||||||
|
23 => Particle::Explosion,
|
||||||
|
24 => Particle::SonicBoom,
|
||||||
|
25 => Particle::FallingDust(BlockState::decode(r)?),
|
||||||
|
26 => Particle::Firework,
|
||||||
|
27 => Particle::Fishing,
|
||||||
|
28 => Particle::Flame,
|
||||||
|
29 => Particle::SculkSoul,
|
||||||
|
30 => Particle::SculkCharge {
|
||||||
|
roll: f32::decode(r)?,
|
||||||
|
},
|
||||||
|
31 => Particle::SculkChargePop,
|
||||||
|
32 => Particle::SoulFireFlame,
|
||||||
|
33 => Particle::Soul,
|
||||||
|
34 => Particle::Flash,
|
||||||
|
35 => Particle::HappyVillager,
|
||||||
|
36 => Particle::Composter,
|
||||||
|
37 => Particle::Heart,
|
||||||
|
38 => Particle::InstantEffect,
|
||||||
|
39 => Particle::Item(Decode::decode(r)?),
|
||||||
|
40 => match <&str>::decode(r)? {
|
||||||
|
"block" => Particle::VibrationBlock {
|
||||||
|
block_pos: BlockPos::decode(r)?,
|
||||||
|
ticks: VarInt::decode(r)?.0,
|
||||||
|
},
|
||||||
|
"entity" => Particle::VibrationEntity {
|
||||||
|
entity_id: VarInt::decode(r)?.0,
|
||||||
|
entity_eye_height: f32::decode(r)?,
|
||||||
|
ticks: VarInt::decode(r)?.0,
|
||||||
|
},
|
||||||
|
invalid => bail!("invalid vibration position source of \"{invalid}\""),
|
||||||
|
},
|
||||||
|
41 => Particle::ItemSlime,
|
||||||
|
42 => Particle::ItemSnowball,
|
||||||
|
43 => Particle::LargeSmoke,
|
||||||
|
44 => Particle::Lava,
|
||||||
|
45 => Particle::Mycelium,
|
||||||
|
46 => Particle::Note,
|
||||||
|
47 => Particle::Poof,
|
||||||
|
48 => Particle::Portal,
|
||||||
|
49 => Particle::Rain,
|
||||||
|
50 => Particle::Smoke,
|
||||||
|
51 => Particle::Sneeze,
|
||||||
|
52 => Particle::Spit,
|
||||||
|
53 => Particle::SquidInk,
|
||||||
|
54 => Particle::SweepAttack,
|
||||||
|
55 => Particle::TotemOfUndying,
|
||||||
|
56 => Particle::Underwater,
|
||||||
|
57 => Particle::Splash,
|
||||||
|
58 => Particle::Witch,
|
||||||
|
59 => Particle::BubblePop,
|
||||||
|
60 => Particle::CurrentDown,
|
||||||
|
61 => Particle::BubbleColumnUp,
|
||||||
|
62 => Particle::Nautilus,
|
||||||
|
63 => Particle::Dolphin,
|
||||||
|
64 => Particle::CampfireCosySmoke,
|
||||||
|
65 => Particle::CampfireSignalSmoke,
|
||||||
|
66 => Particle::DrippingHoney,
|
||||||
|
67 => Particle::FallingHoney,
|
||||||
|
68 => Particle::LandingHoney,
|
||||||
|
69 => Particle::FallingNectar,
|
||||||
|
70 => Particle::FallingSporeBlossom,
|
||||||
|
71 => Particle::Ash,
|
||||||
|
72 => Particle::CrimsonSpore,
|
||||||
|
73 => Particle::WarpedSpore,
|
||||||
|
74 => Particle::SporeBlossomAir,
|
||||||
|
75 => Particle::DrippingObsidianTear,
|
||||||
|
76 => Particle::FallingObsidianTear,
|
||||||
|
77 => Particle::LandingObsidianTear,
|
||||||
|
78 => Particle::ReversePortal,
|
||||||
|
79 => Particle::WhiteAsh,
|
||||||
|
80 => Particle::SmallFlame,
|
||||||
|
81 => Particle::Snowflake,
|
||||||
|
82 => Particle::DrippingDripstoneLava,
|
||||||
|
83 => Particle::FallingDripstoneLava,
|
||||||
|
84 => Particle::DrippingDripstoneWater,
|
||||||
|
85 => Particle::FallingDripstoneWater,
|
||||||
|
86 => Particle::GlowSquidInk,
|
||||||
|
87 => Particle::Glow,
|
||||||
|
88 => Particle::WaxOn,
|
||||||
|
89 => Particle::WaxOff,
|
||||||
|
90 => Particle::ElectricSpark,
|
||||||
|
91 => Particle::Scrape,
|
||||||
|
id => bail!("invalid particle ID of {id}"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encode for ParticleS2c<'_> {
|
impl Encode for ParticleS2c<'_> {
|
||||||
|
@ -243,7 +362,34 @@ impl Encode for ParticleS2c<'_> {
|
||||||
self.max_speed.encode(&mut w)?;
|
self.max_speed.encode(&mut w)?;
|
||||||
self.count.encode(&mut w)?;
|
self.count.encode(&mut w)?;
|
||||||
|
|
||||||
match self.particle.as_ref() {
|
self.particle.as_ref().encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Decode<'a> for ParticleS2c<'a> {
|
||||||
|
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
|
||||||
|
let particle_id = VarInt::decode(r)?.0;
|
||||||
|
let long_distance = bool::decode(r)?;
|
||||||
|
let position = <[f64; 3]>::decode(r)?;
|
||||||
|
let offset = <[f32; 3]>::decode(r)?;
|
||||||
|
let max_speed = f32::decode(r)?;
|
||||||
|
let particle_count = i32::decode(r)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
particle: Cow::Owned(Particle::decode_with_id(particle_id, r)?),
|
||||||
|
long_distance,
|
||||||
|
position,
|
||||||
|
offset,
|
||||||
|
max_speed,
|
||||||
|
count: particle_count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes the particle without an ID.
|
||||||
|
impl Encode for Particle {
|
||||||
|
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
|
||||||
|
match self {
|
||||||
Particle::Block(block_state) => block_state.encode(w),
|
Particle::Block(block_state) => block_state.encode(w),
|
||||||
Particle::BlockMarker(block_state) => block_state.encode(w),
|
Particle::BlockMarker(block_state) => block_state.encode(w),
|
||||||
Particle::Dust { rgb, scale } => {
|
Particle::Dust { rgb, scale } => {
|
||||||
|
@ -281,137 +427,3 @@ impl Encode for ParticleS2c<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Decode<'a> for ParticleS2c<'a> {
|
|
||||||
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
|
|
||||||
let particle_id = VarInt::decode(r)?.0;
|
|
||||||
let long_distance = bool::decode(r)?;
|
|
||||||
let position = <[f64; 3]>::decode(r)?;
|
|
||||||
let offset = <[f32; 3]>::decode(r)?;
|
|
||||||
let max_speed = f32::decode(r)?;
|
|
||||||
let particle_count = i32::decode(r)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
particle: Cow::Owned(match particle_id {
|
|
||||||
0 => Particle::AmbientEntityEffect,
|
|
||||||
1 => Particle::AngryVillager,
|
|
||||||
2 => Particle::Block(BlockState::decode(r)?),
|
|
||||||
3 => Particle::BlockMarker(BlockState::decode(r)?),
|
|
||||||
4 => Particle::Bubble,
|
|
||||||
5 => Particle::Cloud,
|
|
||||||
6 => Particle::Crit,
|
|
||||||
7 => Particle::DamageIndicator,
|
|
||||||
8 => Particle::DragonBreath,
|
|
||||||
9 => Particle::DrippingLava,
|
|
||||||
10 => Particle::FallingLava,
|
|
||||||
11 => Particle::LandingLava,
|
|
||||||
12 => Particle::DrippingWater,
|
|
||||||
13 => Particle::FallingWater,
|
|
||||||
14 => Particle::Dust {
|
|
||||||
rgb: <[f32; 3]>::decode(r)?,
|
|
||||||
scale: f32::decode(r)?,
|
|
||||||
},
|
|
||||||
15 => Particle::DustColorTransition {
|
|
||||||
from_rgb: <[f32; 3]>::decode(r)?,
|
|
||||||
scale: f32::decode(r)?,
|
|
||||||
to_rgb: <[f32; 3]>::decode(r)?,
|
|
||||||
},
|
|
||||||
16 => Particle::Effect,
|
|
||||||
17 => Particle::ElderGuardian,
|
|
||||||
18 => Particle::EnchantedHit,
|
|
||||||
19 => Particle::Enchant,
|
|
||||||
20 => Particle::EndRod,
|
|
||||||
21 => Particle::EntityEffect,
|
|
||||||
22 => Particle::ExplosionEmitter,
|
|
||||||
23 => Particle::Explosion,
|
|
||||||
24 => Particle::SonicBoom,
|
|
||||||
25 => Particle::FallingDust(BlockState::decode(r)?),
|
|
||||||
26 => Particle::Firework,
|
|
||||||
27 => Particle::Fishing,
|
|
||||||
28 => Particle::Flame,
|
|
||||||
29 => Particle::SculkSoul,
|
|
||||||
30 => Particle::SculkCharge {
|
|
||||||
roll: f32::decode(r)?,
|
|
||||||
},
|
|
||||||
31 => Particle::SculkChargePop,
|
|
||||||
32 => Particle::SoulFireFlame,
|
|
||||||
33 => Particle::Soul,
|
|
||||||
34 => Particle::Flash,
|
|
||||||
35 => Particle::HappyVillager,
|
|
||||||
36 => Particle::Composter,
|
|
||||||
37 => Particle::Heart,
|
|
||||||
38 => Particle::InstantEffect,
|
|
||||||
39 => Particle::Item(Decode::decode(r)?),
|
|
||||||
40 => match <&str>::decode(r)? {
|
|
||||||
"block" => Particle::VibrationBlock {
|
|
||||||
block_pos: BlockPos::decode(r)?,
|
|
||||||
ticks: VarInt::decode(r)?.0,
|
|
||||||
},
|
|
||||||
"entity" => Particle::VibrationEntity {
|
|
||||||
entity_id: VarInt::decode(r)?.0,
|
|
||||||
entity_eye_height: f32::decode(r)?,
|
|
||||||
ticks: VarInt::decode(r)?.0,
|
|
||||||
},
|
|
||||||
invalid => bail!("invalid vibration position source of \"{invalid}\""),
|
|
||||||
},
|
|
||||||
41 => Particle::ItemSlime,
|
|
||||||
42 => Particle::ItemSnowball,
|
|
||||||
43 => Particle::LargeSmoke,
|
|
||||||
44 => Particle::Lava,
|
|
||||||
45 => Particle::Mycelium,
|
|
||||||
46 => Particle::Note,
|
|
||||||
47 => Particle::Poof,
|
|
||||||
48 => Particle::Portal,
|
|
||||||
49 => Particle::Rain,
|
|
||||||
50 => Particle::Smoke,
|
|
||||||
51 => Particle::Sneeze,
|
|
||||||
52 => Particle::Spit,
|
|
||||||
53 => Particle::SquidInk,
|
|
||||||
54 => Particle::SweepAttack,
|
|
||||||
55 => Particle::TotemOfUndying,
|
|
||||||
56 => Particle::Underwater,
|
|
||||||
57 => Particle::Splash,
|
|
||||||
58 => Particle::Witch,
|
|
||||||
59 => Particle::BubblePop,
|
|
||||||
60 => Particle::CurrentDown,
|
|
||||||
61 => Particle::BubbleColumnUp,
|
|
||||||
62 => Particle::Nautilus,
|
|
||||||
63 => Particle::Dolphin,
|
|
||||||
64 => Particle::CampfireCosySmoke,
|
|
||||||
65 => Particle::CampfireSignalSmoke,
|
|
||||||
66 => Particle::DrippingHoney,
|
|
||||||
67 => Particle::FallingHoney,
|
|
||||||
68 => Particle::LandingHoney,
|
|
||||||
69 => Particle::FallingNectar,
|
|
||||||
70 => Particle::FallingSporeBlossom,
|
|
||||||
71 => Particle::Ash,
|
|
||||||
72 => Particle::CrimsonSpore,
|
|
||||||
73 => Particle::WarpedSpore,
|
|
||||||
74 => Particle::SporeBlossomAir,
|
|
||||||
75 => Particle::DrippingObsidianTear,
|
|
||||||
76 => Particle::FallingObsidianTear,
|
|
||||||
77 => Particle::LandingObsidianTear,
|
|
||||||
78 => Particle::ReversePortal,
|
|
||||||
79 => Particle::WhiteAsh,
|
|
||||||
80 => Particle::SmallFlame,
|
|
||||||
81 => Particle::Snowflake,
|
|
||||||
82 => Particle::DrippingDripstoneLava,
|
|
||||||
83 => Particle::FallingDripstoneLava,
|
|
||||||
84 => Particle::DrippingDripstoneWater,
|
|
||||||
85 => Particle::FallingDripstoneWater,
|
|
||||||
86 => Particle::GlowSquidInk,
|
|
||||||
87 => Particle::Glow,
|
|
||||||
88 => Particle::WaxOn,
|
|
||||||
89 => Particle::WaxOff,
|
|
||||||
90 => Particle::ElectricSpark,
|
|
||||||
91 => Particle::Scrape,
|
|
||||||
id => bail!("invalid particle ID of {id}"),
|
|
||||||
}),
|
|
||||||
long_distance,
|
|
||||||
position,
|
|
||||||
offset,
|
|
||||||
max_speed,
|
|
||||||
count: particle_count,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,194 +0,0 @@
|
||||||
//! Types used in the entity metadata packet.
|
|
||||||
|
|
||||||
use crate::{Decode, Encode};
|
|
||||||
|
|
||||||
/// Represents an optional `u32` value excluding [`u32::MAX`].
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
|
|
||||||
pub struct OptionalInt(u32);
|
|
||||||
|
|
||||||
impl OptionalInt {
|
|
||||||
/// Returns `None` iff `n` is Some(u32::MAX).
|
|
||||||
pub fn new(n: impl Into<Option<u32>>) -> Option<Self> {
|
|
||||||
match n.into() {
|
|
||||||
None => Some(Self(0)),
|
|
||||||
Some(u32::MAX) => None,
|
|
||||||
Some(n) => Some(Self(n + 1)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(self) -> Option<u32> {
|
|
||||||
self.0.checked_sub(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Encode, Decode)]
|
|
||||||
pub struct EulerAngle {
|
|
||||||
pub pitch: f32,
|
|
||||||
pub yaw: f32,
|
|
||||||
pub roll: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encode, Decode)]
|
|
||||||
pub enum Facing {
|
|
||||||
Down,
|
|
||||||
Up,
|
|
||||||
North,
|
|
||||||
South,
|
|
||||||
West,
|
|
||||||
East,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encode, Decode)]
|
|
||||||
pub struct VillagerData {
|
|
||||||
pub kind: VillagerKind,
|
|
||||||
pub profession: VillagerProfession,
|
|
||||||
pub level: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VillagerData {
|
|
||||||
pub const fn new(kind: VillagerKind, profession: VillagerProfession, level: i32) -> Self {
|
|
||||||
Self {
|
|
||||||
kind,
|
|
||||||
profession,
|
|
||||||
level,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for VillagerData {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
kind: Default::default(),
|
|
||||||
profession: Default::default(),
|
|
||||||
level: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
|
|
||||||
pub enum VillagerKind {
|
|
||||||
Desert,
|
|
||||||
Jungle,
|
|
||||||
#[default]
|
|
||||||
Plains,
|
|
||||||
Savanna,
|
|
||||||
Snow,
|
|
||||||
Swamp,
|
|
||||||
Taiga,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
|
|
||||||
pub enum VillagerProfession {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Armorer,
|
|
||||||
Butcher,
|
|
||||||
Cartographer,
|
|
||||||
Cleric,
|
|
||||||
Farmer,
|
|
||||||
Fisherman,
|
|
||||||
Fletcher,
|
|
||||||
Leatherworker,
|
|
||||||
Librarian,
|
|
||||||
Mason,
|
|
||||||
Nitwit,
|
|
||||||
Shepherd,
|
|
||||||
Toolsmith,
|
|
||||||
Weaponsmith,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
|
|
||||||
pub enum Pose {
|
|
||||||
#[default]
|
|
||||||
Standing,
|
|
||||||
FallFlying,
|
|
||||||
Sleeping,
|
|
||||||
Swimming,
|
|
||||||
SpinAttack,
|
|
||||||
Sneaking,
|
|
||||||
LongJumping,
|
|
||||||
Dying,
|
|
||||||
Croaking,
|
|
||||||
UsingTongue,
|
|
||||||
Roaring,
|
|
||||||
Sniffing,
|
|
||||||
Emerging,
|
|
||||||
Digging,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
|
|
||||||
pub enum BoatKind {
|
|
||||||
#[default]
|
|
||||||
Oak,
|
|
||||||
Spruce,
|
|
||||||
Birch,
|
|
||||||
Jungle,
|
|
||||||
Acacia,
|
|
||||||
DarkOak,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
|
|
||||||
pub enum CatKind {
|
|
||||||
Tabby,
|
|
||||||
#[default]
|
|
||||||
Black,
|
|
||||||
Red,
|
|
||||||
Siamese,
|
|
||||||
BritishShorthair,
|
|
||||||
Calico,
|
|
||||||
Persian,
|
|
||||||
Ragdoll,
|
|
||||||
White,
|
|
||||||
Jellie,
|
|
||||||
AllBlack,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
|
|
||||||
pub enum FrogKind {
|
|
||||||
#[default]
|
|
||||||
Temperate,
|
|
||||||
Warm,
|
|
||||||
Cold,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
|
|
||||||
pub enum PaintingKind {
|
|
||||||
#[default]
|
|
||||||
Kebab,
|
|
||||||
Aztec,
|
|
||||||
Alban,
|
|
||||||
Aztec2,
|
|
||||||
Bomb,
|
|
||||||
Plant,
|
|
||||||
Wasteland,
|
|
||||||
Pool,
|
|
||||||
Courbet,
|
|
||||||
Sea,
|
|
||||||
Sunset,
|
|
||||||
Creebet,
|
|
||||||
Wanderer,
|
|
||||||
Graham,
|
|
||||||
Match,
|
|
||||||
Bust,
|
|
||||||
Stage,
|
|
||||||
Void,
|
|
||||||
SkullAndRoses,
|
|
||||||
Wither,
|
|
||||||
Fighters,
|
|
||||||
Pointer,
|
|
||||||
Pigscene,
|
|
||||||
BurningSkull,
|
|
||||||
Skeleton,
|
|
||||||
Earth,
|
|
||||||
Wind,
|
|
||||||
Water,
|
|
||||||
Fire,
|
|
||||||
DonkeyKong,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Encode, Decode)]
|
|
||||||
pub enum Particle {
|
|
||||||
#[tag = 21]
|
|
||||||
EntityEffect,
|
|
||||||
}
|
|
|
@ -100,7 +100,7 @@ pub async fn make_session<'a>(params: &SessionParams<'a>) -> anyhow::Result<()>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{sess_name} logined");
|
println!("{sess_name} logged in");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
while !dec.has_next_packet()? {
|
while !dec.has_next_packet()? {
|
||||||
|
@ -128,8 +128,6 @@ pub async fn make_session<'a>(params: &SessionParams<'a>) -> anyhow::Result<()>
|
||||||
|
|
||||||
enc.append_packet(&KeepAliveC2s { id: p.id })?;
|
enc.append_packet(&KeepAliveC2s { id: p.id })?;
|
||||||
conn.write_all(&enc.take()).await?;
|
conn.write_all(&enc.take()).await?;
|
||||||
|
|
||||||
println!("{sess_name} keep alive")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
S2cPlayPacket::PlayerPositionLookS2c(p) => {
|
S2cPlayPacket::PlayerPositionLookS2c(p) => {
|
||||||
|
@ -145,8 +143,6 @@ pub async fn make_session<'a>(params: &SessionParams<'a>) -> anyhow::Result<()>
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
conn.write_all(&enc.take()).await?;
|
conn.write_all(&enc.take()).await?;
|
||||||
|
|
||||||
println!("{sess_name} spawned")
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -29,116 +29,6 @@ import java.lang.reflect.ParameterizedType;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class Entities implements Main.Extractor {
|
public class Entities implements Main.Extractor {
|
||||||
private final static Map<String, Bit[]> BIT_FIELDS = Map.ofEntries(
|
|
||||||
// @formatter:off
|
|
||||||
bits(
|
|
||||||
"flags",
|
|
||||||
bit("on_fire", 0),
|
|
||||||
bit("sneaking", 1),
|
|
||||||
bit("sprinting", 3),
|
|
||||||
bit("swimming", 4),
|
|
||||||
bit("invisible", 5),
|
|
||||||
bit("glowing", 6),
|
|
||||||
bit("fall_flying", 7)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"projectile_flags",
|
|
||||||
bit("critical", 0),
|
|
||||||
bit("no_clip", 1)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"living_flags",
|
|
||||||
bit("using_item", 0),
|
|
||||||
bit("off_hand_active", 1),
|
|
||||||
bit("using_riptide", 2)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"player_model_parts",
|
|
||||||
bit("cape", 0),
|
|
||||||
bit("jacket", 1),
|
|
||||||
bit("left_sleeve", 2),
|
|
||||||
bit("right_sleeve", 3),
|
|
||||||
bit("left_pants_leg", 4),
|
|
||||||
bit("right_pants_leg", 5),
|
|
||||||
bit("hat", 6)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"armor_stand_flags",
|
|
||||||
bit("small", 0),
|
|
||||||
bit("show_arms", 1),
|
|
||||||
bit("hide_base_plate", 2),
|
|
||||||
bit("marker", 3)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"mob_flags",
|
|
||||||
bit("ai_disabled", 0),
|
|
||||||
bit("left_handed", 1),
|
|
||||||
bit("attacking", 2)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"bat_flags",
|
|
||||||
bit("hanging", 0)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"horse_flags",
|
|
||||||
bit("tamed", 1),
|
|
||||||
bit("saddled", 2),
|
|
||||||
bit("bred", 3),
|
|
||||||
bit("eating_grass", 4),
|
|
||||||
bit("angry", 5),
|
|
||||||
bit("eating", 6)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"bee_flags",
|
|
||||||
bit("near_target", 1),
|
|
||||||
bit("has_stung", 2),
|
|
||||||
bit("has_nectar", 3)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"fox_flags",
|
|
||||||
bit("sitting", 0),
|
|
||||||
bit("crouching", 2),
|
|
||||||
bit("rolling_head", 3),
|
|
||||||
bit("chasing", 4),
|
|
||||||
bit("sleeping", 5),
|
|
||||||
bit("walking", 6),
|
|
||||||
bit("aggressive", 7)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"panda_flags",
|
|
||||||
bit("sneezing", 1),
|
|
||||||
bit("playing", 2),
|
|
||||||
bit("sitting", 3),
|
|
||||||
bit("lying_on_back", 4)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"tameable_flags",
|
|
||||||
bit("sitting_pose", 0),
|
|
||||||
bit("tamed", 2)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"iron_golem_flags",
|
|
||||||
bit("player_created", 0)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"snow_golem_flags",
|
|
||||||
bit("has_pumpkin", 4)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"blaze_flags",
|
|
||||||
bit("fire_active", 0)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"vex_flags",
|
|
||||||
bit("charging", 0)
|
|
||||||
),
|
|
||||||
bits(
|
|
||||||
"spider_flags",
|
|
||||||
bit("climbing_wall", 0)
|
|
||||||
)
|
|
||||||
// @formatter:on
|
|
||||||
);
|
|
||||||
|
|
||||||
public Entities() {
|
public Entities() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,15 +211,6 @@ public class Entities implements Main.Extractor {
|
||||||
fieldJson.addProperty("type", data.left());
|
fieldJson.addProperty("type", data.left());
|
||||||
fieldJson.add("default_value", data.right());
|
fieldJson.add("default_value", data.right());
|
||||||
|
|
||||||
var bitsJson = new JsonArray();
|
|
||||||
for (var bit : BIT_FIELDS.getOrDefault(fieldName, new Bit[]{})) {
|
|
||||||
var bitJson = new JsonObject();
|
|
||||||
bitJson.addProperty("name", bit.name);
|
|
||||||
bitJson.addProperty("index", bit.index);
|
|
||||||
bitsJson.add(bitJson);
|
|
||||||
}
|
|
||||||
fieldJson.add("bits", bitsJson);
|
|
||||||
|
|
||||||
fieldsJson.add(fieldJson);
|
fieldsJson.add(fieldJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue