Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

Commit

Permalink
Add a change set for entities
Browse files Browse the repository at this point in the history
  • Loading branch information
Mubelotix committed Jan 4, 2024
1 parent 3cfac8f commit 3bbdfc8
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 19 deletions.
4 changes: 2 additions & 2 deletions minecraft-server/src/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ impl<T> Handler<T> where AnyEntity: TryAsEntityRef<T> {
self.world.observe_entity(self.eid, observer).await
}

pub async fn mutate<R>(&self, mutator: impl FnOnce(&mut T) -> (R, EntityChanges)) -> Option<R> {
pub async fn mutate<R>(&self, mutator: impl FnOnce(&mut T) -> R) -> Option<R> {
self.world.mutate_entity(self.eid, move |entity| {
mutator(entity.try_as_entity_mut().expect("Called mutate on the wrong entity"))
}).await
}

pub async fn mutate_any<R>(&self, mutator: impl FnOnce(&mut AnyEntity) -> (R, EntityChanges)) -> Option<R> {
pub async fn mutate_any<R>(&self, mutator: impl FnOnce(&mut AnyEntity) -> R) -> Option<R> {
self.world.mutate_entity(self.eid, mutator).await
}
}
Expand Down
10 changes: 5 additions & 5 deletions minecraft-server/src/entities/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl Handler<Player> {
let old_center_chunk = player.center_chunk.clone();
let new_center_chunk = player.get_entity().position.chunk();
player.center_chunk = new_center_chunk.clone();
((old_center_chunk, new_center_chunk, player.render_distance), EntityChanges::other())
(old_center_chunk, new_center_chunk, player.render_distance)
}).await else {return};

// Tell the client which chunk he is in
Expand All @@ -113,7 +113,7 @@ impl Handler<Player> {

// Select chunks to load (max 50) and unload
let Some((loaded_chunks_after, newly_loaded_chunks, unloaded_chunks, uuid)) = self.mutate(|player| {
if loaded_chunks_after == player.loaded_chunks { return (None, EntityChanges::nothing()) };
if loaded_chunks_after == player.loaded_chunks { return None };
let mut newly_loaded_chunks: Vec<_> = loaded_chunks_after.difference(&player.loaded_chunks).cloned().collect();
let unloaded_chunks: Vec<_> = player.loaded_chunks.difference(&loaded_chunks_after).cloned().collect();
for skipped in newly_loaded_chunks.iter().skip(50) {
Expand All @@ -122,7 +122,7 @@ impl Handler<Player> {
newly_loaded_chunks.truncate(50);
let uuid = player.info.uuid;
player.loaded_chunks = loaded_chunks_after.clone();
(Some((loaded_chunks_after, newly_loaded_chunks, unloaded_chunks, uuid)), EntityChanges::other())
Some((loaded_chunks_after, newly_loaded_chunks, unloaded_chunks, uuid))
}).await.flatten() else { return };

// Tell the world about the changes
Expand Down Expand Up @@ -177,7 +177,7 @@ impl Handler<Player> {
let packet = packet.serialize_minecraft_packet().unwrap();
let packets_sent = self.mutate(|player| {
player.packets_sent += 1;
(player.packets_sent, EntityChanges::other())
player.packets_sent
}).await.unwrap_or(0);
if packets_sent > 500 {
warn!("Many packets sent ({packets_sent})");
Expand Down Expand Up @@ -234,7 +234,7 @@ impl Handler<Player> {
},
WorldChange::EntityMetadata { eid, metadata } => todo!(),
WorldChange::EntityPosition { eid, position } => {
let Some(prev_position) = self.mutate(|player| ((player.entity_prev_positions.insert(eid, position.clone())), EntityChanges::other())).await else {return};
let Some(prev_position) = self.mutate(|player| player.entity_prev_positions.insert(eid, position.clone())).await else {return};
match prev_position {
Some(prev_position) => {
self.send_packet(PlayClientbound::UpdateEntityPosition {
Expand Down
15 changes: 9 additions & 6 deletions minecraft-server/src/world/change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,10 @@ pub enum WorldChange {
},
}

#[derive(Clone, Copy)]
pub struct EntityChanges(u8);

impl EntityChanges {
pub const fn other() -> EntityChanges {
EntityChanges(0)
}

pub const fn nothing() -> EntityChanges {
EntityChanges(0)
}
Expand All @@ -61,7 +58,7 @@ impl EntityChanges {
EntityChanges(1 << 2)
}

pub const fn metadata() -> EntityChanges {
pub const fn other() -> EntityChanges {
EntityChanges(1 << 3)
}

Expand All @@ -81,7 +78,7 @@ impl EntityChanges {
self.0 & (1 << 2) != 0
}

pub const fn metadata_changed(&self) -> bool {
pub const fn other_changed(&self) -> bool {
self.0 & (1 << 3) != 0
}
}
Expand All @@ -99,3 +96,9 @@ impl std::ops::AddAssign<EntityChanges> for EntityChanges {
self.0 |= rhs.0;
}
}

impl Default for EntityChanges {
fn default() -> EntityChanges {
EntityChanges::nothing()
}
}
18 changes: 16 additions & 2 deletions minecraft-server/src/world/ecs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct Entities {
uuid_counter: std::sync::atomic::AtomicU64,
tasks: RwLock<HashMap<Eid, EntityTask>>,
entities: RwLock<HashMap<Eid, AnyEntity>>,
change_set: RwLock<HashMap<Eid, EntityChanges>>,

/// A hashmap of chunk positions to get a list of entities in a chunk
chunks: RwLock<HashMap<ChunkColumnPosition, HashSet<Eid>>>,
Expand All @@ -21,6 +22,7 @@ impl Entities {
uuid_counter: std::sync::atomic::AtomicU64::new(0),
tasks: RwLock::new(HashMap::new()),
entities: RwLock::new(HashMap::new()),
change_set: RwLock::new(HashMap::new()),
chunks: RwLock::new(HashMap::new()),
uuids: RwLock::new(HashMap::new()),
}
Expand Down Expand Up @@ -48,22 +50,34 @@ impl Entities {
results
}

// TODO don't return [EntityChanges]
/// Mutate an entity through a closure
pub(super) async fn mutate_entity<R>(&self, eid: Eid, mutator: impl FnOnce(&mut AnyEntity) -> (R, EntityChanges)) -> Option<(R, EntityChanges)> {
pub(super) async fn mutate_entity<R>(&self, eid: Eid, mutator: impl FnOnce(&mut AnyEntity) -> R) -> Option<(R, EntityChanges)> {
let mut entities = self.entities.write().await;

if let Some(entity) = entities.get_mut(&eid) {
let prev_position = entity.as_entity().position.clone();
let prev_velocity = entity.as_entity().velocity.clone();
let prev_pitch = entity.as_entity().pitch;
let r = mutator(entity);
let mut changes = EntityChanges::other();
if prev_velocity != entity.as_entity().velocity {
changes += EntityChanges::velocity();
}
if prev_pitch != entity.as_entity().pitch { // TODO: detect yaw changes
changes += EntityChanges::pitch();
}
if prev_position != entity.as_entity().position {
changes += EntityChanges::position();
let old_chunk = prev_position.chunk_column();
let new_chunk = entity.as_entity().position.chunk_column();
drop(entities);
let mut chunks = self.chunks.write().await;
chunks.entry(old_chunk).and_modify(|set| { set.remove(&eid); }); // TODO: ensure it gets removed
chunks.entry(new_chunk).or_insert(HashSet::new()).insert(eid);
}
Some(r)
*self.change_set.write().await.entry(eid).or_default() += changes;
Some((r, changes))
} else {
None
}
Expand Down
5 changes: 1 addition & 4 deletions minecraft-server/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl World {
}

// TODO: add version that doesn't notify modified entity
pub async fn mutate_entity<R>(&self, eid: Eid, mutator: impl FnOnce(&mut AnyEntity) -> (R, EntityChanges)) -> Option<R> {
pub async fn mutate_entity<R>(&self, eid: Eid, mutator: impl FnOnce(&mut AnyEntity) -> R) -> Option<R> {
// TODO: change events
match self.entities.mutate_entity(eid, mutator).await {
Some((r, changes)) => {
Expand Down Expand Up @@ -139,9 +139,6 @@ impl World {
head_yaw,
}).await;
}
if changes.metadata_changed() {
todo!()
}
Some(r)
},
None => None,
Expand Down

0 comments on commit 3bbdfc8

Please sign in to comment.