feat: building wither

This commit is contained in:
Alexander Medvedev 2026-01-20 22:47:22 +01:00
parent 59c82aa30e
commit 20a14d730b
11 changed files with 161 additions and 13 deletions

View File

@ -13,7 +13,7 @@
and customizable experience. It prioritizes performance and player enjoyment while adhering to the core mechanics of the game.
<div align="center">
![chunk loading](/assets/pumpkin_chunk_loading.GIF)
![chunk loading](/assets/pumpkin_chunk_loading.webp)
</div>
@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 MiB

After

Width:  |  Height:  |  Size: 23 MiB

View File

@ -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;
}

View File

@ -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;

View File

@ -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",

View File

@ -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(&center_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;
}
}
}
})
}
}

View File

@ -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<BlockRegistry> {
manager.register(CactusBlock);
manager.register(CarpetBlock);
manager.register(CarvedPumpkinBlock);
manager.register(WitherSkeletonSkullBlock);
manager.register(CampfireBlock);
manager.register(MossCarpetBlock);
manager.register(PaleMossCarpetBlock);

View File

@ -0,0 +1 @@
pub mod wither;

View File

@ -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<Self> {
let mob_entity = MobEntity::new(entity);
let wither = Self { mob_entity };
let mob_arc = Arc::new(wither);
let mob_weak: Weak<dyn Mob> = {
let mob_arc: Arc<dyn Mob> = 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
}
}

View File

@ -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;

View File

@ -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)),