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

Commit

Permalink
Make zombie follow player
Browse files Browse the repository at this point in the history
  • Loading branch information
Mubelotix committed Nov 14, 2023
1 parent 5c6aee4 commit da6f6bb
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 15 deletions.
6 changes: 6 additions & 0 deletions minecraft-entities-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ pub fn MinecraftEntity(attr: proc_macro::TokenStream, item: proc_macro::TokenStr
codes.push(code);

let code = quote! {
impl From<#this> for AnyEntity {
fn from(val: #this) -> Self {
AnyEntity::#this(val)
}
}

#[cfg(test)]
#[automatically_derived]
#[test]
Expand Down
2 changes: 1 addition & 1 deletion minecraft-server/src/entities/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub struct Entity {
}

impl Handler<Entity> {
async fn init(self, server_msg_rcvr: BroadcastReceiver<ServerMessage>) {
pub async fn init(self, server_msg_rcvr: BroadcastReceiver<ServerMessage>) {
self.insert_task("newton", tokio::spawn(newton_task(self.clone(), server_msg_rcvr))).await;
}
}
Expand Down
4 changes: 2 additions & 2 deletions minecraft-server/src/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl<T> Handler<T> where AnyEntity: TryAsEntityRef<T> {

pub async fn observe<R>(&self, observer: impl FnOnce(&T) -> R) -> Option<R> {
self.world.observe_entity(self.eid, move |entity| {
observer(entity.try_as_entity_ref().unwrap())
observer(entity.try_as_entity_ref().expect("Called observe on the wrong entity"))
}).await
}

Expand All @@ -113,7 +113,7 @@ impl<T> Handler<T> where AnyEntity: TryAsEntityRef<T> {

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

Expand Down
2 changes: 1 addition & 1 deletion minecraft-server/src/entities/monsters/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::*;
pub use super::*;

mod piglin;
pub use piglin::*;
Expand Down
55 changes: 55 additions & 0 deletions minecraft-server/src/entities/monsters/zombies.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use minecraft_protocol::network;

use super::*;

#[derive(Default)]
#[MinecraftEntity(
inheritable,
ancestors { Monster, PathfinderMob, Mob, LivingEntity, Entity },
descendants { ZombieVillager, Husk, Drowned, ZombifiedPiglin },
defines {
Entity.init(self, server_msg_rcvr: BroadcastReceiver<ServerMessage>);
}
)]
pub struct Zombie {
pub monster: Monster,
Expand All @@ -13,6 +18,56 @@ pub struct Zombie {
pub is_becoming_drowned: bool,
}

impl Handler<Zombie> {
pub async fn init(self, server_msg_rcvr: BroadcastReceiver<ServerMessage>) {
self.insert_task("newton", tokio::spawn(newton_task(self.clone(), server_msg_rcvr.resubscribe()))).await;
self.insert_task("zombie-ai", tokio::spawn(zombie_ai_task(self.clone(), server_msg_rcvr))).await;
}
}

pub async fn zombie_ai_task<T: EntityDescendant + ZombieDescendant>(h: Handler<T>, mut server_msg_rcvr: BroadcastReceiver<ServerMessage>) where AnyEntity: TryAsEntityRef<T> {
loop {
let msg = server_msg_rcvr.recv().await.unwrap();

if !matches!(&msg, &ServerMessage::Tick) {
continue;
}

let self_position = h.observe(|e| e.get_entity().position.clone()).await.unwrap();
let chunk = self_position.chunk_column();
let player_positions = h.world.observe_entities(chunk, |entity| {
let network_entity = entity.to_network().unwrap();
TryAsEntityRef::<Player>::try_as_entity_ref(entity).map(|player| {
(player.get_entity().position.clone(), network_entity)
})
}).await;

let Some((target_position, network_entity)) = player_positions.get(0) else { continue };
let target_object = CollisionShape {
x1: target_position.x - network_entity.width() as f64 / 2.0,
y1: target_position.y,
z1: target_position.z - network_entity.width() as f64 / 2.0,
x2: target_position.x + network_entity.width() as f64 / 2.0,
y2: target_position.y + network_entity.height() as f64,
z2: target_position.z + network_entity.width() as f64 / 2.0,
};

let mut translation = Translation {
x: target_position.x - self_position.x,
y: target_position.y - self_position.y,
z: target_position.z - self_position.z,
};
translation.set_norm(0.23000000417232513);

let authorized_translation = h.world.try_move(&target_object, &translation).await;

h.mutate(|e| {
e.get_entity_mut().position += authorized_translation;
((), EntityChanges::position())
}).await;
}
}

#[derive(Default)]
#[MinecraftEntity(
ancestors { Zombie, Monster, PathfinderMob, Mob, LivingEntity, Entity },
Expand Down
9 changes: 9 additions & 0 deletions minecraft-server/src/entities/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use super::*;

#[MinecraftEntity(
ancestors { LivingEntity, Entity },
defines {
Entity.init(self, server_msg_rcvr: BroadcastReceiver<ServerMessage>);
}
)]
pub struct Player {
pub living_entity: LivingEntity,
Expand All @@ -19,6 +22,12 @@ pub struct Player {
pub right_shoulder_entity: NbtTag,
}

impl Handler<Player> {
pub async fn init(self, server_msg_rcvr: BroadcastReceiver<ServerMessage>) {
//self.insert_task("newton", tokio::spawn(newton_task(self.clone(), server_msg_rcvr))).await;
}
}

impl Default for Player {
fn default() -> Self {
Player {
Expand Down
4 changes: 2 additions & 2 deletions minecraft-server/src/player_handler/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl PlayerHandler {
let mut position = self.position.clone();
position.y += 20.0;
zombie.get_entity_mut().position = position;
self.world.spawn_entity(AnyEntity::Zombie(zombie)).await;
self.world.spawn_entity::<Zombie>(AnyEntity::Zombie(zombie)).await;
}
}
packet => warn!("Unsupported packet received: {packet:?}"),
Expand All @@ -216,7 +216,7 @@ impl PlayerHandler {

pub async fn handle_player(stream: TcpStream, player_info: PlayerInfo, mut server_msg_rcvr: BroadcastReceiver<ServerMessage>, world: &'static World, mut change_receiver: MpscReceiver<WorldChange>) -> Result<(), ()> {
let (packet_sender, mut packet_receiver) = mpsc_channel(100);
let eid = world.spawn_entity(AnyEntity::Player(Player::default())).await;
let eid = world.spawn_entity::<Player>(AnyEntity::Player(Player::default())).await;

let mut handler = PlayerHandler {
eid,
Expand Down
13 changes: 12 additions & 1 deletion minecraft-server/src/world/collisions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,21 @@ impl Translation {
self.z *= limit;
}

fn norm(&self) -> f64 {
pub fn norm(&self) -> f64 {
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}

/// Keep direction but change norm
pub fn set_norm(&mut self, norm: f64) {
let current_norm = self.norm();
if current_norm == 0.0 {
return;
}
self.x *= norm / current_norm;
self.y *= norm / current_norm;
self.z *= norm / current_norm;
}

pub fn is_zero(&self) -> bool {
self.x == 0.0 && self.y == 0.0 && self.z == 0.0
}
Expand Down
30 changes: 24 additions & 6 deletions minecraft-server/src/world/ecs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub struct Entities {
pub entities: RwLock<HashMap<Eid, AnyEntity>>,

/// A hashmap of chunk positions to get a list of entities in a chunk
pub chunks: RwLock<HashMap<ChunkPosition, HashSet<Eid>>>,
pub chunks: RwLock<HashMap<ChunkColumnPosition, HashSet<Eid>>>,
pub uuids: RwLock<HashMap<UUID, Eid>>,
pub entity_tasks: RwLock<HashMap<Eid, HashMap<&'static str, EntityTaskHandle>>>,
}
Expand All @@ -34,6 +34,22 @@ impl Entities {
self.entities.read().await.get(&eid).map(observer)
}

/// Observe entities in a chunk through a closure
/// That closure will be applied to each entity, and the results will be returned in a vector
pub(super) async fn observe_entities<R>(&self, chunk: ChunkColumnPosition, mut observer: impl FnMut(&AnyEntity) -> Option<R>) -> Vec<R> {
let entities = self.chunks.read().await;
let Some(eids) = entities.get(&chunk) else {return Vec::new()};
let mut results = Vec::with_capacity(eids.len());
for eid in eids {
if let Some(entity) = self.entities.read().await.get(eid) {
if let Some(r) = observer(entity) {
results.push(r);
}
}
}
results
}

/// 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)> {
let mut entities = self.entities.write().await;
Expand All @@ -42,8 +58,8 @@ impl Entities {
let prev_position = entity.as_entity().position.clone();
let r = mutator(entity);
if prev_position != entity.as_entity().position {
let old_chunk = prev_position.chunk();
let new_chunk = entity.as_entity().position.chunk();
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
Expand All @@ -55,19 +71,21 @@ impl Entities {
}
}

pub(super) async fn spawn_entity(&self, entity: AnyEntity, world: &'static World, receiver: BroadcastReceiver<ServerMessage>) -> (Eid, UUID) {
pub(super) async fn spawn_entity<E>(&self, entity: AnyEntity, world: &'static World, receiver: BroadcastReceiver<ServerMessage>) -> (Eid, UUID)
where AnyEntity: TryAsEntityRef<E>, Handler<E>: EntityExt
{
let eid = self.eid_counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let uuid = self.uuid_counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u128;
let mut entities = self.entities.write().await;
let mut chunks = self.chunks.write().await;
let mut uuids = self.uuids.write().await;
chunks.entry(entity.as_entity().position.chunk()).or_insert(HashSet::new()).insert(eid);
chunks.entry(entity.as_entity().position.chunk_column()).or_insert(HashSet::new()).insert(eid);
entities.insert(eid, entity);
uuids.insert(uuid, eid);
drop(entities);
drop(chunks);
drop(uuids);
let h = Handler::<Zombie>::assume(eid, world); // TODO other than zombie
let h = Handler::<E>::assume(eid, world);
h.init(receiver).await;
(eid, uuid)
}
Expand Down
10 changes: 8 additions & 2 deletions minecraft-server/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,16 @@ impl World {
}
}

pub async fn spawn_entity(&'static self, entity: AnyEntity) -> Eid {
pub async fn spawn_entity<E>(&'static self, entity: AnyEntity) -> Eid
where AnyEntity: TryAsEntityRef<E>, Handler<E>: EntityExt
{
let position = entity.as_entity().position.clone();
let velocity = entity.as_entity().velocity.clone();
let ty = entity.to_network().unwrap(); // TODO: error handling
let pitch = entity.as_entity().pitch;
let yaw = entity.as_entity().yaw;
let head_yaw = entity.as_other::<LivingEntity>().map(|e| e.head_yaw).unwrap_or(0.0);
let (eid, uuid) = self.entities.spawn_entity(entity, self, self.receiver.resubscribe()).await;
let (eid, uuid) = self.entities.spawn_entity::<E>(entity, self, self.receiver.resubscribe()).await;
self.notify(&position.chunk_column(), WorldChange::EntitySpawned {
eid,
uuid,
Expand All @@ -104,6 +106,10 @@ impl World {
self.entities.observe_entity(eid, observer).await
}

pub async fn observe_entities<R>(&self, chunk: ChunkColumnPosition, observer: impl FnMut(&AnyEntity) -> Option<R>) -> Vec<R> {
self.entities.observe_entities(chunk, observer).await
}

pub async fn mutate_entity<R>(&self, eid: Eid, mutator: impl FnOnce(&mut AnyEntity) -> (R, EntityChanges)) -> Option<R> {
// TODO change events
match self.entities.mutate_entity(eid, mutator).await {
Expand Down

0 comments on commit da6f6bb

Please sign in to comment.