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. and customizable experience. It prioritizes performance and player enjoyment while adhering to the core mechanics of the game.
<div align="center"> <div align="center">
![chunk loading](/assets/pumpkin_chunk_loading.GIF) ![chunk loading](/assets/pumpkin_chunk_loading.webp)
</div> </div>
@ -35,12 +35,6 @@ and customizable experience. It prioritizes performance and player enjoyment whi
- Networking - Networking
- [x] Encryption - [x] Encryption
- [x] Packet Compression - [x] Packet Compression
- Player Configuration
- [x] Registries (biome types, paintings, dimensions)
- [x] Server Brand
- [x] Server Links
- [x] Set Resource Pack
- [x] Cookies
- World - World
- [x] World Joining - [x] World Joining
- [x] Player Tab-list - [x] Player Tab-list
@ -53,7 +47,6 @@ and customizable experience. It prioritizes performance and player enjoyment whi
- [x] Entity Spawning - [x] Entity Spawning
- [x] Item drops (W.I.P) - [x] Item drops (W.I.P)
- [x] Bossbar - [x] Bossbar
- [x] TNT
- [x] Chunk Loading (Vanilla, Linear) - [x] Chunk Loading (Vanilla, Linear)
- [x] Chunk Generation - [x] Chunk Generation
- [x] Chunk Saving (Vanilla, Linear) - [x] Chunk Saving (Vanilla, Linear)
@ -64,7 +57,6 @@ and customizable experience. It prioritizes performance and player enjoyment whi
- [ ] Structure Generation - [ ] Structure Generation
- Player - Player
- [x] Skins - [x] Skins
- [x] Client brand
- [x] Teleport - [x] Teleport
- [x] Movement - [x] Movement
- [x] Animation - [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), | (uuid[3] as u128),
), ),
None => { None => {
println!( log::debug!(
"Entity in chunk {},{} is missing UUID: {:?}", "Entity in chunk {},{} is missing UUID: {:?}",
position.x, position.y, entity_nbt position.x,
position.y,
entity_nbt
); );
continue; continue;
} }

View File

@ -8,7 +8,6 @@ pub mod candle_cakes;
pub mod candles; pub mod candles;
pub mod carpet; pub mod carpet;
pub mod carved_pumpkin; pub mod carved_pumpkin;
pub mod wither_skull;
pub mod chain; pub mod chain;
pub mod chests; pub mod chests;
pub mod chiseled_bookshelf; pub mod chiseled_bookshelf;
@ -54,6 +53,7 @@ pub mod torches;
pub mod trapdoor; pub mod trapdoor;
pub mod vine; pub mod vine;
pub mod walls; pub mod walls;
pub mod wither_skull;
pub mod banners; pub mod banners;
pub mod shulker_box; pub mod shulker_box;

View File

@ -17,7 +17,6 @@ impl BlockMetadata for SkullBlock {
fn ids(&self) -> &'static [&'static str] { fn ids(&self) -> &'static [&'static str] {
&[ &[
"skeleton_skull", "skeleton_skull",
"wither_skeleton_skull",
"player_head", "player_head",
"zombie_head", "zombie_head",
"creeper_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::trapdoor::TrapDoorBlock;
use crate::block::blocks::vine::VineBlock; use crate::block::blocks::vine::VineBlock;
use crate::block::blocks::walls::WallBlock; use crate::block::blocks::walls::WallBlock;
use crate::block::blocks::wither_skull::WitherSkeletonSkullBlock;
use crate::block::fluid::lava::FlowingLava; use crate::block::fluid::lava::FlowingLava;
use crate::block::fluid::water::FlowingWater; use crate::block::fluid::water::FlowingWater;
use crate::block::{ use crate::block::{
@ -153,6 +154,7 @@ pub fn default_registry() -> Arc<BlockRegistry> {
manager.register(CactusBlock); manager.register(CactusBlock);
manager.register(CarpetBlock); manager.register(CarpetBlock);
manager.register(CarvedPumpkinBlock); manager.register(CarvedPumpkinBlock);
manager.register(WitherSkeletonSkullBlock);
manager.register(CampfireBlock); manager.register(CampfireBlock);
manager.register(MossCarpetBlock); manager.register(MossCarpetBlock);
manager.register(PaleMossCarpetBlock); 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; use uuid::Uuid;
pub mod ai; pub mod ai;
pub mod boss;
pub mod decoration; pub mod decoration;
pub mod effect; pub mod effect;
pub mod experience_orb; pub mod experience_orb;

View File

@ -7,6 +7,7 @@ use uuid::Uuid;
use crate::{ use crate::{
entity::{ entity::{
Entity, EntityBase, Entity, EntityBase,
boss::wither::WitherEntity,
decoration::{ decoration::{
armor_stand::ArmorStandEntity, end_crystal::EndCrystalEntity, painting::PaintingEntity, 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::CREEPER.id => CreeperEntity::make(entity).await,
id if id == EntityType::SNOW_GOLEM.id => SnowGolemEntity::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::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::ARMOR_STAND.id => Arc::new(ArmorStandEntity::new(entity)),
id if id == EntityType::PAINTING.id => Arc::new(PaintingEntity::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)), id if id == EntityType::END_CRYSTAL.id => Arc::new(EndCrystalEntity::new(entity)),