Skip to content

Commit c404898

Browse files
committed
implemented bevy-ecs for better entities
1 parent 6c6c6d4 commit c404898

File tree

30 files changed

+1386
-469
lines changed

30 files changed

+1386
-469
lines changed

Cargo.lock

Lines changed: 689 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ glam = "0.30.5"
2424
enumset = "1.1.10"
2525
fstr = { path = "crates/fstr" }
2626
include_dir = "0.7.4"
27+
bevy_ecs = "0.18.0"
2728

2829
[profile.dev.package."*"]
2930
opt-level = 3

server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ replays = { path = "../replays" }
1717
glam = "0.30.8"
1818
enumset = "1.1.10"
1919
slotmap = "1.0.7"
20+
bevy_ecs = "0.18.0"

server/src/constants/particle.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub enum Particle {
4646
BlockDust = 38,
4747
Droplet = 39,
4848
ItemTake = 40,
49-
MobAppearance = 41,
49+
MobAppear = 41,
5050
}
5151

5252
impl PacketSerializable for Particle {

server/src/constants/sounds.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@ use bytes::BytesMut;
44
#[derive(Debug, Copy, Clone)]
55
pub enum Sound {
66
EnderDragonHit,
7+
RandomExplode,
78
ZombieRemedy,
89
FireIgnite,
10+
DonkeyHit,
911
NoteHat,
1012
}
1113

1214
impl Sound {
1315
fn get_sound(&self) -> &'static str {
1416
match self {
1517
Sound::EnderDragonHit => "mob.enderdragon.hit",
18+
Sound::RandomExplode => "random.explode",
1619
Sound::ZombieRemedy => "mob.zombie.remedy",
1720
Sound::FireIgnite => "fire.ignite",
21+
Sound::DonkeyHit => "mob.horse.donkey.hit",
1822
Sound::NoteHat => "note.hat",
1923
}
2024
}
Lines changed: 42 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,35 @@
11
use crate::constants::EntityVariant;
2-
use crate::entity::entity::EntityBase;
2+
use crate::entity::entity::MinecraftEntity;
33
use crate::entity::entity_metadata::{EntityMetadata, PlayerMetadata};
44
use crate::network::binary::var_int::VarInt;
55
use crate::network::packets::packet_buffer::PacketBuffer;
66
use crate::network::protocol::play::clientbound::{DestroyEntites, EntityRotate, EntityTeleport, EntityYawRotate, PlayerData, PlayerListItem, SpawnMob, SpawnPlayer};
77
use crate::{GameProfile, GameProfileProperty, Player, WorldExtension};
8+
use bevy_ecs::component::Mutable;
9+
use bevy_ecs::prelude::Component;
810
use fstr::FString;
911
use std::collections::HashMap;
1012
use uuid::Uuid;
1113

12-
pub trait EntityAppearance<W: WorldExtension> {
13-
fn initialize(&self, entity: &mut EntityBase<W>);
14+
pub trait EntityAppearance<W: WorldExtension + 'static>: Component<Mutability = Mutable> + Sized {
1415

15-
fn destroy(&self, entity: &mut EntityBase<W>, packet: &mut DestroyEntites);
16+
fn enter_player_view(&self, entity: &MinecraftEntity<W>, player: &mut Player<W::Player>);
1617

17-
fn enter_player_view(&self, entity: &mut EntityBase<W>, player: &mut Player<W::Player>);
18+
fn leave_player_view(&self, entity: &MinecraftEntity<W>, player: &mut Player<W::Player>);
1819

19-
fn leave_player_view(&self, entity: &mut EntityBase<W>, player: &mut Player<W::Player>);
20+
fn update_position(&self, entity: &MinecraftEntity<W>, packet_buffer: &mut PacketBuffer);
2021

21-
fn update_position(&self, entity: &mut EntityBase<W>, chunk_buffer: &mut PacketBuffer);
22-
23-
fn update_rotation(&self, entity: &mut EntityBase<W>, chunk_buffer: &mut PacketBuffer);
22+
fn destroy(&self, entity: &MinecraftEntity<W>, packet: &mut DestroyEntites);
2423
}
2524

25+
#[derive(Component)]
2626
pub struct MobAppearance {
2727
pub variant: EntityVariant,
2828
pub metadata: EntityMetadata,
2929
}
3030

31-
impl<W: WorldExtension> EntityAppearance<W> for MobAppearance {
32-
fn initialize(&self, _: &mut EntityBase<W>) {}
33-
34-
fn destroy(&self, entity_base: &mut EntityBase<W>, packet: &mut DestroyEntites) {
35-
packet.entities.push(VarInt(entity_base.id))
36-
}
37-
38-
fn enter_player_view(&self, entity: &mut EntityBase<W>, player: &mut Player<W::Player>) {
31+
impl<W: WorldExtension + 'static> EntityAppearance<W> for MobAppearance {
32+
fn enter_player_view(&self, entity: &MinecraftEntity<W>, player: &mut Player<W::Player>) {
3933
player.write_packet(&SpawnMob {
4034
entity_id: entity.id,
4135
entity_variant: self.variant,
@@ -53,41 +47,25 @@ impl<W: WorldExtension> EntityAppearance<W> for MobAppearance {
5347
player.write_packet(&EntityYawRotate {
5448
entity_id: entity.id,
5549
yaw: entity.yaw,
56-
})
50+
});
5751
}
5852

59-
fn leave_player_view(&self, entity: &mut EntityBase<W>, player: &mut Player<W::Player>) {
53+
fn leave_player_view(&self, entity: &MinecraftEntity<W>, player: &mut Player<W::Player>) {
6054
player.write_packet(&DestroyEntites {
6155
entities: vec![VarInt(entity.id)],
6256
})
6357
}
6458

65-
fn update_position(&self, entity: &mut EntityBase<W>, packet_buffer: &mut PacketBuffer) {
66-
packet_buffer.write_packet(&EntityTeleport {
67-
entity_id: entity.id,
68-
pos_x: entity.position.x,
69-
pos_y: entity.position.y,
70-
pos_z: entity.position.z,
71-
yaw: entity.yaw,
72-
pitch: entity.pitch,
73-
on_ground: false,
74-
})
59+
fn update_position(&self, entity: &MinecraftEntity<W>, packet_buffer: &mut PacketBuffer) {
60+
update_position(entity, packet_buffer)
7561
}
7662

77-
fn update_rotation(&self, entity: &mut EntityBase<W>, packet_buffer: &mut PacketBuffer) {
78-
packet_buffer.write_packet(&EntityRotate {
79-
entity_id: entity.id,
80-
yaw: entity.yaw,
81-
pitch: entity.pitch,
82-
on_ground: false,
83-
});
84-
packet_buffer.write_packet(&EntityYawRotate {
85-
entity_id: entity.id,
86-
yaw: entity.yaw,
87-
});
63+
fn destroy(&self, entity: &MinecraftEntity<W>, packet: &mut DestroyEntites) {
64+
packet.entities.push(VarInt(entity.id))
8865
}
8966
}
9067

68+
#[derive(Component)]
9169
pub struct PlayerAppearance {
9270
name: &'static str,
9371
metadata: PlayerMetadata,
@@ -97,7 +75,6 @@ pub struct PlayerAppearance {
9775
}
9876

9977
impl PlayerAppearance {
100-
10178
pub fn new(
10279
name: &'static str,
10380
metadata: PlayerMetadata,
@@ -114,14 +91,8 @@ impl PlayerAppearance {
11491
}
11592
}
11693

117-
impl<W: WorldExtension> EntityAppearance<W> for PlayerAppearance {
118-
fn initialize(&self, _: &mut EntityBase<W>) {}
119-
120-
fn destroy(&self, entity: &mut EntityBase<W>, packet: &mut DestroyEntites) {
121-
packet.entities.push(VarInt(entity.id))
122-
}
123-
124-
fn enter_player_view(&self, entity: &mut EntityBase<W>, player: &mut Player<W::Player>) {
94+
impl<W: WorldExtension + 'static> EntityAppearance<W> for PlayerAppearance {
95+
fn enter_player_view(&self, entity: &MinecraftEntity<W>, player: &mut Player<W::Player>) {
12596
player.write_packet(&PlayerListItem {
12697
action: VarInt(0),
12798
players: &[PlayerData {
@@ -157,39 +128,38 @@ impl<W: WorldExtension> EntityAppearance<W> for PlayerAppearance {
157128
});
158129
player.write_packet(&EntityYawRotate {
159130
entity_id: entity.id,
160-
yaw: entity.yaw,
131+
yaw: entity.yaw
161132
});
162133
player.add_delayed_profile_remove(self.uuid);
163134
}
164135

165-
fn leave_player_view(&self, entity: &mut EntityBase<W>, player: &mut Player<W::Player>) {
136+
fn leave_player_view(&self, entity: &MinecraftEntity<W>, player: &mut Player<W::Player>) {
166137
player.write_packet(&DestroyEntites {
167138
entities: vec![VarInt(entity.id)],
168139
})
169140
}
170141

171-
fn update_position(&self, entity: &mut EntityBase<W>, packet_buffer: &mut PacketBuffer) {
172-
packet_buffer.write_packet(&EntityTeleport {
173-
entity_id: entity.id,
174-
pos_x: entity.position.x,
175-
pos_y: entity.position.y,
176-
pos_z: entity.position.z,
177-
yaw: entity.yaw,
178-
pitch: entity.pitch,
179-
on_ground: false,
180-
})
142+
fn update_position(&self, entity: &MinecraftEntity<W>, packet_buffer: &mut PacketBuffer) {
143+
update_position(entity, packet_buffer)
181144
}
182145

183-
fn update_rotation(&self, entity: &mut EntityBase<W>, packet_buffer: &mut PacketBuffer) {
184-
packet_buffer.write_packet(&EntityRotate {
185-
entity_id: entity.id,
186-
yaw: entity.yaw,
187-
pitch: entity.pitch,
188-
on_ground: false,
189-
});
190-
packet_buffer.write_packet(&EntityYawRotate {
191-
entity_id: entity.id,
192-
yaw: entity.yaw,
193-
});
146+
fn destroy(&self, entity: &MinecraftEntity<W>, packet: &mut DestroyEntites) {
147+
packet.entities.push(VarInt(entity.id))
194148
}
195149
}
150+
151+
fn update_position<W : WorldExtension>(entity: &MinecraftEntity<W>, buffer: &mut PacketBuffer) {
152+
buffer.write_packet(&EntityTeleport {
153+
entity_id: entity.id,
154+
pos_x: entity.position.x,
155+
pos_y: entity.position.y,
156+
pos_z: entity.position.z,
157+
yaw: entity.yaw,
158+
pitch: entity.pitch,
159+
on_ground: false,
160+
});
161+
buffer.write_packet(&EntityYawRotate {
162+
entity_id: entity.id,
163+
yaw: entity.yaw,
164+
});
165+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::entity::entity::MinecraftEntity;
2+
use crate::WorldExtension;
3+
use bevy_ecs::component::{Component, Mutable};
4+
use bevy_ecs::world::World;
5+
6+
// could maybe use a macro to provide custom query options somehow?
7+
pub trait EntityBehaviour<W : WorldExtension + 'static>: Component<Mutability = Mutable> + Sized {
8+
fn tick(entity: &mut MinecraftEntity<W>, component: &mut Self);
9+
10+
fn query(world: &mut World) {
11+
let mut query = world.query::<(&mut MinecraftEntity<W>, &mut Self)>();
12+
for (mut entity, mut component) in query.iter_mut(world) {
13+
Self::tick(&mut *entity, &mut *component);
14+
}
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::{Player, WorldExtension};
2+
use bevy_ecs::component::Component;
3+
use bevy_ecs::world::EntityWorldMut;
4+
5+
#[derive(Component)]
6+
pub struct Interactable<W: WorldExtension> {
7+
pub callback: fn(EntityWorldMut, player: &mut Player<W::Player>)
8+
}
9+
10+
impl<W: WorldExtension> Interactable<W> {
11+
pub fn new(callback: fn(EntityWorldMut, player: &mut Player<W::Player>)) -> Self {
12+
Self {
13+
callback,
14+
}
15+
}
16+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
pub mod entity_behaviour;
2+
pub mod entity_appearance;
3+
pub mod interactable;
4+
5+
pub use entity_appearance::EntityAppearance;
6+
pub use entity_appearance::MobAppearance;
7+
pub use entity_behaviour::EntityBehaviour;
8+
pub use interactable::Interactable;

server/src/entity/entities.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use crate::entity::components::{EntityAppearance, EntityBehaviour};
2+
use crate::entity::entity::MinecraftEntity;
3+
use crate::WorldExtension;
4+
use bevy_ecs::bundle::Bundle;
5+
use bevy_ecs::entity::Entity;
6+
use bevy_ecs::world::{EntityRef, EntityWorldMut, World};
7+
use std::collections::{HashMap, HashSet};
8+
use std::marker::PhantomData;
9+
10+
pub type MCEntityId = i32;
11+
12+
#[derive(Default)]
13+
struct Systems {
14+
pre: HashSet<fn(&mut World)>,
15+
post: HashSet<fn(&mut World)>,
16+
}
17+
18+
pub struct Entities<W: WorldExtension> {
19+
ecs: World,
20+
tick_systems: Systems,
21+
22+
current_entity_id: MCEntityId,
23+
id_to_entities: HashMap<MCEntityId, Entity>,
24+
25+
phantom: PhantomData<W>
26+
}
27+
28+
impl<W: WorldExtension + 'static> Entities<W> {
29+
30+
pub fn new() -> Self {
31+
Self {
32+
ecs: World::new(),
33+
tick_systems: Default::default(),
34+
35+
current_entity_id: 0,
36+
id_to_entities: Default::default(),
37+
38+
phantom: Default::default(),
39+
}
40+
}
41+
42+
pub fn tick(&mut self) {
43+
for system in self.tick_systems.pre.iter() {
44+
system(&mut self.ecs)
45+
}
46+
for system in self.tick_systems.post.iter() {
47+
system(&mut self.ecs)
48+
}
49+
}
50+
51+
pub fn register_tick_system(&mut self, callback: fn(&mut World)) {
52+
self.tick_systems.pre.insert(callback);
53+
}
54+
55+
pub fn register_behaviour<T: EntityBehaviour<W>>(&mut self) {
56+
self.tick_systems.pre.insert(T::query);
57+
}
58+
59+
// maybe rename, since this is what also ticks mc entity
60+
pub(crate) fn register_appearance_update<T: EntityAppearance<W>>(&mut self) {
61+
self.tick_systems.post.insert(|world| {
62+
let mut query = world.query::<(&mut MinecraftEntity<W>, &T)>();
63+
for (mut entity, appearance) in query.iter_mut(world) {
64+
entity.update(appearance);
65+
}
66+
});
67+
}
68+
69+
pub fn mc_id_to_entity(&self, id: MCEntityId) -> Option<&Entity> {
70+
self.id_to_entities.get(&id)
71+
}
72+
73+
pub fn get_entity(&'_ self, entity: Entity) -> EntityRef<'_> {
74+
self.ecs.entity(entity)
75+
}
76+
77+
pub fn get_entity_mut(&'_ mut self, entity: Entity) -> EntityWorldMut<'_> {
78+
self.ecs.entity_mut(entity)
79+
}
80+
81+
pub const fn next_entity_id(&mut self) -> MCEntityId {
82+
self.current_entity_id += 1;
83+
self.current_entity_id
84+
}
85+
86+
pub fn spawn<B: Bundle>(&mut self, entity: B) -> Entity {
87+
let entity = self.ecs.spawn(entity);
88+
if let Some(base) = entity.get::<MinecraftEntity<W>>() {
89+
let mc_id = base.id;
90+
self.id_to_entities.insert(mc_id, entity.id());
91+
};
92+
entity.id()
93+
}
94+
95+
pub fn despawn(&mut self, entity: Entity) {
96+
let entity = self.ecs.entity_mut(entity);
97+
if let Some(base) = entity.get::<MinecraftEntity<W>>() {
98+
self.id_to_entities.remove(&base.id);
99+
}
100+
entity.despawn();
101+
}
102+
}

0 commit comments

Comments
 (0)