1#![doc = include_str!("../README.md")]
2#![allow(
3 clippy::unseparated_literal_suffix,
4 clippy::manual_string_new,
5 clippy::needless_raw_strings
6)]
7
8pub mod active_status_effects;
9pub mod attributes;
10mod flags;
11pub mod hitbox;
12pub mod manager;
13pub mod query;
14pub mod tracked_data;
15
16use bevy_app::prelude::*;
17use bevy_ecs::prelude::*;
18use chunkedge_binary::{Decode, Encode, IdOr, TextComponent, VarInt};
19use chunkedge_math::{DVec3, Vec3};
20use chunkedge_server_common::{Despawned, UniqueId};
21use derive_more::{Deref, DerefMut};
22pub use manager::EntityManager;
23use paste::paste;
24use tracing::warn;
25use tracked_data::TrackedData;
26
27use crate::attributes::TrackedEntityAttributes;
28
29include!(concat!(env!("OUT_DIR"), "/entity.rs"));
30
31pub struct EntityPlugin;
32
33#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
41pub struct InitEntitiesSet;
42
43#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
48pub struct UpdateTrackedDataSet;
49
50#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
55pub struct UpdateDerivedEntityDataSet;
56
57#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
64pub struct ClearEntityChangesSet;
65
66impl Plugin for EntityPlugin {
67 fn build(&self, app: &mut App) {
68 app.insert_resource(EntityManager::new())
69 .configure_sets(
70 PostUpdate,
71 (
72 InitEntitiesSet,
73 UpdateDerivedEntityDataSet.after(InitEntitiesSet),
74 UpdateTrackedDataSet.after(UpdateDerivedEntityDataSet),
75 ClearEntityChangesSet
76 .after(InitEntitiesSet)
77 .after(UpdateDerivedEntityDataSet)
78 .after(UpdateTrackedDataSet),
79 ),
80 )
81 .add_systems(
82 PostUpdate,
83 (remove_despawned_from_manager, init_entities)
84 .chain()
85 .in_set(InitEntitiesSet),
86 )
87 .add_systems(
88 PostUpdate,
89 (
90 clear_status_changes,
91 clear_animation_changes,
92 clear_tracked_data_changes,
93 clear_tracked_attributes_changes,
94 update_old_position,
95 update_old_layer_id,
96 )
97 .in_set(ClearEntityChangesSet),
98 );
99
100 add_tracked_data_systems(app);
101 }
102}
103
104fn update_old_position(mut query: Query<(&Position, &mut OldPosition)>) {
105 for (pos, mut old_pos) in &mut query {
106 old_pos.0 = pos.0;
107 }
108}
109
110fn update_old_layer_id(mut query: Query<(&EntityLayerId, &mut OldEntityLayerId)>) {
111 for (loc, mut old_loc) in &mut query {
112 old_loc.0 = loc.0;
113 }
114}
115
116fn remove_despawned_from_manager(
117 entities: Query<&EntityId, (With<EntityKind>, With<Despawned>)>,
118 mut manager: ResMut<EntityManager>,
119) {
120 for id in &entities {
121 manager.id_to_entity.remove(&id.0);
122 }
123}
124
125fn init_entities(
126 mut entities: Query<
127 (Entity, &mut EntityId, &Position, &mut OldPosition),
128 (Added<EntityKind>, Without<Despawned>),
129 >,
130 mut manager: ResMut<EntityManager>,
131) {
132 for (entity, mut id, pos, mut old_pos) in &mut entities {
133 *old_pos = OldPosition::new(pos.0);
134
135 if *id == EntityId::default() {
136 *id = manager.next_id();
137 }
138
139 if let Some(conflict) = manager.id_to_entity.insert(id.0, entity) {
140 warn!(
141 "entity {entity:?} has conflicting entity ID of {} with entity {conflict:?}",
142 id.0
143 );
144 }
145 }
146}
147
148fn clear_status_changes(mut statuses: Query<&mut EntityStatuses, Changed<EntityStatuses>>) {
149 for mut statuses in &mut statuses {
150 statuses.0 = 0;
151 }
152}
153
154fn clear_animation_changes(
155 mut animations: Query<&mut EntityAnimations, Changed<EntityAnimations>>,
156) {
157 for mut animations in &mut animations {
158 animations.0 = 0;
159 }
160}
161
162fn clear_tracked_data_changes(mut tracked_data: Query<&mut TrackedData, Changed<TrackedData>>) {
163 for mut tracked_data in &mut tracked_data {
164 tracked_data.clear_update_values();
165 }
166}
167
168fn clear_tracked_attributes_changes(
169 mut attributes: Query<&mut TrackedEntityAttributes, Changed<TrackedEntityAttributes>>,
170) {
171 for mut attributes in &mut attributes {
172 attributes.clear();
173 }
174}
175
176#[derive(Component, Copy, Clone, PartialEq, Eq, Debug, Deref)]
178pub struct EntityLayerId(pub Entity);
179
180impl Default for EntityLayerId {
181 fn default() -> Self {
182 Self(Entity::PLACEHOLDER)
183 }
184}
185
186impl PartialEq<OldEntityLayerId> for EntityLayerId {
187 fn eq(&self, other: &OldEntityLayerId) -> bool {
188 self.0 == other.0
189 }
190}
191
192#[derive(Component, Copy, Clone, PartialEq, Eq, Debug, Deref)]
194pub struct OldEntityLayerId(Entity);
195
196impl OldEntityLayerId {
197 pub fn get(&self) -> Entity {
198 self.0
199 }
200}
201
202impl Default for OldEntityLayerId {
203 fn default() -> Self {
204 Self(Entity::PLACEHOLDER)
205 }
206}
207
208impl PartialEq<EntityLayerId> for OldEntityLayerId {
209 fn eq(&self, other: &EntityLayerId) -> bool {
210 self.0 == other.0
211 }
212}
213
214#[derive(Component, Copy, Clone, PartialEq, Default, Debug, Deref, DerefMut)]
215pub struct Position(pub DVec3);
216
217impl Position {
218 pub fn new<P: Into<DVec3>>(pos: P) -> Self {
219 Self(pos.into())
220 }
221
222 pub fn get(self) -> DVec3 {
223 self.0
224 }
225
226 pub fn set<P: Into<DVec3>>(&mut self, pos: P) {
227 self.0 = pos.into();
228 }
229}
230
231impl PartialEq<OldPosition> for Position {
232 fn eq(&self, other: &OldPosition) -> bool {
233 self.0 == other.0
234 }
235}
236
237#[derive(Component, Clone, PartialEq, Default, Debug, Deref)]
241pub struct OldPosition(DVec3);
242
243impl OldPosition {
244 pub fn new<P: Into<DVec3>>(pos: P) -> Self {
245 Self(pos.into())
246 }
247
248 pub fn get(&self) -> DVec3 {
249 self.0
250 }
251}
252
253impl PartialEq<Position> for OldPosition {
254 fn eq(&self, other: &Position) -> bool {
255 self.0 == other.0
256 }
257}
258
259#[derive(Component, Copy, Clone, PartialEq, Default, Debug)]
261pub struct Look {
262 pub yaw: f32,
270 pub pitch: f32,
275}
276
277impl Look {
278 pub const fn new(yaw: f32, pitch: f32) -> Self {
279 Self { yaw, pitch }
280 }
281
282 pub fn vec(self) -> Vec3 {
284 let (yaw_sin, yaw_cos) = (self.yaw + 90.0).to_radians().sin_cos();
285 let (pitch_sin, pitch_cos) = (-self.pitch).to_radians().sin_cos();
286
287 Vec3::new(yaw_cos * pitch_cos, pitch_sin, yaw_sin * pitch_cos)
288 }
289
290 pub fn set_vec(&mut self, dir: Vec3) {
292 debug_assert!(
293 dir.is_normalized(),
294 "the direction vector should be normalized"
295 );
296
297 if dir.x != 0.0 || dir.z != 0.0 {
299 self.yaw = f32::atan2(dir.z, dir.x).to_degrees() - 90.0;
300 }
301
302 self.pitch = -(dir.y).asin().to_degrees();
303 }
304}
305
306#[derive(Component, Copy, Clone, PartialEq, Eq, Default, Debug, Deref, DerefMut)]
307pub struct OnGround(pub bool);
308
309#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Deref)]
317pub struct EntityId(i32);
318
319impl EntityId {
320 pub fn get(self) -> i32 {
322 self.0
323 }
324}
325
326impl Default for EntityId {
328 fn default() -> Self {
329 Self(-1)
330 }
331}
332
333#[derive(Component, Copy, Clone, PartialEq, Default, Debug, Deref, DerefMut)]
334pub struct HeadYaw(pub f32);
335
336#[derive(Component, Copy, Clone, Default, Debug, Deref, DerefMut)]
338pub struct Velocity(pub DVec3);
339
340impl Velocity {
341 pub fn to_packet_units(self) -> chunkedge_protocol::Velocity {
342 chunkedge_protocol::Velocity::from_ms_f64(self.0.into())
343 }
344}
345
346#[derive(Component, Copy, Clone, Default, Debug, Deref, DerefMut)]
349pub struct EntityStatuses(pub u64);
350
351impl EntityStatuses {
352 pub fn trigger(&mut self, status: EntityStatus) {
353 self.set(status, true);
354 }
355
356 pub fn set(&mut self, status: EntityStatus, triggered: bool) {
357 self.0 |= u64::from(triggered) << status as u64;
358 }
359
360 pub fn get(&self, status: EntityStatus) -> bool {
361 (self.0 >> status as u64) & 1 == 1
362 }
363}
364
365#[derive(Component, Default, Debug, Copy, Clone, Deref, DerefMut)]
366pub struct EntityAnimations(pub u8);
367
368impl EntityAnimations {
369 pub fn trigger(&mut self, anim: EntityAnimation) {
370 self.set(anim, true);
371 }
372
373 pub fn set(&mut self, anim: EntityAnimation, triggered: bool) {
374 self.0 |= u8::from(triggered) << anim as u8;
375 }
376
377 pub fn get(&self, anim: EntityAnimation) -> bool {
378 (self.0 >> anim as u8) & 1 == 1
379 }
380}
381
382#[derive(Component, Default, Debug, Deref, DerefMut)]
393pub struct ObjectData(pub i32);
394
395#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
396pub struct VillagerData {
397 pub kind: VillagerKind,
398 pub profession: VillagerProfession,
399 pub level: i32,
400}
401
402impl VillagerData {
403 pub const fn new(kind: VillagerKind, profession: VillagerProfession, level: i32) -> Self {
404 Self {
405 kind,
406 profession,
407 level,
408 }
409 }
410}
411
412impl Default for VillagerData {
413 fn default() -> Self {
414 Self {
415 kind: Default::default(),
416 profession: Default::default(),
417 level: 1,
418 }
419 }
420}
421
422impl Encode for VillagerData {
423 fn encode(&self, mut w: impl std::io::Write) -> anyhow::Result<()> {
424 self.kind.encode(&mut w)?;
425 self.profession.encode(&mut w)?;
426 VarInt(self.level).encode(w)
427 }
428}
429
430impl Decode<'_> for VillagerData {
431 fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
432 Ok(Self {
433 kind: VillagerKind::decode(r)?,
434 profession: VillagerProfession::decode(r)?,
435 level: VarInt::decode(r)?.0,
436 })
437 }
438}
439
440#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
441pub enum VillagerKind {
442 Desert,
443 Jungle,
444 #[default]
445 Plains,
446 Savanna,
447 Snow,
448 Swamp,
449 Taiga,
450}
451
452#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
453pub enum VillagerProfession {
454 #[default]
455 None,
456 Armorer,
457 Butcher,
458 Cartographer,
459 Cleric,
460 Farmer,
461 Fisherman,
462 Fletcher,
463 Leatherworker,
464 Librarian,
465 Mason,
466 Nitwit,
467 Shepherd,
468 Toolsmith,
469 Weaponsmith,
470}
471
472#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
473pub enum Pose {
474 #[default]
475 Standing,
476 FallFlying,
477 Sleeping,
478 Swimming,
479 SpinAttack,
480 Sneaking,
481 LongJumping,
482 Dying,
483 Croaking,
484 UsingTongue,
485 Sitting,
486 Roaring,
487 Sniffing,
488 Emerging,
489 Digging,
490 Sliding,
491 Shooting,
492 Inhaling,
493}
494
495#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
496pub enum BoatKind {
497 #[default]
498 Oak,
499 Spruce,
500 Birch,
501 Jungle,
502 Acacia,
503 DarkOak,
504}
505
506#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
507pub enum CatKind {
508 AllBlack,
509 #[default]
510 Black,
511 BritishShorthair,
512 Calico,
513 Jellie,
514 Persian,
515 Ragdoll,
516 Red,
517 Siamese,
518 Tabby,
519 White,
520}
521#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
522pub enum CowKind {
523 Cold,
524 #[default]
525 Temperate,
526 Warm,
527}
528#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
529pub enum WolfKind {
530 Ashen,
531 Black,
532 Chestnut,
533 #[default]
534 Pale,
535 Rusty,
536 Snowy,
537 Spotted,
538 Striped,
539 Woods,
540}
541#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
542pub enum WolfSoundKind {
543 Angry,
544 Big,
545 #[default]
546 Classic,
547 Cute,
548 Grumpy,
549 Puglin,
550 Sad,
551}
552#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
553pub enum ArmadilloState {
554 #[default]
555 Idle,
556 Rolling,
557 Scared,
558 Unrolling,
559}
560
561#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
562pub enum FrogKind {
563 Cold,
564 #[default]
565 Temperate,
566 Warm,
567}
568#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
569pub enum PigKind {
570 Cold,
571 #[default]
572 Temperate,
573 Warm,
574}
575#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
576pub enum ChickenKind {
577 Cold,
578 #[default]
579 Temperate,
580 Warm,
581}
582#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
583pub enum PaintingKind {
584 #[default]
585 Alban,
586 Aztec,
587 Aztec2,
588 Backyard,
589 Baroque,
590 Bomb,
591 Bouquet,
592 BurningSkull,
593 Bust,
594 Cavebird,
595 Changing,
596 Cotan,
597 Courbet,
598 Creebet,
599 DonkeyKong,
600 Earth,
601 Endboss,
602 Fern,
603 Fighters,
604 Finding,
605 Fire,
606 Graham,
607 Humble,
608 Kebab,
609 Lowmist,
610 Match,
611 Meditative,
612 Orb,
613 Owlemons,
614 Passage,
615 Pointer,
616 Pigscene,
617 Plant,
618 Pond,
619 Pool,
620 PrairieRide,
621 Sea,
622 Skeleton,
623 SkullAndRoses,
624 Stage,
625 Sunflowers,
626 Sunset,
627 Tides,
628 Unpacked,
629 Void,
630 Wanderer,
631 Wasteland,
632 Water,
633 Wind,
634 Wither,
635}
636
637impl PaintingKind {
638 pub const ALL: [Self; 50] = [
639 Self::Alban,
640 Self::Aztec,
641 Self::Aztec2,
642 Self::Backyard,
643 Self::Baroque,
644 Self::Bomb,
645 Self::Bouquet,
646 Self::BurningSkull,
647 Self::Bust,
648 Self::Cavebird,
649 Self::Changing,
650 Self::Cotan,
651 Self::Courbet,
652 Self::Creebet,
653 Self::DonkeyKong,
654 Self::Earth,
655 Self::Endboss,
656 Self::Fern,
657 Self::Fighters,
658 Self::Finding,
659 Self::Fire,
660 Self::Graham,
661 Self::Humble,
662 Self::Kebab,
663 Self::Lowmist,
664 Self::Match,
665 Self::Meditative,
666 Self::Orb,
667 Self::Owlemons,
668 Self::Passage,
669 Self::Pointer,
670 Self::Pigscene,
671 Self::Plant,
672 Self::Pond,
673 Self::Pool,
674 Self::PrairieRide,
675 Self::Sea,
676 Self::Skeleton,
677 Self::SkullAndRoses,
678 Self::Stage,
679 Self::Sunflowers,
680 Self::Sunset,
681 Self::Tides,
682 Self::Unpacked,
683 Self::Void,
684 Self::Wanderer,
685 Self::Wasteland,
686 Self::Water,
687 Self::Wind,
688 Self::Wither,
689 ];
690
691 pub fn registry_id(self) -> chunkedge_protocol::RegistryId {
692 chunkedge_protocol::RegistryId::new(self as i32)
693 }
694
695 pub fn from_registry_id(id: chunkedge_protocol::RegistryId) -> Option<Self> {
696 let Ok(idx) = usize::try_from(id.id()) else {
697 return None;
698 };
699
700 Self::ALL.get(idx).copied()
701 }
702}
703
704#[derive(Clone, PartialEq, Debug)]
705pub struct PaintingVariantDefinition {
706 pub width: i32,
707 pub height: i32,
708 pub asset_id: String,
709 pub title: Option<chunkedge_protocol::Text>,
710 pub author: Option<chunkedge_protocol::Text>,
711}
712
713impl Encode for PaintingVariantDefinition {
714 fn encode(&self, mut w: impl std::io::Write) -> anyhow::Result<()> {
715 VarInt(self.width).encode(&mut w)?;
716 VarInt(self.height).encode(&mut w)?;
717 self.asset_id.encode(&mut w)?;
718 self.title.clone().map(TextComponent::from).encode(&mut w)?;
719 self.author.clone().map(TextComponent::from).encode(w)
720 }
721}
722
723#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encode, Decode)]
724pub enum SnifferState {
725 #[default]
726 Idling,
727 FeelingHappy,
728 Scenting,
729 Sniffing,
730 Searching,
731 Digging,
732 Rising,
733}
734
735#[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Encode, Decode)]
736pub struct EulerAngle {
737 pub pitch: f32,
738 pub yaw: f32,
739 pub roll: f32,
740}
741
742#[derive(Copy, Clone)]
743struct OptionalInt(Option<i32>);
744
745impl Encode for OptionalInt {
746 fn encode(&self, w: impl std::io::Write) -> anyhow::Result<()> {
747 if let Some(n) = self.0 {
748 VarInt(n.wrapping_add(1))
749 } else {
750 VarInt(0)
751 }
752 .encode(w)
753 }
754}
755
756impl Decode<'_> for OptionalInt {
757 fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
758 let n = VarInt::decode(r)?.0;
759
760 Ok(Self(if n == 0 {
761 None
762 } else {
763 Some(n.wrapping_sub(1))
764 }))
765 }
766}
767
768#[derive(Clone, Copy)]
769struct OptionalBlockState(Option<chunkedge_protocol::BlockState>);
770
771impl Encode for OptionalBlockState {
772 fn encode(&self, w: impl std::io::Write) -> anyhow::Result<()> {
773 match self.0 {
774 None => VarInt(0).encode(w),
775 Some(state) => {
776 let id = i32::from(state.to_raw());
777
778 if id == 0 {
779 anyhow::bail!("air cannot be encoded as optional block state");
780 }
781
782 VarInt(id).encode(w)
783 }
784 }
785 }
786}
787
788impl Decode<'_> for OptionalBlockState {
789 fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
790 let id = VarInt::decode(r)?.0;
791
792 if id == 0 {
793 return Ok(Self(None));
794 }
795
796 let id =
797 u16::try_from(id).map_err(|_| anyhow::anyhow!("invalid optional block state ID"))?;
798 let state = chunkedge_protocol::BlockState::from_raw(id)
799 .ok_or_else(|| anyhow::anyhow!("invalid optional block state ID"))?;
800
801 Ok(Self(Some(state)))
802 }
803}
804
805#[derive(Clone, Copy)]
806struct PaintingVariant<'a>(&'a IdOr<PaintingVariantDefinition>);
807
808impl Encode for PaintingVariant<'_> {
809 fn encode(&self, w: impl std::io::Write) -> anyhow::Result<()> {
810 self.0.encode(w)
811 }
812}