diff --git a/README.md b/README.md
index 5730fb3cb..ef6e801f8 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
and customizable experience. It prioritizes performance and player enjoyment while adhering to the core mechanics of the game.
-
+
@@ -35,12 +35,6 @@ and customizable experience. It prioritizes performance and player enjoyment whi
- Networking
- [x] Encryption
- [x] Packet Compression
-- Player Configuration
- - [x] Registries (biome types, paintings, dimensions)
- - [x] Server Brand
- - [x] Server Links
- - [x] Set Resource Pack
- - [x] Cookies
- World
- [x] World Joining
- [x] Player Tab-list
@@ -53,7 +47,6 @@ and customizable experience. It prioritizes performance and player enjoyment whi
- [x] Entity Spawning
- [x] Item drops (W.I.P)
- [x] Bossbar
- - [x] TNT
- [x] Chunk Loading (Vanilla, Linear)
- [x] Chunk Generation
- [x] Chunk Saving (Vanilla, Linear)
@@ -64,7 +57,6 @@ and customizable experience. It prioritizes performance and player enjoyment whi
- [ ] Structure Generation
- Player
- [x] Skins
- - [x] Client brand
- [x] Teleport
- [x] Movement
- [x] Animation
diff --git a/assets/pumpkin_chunk_loading.GIF b/assets/pumpkin_chunk_loading.webp
similarity index 68%
rename from assets/pumpkin_chunk_loading.GIF
rename to assets/pumpkin_chunk_loading.webp
index e334286bc..a28912c55 100644
Binary files a/assets/pumpkin_chunk_loading.GIF and b/assets/pumpkin_chunk_loading.webp differ
diff --git a/pumpkin-world/src/chunk/format/mod.rs b/pumpkin-world/src/chunk/format/mod.rs
index 5ebf4b550..2e3df4c4e 100644
--- a/pumpkin-world/src/chunk/format/mod.rs
+++ b/pumpkin-world/src/chunk/format/mod.rs
@@ -299,9 +299,11 @@ impl ChunkEntityData {
| (uuid[3] as u128),
),
None => {
- println!(
+ log::debug!(
"Entity in chunk {},{} is missing UUID: {:?}",
- position.x, position.y, entity_nbt
+ position.x,
+ position.y,
+ entity_nbt
);
continue;
}
diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs
index cb2c040a2..95f625201 100644
--- a/pumpkin/src/block/blocks/mod.rs
+++ b/pumpkin/src/block/blocks/mod.rs
@@ -8,7 +8,6 @@ pub mod candle_cakes;
pub mod candles;
pub mod carpet;
pub mod carved_pumpkin;
-pub mod wither_skull;
pub mod chain;
pub mod chests;
pub mod chiseled_bookshelf;
@@ -54,6 +53,7 @@ pub mod torches;
pub mod trapdoor;
pub mod vine;
pub mod walls;
+pub mod wither_skull;
pub mod banners;
pub mod shulker_box;
diff --git a/pumpkin/src/block/blocks/skull_block.rs b/pumpkin/src/block/blocks/skull_block.rs
index 08b22193f..2588fd0fa 100644
--- a/pumpkin/src/block/blocks/skull_block.rs
+++ b/pumpkin/src/block/blocks/skull_block.rs
@@ -17,7 +17,6 @@ impl BlockMetadata for SkullBlock {
fn ids(&self) -> &'static [&'static str] {
&[
"skeleton_skull",
- "wither_skeleton_skull",
"player_head",
"zombie_head",
"creeper_head",
diff --git a/pumpkin/src/block/blocks/wither_skull.rs b/pumpkin/src/block/blocks/wither_skull.rs
index e69de29bb..6412ca648 100644
--- a/pumpkin/src/block/blocks/wither_skull.rs
+++ b/pumpkin/src/block/blocks/wither_skull.rs
@@ -0,0 +1,103 @@
+use pumpkin_data::{Block, BlockDirection, entity::EntityType, world::WorldEvent};
+use pumpkin_world::{BlockStateId, world::BlockFlags};
+
+use crate::{
+ block::{
+ BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs, PlacedArgs,
+ blocks::skull_block::SkullBlock,
+ },
+ entity::{Entity, boss::wither::WitherEntity},
+};
+
+pub struct WitherSkeletonSkullBlock;
+
+impl BlockMetadata for WitherSkeletonSkullBlock {
+ fn namespace(&self) -> &'static str {
+ "minecraft"
+ }
+
+ fn ids(&self) -> &'static [&'static str] {
+ &[Block::WITHER_SKELETON_SKULL.name]
+ }
+}
+
+impl BlockBehaviour for WitherSkeletonSkullBlock {
+ fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
+ SkullBlock::on_place(&SkullBlock, args)
+ }
+
+ fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
+ Box::pin(async move {
+ let world = args.world;
+ let pos = args.position;
+
+ let is_soul_block =
+ |block: &Block| block == &Block::SOUL_SAND || block == &Block::SOUL_SOIL;
+ let is_skull = |block: &Block| block == &Block::WITHER_SKELETON_SKULL;
+
+ for dir in [BlockDirection::North, BlockDirection::West] {
+ let opposite = dir.opposite();
+ for offset in 0..3 {
+ let center_skull_pos = match offset {
+ 0 => *pos,
+ 1 => pos.offset(opposite.to_offset()),
+ 2 => pos.offset(dir.to_offset()),
+ _ => unreachable!(),
+ };
+
+ let top_middle = center_skull_pos.down();
+ let base = top_middle.down();
+ let arm1 = top_middle.offset(dir.to_offset());
+ let arm2 = top_middle.offset(opposite.to_offset());
+ let skull1_pos = arm1.up();
+ let skull2_pos = arm2.up();
+
+ if is_soul_block(world.get_block(&top_middle).await)
+ && is_soul_block(world.get_block(&base).await)
+ && is_soul_block(world.get_block(&arm1).await)
+ && is_soul_block(world.get_block(&arm2).await)
+ && is_skull(world.get_block(¢er_skull_pos).await)
+ && is_skull(world.get_block(&skull1_pos).await)
+ && is_skull(world.get_block(&skull2_pos).await)
+ {
+ let pattern = [
+ center_skull_pos,
+ skull1_pos,
+ skull2_pos,
+ top_middle,
+ arm1,
+ arm2,
+ base,
+ ];
+
+ for p in pattern {
+ world
+ .set_block_state(
+ &p,
+ Block::AIR.default_state.id,
+ BlockFlags::NOTIFY_ALL,
+ )
+ .await;
+ world
+ .sync_world_event(
+ WorldEvent::BlockBroken,
+ p,
+ Block::SOUL_SAND.default_state.id.into(),
+ )
+ .await;
+ }
+
+ let entity = Entity::new(
+ world.clone(),
+ top_middle.to_centered_f64(),
+ &EntityType::WITHER,
+ );
+ let wither = WitherEntity::make(entity).await;
+ world.spawn_entity(wither).await;
+ return;
+ }
+ }
+ }
+ })
+ }
+}
diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs
index b4fa62fca..c5e24c279 100644
--- a/pumpkin/src/block/registry.rs
+++ b/pumpkin/src/block/registry.rs
@@ -76,6 +76,7 @@ use crate::block::blocks::torches::TorchBlock;
use crate::block::blocks::trapdoor::TrapDoorBlock;
use crate::block::blocks::vine::VineBlock;
use crate::block::blocks::walls::WallBlock;
+use crate::block::blocks::wither_skull::WitherSkeletonSkullBlock;
use crate::block::fluid::lava::FlowingLava;
use crate::block::fluid::water::FlowingWater;
use crate::block::{
@@ -153,6 +154,7 @@ pub fn default_registry() -> Arc {
manager.register(CactusBlock);
manager.register(CarpetBlock);
manager.register(CarvedPumpkinBlock);
+ manager.register(WitherSkeletonSkullBlock);
manager.register(CampfireBlock);
manager.register(MossCarpetBlock);
manager.register(PaleMossCarpetBlock);
diff --git a/pumpkin/src/entity/boss/mod.rs b/pumpkin/src/entity/boss/mod.rs
new file mode 100644
index 000000000..4ad811449
--- /dev/null
+++ b/pumpkin/src/entity/boss/mod.rs
@@ -0,0 +1 @@
+pub mod wither;
diff --git a/pumpkin/src/entity/boss/wither.rs b/pumpkin/src/entity/boss/wither.rs
new file mode 100644
index 000000000..01bf35d8f
--- /dev/null
+++ b/pumpkin/src/entity/boss/wither.rs
@@ -0,0 +1,46 @@
+use std::sync::{Arc, Weak};
+
+use pumpkin_data::entity::EntityType;
+
+use crate::entity::{
+ Entity, NBTStorage,
+ ai::goal::{look_around::LookAroundGoal, look_at_entity::LookAtEntityGoal},
+ mob::{Mob, MobEntity},
+};
+
+pub struct WitherEntity {
+ pub mob_entity: MobEntity,
+}
+
+impl WitherEntity {
+ pub async fn make(entity: Entity) -> Arc {
+ let mob_entity = MobEntity::new(entity);
+ let wither = Self { mob_entity };
+ let mob_arc = Arc::new(wither);
+ let mob_weak: Weak = {
+ let mob_arc: Arc = mob_arc.clone();
+ Arc::downgrade(&mob_arc)
+ };
+
+ {
+ let mut goal_selector = mob_arc.mob_entity.goals_selector.lock().await;
+
+ // TODO
+ goal_selector.add_goal(
+ 8,
+ LookAtEntityGoal::with_default(mob_weak, &EntityType::PLAYER, 8.0),
+ );
+ goal_selector.add_goal(8, Box::new(LookAroundGoal::default()));
+ };
+
+ mob_arc
+ }
+}
+
+impl NBTStorage for WitherEntity {}
+
+impl Mob for WitherEntity {
+ fn get_mob_entity(&self) -> &MobEntity {
+ &self.mob_entity
+ }
+}
diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs
index a8abb1357..6463d3b51 100644
--- a/pumpkin/src/entity/mod.rs
+++ b/pumpkin/src/entity/mod.rs
@@ -54,6 +54,7 @@ use tokio::sync::Mutex;
use uuid::Uuid;
pub mod ai;
+pub mod boss;
pub mod decoration;
pub mod effect;
pub mod experience_orb;
diff --git a/pumpkin/src/entity/type.rs b/pumpkin/src/entity/type.rs
index 87b0b5ebe..d64e21264 100644
--- a/pumpkin/src/entity/type.rs
+++ b/pumpkin/src/entity/type.rs
@@ -7,6 +7,7 @@ use uuid::Uuid;
use crate::{
entity::{
Entity, EntityBase,
+ boss::wither::WitherEntity,
decoration::{
armor_stand::ArmorStandEntity, end_crystal::EndCrystalEntity, painting::PaintingEntity,
},
@@ -35,6 +36,7 @@ pub async fn from_type(
id if id == EntityType::CREEPER.id => CreeperEntity::make(entity).await,
id if id == EntityType::SNOW_GOLEM.id => SnowGolemEntity::make(entity).await,
id if id == EntityType::WOLF.id => WolfEntity::make(entity).await,
+ id if id == EntityType::WITHER.id => WitherEntity::make(entity).await,
id if id == EntityType::ARMOR_STAND.id => Arc::new(ArmorStandEntity::new(entity)),
id if id == EntityType::PAINTING.id => Arc::new(PaintingEntity::new(entity)),
id if id == EntityType::END_CRYSTAL.id => Arc::new(EndCrystalEntity::new(entity)),