feat: Implement fireworks

also fix: https://github.com/Pumpkin-MC/Pumpkin/issues/1217
also includes some bedrock work, but still broken
This commit is contained in:
Alexander Medvedev 2026-01-13 19:09:19 +01:00
parent 60d65f1f48
commit e46459915c
70 changed files with 1051 additions and 880 deletions

32
Cargo.lock generated
View File

@ -227,9 +227,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.8.2"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "bitflags"
@ -951,9 +951,9 @@ checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
[[package]]
name = "flate2"
version = "1.1.5"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
dependencies = [
"crc32fast",
"miniz_oxide",
@ -1082,9 +1082,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.16"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
@ -2059,9 +2059,9 @@ dependencies = [
[[package]]
name = "prost"
version = "0.14.1"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d"
checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568"
dependencies = [
"bytes",
"prost-derive",
@ -2069,9 +2069,9 @@ dependencies = [
[[package]]
name = "prost-derive"
version = "0.14.1"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425"
checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b"
dependencies = [
"anyhow",
"itertools 0.14.0",
@ -2082,9 +2082,9 @@ dependencies = [
[[package]]
name = "prost-types"
version = "0.14.1"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72"
checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7"
dependencies = [
"prost",
]
@ -2336,7 +2336,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.16",
"getrandom 0.2.17",
]
[[package]]
@ -2421,7 +2421,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.16",
"getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
@ -3763,6 +3763,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.12"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8"
checksum = "ac93432f5b761b22864c774aac244fa5c0fd877678a4c37ebf6cf42208f9c9ec"

View File

@ -65,7 +65,7 @@ dashmap = "6.1"
ecdsa = "0.16.9"
enum_dispatch = "0.3.13"
env_logger = "0.11.8"
flate2 = "1.1.5"
flate2 = "1.1.8"
heck = "0.5"
hmac = "=0.13.0-rc.3"
indexmap = "2.13"

View File

@ -0,0 +1,41 @@
{
"byte": 0,
"integer": 1,
"long": 2,
"float": 3,
"string": 4,
"text_component": 5,
"optional_text_component": 6,
"item_stack": 7,
"block_state": 14,
"optional_block_state": 15,
"boolean": 8,
"particle": 16,
"particle_list": 17,
"rotation": 9,
"block_pos": 10,
"optional_block_pos": 11,
"facing": 12,
"lazy_entity_reference": 13,
"optional_global_pos": 29,
"villager_data": 18,
"optional_int": 19,
"entity_pose": 20,
"cat_variant": 21,
"chicken_variant": 27,
"cow_variant": 22,
"wolf_variant": 23,
"wolf_sound_variant": 24,
"frog_variant": 25,
"pig_variant": 26,
"zombie_nautilus_variant": 28,
"painting_variant": 30,
"armadillo_state": 32,
"sniffer_state": 31,
"oxidation_level": 34,
"copper_golem_state": 33,
"vector_3f": 35,
"quaternion_f": 36,
"profile": 37,
"arm": 38
}

178
assets/tracked_data.json Normal file
View File

@ -0,0 +1,178 @@
{
"LEFT_PADDLE_MOVING": 11,
"RIGHT_PADDLE_MOVING": 12,
"BUBBLE_WOBBLE_TICKS": 13,
"DAMAGE_WOBBLE_TICKS": 8,
"DAMAGE_WOBBLE_SIDE": 9,
"DAMAGE_WOBBLE_STRENGTH": 10,
"FLAGS": 0,
"AIR": 1,
"CUSTOM_NAME": 2,
"NAME_VISIBLE": 3,
"SILENT": 4,
"NO_GRAVITY": 5,
"POSE": 6,
"FROZEN_TICKS": 7,
"DANCING": 19,
"CAN_DUPLICATE": 17,
"MOB_FLAGS": 15,
"LIVING_FLAGS": 8,
"HEALTH": 9,
"POTION_SWIRLS": 10,
"POTION_SWIRLS_AMBIENT": 11,
"STUCK_ARROW_COUNT": 12,
"STINGER_COUNT": 13,
"SLEEPING_POSITION": 14,
"RADIUS": 8,
"WAITING": 9,
"PARTICLE": 10,
"STATE": 17,
"CHILD": 16,
"ARMOR_STAND_FLAGS": 15,
"TRACKER_HEAD_ROTATION": 16,
"TRACKER_BODY_ROTATION": 17,
"TRACKER_LEFT_ARM_ROTATION": 18,
"TRACKER_RIGHT_ARM_ROTATION": 19,
"TRACKER_LEFT_LEG_ROTATION": 20,
"TRACKER_RIGHT_LEG_ROTATION": 21,
"COLOR": 18,
"PROJECTILE_FLAGS": 8,
"PIERCE_LEVEL": 9,
"IN_GROUND": 10,
"VARIANT": 20,
"PLAYING_DEAD": 18,
"FROM_BUCKET": 16,
"BAT_FLAGS": 16,
"BEE_FLAGS": 17,
"ANGER_END_TIME": 21,
"BLAZE_FLAGS": 16,
"BLOCK_STATE": 9,
"START_INTERPOLATION": 8,
"INTERPOLATION_DURATION": 9,
"TELEPORT_DURATION": 10,
"TRANSLATION": 11,
"SCALE": 12,
"LEFT_ROTATION": 13,
"RIGHT_ROTATION": 14,
"BILLBOARD": 15,
"BRIGHTNESS": 16,
"VIEW_RANGE": 17,
"SHADOW_RADIUS": 18,
"SHADOW_STRENGTH": 19,
"WIDTH": 20,
"HEIGHT": 21,
"GLOW_COLOR_OVERRIDE": 22,
"SHEARED": 16,
"DASHING": 19,
"LAST_POSE_TICK": 19,
"HORSE_FLAGS": 17,
"CAT_VARIANT": 19,
"IN_SLEEPING_POSE": 20,
"HEAD_DOWN": 21,
"COLLAR_COLOR": 20,
"TAMEABLE_FLAGS": 17,
"OWNER_UUID": 18,
"SPIDER_FLAGS": 16,
"CUSTOM_BLOCK_STATE": 11,
"BLOCK_OFFSET": 12,
"OXIDATION_LEVEL": 16,
"COPPER_GOLEM_STATE": 17,
"COMMAND": 13,
"LAST_OUTPUT": 14,
"UNROOTED": 16,
"ACTIVE": 17,
"CRUMBLING": 18,
"HOME_POS": 19,
"FUSE_SPEED": 16,
"CHARGED": 8,
"IGNITED": 18,
"HAS_FISH": 17,
"MOISTNESS": 18,
"CHEST": 18,
"BABY": 16,
"ZOMBIE_TYPE": 17,
"CONVERTING_IN_WATER": 18,
"ITEM": 8,
"SPIKES_RETRACTED": 16,
"BEAM_TARGET_ID": 17,
"CARRIED_BLOCK": 16,
"ANGRY": 17,
"PROVOKED": 18,
"PHASE_TYPE": 16,
"BEAM_TARGET": 8,
"SHOW_BOTTOM": 9,
"SPELL": 17,
"CELEBRATING": 16,
"VALUE": 8,
"BLOCK_POS": 8,
"SHOOTER_ENTITY_ID": 9,
"SHOT_AT_ANGLE": 10,
"FOX_FLAGS": 18,
"OWNER": 19,
"OTHER_TRUSTED": 20,
"TARGET": 18,
"LIT": 13,
"SHOOTING": 16,
"HAS_ROPES": 17,
"STAYING_STILL": 18,
"ITEM_STACK": 9,
"ROTATION": 10,
"FACING": 8,
"DARK_TICKS_REMAINING": 17,
"SCREAMING": 17,
"LEFT_HORN": 18,
"RIGHT_HORN": 19,
"RESPONSE": 10,
"IRON_GOLEM_FLAGS": 16,
"STACK": 8,
"ITEM_DISPLAY": 24,
"STRENGTH": 19,
"SLIME_SIZE": 16,
"PROFILE": 17,
"IMMOVABLE": 18,
"DESCRIPTION": 19,
"MAIN_ARM_ID": 15,
"PLAYER_MODE_CUSTOMIZATION_ID": 16,
"TRUSTING": 17,
"ASK_FOR_BAMBOO_TICKS": 17,
"SNEEZE_PROGRESS": 18,
"EATING_TICKS": 19,
"MAIN_GENE": 20,
"HIDDEN_GENE": 21,
"PANDA_FLAGS": 22,
"SIZE": 16,
"BOOST_TIME": 17,
"CHARGING": 17,
"IMMUNE_TO_ZOMBIFICATION": 16,
"WARNING": 17,
"PUFF_STATE": 17,
"ATTACHED_FACE": 16,
"PEEK_AMOUNT": 17,
"CONVERTING": 19,
"FINISH_DIG_TIME": 18,
"SNOW_GOLEM_FLAGS": 16,
"COLD": 18,
"TEXT": 23,
"LINE_WIDTH": 24,
"BACKGROUND": 25,
"TEXT_OPACITY": 26,
"TEXT_DISPLAY_FLAGS": 27,
"FUSE": 8,
"LOYALTY": 11,
"ENCHANTED": 12,
"HAS_EGG": 17,
"DIGGING_SAND": 18,
"VEX_FLAGS": 16,
"VILLAGER_DATA": 20,
"HEAD_ROLLING_TIME_LEFT": 17,
"ANGER": 16,
"DRINKING": 17,
"TRACKED_ENTITY_ID_1": 16,
"TRACKED_ENTITY_ID_2": 17,
"TRACKED_ENTITY_ID_3": 18,
"INVUL_TIMER": 19,
"BEGGING": 19,
"SOUND_VARIANT": 23,
"HOOK_ENTITY_ID": 8,
"CAUGHT_FISH": 9
}

View File

@ -30,6 +30,7 @@ mod game_rules;
mod item;
pub mod loot;
mod message_type;
mod meta_data_type;
mod noise_parameter;
mod noise_router;
mod packet;
@ -44,6 +45,7 @@ mod sound;
mod sound_category;
mod spawn_egg;
mod tag;
mod tracked_data;
mod world_event;
pub const OUT_DIR: &str = "src/generated";
@ -60,6 +62,8 @@ pub fn main() {
(screen::build, "screen.rs"),
(particle::build, "particle.rs"),
(sound::build, "sound.rs"),
(meta_data_type::build, "meta_data_type.rs"),
(tracked_data::build, "tracked_data.rs"),
(chunk_status::build, "chunk_status.rs"),
(game_event::build, "game_event.rs"),
(game_rules::build, "game_rules.rs"),

View File

@ -0,0 +1,54 @@
use std::{collections::HashMap, fs};
use heck::ToPascalCase;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub(crate) fn build() -> TokenStream {
println!("cargo:rerun-if-changed=../assets/meta_data_type.json");
let handlers_map: HashMap<String, i32> =
serde_json::from_str(&fs::read_to_string("../assets/meta_data_type.json").unwrap())
.expect("Failed to parse meta_data_type.json");
let mut handlers: Vec<(&String, &i32)> = handlers_map.iter().collect();
handlers.sort_by_key(|&(_, id)| id);
let variants = handlers.iter().map(|(name, _)| {
let ident = format_ident!("{}", name.to_pascal_case());
quote! { #ident }
});
let from_id = handlers.iter().map(|(name, id)| {
let ident = format_ident!("{}", name.to_pascal_case());
quote! { #id => Some(Self::#ident), }
});
let to_id = handlers.iter().map(|(name, id)| {
let ident = format_ident!("{}", name.to_pascal_case());
quote! { Self::#ident => #id, }
});
quote! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(i32)]
pub enum MetaDataType {
#(#variants),*
}
impl MetaDataType {
pub fn from_id(id: i32) -> Option<Self> {
match id {
#(#from_id)*
_ => None,
}
}
pub const fn id(&self) -> i32 {
match self {
#(#to_id)*
}
}
}
}
}

View File

@ -0,0 +1,33 @@
use std::fs;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub(crate) fn build() -> TokenStream {
println!("cargo:rerun-if-changed=../assets/tracked_data.json");
let mut handlers: Vec<(String, u8)> =
serde_json::from_str::<std::collections::HashMap<String, u8>>(
&fs::read_to_string("../assets/tracked_data.json").unwrap(),
)
.expect("Failed to parse tracked_data.json")
.into_iter()
.collect();
handlers.sort_by_key(|&(_, id)| id);
let constants = handlers.iter().map(|(name, id)| {
let ident = format_ident!("DATA_{}", name.to_uppercase());
quote! {
pub const #ident: u8 = #id;
}
});
quote! {
pub struct TrackedData;
impl TrackedData {
#(#constants)*
}
}
}

View File

@ -35,7 +35,6 @@ pub enum PistonBehavior {
PushOnly,
}
// Add your methods here
impl BlockState {
pub const fn is_air(&self) -> bool {
self.state_flags & IS_AIR != 0

View File

@ -42,6 +42,14 @@ pub mod sound {
pub use crate::sound_enum::*;
}
#[rustfmt::skip]
#[path = "generated/tracked_data.rs"]
pub mod tracked_data;
#[rustfmt::skip]
#[path = "generated/meta_data_type.rs"]
pub mod meta_data_type;
#[rustfmt::skip]
#[path = "generated/noise_parameter.rs"]
pub mod noise_parameter;

View File

@ -4,14 +4,14 @@ use pumpkin_macros::packet;
use pumpkin_util::math::{position::BlockPos, vector2::Vector2};
use crate::{
codec::{var_int::VarInt, var_uint::VarUInt},
codec::{bedrock_block_pos::NetworkPos, var_int::VarInt, var_uint::VarUInt},
serial::PacketWrite,
};
#[packet(121)]
pub struct CNetworkChunkPublisherUpdate {
// https://mojang.github.io/bedrock-protocol-docs/html/NetworkChunkPublisherUpdatePacket.html
pub pos_for_view: BlockPos,
pub pos_for_view: NetworkPos,
// Is in blocks, not chunks!
pub new_radius: VarUInt,
// TODO
@ -21,8 +21,8 @@ pub struct CNetworkChunkPublisherUpdate {
impl CNetworkChunkPublisherUpdate {
pub fn new(pos_for_view: BlockPos, new_radius: u32) -> Self {
Self {
pos_for_view,
new_radius: VarUInt(new_radius << 4),
pos_for_view: NetworkPos(pos_for_view),
new_radius: VarUInt(new_radius),
server_build_chunk_list: Vec::new(),
}
}
@ -30,7 +30,7 @@ impl CNetworkChunkPublisherUpdate {
impl PacketWrite for CNetworkChunkPublisherUpdate {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
self.pos_for_view.write(writer)?;
self.pos_for_view.write_signed(writer)?;
self.new_radius.write(writer)?;
(self.server_build_chunk_list.len() as u32).write(writer)?;

View File

@ -6,11 +6,10 @@ use crate::{
#[derive(PacketWrite)]
#[packet(7)]
/// https://mojang.github.io/bedrock-protocol-docs/html/ResourcePackStackPacket.html
pub struct CResourcePackStackPacket {
// https://mojang.github.io/bedrock-protocol-docs/html/ResourcePackStackPacket.html
resource_pack_required: bool,
addons_list_size: VarUInt,
texture_pack_list_size: VarUInt,
game_version: String,
experiments: Experiments,
/// When connecting to an Editor world, include the vanilla editor packs in the stack
@ -21,7 +20,6 @@ impl CResourcePackStackPacket {
pub fn new(
resource_pack_required: bool,
addons_list_size: VarUInt,
texture_pack_list_size: VarUInt,
game_version: String,
experiments: Experiments,
include_editor_packs: bool,
@ -29,7 +27,6 @@ impl CResourcePackStackPacket {
Self {
resource_pack_required,
addons_list_size,
texture_pack_list_size,
game_version,
experiments,
include_editor_packs,

View File

@ -1,8 +1,10 @@
use std::io::{Error, Write};
use crate::serial::PacketWrite;
use pumpkin_macros::packet;
#[derive(PacketWrite)]
#[packet(6)]
/// https://mojang.github.io/bedrock-protocol-docs/html/ResourcePacksInfoPacket.html
pub struct CResourcePacksInfo {
resource_pack_required: bool,
has_addon_packs: bool,
@ -14,6 +16,20 @@ pub struct CResourcePacksInfo {
resource_packs: Vec<ResourcePack>,
}
impl PacketWrite for CResourcePacksInfo {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
self.resource_pack_required.write(writer)?;
self.has_addon_packs.write(writer)?;
self.has_scripts.write(writer)?;
self.is_vibrant_visuals_force_disabled.write(writer)?;
self.world_template_id.write(writer)?;
self.world_template_version.write(writer)?;
self.resource_packs_size.write(writer)?;
self.resource_packs.write(writer)?;
Ok(())
}
}
#[derive(PacketWrite)]
pub struct ResourcePack {
pack_id: uuid::Uuid,

View File

@ -34,6 +34,7 @@ pub struct CStartGame {
pub current_level_time: u64,
pub enchantment_seed: VarInt,
pub block_properties_size: VarUInt,
//pub block_properties: [GG; 2],
pub multiplayer_correlation_id: String,
@ -50,8 +51,8 @@ pub struct CStartGame {
pub enable_clientside_generation: bool,
pub blocknetwork_ids_are_hashed: bool,
pub tick_death_system_enabled: bool,
pub server_auth_sounds: bool,
// NetworkPermissions
pub disable_client_sounds: bool,
}
#[derive(PacketWrite)]
@ -114,6 +115,7 @@ pub struct LevelSettings {
pub new_nether: bool,
pub edu_shared_uri_button_name: String,
pub edu_shared_uri_link_uri: String,
pub override_force_experimental_gameplay_has_value: bool,
pub chat_restriction_level: i8,
pub disable_player_interactions: bool,

View File

@ -5,30 +5,13 @@ use pumpkin_util::math::vector3::Vector3;
use crate::{codec::var_ulong::VarULong, serial::PacketRead};
#[derive(Debug)]
#[derive(Debug, PacketRead)]
#[packet(33)]
pub struct SInteraction {
// https://mojang.github.io/bedrock-protocol-docs/html/InteractPacket.html
pub action: Action,
pub target_runtime_id: VarULong,
pub position: Vector3<f32>,
}
impl PacketRead for SInteraction {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
let action = Action::read(reader)?;
let target_runtime_id = VarULong::read(reader)?;
let mut position = Vector3::default();
if matches!(action, Action::InteractUpdate | Action::StopRiding) {
position = Vector3::read(reader)?;
}
Ok(Self {
action,
target_runtime_id,
position,
})
}
pub position: Option<Vector3<f32>>,
}
#[derive(Debug)]

View File

@ -7,3 +7,10 @@ pub struct SResourcePackResponse {
pub response: u8,
pub download_size: u16,
}
impl SResourcePackResponse {
pub const STATUS_REFUSED: u8 = 1;
pub const STATUS_SEND_PACKS: u8 = 2;
pub const STATUS_HAVE_ALL_PACKS: u8 = 3;
pub const STATUS_COMPLETED: u8 = 4;
}

View File

@ -10,6 +10,14 @@ use crate::{
/// Bedrocks Writes and Reads BlockPos types in Packets differently
pub struct NetworkPos(pub BlockPos);
impl NetworkPos {
pub fn write_signed<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
VarInt(self.0.0.x).write(writer)?;
VarInt(self.0.0.y).write(writer)?;
VarInt(self.0.0.z).write(writer)
}
}
impl PacketWrite for NetworkPos {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
VarInt(self.0.0.x).write(writer)?;

View File

@ -4,6 +4,7 @@ pub mod bitset;
pub mod data_component;
pub mod item_stack_seralizer;
pub mod little_endian;
pub mod optional_int;
mod u24_type;
pub mod uuid;
pub mod var_int;

View File

@ -0,0 +1,17 @@
use serde::{Serialize, Serializer};
use crate::codec::var_int::VarInt;
#[derive(Debug, Clone, Copy)]
pub struct OptionalInt(pub Option<i32>);
impl Serialize for OptionalInt {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
// Vanilla logic: empty is 0, otherwise value + 1
let val = match self.0 {
None => 0,
Some(id) => id + 1,
};
VarInt(val).serialize(serializer)
}
}

View File

@ -1,4 +1,4 @@
use pumpkin_data::packet::clientbound::PLAY_SET_ENTITY_DATA;
use pumpkin_data::{meta_data_type::MetaDataType, packet::clientbound::PLAY_SET_ENTITY_DATA};
use pumpkin_macros::packet;
use serde::Serialize;
@ -38,43 +38,3 @@ impl<T> Metadata<T> {
}
}
}
pub enum MetaDataType {
Byte = 0,
Integer = 1,
Long = 2,
Float = 3,
String = 4,
TextComponent = 5,
OptionalTextComponent = 6,
ItemStack = 7,
Boolean = 8,
Rotation = 9,
BlockPos = 10,
OptionalBlockPos = 11,
Facing = 12,
LazyEntityReference = 13,
BlockState = 14,
OptionalBlockState = 15,
Particle = 16,
ParticleList = 17,
VillagerData = 18,
OptionalInt = 19,
EntityPose = 20,
CatVariant = 21,
ChickenVariant = 22,
CowVariant = 23,
WolfVariant = 24,
WolfSoundVariant = 25,
FrogVariant = 26,
PigVariant = 27,
OptionalGlobalPos = 28,
PaintingVariant = 29,
SnifferState = 30,
ArmadilloState = 31,
CopperGolemState = 32,
OxidationLevel = 33,
Vector3f = 34,
QuaternionF = 35,
Profile = 36,
}

View File

@ -77,7 +77,9 @@ pub const fn smallest_encompassing_power_of_two(value: u32) -> u32 {
#[inline]
pub fn floor_div<T: PrimInt + Zero + One>(x: T, y: T) -> T {
let div = x / y;
if (x ^ y) < T::zero() && div * y != x {
let rem = x % y;
if (x ^ y) < T::zero() && rem != T::zero() {
div - T::one()
} else {
div

View File

@ -233,6 +233,17 @@ impl<T: Math + Copy> Add for Vector3<T> {
}
}
impl<T: Math + Copy> Sub for Vector3<T> {
type Output = Vector3<T>;
fn sub(self, rhs: Self) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl<T: Math + Copy> AddAssign for Vector3<T> {
fn add_assign(&mut self, other: Self) {
self.x += other.x;
@ -279,6 +290,16 @@ impl<T: Math + Copy + Into<f64>> Vector3<T> {
}
}
impl<T: Math + Copy + Into<f32>> Vector3<T> {
pub fn to_f32(&self) -> Vector3<f32> {
Vector3 {
x: self.x.into(),
y: self.y.into(),
z: self.z.into(),
}
}
}
impl<T: Math + Copy + Into<f64>> Vector3<T> {
pub fn to_i32(&self) -> Vector3<i32> {
let x: f64 = self.x.into();

View File

@ -1028,7 +1028,7 @@ impl GenerationCache for Cache {
);
}
Chunk::Proto(data) => {
data.set_block_state(pos, block_state);
data.set_block_state(pos.x, pos.y, pos.z, block_state);
}
}
}
@ -1745,7 +1745,7 @@ impl GenerationSchedule {
send: crossfire::MTx<(ChunkPos, RecvChunk)>,
level: Arc<Level>,
) {
log::info!(
log::debug!(
"generation thread start id: {:?} name: {}",
thread::current().id(),
thread::current().name().unwrap_or("unknown")
@ -1767,7 +1767,7 @@ impl GenerationSchedule {
break;
}
}
log::info!(
log::debug!(
"generation thread stop id: {:?} name: {}",
thread::current().id(),
thread::current().name().unwrap_or("unknown")

View File

@ -1,382 +0,0 @@
// Entity
pub const DATA_SHARED_FLAGS_ID: u8 = 0;
pub const DATA_AIR_SUPPLY_ID: u8 = 1;
pub const DATA_CUSTOM_NAME: u8 = 2;
pub const DATA_CUSTOM_NAME_VISIBLE: u8 = 3;
pub const DATA_SILENT: u8 = 4;
pub const DATA_NO_GRAVITY: u8 = 5;
pub const DATA_POSE: u8 = 6;
pub const DATA_TICKS_FROZEN: u8 = 7;
// OminousItemSpawner
// DATA_ITEM
pub const DATA_ITEM_OMINOUS_ITEM_SPAWNER: u8 = 8;
// LivingEntity
pub const DATA_LIVING_ENTITY_FLAGS: u8 = 8;
pub const DATA_HEALTH_ID: u8 = 9;
pub const DATA_EFFECT_PARTICLES: u8 = 10;
pub const DATA_EFFECT_AMBIENCE_ID: u8 = 11;
pub const DATA_ARROW_COUNT_ID: u8 = 12;
pub const DATA_STINGER_COUNT_ID: u8 = 13;
pub const SLEEPING_POS_ID: u8 = 14;
// Mob
pub const DATA_MOB_FLAGS_ID: u8 = 15;
// PathfinderMob
// AgeableMob
// DATA_BABY_ID
pub const DATA_BABY_ID_AGEABLE_MOB: u8 = 16;
// Interaction
// DATA_WIDTH_ID
pub const DATA_WIDTH_ID_INTERACTION: u8 = 8;
// DATA_HEIGHT_ID
pub const DATA_HEIGHT_ID_INTERACTION: u8 = 9;
pub const DATA_RESPONSE_ID: u8 = 10;
// Animal
// TamableAnimal
// DATA_FLAGS_ID
pub const DATA_FLAGS_ID_TAMABLE_ANIMAL: u8 = 17;
pub const DATA_OWNERUUID_ID: u8 = 18;
// Display
pub const DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID: u8 = 8;
pub const DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID: u8 = 9;
pub const DATA_POS_ROT_INTERPOLATION_DURATION_ID: u8 = 10;
pub const DATA_TRANSLATION_ID: u8 = 11;
pub const DATA_SCALE_ID: u8 = 12;
pub const DATA_LEFT_ROTATION_ID: u8 = 13;
pub const DATA_RIGHT_ROTATION_ID: u8 = 14;
pub const DATA_BILLBOARD_RENDER_CONSTRAINTS_ID: u8 = 15;
pub const DATA_BRIGHTNESS_OVERRIDE_ID: u8 = 16;
pub const DATA_VIEW_RANGE_ID: u8 = 17;
pub const DATA_SHADOW_RADIUS_ID: u8 = 18;
pub const DATA_SHADOW_STRENGTH_ID: u8 = 19;
// DATA_WIDTH_ID
pub const DATA_WIDTH_ID_DISPLAY: u8 = 20;
// DATA_HEIGHT_ID
pub const DATA_HEIGHT_ID_DISPLAY: u8 = 21;
pub const DATA_GLOW_COLOR_OVERRIDE_ID: u8 = 22;
// Display.BlockDisplay
// DATA_BLOCK_STATE_ID
pub const DATA_BLOCK_STATE_ID_DISPLAY_BLOCK_DISPLAY: u8 = 0;
// Display.ItemDisplay
pub const DATA_ITEM_STACK_ID: u8 = 0;
pub const DATA_ITEM_DISPLAY_ID: u8 = 1;
// Display.TextDisplay
pub const DATA_TEXT_ID: u8 = 0;
pub const DATA_LINE_WIDTH_ID: u8 = 1;
pub const DATA_BACKGROUND_COLOR_ID: u8 = 2;
pub const DATA_TEXT_OPACITY_ID: u8 = 3;
pub const DATA_STYLE_FLAGS_ID: u8 = 4;
// AgeableWaterCreature
// Squid
// GlowSquid
pub const DATA_DARK_TICKS_REMAINING: u8 = 17;
// ExperienceOrb
pub const DATA_VALUE: u8 = 8;
// AreaEffectCloud
pub const DATA_RADIUS: u8 = 8;
pub const DATA_WAITING: u8 = 9;
pub const DATA_PARTICLE: u8 = 10;
// ArmorStand
pub const DATA_CLIENT_FLAGS: u8 = 15;
pub const DATA_HEAD_POSE: u8 = 16;
pub const DATA_BODY_POSE: u8 = 17;
pub const DATA_LEFT_ARM_POSE: u8 = 18;
pub const DATA_RIGHT_ARM_POSE: u8 = 19;
pub const DATA_LEFT_LEG_POSE: u8 = 20;
pub const DATA_RIGHT_LEG_POSE: u8 = 21;
// BlockAttachedEntity
// HangingEntity
// ItemFrame
// DATA_ITEM
pub const DATA_ITEM_ITEM_FRAME: u8 = 8;
pub const DATA_ROTATION: u8 = 9;
// Painting
pub const DATA_PAINTING_VARIANT_ID: u8 = 8;
// VehicleEntity
pub const DATA_ID_HURT: u8 = 8;
pub const DATA_ID_HURTDIR: u8 = 9;
pub const DATA_ID_DAMAGE: u8 = 10;
// AbstractBoat
pub const DATA_ID_PADDLE_LEFT: u8 = 11;
pub const DATA_ID_PADDLE_RIGHT: u8 = 12;
pub const DATA_ID_BUBBLE_TIME: u8 = 13;
// AbstractMinecart
pub const DATA_ID_CUSTOM_DISPLAY_BLOCK: u8 = 11;
pub const DATA_ID_DISPLAY_OFFSET: u8 = 12;
// MinecartFurnace
pub const DATA_ID_FUEL: u8 = 13;
// MinecartCommandBlock
pub const DATA_ID_COMMAND_NAME: u8 = 13;
pub const DATA_ID_LAST_OUTPUT: u8 = 14;
// AmbientCreature
// Bat
// DATA_ID_FLAGS
pub const DATA_ID_FLAGS_BAT: u8 = 16;
// EndCrystal
pub const DATA_BEAM_TARGET: u8 = 8;
pub const DATA_SHOW_BOTTOM: u8 = 9;
// EnderDragon
pub const DATA_PHASE: u8 = 16;
// Monster
// WitherBoss
pub const DATA_TARGET_A: u8 = 16;
pub const DATA_TARGET_B: u8 = 17;
pub const DATA_TARGET_C: u8 = 18;
pub const DATA_ID_INV: u8 = 19;
// Projectile
// FishingHook
pub const DATA_HOOKED_ENTITY: u8 = 8;
pub const DATA_BITING: u8 = 9;
// EyeOfEnder
// DATA_ITEM_STACK
pub const DATA_ITEM_STACK_EYE_OF_ENDER: u8 = 8;
// AbstractArrow
pub const ID_FLAGS: u8 = 8;
pub const PIERCE_LEVEL: u8 = 9;
pub const IN_GROUND: u8 = 10;
// ThrownTrident
pub const ID_LOYALTY: u8 = 11;
pub const ID_FOIL: u8 = 12;
// Arrow
pub const ID_EFFECT_COLOR: u8 = 11;
// AbstractHurtingProjectile
// WitherSkull
pub const DATA_DANGEROUS: u8 = 8;
// Fireball
// DATA_ITEM_STACK
pub const DATA_ITEM_STACK_FIREBALL: u8 = 8;
// FireworkRocketEntity
pub const DATA_ID_FIREWORKS_ITEM: u8 = 8;
pub const DATA_ATTACHED_TO_TARGET: u8 = 9;
pub const DATA_SHOT_AT_ANGLE: u8 = 10;
// ThrowableProjectile
// ThrowableItemProjectile
// DATA_ITEM_STACK
pub const DATA_ITEM_STACK_THROWABLE_ITEM_PROJECTILE: u8 = 8;
// FallingBlockEntity
pub const DATA_START_POS: u8 = 8;
// PrimedTnt
pub const DATA_FUSE_ID: u8 = 8;
// DATA_BLOCK_STATE_ID
pub const DATA_BLOCK_STATE_ID_PRIMED_TNT: u8 = 9;
// ItemEntity
// DATA_ITEM
pub const DATA_ITEM_ITEM_ENTITY: u8 = 8;
// PatrollingMonster
// Raider
pub const IS_CELEBRATING: u8 = 16;
// Rabbit
// DATA_TYPE_ID
pub const DATA_TYPE_ID_RABBIT: u8 = 17;
// ShoulderRidingEntity
// Parrot
// DATA_VARIANT_ID
pub const DATA_VARIANT_ID_PARROT: u8 = 19;
// Turtle
pub const HAS_EGG: u8 = 17;
pub const LAYING_EGG: u8 = 18;
// WaterAnimal
// AbstractFish
// FROM_BUCKET
pub const FROM_BUCKET_ABSTRACT_FISH: u8 = 16;
// Pufferfish
pub const PUFF_STATE: u8 = 17;
// AbstractGolem
// IronGolem
// DATA_FLAGS_ID
pub const DATA_FLAGS_ID_IRON_GOLEM: u8 = 16;
// Bee
// DATA_FLAGS_ID
pub const DATA_FLAGS_ID_BEE: u8 = 17;
// DATA_REMAINING_ANGER_TIME
pub const DATA_REMAINING_ANGER_TIME_BEE: u8 = 18;
// SnowGolem
pub const DATA_PUMPKIN_ID: u8 = 16;
// AbstractCow
// Cow
// DATA_VARIANT_ID
pub const DATA_VARIANT_ID_COW: u8 = 17;
// AbstractSchoolingFish
// Salmon
// DATA_TYPE
pub const DATA_TYPE_SALMON: u8 = 17;
// Cat
// DATA_VARIANT_ID
pub const DATA_VARIANT_ID_CAT: u8 = 19;
pub const IS_LYING: u8 = 20;
pub const RELAX_STATE_ONE: u8 = 21;
// DATA_COLLAR_COLOR
pub const DATA_COLLAR_COLOR_CAT: u8 = 22;
// PolarBear
pub const DATA_STANDING_ID: u8 = 17;
// TropicalFish
// DATA_ID_TYPE_VARIANT
pub const DATA_ID_TYPE_VARIANT_TROPICAL_FISH: u8 = 17;
// Fox
// DATA_TYPE_ID
pub const DATA_TYPE_ID_FOX: u8 = 17;
// DATA_FLAGS_ID
pub const DATA_FLAGS_ID_FOX: u8 = 18;
pub const DATA_TRUSTED_ID_0: u8 = 19;
pub const DATA_TRUSTED_ID_1: u8 = 20;
// Ocelot
pub const DATA_TRUSTING: u8 = 17;
// Chicken
// DATA_VARIANT_ID
pub const DATA_VARIANT_ID_CHICKEN: u8 = 17;
// Panda
pub const UNHAPPY_COUNTER: u8 = 17;
pub const SNEEZE_COUNTER: u8 = 18;
pub const EAT_COUNTER: u8 = 19;
pub const MAIN_GENE_ID: u8 = 20;
pub const HIDDEN_GENE_ID: u8 = 21;
// DATA_ID_FLAGS
pub const DATA_ID_FLAGS_PANDA: u8 = 22;
// MushroomCow
// DATA_TYPE
pub const DATA_TYPE_MUSHROOM_COW: u8 = 17;
// Pig
// DATA_BOOST_TIME
pub const DATA_BOOST_TIME_PIG: u8 = 17;
// DATA_VARIANT_ID
pub const DATA_VARIANT_ID_PIG: u8 = 18;
// Dolphin
pub const GOT_FISH: u8 = 17;
pub const MOISTNESS_LEVEL: u8 = 18;
// Sniffer
pub const DATA_STATE: u8 = 17;
pub const DATA_DROP_SEED_AT_TICK: u8 = 18;
// Allay
pub const DATA_DANCING: u8 = 16;
pub const DATA_CAN_DUPLICATE: u8 = 17;
// Sheep
pub const DATA_WOOL_ID: u8 = 17;
// AbstractHorse
// DATA_ID_FLAGS
pub const DATA_ID_FLAGS_ABSTRACT_HORSE: u8 = 17;
// Camel
pub const DASH: u8 = 18;
pub const LAST_POSE_CHANGE_TICK: u8 = 19;
// Goat
pub const DATA_IS_SCREAMING_GOAT: u8 = 17;
pub const DATA_HAS_LEFT_HORN: u8 = 18;
pub const DATA_HAS_RIGHT_HORN: u8 = 19;
// Wolf
pub const DATA_INTERESTED_ID: u8 = 19;
// DATA_COLLAR_COLOR
pub const DATA_COLLAR_COLOR_WOLF: u8 = 20;
// DATA_REMAINING_ANGER_TIME
pub const DATA_REMAINING_ANGER_TIME_WOLF: u8 = 21;
// DATA_VARIANT_ID
pub const DATA_VARIANT_ID_WOLF: u8 = 22;
pub const DATA_SOUND_VARIANT_ID: u8 = 23;
// Frog
// DATA_VARIANT_ID
pub const DATA_VARIANT_ID_FROG: u8 = 17;
pub const DATA_TONGUE_TARGET_ID: u8 = 18;
// Horse
// DATA_ID_TYPE_VARIANT
pub const DATA_ID_TYPE_VARIANT_HORSE: u8 = 18;
// AbstractChestedHorse
pub const DATA_ID_CHEST: u8 = 18;
// Llama
pub const DATA_STRENGTH_ID: u8 = 19;
// DATA_VARIANT_ID
pub const DATA_VARIANT_ID_LLAMA: u8 = 20;
// Axolotl
pub const DATA_VARIANT: u8 = 17;
pub const DATA_PLAYING_DEAD: u8 = 18;
// FROM_BUCKET
pub const FROM_BUCKET_AXOLOTL: u8 = 19;
// Armadillo
pub const ARMADILLO_STATE: u8 = 17;
// AbstractVillager
pub const DATA_UNHAPPY_COUNTER: u8 = 17;
// Villager
// DATA_VILLAGER_DATA
pub const DATA_VILLAGER_DATA_VILLAGER: u8 = 18;
// Vex
// DATA_FLAGS_ID
pub const DATA_FLAGS_ID_VEX: u8 = 16;
// FlyingMob
// Ghast
pub const DATA_IS_CHARGING: u8 = 16;
// Zoglin
// DATA_BABY_ID
pub const DATA_BABY_ID_ZOGLIN: u8 = 16;
// Zombie
// DATA_BABY_ID
pub const DATA_BABY_ID_ZOMBIE: u8 = 16;
pub const DATA_SPECIAL_TYPE_ID: u8 = 17;
pub const DATA_DROWNED_CONVERSION_ID: u8 = 18;
// Blaze
// DATA_FLAGS_ID
pub const DATA_FLAGS_ID_BLAZE: u8 = 16;
// Guardian
pub const DATA_ID_MOVING: u8 = 16;
pub const DATA_ID_ATTACK_TARGET: u8 = 17;
// Strider
// DATA_BOOST_TIME
pub const DATA_BOOST_TIME_STRIDER: u8 = 17;
pub const DATA_SUFFOCATING: u8 = 18;
// Spider
// DATA_FLAGS_ID
pub const DATA_FLAGS_ID_SPIDER: u8 = 16;
// Phantom
// ID_SIZE
pub const ID_SIZE_PHANTOM: u8 = 16;
// AbstractSkeleton
// Skeleton
pub const DATA_STRAY_CONVERSION_ID: u8 = 16;
// AbstractIllager
// SpellcasterIllager
pub const DATA_SPELL_CASTING_ID: u8 = 17;
// Witch
pub const DATA_USING_ITEM: u8 = 17;
// Bogged
pub const DATA_SHEARED: u8 = 16;
// Slime
// ID_SIZE
pub const ID_SIZE_SLIME: u8 = 16;
// Creeper
pub const DATA_SWELL_DIR: u8 = 16;
pub const DATA_IS_POWERED: u8 = 17;
pub const DATA_IS_IGNITED: u8 = 18;
// EnderMan
pub const DATA_CARRY_STATE: u8 = 16;
pub const DATA_CREEPY: u8 = 17;
pub const DATA_STARED_AT: u8 = 18;
// Pillager
pub const IS_CHARGING_CROSSBOW: u8 = 17;
// ZombieVillager
pub const DATA_CONVERTING_ID: u8 = 19;
// DATA_VILLAGER_DATA
pub const DATA_VILLAGER_DATA_ZOMBIE_VILLAGER: u8 = 20;
// Shulker
pub const DATA_ATTACH_FACE_ID: u8 = 16;
pub const DATA_PEEK_ID: u8 = 17;
pub const DATA_COLOR_ID: u8 = 18;
// Creaking
pub const CAN_MOVE: u8 = 16;
pub const IS_ACTIVE: u8 = 17;
pub const IS_TEARING_DOWN: u8 = 18;
pub const HOME_POS: u8 = 19;
// AbstractPiglin
// DATA_IMMUNE_TO_ZOMBIFICATION
pub const DATA_IMMUNE_TO_ZOMBIFICATION_ABSTRACT_PIGLIN: u8 = 16;
// Piglin
// DATA_BABY_ID
pub const DATA_BABY_ID_PIGLIN: u8 = 17;
pub const DATA_IS_CHARGING_CROSSBOW: u8 = 18;
pub const DATA_IS_DANCING: u8 = 19;
// Hoglin
// DATA_IMMUNE_TO_ZOMBIFICATION
pub const DATA_IMMUNE_TO_ZOMBIFICATION_HOGLIN: u8 = 17;
// Warden
pub const CLIENT_ANGER_LEVEL: u8 = 16;
// Player
pub const DATA_PLAYER_ABSORPTION_ID: u8 = 15;
pub const DATA_SCORE_ID: u8 = 16;
pub const DATA_PLAYER_MODE_CUSTOMISATION: u8 = 17;
pub const DATA_PLAYER_MAIN_HAND: u8 = 18;
pub const DATA_SHOULDER_LEFT: u8 = 19;
pub const DATA_SHOULDER_RIGHT: u8 = 20;

View File

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

View File

@ -1,7 +1,7 @@
use enum_dispatch::enum_dispatch;
use pumpkin_data::{Block, BlockState};
use pumpkin_util::{
math::{clamped_map, floor_div, vector3::Vector3},
math::{clamped_map, floor_div},
random::{RandomDeriver, RandomDeriverImpl, RandomImpl},
};
@ -170,7 +170,7 @@ impl WorldAquiferSampler {
let index =
packed_position_index!(offset_x, offset_y, offset_z, size_y, size_z);
packed_positions[index as usize] =
block_pos::packed(&Vector3::new(rand_x, rand_y, rand_z));
block_pos::packed(rand_x as i64, rand_y as i64, rand_z as i64);
}
}
}
@ -196,30 +196,31 @@ impl WorldAquiferSampler {
}
fn random_positions_for_pos(&self, x: i32, y: i32, z: i32) -> [i64; 12] {
// (x, y - 1, z) to (x + 1, y + 1, z + 1)
// y values are contiguous in packed array:
// ($local_x * $dim_z + $local_z) * $dim_y + $local_y
let sy = self.size_y;
let syz = self.size_y * self.size_z;
let x0z0_index = self.packed_position_index(x, y - 1, z);
let x0z1_index = x0z0_index + self.size_y;
let x1z0_index = x0z0_index + self.size_y * self.size_z;
let x1z1_index = x1z0_index + self.size_y;
// Remove bounds checks on subsequent indexes
assert!(x1z1_index + 2 < self.packed_positions.len());
let i00 = self.packed_position_index(x, y - 1, z);
let i01 = i00 + sy;
let i10 = i00 + syz;
let i11 = i10 + sy;
assert!(i11 + 2 < self.packed_positions.len(), "Index out of bounds");
let p = &self.packed_positions;
[
self.packed_positions[x1z1_index + 2],
self.packed_positions[x1z0_index + 2],
self.packed_positions[x0z1_index + 2],
self.packed_positions[x0z0_index + 2],
self.packed_positions[x1z1_index + 1],
self.packed_positions[x1z0_index + 1],
self.packed_positions[x0z1_index + 1],
self.packed_positions[x0z0_index + 1],
self.packed_positions[x1z1_index],
self.packed_positions[x1z0_index],
self.packed_positions[x0z1_index],
self.packed_positions[x0z0_index],
p[i11 + 2],
p[i10 + 2],
p[i01 + 2],
p[i00 + 2],
p[i11 + 1],
p[i10 + 1],
p[i01 + 1],
p[i00 + 1],
p[i11],
p[i10],
p[i01],
p[i00],
]
}
@ -288,20 +289,22 @@ impl WorldAquiferSampler {
let local_z = local_xz!(z);
let index = self.packed_position_index(local_x, local_y, local_z);
let entry = &mut self.levels[index];
if let Some(ref level) = self.levels[index] {
return level;
}
let fluid_level = &self.fluid_level_sampler;
entry.get_or_insert_with(|| {
Self::get_fluid_level(
fluid_level,
x,
y,
z,
router,
height_estimator,
sample_options,
)
})
let sampled = Self::get_fluid_level(
&self.fluid_level_sampler,
x,
y,
z,
router,
height_estimator,
sample_options,
);
self.levels[index] = Some(sampled);
self.levels[index].as_ref().unwrap()
}
fn get_fluid_level(
@ -685,7 +688,7 @@ pub trait AquiferSamplerImpl {
#[cfg(test)]
mod random_positions_and_hypot {
use std::{mem, sync::LazyLock};
use std::sync::LazyLock;
use pumpkin_data::noise_router::OVERWORLD_BASE_NOISE_ROUTER;
@ -693,10 +696,7 @@ mod random_positions_and_hypot {
block::RawBlockState,
generation::{
GlobalRandomConfig, biome_coords,
chunk_noise::{
BlockStateSampler, ChainedBlockStateSampler, ChunkNoiseGenerator, LAVA_BLOCK,
WATER_BLOCK,
},
chunk_noise::{BlockStateSampler, ChunkNoiseGenerator, LAVA_BLOCK, WATER_BLOCK},
noise::router::{
chunk_density_function::{ChunkNoiseFunctionSampleOptions, SampleAction},
chunk_noise_router::ChunkNoiseRouter,
@ -755,20 +755,14 @@ mod random_positions_and_hypot {
);
let options =
ChunkNoiseFunctionSampleOptions::new(false, SampleAction::SkipCellCaches, 0, 0, 0);
let sampler = match noise.state_sampler {
BlockStateSampler::Chained(chained) => chained,
_ => unreachable!(),
};
let mut samplers = sampler.samplers;
let mut samplers_vec = noise.state_sampler.samplers.into_vec();
let first_sampler = samplers_vec.remove(0);
let mut dummy_sampler =
BlockStateSampler::Chained(ChainedBlockStateSampler::new(Box::new([])));
mem::swap(&mut dummy_sampler, &mut samplers[0]);
let sampler = match dummy_sampler {
BlockStateSampler::Aquifer(aquifer) => aquifer,
_ => unreachable!(),
let sampler = match first_sampler {
BlockStateSampler::Aquifer(a) => a,
_ => panic!("Expected Aquifer"),
};
let aquifer = match sampler {
AquiferSampler::Aquifer(aquifer) => aquifer,
_ => unreachable!(),

View File

@ -1,5 +1,5 @@
use pumpkin_data::{Block, BlockState};
use pumpkin_util::math::{floor_div, floor_mod, vector2::Vector2};
use pumpkin_util::math::{floor_div, floor_mod};
use crate::generation::section_coords;
@ -32,7 +32,6 @@ pub const CHUNK_DIM: u8 = 16;
pub enum BlockStateSampler {
Aquifer(AquiferSampler),
Ore(OreVeinSampler),
Chained(ChainedBlockStateSampler),
}
impl BlockStateSampler {
@ -46,7 +45,6 @@ impl BlockStateSampler {
match self {
Self::Aquifer(aquifer) => aquifer.apply(router, pos, sample_options, height_estimator),
Self::Ore(ore) => ore.sample(router, pos, sample_options),
Self::Chained(chained) => chained.sample(router, pos, sample_options, height_estimator),
}
}
}
@ -67,11 +65,12 @@ impl ChainedBlockStateSampler {
sample_options: &ChunkNoiseFunctionSampleOptions,
height_estimator: &mut SurfaceHeightEstimateSampler,
) -> Option<&'static BlockState> {
self.samplers
.iter_mut()
.map(|sampler| sampler.sample(router, pos, sample_options, height_estimator))
.find(|state| state.is_some())
.unwrap_or(None)
for sampler in self.samplers.iter_mut() {
if let Some(state) = sampler.sample(router, pos, sample_options, height_estimator) {
return Some(state);
}
}
None
}
}
@ -140,7 +139,7 @@ impl IndexToNoisePos for ChunkIndexMapper {
}
pub struct ChunkNoiseGenerator<'a> {
pub state_sampler: BlockStateSampler,
pub state_sampler: ChainedBlockStateSampler,
generation_shape: &'a GenerationShapeConfig,
start_cell_pos_x: i32,
start_cell_pos_z: i32,
@ -176,10 +175,6 @@ impl<'a> ChunkNoiseGenerator<'a> {
generation_shape.horizontal_cell_block_count() as i32,
);
let biome_pos = Vector2::new(
biome_coords::from_block(start_block_x),
biome_coords::from_block(start_block_z),
);
let horizontal_biome_end = biome_coords::from_block(
horizontal_cell_count * generation_shape.horizontal_cell_block_count() as usize,
);
@ -199,8 +194,8 @@ impl<'a> ChunkNoiseGenerator<'a> {
vertical_cell_block_count as usize,
vertical_cell_count,
horizontal_cell_count,
biome_pos.x,
biome_pos.y,
biome_coords::from_block(start_block_x),
biome_coords::from_block(start_block_z),
horizontal_biome_end,
);
@ -219,20 +214,16 @@ impl<'a> ChunkNoiseGenerator<'a> {
AquiferSampler::SeaLevel(SeaLevelAquiferSampler::new(level_sampler))
};
let state_sampler = if ore_veins {
let samplers: Box<[BlockStateSampler]> = if ore_veins {
let ore_sampler = OreVeinSampler::new(random_config.ore_random_deriver.clone());
let samplers: Box<[BlockStateSampler]> = Box::new([
Box::new([
BlockStateSampler::Aquifer(aquifer_sampler),
BlockStateSampler::Ore(ore_sampler),
]);
BlockStateSampler::Chained(ChainedBlockStateSampler::new(samplers))
])
} else {
let samplers: Box<[BlockStateSampler]> =
Box::new([BlockStateSampler::Aquifer(aquifer_sampler)]);
BlockStateSampler::Chained(ChainedBlockStateSampler::new(samplers))
Box::new([BlockStateSampler::Aquifer(aquifer_sampler)])
};
let state_sampler = ChainedBlockStateSampler::new(samplers);
let router = ChunkNoiseRouter::generate(noise_router_base, &builder_options);
@ -258,8 +249,8 @@ impl<'a> ChunkNoiseGenerator<'a> {
}
#[inline]
pub fn sample_end_density(&mut self, cell_x: u8) {
self.sample_density(false, self.start_cell_pos_x + cell_x as i32 + 1);
pub fn sample_end_density(&mut self, cell_x: i32) {
self.sample_density(false, self.start_cell_pos_x + cell_x + 1);
}
fn sample_density(&mut self, start: bool, current_x: i32) {
@ -330,17 +321,14 @@ impl<'a> ChunkNoiseGenerator<'a> {
self.router.swap_buffers();
}
pub fn on_sampled_cell_corners(&mut self, cell_x: u8, cell_y: u16, cell_z: u8) {
pub fn on_sampled_cell_corners(&mut self, cell_x: i32, cell_y: i32, cell_z: i32) {
self.router
.on_sampled_cell_corners(cell_y as usize, cell_z as usize);
self.cache_fill_unique_id += 1;
let start_x =
(self.start_cell_pos_x + cell_x as i32) * self.horizontal_cell_block_count() as i32;
let start_y =
(cell_y as i32 + self.minimum_cell_y) * self.vertical_cell_block_count() as i32;
let start_z =
(self.start_cell_pos_z + cell_z as i32) * self.horizontal_cell_block_count() as i32;
let start_x = (self.start_cell_pos_x + cell_x) * self.horizontal_cell_block_count() as i32;
let start_y = (cell_y + self.minimum_cell_y) * self.vertical_cell_block_count() as i32;
let start_z = (self.start_cell_pos_z + cell_z) * self.horizontal_cell_block_count() as i32;
let mapper = ChunkIndexMapper {
start_x,

View File

@ -22,6 +22,11 @@ impl FallenTreeFeature {
}
fn gen_stump(&self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, pos: BlockPos) {
chunk.set_block_state(&pos.0, self.trunk_provider.get(random, pos));
chunk.set_block_state(
pos.0.x,
pos.0.y,
pos.0.z,
self.trunk_provider.get(random, pos),
);
}
}

View File

@ -1,3 +1,5 @@
use std::num::NonZeroU32;
use pumpkin_util::random::{RandomGenerator, RandomImpl};
use serde::Deserialize;
@ -28,14 +30,14 @@ impl HeightProvider {
pub struct VeryBiasedToBottomHeightProvider {
min_inclusive: YOffset,
max_inclusive: YOffset,
inner: Option<u32>,
inner: Option<NonZeroU32>,
}
impl VeryBiasedToBottomHeightProvider {
pub fn get(&self, random: &mut RandomGenerator, min_y: i8, height: u16) -> i32 {
let min = self.min_inclusive.get_y(min_y as i16, height);
let max = self.max_inclusive.get_y(min_y as i16, height);
let inner = self.inner.unwrap_or(1) as i32;
let inner = self.inner.map(|i| i.get()).unwrap_or(1) as i32;
let min_rnd = random.next_inbetween_i32(min + inner, max);
let max_rnd = random.next_inbetween_i32(min, min_rnd - 1);

View File

@ -68,33 +68,43 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
let biome_aligned_z = biome_coords::to_block(biome_coords::from_block(block_z));
let packed_column = chunk_pos::packed(biome_aligned_x as u64, biome_aligned_z as u64);
if let Some(estimate) = self.cache.get(&packed_column) {
*estimate
} else {
let estimate = self.calculate_height_estimate(biome_aligned_x, biome_aligned_z);
self.cache.insert(packed_column, estimate);
estimate
if let Some(&estimate) = self.cache.get(&packed_column) {
return estimate;
}
let estimate = self.calculate_height_estimate(biome_aligned_x, biome_aligned_z);
self.cache.insert(packed_column, estimate);
estimate
}
fn calculate_height_estimate(&mut self, aligned_x: i32, aligned_z: i32) -> i32 {
for y in (self.minimum_y..=self.maximum_y)
.rev()
.step_by(self.y_level_step_count)
{
let pos = UnblendedNoisePos::new(aligned_x, y, aligned_z);
let density_sample = ChunkNoiseFunctionComponent::sample_from_stack(
let mut low = self.minimum_y;
let mut high = self.maximum_y;
let mut result = i32::MAX;
let sample_options =
ChunkNoiseFunctionSampleOptions::new(false, SampleAction::SkipCellCaches, 0, 0, 0);
while low <= high {
let mid = low + ((high - low) / 2);
let stepped_mid = mid - (mid % self.y_level_step_count as i32);
let pos = UnblendedNoisePos::new(aligned_x, stepped_mid, aligned_z);
let density = ChunkNoiseFunctionComponent::sample_from_stack(
&mut self.component_stack,
&pos,
&ChunkNoiseFunctionSampleOptions::new(false, SampleAction::SkipCellCaches, 0, 0, 0),
&sample_options,
);
if density_sample > Self::NOTCHIAN_SAMPLE_CUTOFF {
return y;
if density > Self::NOTCHIAN_SAMPLE_CUTOFF {
result = stepped_mid;
low = stepped_mid + self.y_level_step_count as i32;
} else {
high = stepped_mid - self.y_level_step_count as i32;
}
}
i32::MAX
result
}
pub fn generate(

View File

@ -1,8 +1,6 @@
use pumpkin_util::math::{floor_log2, smallest_encompassing_power_of_two};
pub mod block_pos {
use pumpkin_util::math::vector3::Vector3;
use super::{
BIT_SHIFT_X, BIT_SHIFT_Z, BITS_X, BITS_Y, BITS_Z, SIZE_BITS_X, SIZE_BITS_Y, SIZE_BITS_Z,
};
@ -23,12 +21,12 @@ pub mod block_pos {
}
#[inline]
pub const fn packed(vec: &Vector3<i32>) -> i64 {
pub const fn packed(x: i64, y: i64, z: i64) -> i64 {
let mut result = 0i64;
// Need to go to i64 first to conserve sign
result |= (vec.x as i64 & BITS_X as i64) << BIT_SHIFT_X;
result |= (vec.z as i64 & BITS_Z as i64) << BIT_SHIFT_Z;
result |= vec.y as i64 & BITS_Y as i64;
result |= (x & BITS_X as i64) << BIT_SHIFT_X;
result |= (z & BITS_Z as i64) << BIT_SHIFT_Z;
result |= y & BITS_Y as i64;
result
}
}
@ -107,8 +105,6 @@ pub const MIN_HEIGHT_CELL: i32 = MIN_HEIGHT << 4;
#[cfg(test)]
mod test {
use pumpkin_util::math::vector3::Vector3;
use super::{block_pos, chunk_pos};
#[test]
@ -123,21 +119,19 @@ mod test {
#[test]
fn test_block_packing() {
let pos = Vector3::new(-30000000, 120, 30000000);
let packed = block_pos::packed(&pos);
let packed = block_pos::packed(-30000000, 120, 30000000);
assert_eq!(packed, -8246337085439999880i64);
assert_eq!(pos.x, block_pos::unpack_x(packed));
assert_eq!(pos.y, block_pos::unpack_y(packed));
assert_eq!(pos.z, block_pos::unpack_z(packed));
assert_eq!(-30000000, block_pos::unpack_x(packed));
assert_eq!(120, block_pos::unpack_y(packed));
assert_eq!(30000000, block_pos::unpack_z(packed));
for x in -10..=10 {
for y in -10..=10 {
for z in -10..=10 {
let pos = Vector3::new(x * 1000000, y * 10, z * 1000000);
let packed = block_pos::packed(&pos);
assert_eq!(pos.x, block_pos::unpack_x(packed));
assert_eq!(pos.y, block_pos::unpack_y(packed));
assert_eq!(pos.z, block_pos::unpack_z(packed));
let packed = block_pos::packed(x * 1000000, y * 10, z * 1000000);
assert_eq!(x * 1000000, block_pos::unpack_x(packed) as i64);
assert_eq!(y * 10, block_pos::unpack_y(packed) as i64);
assert_eq!(z * 1000000, block_pos::unpack_z(packed) as i64);
}
}
}

View File

@ -233,10 +233,7 @@ impl ProtoChunk {
let absolute_y = section_base_y + y as i32 + chunk_data.section.min_y;
proto_chunk.set_block_state(
&Vector3::new(x as i32, absolute_y, z as i32),
block_state,
);
proto_chunk.set_block_state(x as i32, absolute_y, z as i32, block_state);
}
}
}
@ -452,29 +449,27 @@ impl ProtoChunk {
RawBlockState(self.get_block_state_raw(local_pos.x & 15, local_y, local_pos.z & 15))
}
pub fn set_block_state(&mut self, pos: &Vector3<i32>, block_state: &BlockState) {
let local_x = pos.x & 15;
let local_y = pos.y - self.bottom_y() as i32;
let local_z = pos.z & 15;
pub fn set_block_state(&mut self, x: i32, y: i32, z: i32, block_state: &BlockState) {
let local_x = x & 15;
let local_y = y - self.bottom_y() as i32;
let local_z = z & 15;
if local_y < 0 || local_y >= self.height() as i32 {
return;
}
if !block_state.is_air() {
self.maybe_update_surface_height_map(local_x, pos.y, local_z);
self.maybe_update_surface_height_map(local_x, y, local_z);
let block = Block::from_state_id(block_state.id);
let blocks_movement = blocks_movement(block_state, block);
if blocks_movement {
self.maybe_update_ocean_floor_height_map(local_x, pos.y, local_z);
self.maybe_update_ocean_floor_height_map(local_x, y, local_z);
}
if blocks_movement || block_state.is_liquid() {
self.maybe_update_motion_blocking_height_map(local_x, pos.y, local_z);
self.maybe_update_motion_blocking_height_map(local_x, y, local_z);
if !block.has_tag(&tag::Block::MINECRAFT_LEAVES) {
{
self.maybe_update_motion_blocking_no_leaves_height_map(
local_x, pos.y, local_z,
);
self.maybe_update_motion_blocking_no_leaves_height_map(local_x, y, local_z);
}
}
}
@ -657,69 +652,52 @@ impl ProtoChunk {
noise_sampler: &mut ChunkNoiseGenerator,
surface_height_estimate_sampler: &mut SurfaceHeightEstimateSampler,
) {
let horizontal_cell_block_count = noise_sampler.horizontal_cell_block_count();
let vertical_cell_block_count = noise_sampler.vertical_cell_block_count();
let horizontal_cells = CHUNK_DIM / horizontal_cell_block_count;
let h_count = noise_sampler.horizontal_cell_block_count() as i32;
let v_count = noise_sampler.vertical_cell_block_count() as i32;
let horizontal_cells = CHUNK_DIM as i32 / h_count;
let min_y = self.bottom_y();
let minimum_cell_y = min_y / vertical_cell_block_count as i8;
let cell_height = self.height() / vertical_cell_block_count as u16;
let minimum_cell_y = min_y / v_count as i8;
let cell_height = self.height() / v_count as u16;
let start_block_x = self.start_block_x();
let start_block_z = self.start_block_z();
let start_cell_x = self.start_cell_x(horizontal_cell_block_count);
let start_cell_z = self.start_cell_z(horizontal_cell_block_count);
let delta_y_step = 1.0 / v_count as f64;
let delta_x_z_step = 1.0 / h_count as f64;
// TODO: Block state updates when we implement those
noise_sampler.sample_start_density();
for cell_x in 0..horizontal_cells {
noise_sampler.sample_end_density(cell_x);
let sample_start_x =
(start_cell_x + cell_x as i32) * horizontal_cell_block_count as i32;
let sample_start_x = (self.start_cell_x(h_count) + cell_x) * h_count;
let block_x_base = self.start_block_x() + cell_x * h_count;
for cell_z in 0..horizontal_cells {
let sample_start_z =
(start_cell_z + cell_z as i32) * horizontal_cell_block_count as i32;
let sample_start_z = (self.start_cell_z(h_count) + cell_z) * h_count;
let block_z_base = self.start_block_z() + cell_z * h_count;
for cell_y in (0..cell_height).rev() {
noise_sampler.on_sampled_cell_corners(cell_x, cell_y, cell_z);
let sample_start_y =
(minimum_cell_y as i32 + cell_y as i32) * vertical_cell_block_count as i32;
noise_sampler.on_sampled_cell_corners(cell_x, cell_y as i32, cell_z);
let sample_start_y = (minimum_cell_y as i32 + cell_y as i32) * v_count;
let block_y_base = sample_start_y;
let delta_y_step = 1.0 / vertical_cell_block_count as f64;
for local_y in (0..v_count).rev() {
let block_y = sample_start_y + local_y;
noise_sampler.interpolate_y(local_y as f64 * delta_y_step);
for local_y in (0..vertical_cell_block_count).rev() {
let block_y = block_y_base + local_y as i32;
let delta_y = local_y as f64 * delta_y_step;
noise_sampler.interpolate_y(delta_y);
for local_x in 0..h_count {
noise_sampler.interpolate_x(local_x as f64 * delta_x_z_step);
let block_x = block_x_base + local_x;
let block_x_base =
start_block_x + cell_x as i32 * horizontal_cell_block_count as i32;
let delta_x_step = 1.0 / horizontal_cell_block_count as f64;
for local_x in 0..horizontal_cell_block_count {
let block_x = block_x_base + local_x as i32;
let delta_x = local_x as f64 * delta_x_step;
noise_sampler.interpolate_x(delta_x);
let block_z_base =
start_block_z + cell_z as i32 * horizontal_cell_block_count as i32;
let delta_z_step = 1.0 / horizontal_cell_block_count as f64;
for local_z in 0..horizontal_cell_block_count {
let block_z = block_z_base + local_z as i32;
let delta_z = local_z as f64 * delta_z_step;
noise_sampler.interpolate_z(delta_z);
for local_z in 0..h_count {
noise_sampler.interpolate_z(local_z as f64 * delta_x_z_step);
let block_z = block_z_base + local_z;
// The `cell_offset` calculations are still a good idea for clarity and correctness
// but let's confirm the values.
// block_x = start_block_x + cell_x*H + local_x
// sample_start_x = start_cell_x*H + cell_x*H = (start_cell_x+cell_x)*H
// These can be simplified.
let cell_offset_x = local_x as i32;
let cell_offset_x = local_x;
let cell_offset_y = block_y - sample_start_y;
let cell_offset_z = local_z as i32;
let cell_offset_z = local_z;
let block_state = noise_sampler
.sample_block_state(
@ -732,10 +710,7 @@ impl ProtoChunk {
surface_height_estimate_sampler,
)
.unwrap_or(self.default_block);
self.set_block_state(
&Vector3::new(block_x, block_y, block_z),
block_state,
);
self.set_block_state(block_x, block_y, block_z, block_state);
}
}
}
@ -872,7 +847,7 @@ impl ProtoChunk {
);
if let Some(state) = new_state {
self.set_block_state(&pos, state);
self.set_block_state(x, y, z, state);
}
}
}
@ -1126,12 +1101,12 @@ impl ProtoChunk {
center_chunk.stage = StagedChunkEnum::StructureReferences;
}
fn start_cell_x(&self, horizontal_cell_block_count: u8) -> i32 {
self.start_block_x() / horizontal_cell_block_count as i32
fn start_cell_x(&self, horizontal_cell_block_count: i32) -> i32 {
self.start_block_x() / horizontal_cell_block_count
}
fn start_cell_z(&self, horizontal_cell_block_count: u8) -> i32 {
self.start_block_z() / horizontal_cell_block_count as i32
fn start_cell_z(&self, horizontal_cell_block_count: i32) -> i32 {
self.start_block_z() / horizontal_cell_block_count
}
fn start_block_x(&self) -> i32 {

View File

@ -96,7 +96,12 @@ impl StructurePieceBase for BuriedTreasurePiece {
|| Self::is_liquid(down_offset_state.to_block()))
&& dir != BlockDirection::Up
{
chunk.set_block_state(&offset_pos.0, down_raw_state.to_state());
chunk.set_block_state(
offset_pos.0.x,
offset_pos.0.y,
offset_pos.0.z,
down_raw_state.to_state(),
);
continue;
}
@ -105,12 +110,12 @@ impl StructurePieceBase for BuriedTreasurePiece {
} else {
state.to_state()
};
chunk.set_block_state(&offset_pos.0, state1);
chunk.set_block_state(offset_pos.0.x, offset_pos.0.y, offset_pos.0.z, state1);
}
// Place the Chest
// TODO: Add loot table logic here (requires seed)
chunk.set_block_state(&pos.0, Block::CHEST.default_state);
chunk.set_block_state(pos.0.x, pos.0.y, pos.0.z, Block::CHEST.default_state);
return;
}
pos = pos.down();

View File

@ -274,10 +274,8 @@ impl StructurePiece {
for current_y in (end_y..=start_y).rev() {
// We bypass add_block here because add_block transforms X/Z again.
// We use the already-transformed world X/Z but iterate Y manually.
let current_pos = Vector3::new(world_pos.x, current_y, world_pos.z);
if box_limit.contains_pos(&current_pos) {
chunk.set_block_state(&current_pos, state);
if box_limit.contains(world_pos.x, current_y, world_pos.z) {
chunk.set_block_state(world_pos.x, current_y, world_pos.z, state);
}
}
}
@ -344,7 +342,7 @@ impl StructurePiece {
// }
// World interaction
world.set_block_state(&block_pos, block);
world.set_block_state(block_pos.x, block_pos.y, block_pos.z, block);
// let fluid_state = world.get_fluid_state(&block_pos);
// if !fluid_state.is_empty() {

View File

@ -170,7 +170,7 @@ impl SurfaceTerrainBuilder {
}
let default_block = &chunk.default_block;
chunk.set_block_state(&pos, default_block);
chunk.set_block_state(global_x, y, global_z, default_block);
}
}
}
@ -246,10 +246,10 @@ impl SurfaceTerrainBuilder {
&& rand.next_f64() > 0.15)
{
if snow_blocks <= snow_block_count && y > snow_bottom {
chunk.set_block_state(&pos, Self::SNOW_BLOCK.default_state);
chunk.set_block_state(x, y, z, Self::SNOW_BLOCK.default_state);
snow_blocks += 1;
} else {
chunk.set_block_state(&pos, Self::PACKED_ICE.default_state);
chunk.set_block_state(x, y, z, Self::PACKED_ICE.default_state);
}
}
}

View File

@ -9,7 +9,6 @@ pub mod chunk_system;
pub mod cylindrical_chunk_iterator;
pub mod data;
pub mod dimension;
pub mod entity;
pub mod generation;
pub mod inventory;
pub mod item;
@ -23,7 +22,8 @@ pub type BlockId = u16;
pub type BlockStateId = u16;
pub const CURRENT_MC_VERSION: &str = "1.21.11";
pub const CURRENT_BEDROCK_MC_VERSION: &str = "1.21.111";
pub const CURRENT_BEDROCK_MC_VERSION: &str = "1.21.32";
pub const CURRENT_BEDROCK_MC_PROTOCOL: u32 = 898;
#[macro_export]
macro_rules! global_path {

View File

@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::sync::Arc;
use super::CommandExecutor;
@ -19,7 +20,7 @@ impl CommandTree {
#[must_use]
pub fn new(
names: impl IntoIterator<Item: Into<String>>,
description: impl Into<String>,
description: impl Into<Cow<'static, str>>,
) -> Self {
let names_vec = names.into_iter().map(Into::into).collect();

View File

@ -1,6 +1,6 @@
use super::{CommandExecutor, args::ArgumentConsumer};
use crate::command::CommandSender;
use std::{collections::VecDeque, fmt::Debug, sync::Arc};
use std::{borrow::Cow, collections::VecDeque, fmt::Debug, sync::Arc};
pub mod builder;
pub mod format;
@ -59,7 +59,7 @@ pub struct CommandTree {
pub nodes: Vec<Node>,
pub children: Vec<usize>,
pub names: Vec<String>,
pub description: String,
pub description: Cow<'static, str>,
}
impl CommandTree {

View File

@ -1,8 +1,8 @@
use core::f32;
use crate::entity::{Entity, EntityBase, EntityBaseFuture, NBTStorage, living::LivingEntity};
use pumpkin_data::damage::DamageType;
use pumpkin_protocol::java::client::play::{MetaDataType, Metadata};
use pumpkin_data::{damage::DamageType, meta_data_type::MetaDataType, tracked_data::TrackedData};
use pumpkin_protocol::java::client::play::Metadata;
use pumpkin_util::math::vector3::Vector3;
pub struct EndCrystalEntity {
@ -18,7 +18,11 @@ impl EndCrystalEntity {
impl EndCrystalEntity {
pub async fn set_show_bottom(&self, show_bottom: bool) {
self.entity
.send_meta_data(&[Metadata::new(9, MetaDataType::Boolean, show_bottom)])
.send_meta_data(&[Metadata::new(
TrackedData::DATA_SHOW_BOTTOM,
MetaDataType::Boolean,
show_bottom,
)])
.await;
}
}

View File

@ -1,6 +1,7 @@
use pumpkin_data::Block;
use pumpkin_data::entity::EntityType;
use pumpkin_protocol::java::client::play::{MetaDataType, Metadata};
use pumpkin_data::meta_data_type::MetaDataType;
use pumpkin_data::{Block, tracked_data::TrackedData};
use pumpkin_protocol::java::client::play::Metadata;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, world::BlockFlags};
use std::sync::{Arc, atomic::Ordering};
@ -95,7 +96,7 @@ impl EntityBase for FallingEntity {
Box::pin(async move {
self.entity
.send_meta_data(&[Metadata::new(
8,
TrackedData::DATA_BLOCK_POS,
MetaDataType::BlockPos,
self.entity.block_pos.load(),
)])

View File

@ -8,10 +8,10 @@ use std::sync::{
};
use crossbeam::atomic::AtomicCell;
use pumpkin_data::damage::DamageType;
use pumpkin_data::{damage::DamageType, meta_data_type::MetaDataType, tracked_data::TrackedData};
use pumpkin_protocol::{
codec::item_stack_seralizer::ItemStackSerializer,
java::client::play::{CTakeItemEntity, MetaDataType, Metadata},
java::client::play::{CTakeItemEntity, Metadata},
};
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
@ -349,7 +349,7 @@ impl EntityBase for ItemEntity {
Box::pin(async {
self.entity
.send_meta_data(&[Metadata::new(
8,
TrackedData::DATA_STACK,
MetaDataType::ItemStack,
&ItemStackSerializer::from(self.item_stack.lock().await.clone()),
)])

View File

@ -1,4 +1,6 @@
use pumpkin_data::meta_data_type::MetaDataType;
use pumpkin_data::potion::Effect;
use pumpkin_data::tracked_data::TrackedData;
use pumpkin_inventory::build_equipment_slots;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_util::Hand;
@ -31,7 +33,7 @@ use pumpkin_protocol::codec::var_int::VarInt;
use pumpkin_protocol::java::client::play::{CHurtAnimation, CTakeItemEntity};
use pumpkin_protocol::{
codec::item_stack_seralizer::ItemStackSerializer,
java::client::play::{CDamageEvent, CSetEquipment, MetaDataType, Metadata},
java::client::play::{CDamageEvent, CSetEquipment, Metadata},
};
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::text::TextComponent;
@ -78,10 +80,10 @@ pub struct LivingEntity {
}
impl LivingEntity {
const USING_ITEM_FLAG: i32 = 1;
const OFF_HAND_ACTIVE_FLAG: i32 = 2;
const USING_ITEM_FLAG: u8 = 1;
const OFF_HAND_ACTIVE_FLAG: u8 = 2;
#[expect(dead_code)]
const USING_RIPTIDE_FLAG: i32 = 4;
const USING_RIPTIDE_FLAG: u8 = 4;
pub fn new(entity: Entity) -> Self {
let water_movement_speed_multiplier = if entity.entity_type == &EntityType::POLAR_BEAR {
@ -160,8 +162,8 @@ impl LivingEntity {
.await;
}
async fn set_living_flag(&self, flag: i32, value: bool) {
let index = flag as u8;
async fn set_living_flag(&self, flag: u8, value: bool) {
let index = flag;
let mut b = self.livings_flags.load(Ordering::Relaxed);
if value {
b |= index;
@ -170,7 +172,11 @@ impl LivingEntity {
}
self.livings_flags.store(b, Ordering::Relaxed);
self.entity
.send_meta_data(&[Metadata::new(8, MetaDataType::Byte, b)])
.send_meta_data(&[Metadata::new(
TrackedData::DATA_LIVING_FLAGS,
MetaDataType::Byte,
b,
)])
.await;
}
@ -191,7 +197,11 @@ impl LivingEntity {
self.health.store(health.max(0.0));
// tell everyone entities health changed
self.entity
.send_meta_data(&[Metadata::new(9, MetaDataType::Float, health)])
.send_meta_data(&[Metadata::new(
TrackedData::DATA_HEALTH,
MetaDataType::Float,
health,
)])
.await;
}

View File

@ -9,6 +9,8 @@ use pumpkin_data::BlockState;
use pumpkin_data::block_properties::{EnumVariants, Integer0To15};
use pumpkin_data::dimension::Dimension;
use pumpkin_data::fluid::Fluid;
use pumpkin_data::meta_data_type::MetaDataType;
use pumpkin_data::tracked_data::TrackedData;
use pumpkin_data::{Block, BlockDirection};
use pumpkin_data::{
block_properties::{Facing, HorizontalFacing},
@ -22,7 +24,7 @@ use pumpkin_protocol::{
codec::var_int::VarInt,
java::client::play::{
CEntityPositionSync, CEntityVelocity, CHeadRot, CSetEntityMetadata, CSpawnEntity,
CUpdateEntityRot, MetaDataType, Metadata,
CUpdateEntityRot, Metadata,
},
ser::serializer::Serializer,
};
@ -37,7 +39,6 @@ use pumpkin_util::math::{
};
use pumpkin_util::text::TextComponent;
use pumpkin_util::text::hover::HoverEvent;
use pumpkin_world::entity::entity_data_flags::DATA_POSE;
use serde::Serialize;
use std::collections::BTreeMap;
use std::f32::consts::PI;
@ -443,7 +444,7 @@ impl Entity {
/// Sets a custom name for the entity, typically used with nametags
pub async fn set_custom_name(&self, name: TextComponent) {
self.send_meta_data(&[Metadata::new(
2,
TrackedData::DATA_CUSTOM_NAME,
MetaDataType::OptionalTextComponent,
Some(name),
)])
@ -499,16 +500,15 @@ impl Entity {
/// Returns entity rotation as vector
pub fn rotation(&self) -> Vector3<f32> {
// Convert degrees to radians if necessary
let yaw_rad = self.yaw.load().to_radians();
let pitch_rad = self.pitch.load().to_radians();
let yaw_rad = -self.yaw.load().to_radians();
Vector3::new(
yaw_rad.cos() * pitch_rad.cos(),
pitch_rad.sin(),
yaw_rad.sin() * pitch_rad.cos(),
)
.normalize()
let cos_yaw = yaw_rad.cos();
let sin_yaw = yaw_rad.sin();
let cos_pitch = pitch_rad.cos();
let sin_pitch = pitch_rad.sin();
Vector3::new(sin_yaw * cos_pitch, -sin_pitch, cos_yaw * cos_pitch)
}
/// Changes this entity's pitch and yaw to look at target
@ -1602,8 +1602,12 @@ impl Entity {
} else {
b &= !(1 << index);
}
self.send_meta_data(&[Metadata::new(0, MetaDataType::Byte, b)])
.await;
self.send_meta_data(&[Metadata::new(
TrackedData::DATA_FLAGS,
MetaDataType::Byte,
b,
)])
.await;
}
/// Plays sound at this entity's position with the entity's sound category
@ -1631,7 +1635,7 @@ impl Entity {
self.pose.store(pose);
let pose = pose as i32;
self.send_meta_data(&[Metadata::new(
DATA_POSE,
TrackedData::DATA_POSE,
MetaDataType::EntityPose,
VarInt(pose),
)])

View File

@ -11,6 +11,8 @@ use crossbeam::atomic::AtomicCell;
use crossbeam::channel::Receiver;
use log::warn;
use pumpkin_data::dimension::Dimension;
use pumpkin_data::meta_data_type::MetaDataType;
use pumpkin_data::tracked_data::TrackedData;
use pumpkin_inventory::player::ender_chest_inventory::EnderChestInventory;
use pumpkin_protocol::bedrock::client::level_chunk::CLevelChunk;
use pumpkin_protocol::bedrock::client::set_time::CSetTime;
@ -54,7 +56,7 @@ use pumpkin_protocol::java::client::play::{
CSetContainerContent, CSetContainerProperty, CSetContainerSlot, CSetCursorItem, CSetEquipment,
CSetExperience, CSetHealth, CSetPlayerInventory, CSetSelectedSlot, CSoundEffect, CStopSound,
CSubtitle, CSystemChatMessage, CTitleAnimation, CTitleText, CUnloadChunk, CUpdateMobEffect,
CUpdateTime, GameEvent, MetaDataType, Metadata, PlayerAction, PlayerInfoFlags, PreviousMessage,
CUpdateTime, GameEvent, Metadata, PlayerAction, PlayerInfoFlags, PreviousMessage,
};
use pumpkin_protocol::java::server::play::SClickSlot;
use pumpkin_util::math::{
@ -68,7 +70,6 @@ use pumpkin_util::text::hover::HoverEvent;
use pumpkin_util::{GameMode, Hand};
use pumpkin_world::biome;
use pumpkin_world::cylindrical_chunk_iterator::Cylindrical;
use pumpkin_world::entity::entity_data_flags::SLEEPING_POS_ID;
use pumpkin_world::item::ItemStack;
use pumpkin_world::level::{Level, SyncChunk, SyncEntityChunk};
@ -719,7 +720,7 @@ impl Player {
.set_pos(bed_head_pos.to_f64().add_raw(0.5, 0.6875, 0.5));
self.get_entity()
.send_meta_data(&[Metadata::new(
SLEEPING_POS_ID,
TrackedData::DATA_SLEEPING_POSITION,
MetaDataType::OptionalBlockPos,
Some(bed_head_pos),
)])
@ -770,7 +771,7 @@ impl Player {
self.living_entity
.entity
.send_meta_data(&[Metadata::new(
SLEEPING_POS_ID,
TrackedData::DATA_SLEEPING_POSITION,
MetaDataType::OptionalBlockPos,
None::<BlockPos>,
)])
@ -1511,24 +1512,23 @@ impl Player {
}
/// Send the player's skin layers and used hand to all players.
pub fn send_client_information(&self) {
//let config = self.config.read().await;
// TODO
// self.living_entity
// .entity
// .send_meta_data(&[
// Metadata::new(
// DATA_PLAYER_MODE_CUSTOMISATION,
// MetaDataType::Byte,
// config.skin_parts,
// ),
// Metadata::new(
// DATA_PLAYER_MAIN_HAND,
// MetaDataType::Byte,
// config.main_hand as u8,
// ),
// ])
// .await;
pub async fn send_client_information(&self) {
let config = self.config.read().await;
self.living_entity
.entity
.send_meta_data(&[
Metadata::new(
TrackedData::DATA_PLAYER_MODE_CUSTOMIZATION_ID,
MetaDataType::Byte,
config.skin_parts,
),
// Metadata::new(
// TrackedData::DATA_MAIN_ARM_ID,
// MetaDataType::Arm,
// VarInt(config.main_hand as u8 as i32),
// ),
])
.await;
}
pub async fn can_harvest(&self, state: &BlockState, block: &'static Block) -> bool {

View File

@ -0,0 +1,130 @@
use std::sync::{
Arc,
atomic::{AtomicU32, Ordering},
};
use pumpkin_data::{entity::EntityStatus, meta_data_type::MetaDataType, tracked_data::TrackedData};
use pumpkin_protocol::{codec::optional_int::OptionalInt, java::client::play::Metadata};
use pumpkin_util::{
math::vector3::Vector3,
random::{RandomGenerator, RandomImpl, get_seed, xoroshiro128::Xoroshiro},
};
use crate::{
entity::{Entity, EntityBase, EntityBaseFuture, NBTStorage, projectile::ThrownItemEntity},
server::Server,
world::World,
};
pub struct FireworkRocketEntity {
entity: ThrownItemEntity,
shooter_id: Option<i32>,
life: AtomicU32,
life_time: AtomicU32,
}
impl FireworkRocketEntity {
pub async fn new(entity: Entity) -> Self {
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(get_seed()));
entity
.set_velocity(Vector3::new(
random.next_triangular(0.0, 0.002_297),
0.05,
random.next_triangular(0.0, 0.002_297),
))
.await;
Self {
entity: ThrownItemEntity { entity },
shooter_id: None,
life: 0.into(),
// TODO
life_time: (10 + random.next_bounded_i32(6) as u32 + random.next_bounded_i32(7) as u32)
.into(),
}
}
pub async fn new_shot(entity: Entity, shooter: &Entity) -> Self {
let mut entity = Self::new(entity).await;
entity.shooter_id = Some(shooter.entity_id);
entity
.entity
.entity
.send_meta_data(&[Metadata::new(
TrackedData::DATA_SHOOTER_ENTITY_ID,
MetaDataType::OptionalInt,
OptionalInt(Some(shooter.entity_id)),
)])
.await;
entity
}
pub async fn explode_and_remove(&self, world: &World) {
let entity = self.get_entity();
world
.send_entity_status(entity, EntityStatus::ExplodeFireworkClient)
.await;
// TODO: Explode
entity.remove().await;
}
}
impl NBTStorage for FireworkRocketEntity {}
impl EntityBase for FireworkRocketEntity {
fn tick<'a>(
&'a self,
caller: Arc<dyn EntityBase>,
server: &'a Server,
) -> EntityBaseFuture<'a, ()> {
Box::pin(async move {
self.entity.tick(caller, server).await;
let entity = self.get_entity();
let world = &entity.world;
let mut velocity = entity.velocity.load();
if let Some(shooter_id) = self.shooter_id {
if let Some(shooter) = world.get_entity_by_id(shooter_id).await {
let shooter = shooter.get_entity();
if shooter.fall_flying.load(Ordering::Relaxed) {
let rotation = shooter.rotation().to_f64();
let shooter_vel = shooter.velocity.load();
let new_shooter_vel =
shooter_vel + (rotation * 0.1 + (rotation * 1.5 - shooter_vel) * 0.5);
shooter.set_velocity(new_shooter_vel).await;
entity.set_pos(shooter.pos.load());
entity.set_velocity(new_shooter_vel).await;
}
}
} else {
velocity.x *= 1.15;
velocity.z *= 1.15;
velocity.y += 0.04;
entity.set_velocity(velocity).await;
}
let current_life = self.life.fetch_add(1, Ordering::Relaxed);
if current_life > self.life_time.load(Ordering::Relaxed) {
self.explode_and_remove(world).await;
}
})
}
fn get_entity(&self) -> &crate::entity::Entity {
&self.entity.entity
}
fn get_living_entity(&self) -> Option<&crate::entity::living::LivingEntity> {
None
}
fn as_nbt_storage(&self) -> &dyn crate::entity::NBTStorage {
self
}
}

View File

@ -6,10 +6,11 @@ use std::{
use super::{Entity, EntityBase, NBTStorage, living::LivingEntity};
use pumpkin_util::math::vector3::Vector3;
pub mod firework_rocket;
pub mod wind_charge;
pub struct ThrownItemEntity {
entity: Entity,
pub entity: Entity,
}
impl ThrownItemEntity {

View File

@ -102,11 +102,11 @@ impl EntityBase for WindChargeEntity {
server: &'a Server,
) -> EntityBaseFuture<'a, ()> {
Box::pin(async move {
self.thrown_item_entity.tick(caller, server).await;
if self.get_deflect_cooldown() > 0 {
self.set_deflect_cooldown(self.get_deflect_cooldown() - 1);
}
self.thrown_item_entity.tick(caller, server).await;
})
}

View File

@ -1,11 +1,8 @@
use super::{Entity, EntityBase, NBTStorage, living::LivingEntity};
use crate::{entity::EntityBaseFuture, server::Server};
use core::f32;
use pumpkin_data::Block;
use pumpkin_protocol::{
codec::var_int::VarInt,
java::client::play::{MetaDataType, Metadata},
};
use pumpkin_data::{Block, meta_data_type::MetaDataType, tracked_data::TrackedData};
use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata};
use pumpkin_util::math::vector3::Vector3;
use std::{
f64::consts::TAU,
@ -88,12 +85,12 @@ impl EntityBase for TNTEntity {
self.entity
.send_meta_data(&[
Metadata::new(
8,
TrackedData::DATA_FUSE,
MetaDataType::Integer,
VarInt(self.fuse.load(Relaxed) as i32),
),
Metadata::new(
9,
TrackedData::DATA_BLOCK_STATE,
MetaDataType::BlockState,
VarInt(i32::from(Block::TNT.default_state.id)),
),

View File

@ -45,6 +45,7 @@ impl ItemBehaviour for ArmorStandItem {
player: &'a Player,
location: BlockPos,
face: BlockDirection,
_cursor_pos: Vector3<f32>,
_block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -9,6 +9,7 @@ use pumpkin_data::block_properties::{OakDoorLikeProperties, PaleOakWoodLikePrope
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag};
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
use pumpkin_world::world::BlockFlags;
@ -27,6 +28,7 @@ impl ItemBehaviour for AxeItem {
player: &'a Player,
location: BlockPos,
_face: BlockDirection,
_cursor_pos: Vector3<f32>,
block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -10,6 +10,7 @@ use pumpkin_data::entity::EntityType;
use pumpkin_data::item::Item;
use pumpkin_data::{Block, BlockDirection};
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
use uuid::Uuid;
@ -28,6 +29,7 @@ impl ItemBehaviour for EndCrystalItem {
player: &'a Player,
location: BlockPos,
_face: BlockDirection,
_cursor_pos: Vector3<f32>,
_block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -4,6 +4,7 @@ use std::sync::Arc;
use crate::{server::Server, world::portal::end::EndPortal};
use pumpkin_data::{Block, BlockDirection, item::Item};
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
use pumpkin_world::world::BlockFlags;
@ -25,6 +26,7 @@ impl ItemBehaviour for EnderEyeItem {
player: &'a Player,
location: BlockPos,
_face: BlockDirection,
_cursor_pos: Vector3<f32>,
block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -0,0 +1,80 @@
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use crate::entity::player::Player;
use crate::entity::projectile::firework_rocket::FireworkRocketEntity;
use crate::entity::{Entity, EntityBase};
use crate::item::{ItemBehaviour, ItemMetadata};
use crate::server::Server;
use pumpkin_data::Block;
use pumpkin_data::BlockDirection;
use pumpkin_data::entity::EntityType;
use pumpkin_data::item::Item;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
use uuid::Uuid;
pub struct FireworkRocketItem;
impl ItemMetadata for FireworkRocketItem {
fn ids() -> Box<[u16]> {
[Item::FIREWORK_ROCKET.id].into()
}
}
impl ItemBehaviour for FireworkRocketItem {
fn use_on_block<'a>(
&'a self,
_item: &'a mut ItemStack,
player: &'a Player,
location: BlockPos,
_face: BlockDirection,
cursor_pos: Vector3<f32>,
_block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
let world = player.world();
let entity = Entity::new(
Uuid::new_v4(),
world.clone(),
Vector3::new(
f64::from(location.0.x) + f64::from(cursor_pos.x),
f64::from(location.0.y) + f64::from(cursor_pos.y),
f64::from(location.0.z) + f64::from(cursor_pos.z),
),
&EntityType::FIREWORK_ROCKET,
false,
);
let entity = FireworkRocketEntity::new(entity).await;
world.spawn_entity(Arc::new(entity)).await;
})
}
fn normal_use<'a>(
&'a self,
_item: &'a Item,
player: &'a Player,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async {
if player.get_entity().fall_flying.load(Ordering::Relaxed) {
let world = player.world();
let entity = Entity::new(
Uuid::new_v4(),
world.clone(),
player.get_entity().pos.load(),
&EntityType::FIREWORK_ROCKET,
false,
);
let entity = FireworkRocketEntity::new_shot(entity, player.get_entity()).await;
world.spawn_entity(Arc::new(entity)).await;
}
})
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}

View File

@ -8,6 +8,7 @@ use pumpkin_data::entity::EntityType;
use pumpkin_data::item::Item;
use pumpkin_data::{Block, tag};
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
use pumpkin_world::world::BlockFlags;
use std::pin::Pin;
@ -29,6 +30,7 @@ impl ItemBehaviour for HoeItem {
player: &'a Player,
location: BlockPos,
face: BlockDirection,
_cursor_pos: Vector3<f32>,
block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -15,6 +15,7 @@ use pumpkin_data::tag::Taggable;
use pumpkin_data::world::WorldEvent;
use pumpkin_data::{Block, tag};
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::block::entities::BlockEntity;
use pumpkin_world::block::entities::sign::SignBlockEntity;
use pumpkin_world::item::ItemStack;
@ -35,6 +36,7 @@ impl ItemBehaviour for HoneyCombItem {
player: &'a Player,
location: BlockPos,
_face: BlockDirection,
_cursor_pos: Vector3<f32>,
block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -7,6 +7,7 @@ use pumpkin_data::item::Item;
use pumpkin_data::sound::Sound;
use pumpkin_data::sound::SoundCategory;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
use pumpkin_world::world::BlockFlags;
@ -31,6 +32,7 @@ impl ItemBehaviour for FireChargeItem {
player: &'a Player,
location: BlockPos,
face: BlockDirection,
_cursor_pos: Vector3<f32>,
block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -7,6 +7,7 @@ use pumpkin_data::Block;
use pumpkin_data::BlockDirection;
use pumpkin_data::item::Item;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
use pumpkin_world::world::BlockFlags;
use std::pin::Pin;
@ -29,6 +30,7 @@ impl ItemBehaviour for FlintAndSteelItem {
player: &'a Player,
location: BlockPos,
face: BlockDirection,
_cursor_pos: Vector3<f32>,
block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -55,6 +55,7 @@ impl ItemBehaviour for MinecartItem {
player: &'a Player,
location: BlockPos,
_face: BlockDirection,
_cursor_pos: Vector3<f32>,
block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -5,6 +5,7 @@ pub mod dye;
pub mod egg;
pub mod end_crystal;
pub mod ender_eye;
pub mod firework_rocket;
pub mod glowing_ink_sac;
pub mod hoe;
pub mod honeycomb;
@ -22,6 +23,7 @@ pub mod wind_charge;
use crate::item::items::armor_stand::ArmorStandItem;
use crate::item::items::end_crystal::EndCrystalItem;
use crate::item::items::firework_rocket::FireworkRocketItem;
use crate::item::items::minecart::MinecartItem;
use crate::item::items::name_tag::NameTagItem;
use crate::item::items::spawn_egg::SpawnEggItem;
@ -69,6 +71,7 @@ pub fn default_registry() -> Arc<ItemRegistry> {
manager.register(EnderEyeItem);
manager.register(FireChargeItem);
manager.register(DyeItem);
manager.register(FireworkRocketItem);
manager.register(InkSacItem);
manager.register(GlowingInkSacItem);
manager.register(ArmorStandItem);

View File

@ -9,6 +9,7 @@ use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::world::WorldEvent;
use pumpkin_data::{Block, tag};
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
use pumpkin_world::world::BlockFlags;
use rand::{Rng, rng};
@ -28,6 +29,7 @@ impl ItemBehaviour for ShovelItem {
player: &'a Player,
location: BlockPos,
face: BlockDirection,
_cursor_pos: Vector3<f32>,
block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -28,6 +28,7 @@ impl ItemBehaviour for SpawnEggItem {
player: &'a Player,
location: BlockPos,
face: BlockDirection,
_cursor_pos: Vector3<f32>,
_block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -28,12 +28,14 @@ pub trait ItemBehaviour: Send + Sync {
Box::pin(async {})
}
#[expect(clippy::too_many_arguments)]
fn use_on_block<'a>(
&'a self,
_item: &'a mut ItemStack,
_player: &'a Player,
_location: BlockPos,
_face: BlockDirection,
_cursor_pos: Vector3<f32>,
_block: &'a Block,
_server: &'a Server,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {

View File

@ -5,6 +5,7 @@ use pumpkin_data::Block;
use pumpkin_data::BlockDirection;
use pumpkin_data::item::Item;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack;
use std::collections::HashMap;
use std::sync::Arc;
@ -32,19 +33,21 @@ impl ItemRegistry {
}
}
#[expect(clippy::too_many_arguments)]
pub async fn use_on_block(
&self,
stack: &mut ItemStack,
player: &Player,
location: BlockPos,
face: BlockDirection,
cursor_pos: Vector3<f32>,
block: &Block,
server: &Server,
) {
let pumpkin_item = self.get_pumpkin_item(stack.item);
if let Some(pumpkin_item) = pumpkin_item {
pumpkin_item
.use_on_block(stack, player, location, face, block, server)
.use_on_block(stack, player, location, face, cursor_pos, block, server)
.await;
}
}

View File

@ -4,6 +4,7 @@ use crate::{
server::Server,
};
use pumpkin_config::networking::compression::CompressionInfo;
use pumpkin_protocol::bedrock::server::resource_pack_response::SResourcePackResponse;
use pumpkin_protocol::{
bedrock::{
client::{
@ -48,12 +49,14 @@ struct CertificateChainPayload {
impl BedrockClient {
pub async fn handle_request_network_settings(&self, _packet: SRequestNetworkSettings) {
log::debug!("Bedrock: Sending Network settings");
self.send_game_packet(&CNetworkSettings::new(0, 0, false, 0, 0.0))
.await;
self.set_compression(CompressionInfo::default()).await;
}
pub async fn handle_login(self: &Arc<Self>, packet: SLogin, server: &Server) -> Option<()> {
log::debug!("Bedrock: handle login");
match self.try_handle_login(packet, server).await {
Ok(()) => Some(()),
Err(error) => {
@ -104,7 +107,7 @@ impl BedrockClient {
false,
false,
false,
false,
true,
uuid::Uuid::default(),
String::new(),
Vec::new(),
@ -112,21 +115,6 @@ impl BedrockClient {
&mut frame_set,
)
.await;
self.write_game_packet_to_set(
&CResourcePackStackPacket::new(
false,
VarUInt(0),
VarUInt(0),
CURRENT_BEDROCK_MC_VERSION.to_string(),
Experiments {
names_size: 0,
experiments_ever_toggled: false,
},
false,
),
&mut frame_set,
)
.await;
self.send_frame_set(frame_set, 0x84).await;
@ -142,4 +130,28 @@ impl BedrockClient {
Ok(())
}
pub async fn handle_resource_pack_response(&self, packet: SResourcePackResponse) {
// TODO: Add all
if packet.response == SResourcePackResponse::STATUS_HAVE_ALL_PACKS {
log::debug!("Bedrock: STATUS_HAVE_ALL_PACKS");
let mut frame_set = FrameSet::default();
self.write_game_packet_to_set(
&CResourcePackStackPacket::new(
false,
VarUInt(0),
CURRENT_BEDROCK_MC_VERSION.to_string(),
Experiments {
names_size: 0,
experiments_ever_toggled: false,
},
false,
),
&mut frame_set,
)
.await;
self.send_frame_set(frame_set, 0x84).await;
}
}
}

View File

@ -489,6 +489,7 @@ impl BedrockClient {
packet: RawPacket,
) -> Result<(), Error> {
let payload = &mut Cursor::new(&packet.payload);
log::error!("packet id {}", packet.id);
match packet.id {
SRequestNetworkSettings::PACKET_ID => {
self.handle_request_network_settings(SRequestNetworkSettings::read(payload)?)
@ -497,9 +498,13 @@ impl BedrockClient {
SLogin::PACKET_ID => {
self.handle_login(SLogin::read(payload)?, server).await;
}
SClientCacheStatus::PACKET_ID | SResourcePackResponse::PACKET_ID => {
SClientCacheStatus::PACKET_ID => {
// TODO
}
SResourcePackResponse::PACKET_ID => {
self.handle_resource_pack_response(SResourcePackResponse::read(payload)?)
.await;
}
_ => {
self.handle_play_packet(self.player.lock().await.as_ref().unwrap(), server, packet)
.await;

View File

@ -1,5 +1,5 @@
use std::{
num::{NonZero, NonZeroU32},
num::{NonZero, NonZeroI32},
sync::Arc,
};
@ -8,7 +8,6 @@ use pumpkin_protocol::{
bedrock::{
client::{
chunk_radius_update::CChunkRadiusUpdate, container_open::CContainerOpen,
network_chunk_publisher_update::CNetworkChunkPublisherUpdate,
set_actor_motion::CSetActorMotion,
},
server::{
@ -20,7 +19,9 @@ use pumpkin_protocol::{
text::SText,
},
},
codec::{bedrock_block_pos::NetworkPos, var_long::VarLong, var_ulong::VarULong},
codec::{
bedrock_block_pos::NetworkPos, var_int::VarInt, var_long::VarLong, var_ulong::VarULong,
},
java::client::play::CSystemChatMessage,
};
use pumpkin_util::{
@ -34,7 +35,7 @@ use crate::{
net::{DisconnectReason, bedrock::BedrockClient},
plugin::player::{player_chat::PlayerChatEvent, player_command_send::PlayerCommandSendEvent},
server::{Server, seasonal_events},
world::chunker,
world::chunker::{self},
};
impl BedrockClient {
@ -52,29 +53,30 @@ impl BedrockClient {
.await;
return;
}
let server = player.world().server.upgrade().unwrap();
self.send_game_packet(&CChunkRadiusUpdate { chunk_radius })
.await;
let view_distance =
chunk_radius.clamp(2, NonZeroI32::from(server.basic_config.view_distance).get());
self.send_game_packet(&CChunkRadiusUpdate {
chunk_radius: VarInt(view_distance),
})
.await;
let old_view_distance = {
let mut config = player.config.write().await;
let old_view_distance = config.view_distance;
config.view_distance = NonZero::new(chunk_radius.0 as u8).unwrap();
config.view_distance = NonZero::new(view_distance as u8).unwrap();
old_view_distance
};
if old_view_distance.get() != chunk_radius.0 as u8 {
if old_view_distance.get() != view_distance as u8 {
log::debug!(
"Player {} updated their render distance: {} -> {}.",
player.gameprofile.name,
old_view_distance,
chunk_radius.0
view_distance
);
self.send_game_packet(&CNetworkChunkPublisherUpdate::new(
player.get_entity().block_pos.load(),
chunk_radius.0 as u32,
))
.await;
chunker::update_position(player).await;
}
}
@ -83,17 +85,6 @@ impl BedrockClient {
if !player.has_client_loaded() {
return;
}
let config = player.config.read().await;
let view_distance = config.view_distance;
self.send_game_packet(&CNetworkChunkPublisherUpdate::new(
BlockPos::new(
packet.position.x.floor() as i32,
packet.position.y.floor() as i32,
packet.position.z.floor() as i32,
),
NonZeroU32::from(view_distance).into(),
))
.await;
let new_pos = packet.position.to_f64();
let old_pos = player.position();
@ -140,7 +131,7 @@ impl BedrockClient {
self.send_game_packet(&CContainerOpen {
container_id: 0,
container_type: 0xff,
position: NetworkPos(packet.position.to_block_pos()),
position: NetworkPos(BlockPos::ZERO),
target_entity_id: VarLong(-1),
})
.await;

View File

@ -7,7 +7,7 @@ use pumpkin_protocol::bedrock::{
use tokio::net::UdpSocket;
use crate::{net::bedrock::BedrockClient, server::Server};
use pumpkin_world::CURRENT_BEDROCK_MC_VERSION;
use pumpkin_world::{CURRENT_BEDROCK_MC_PROTOCOL, CURRENT_BEDROCK_MC_VERSION};
impl BedrockClient {
pub async fn handle_unconnected_ping(
@ -31,7 +31,7 @@ impl BedrockClient {
edition: "MCPE",
// TODO The default motd is to long to be displayed completely
motd_line_1: "Pumpkin Server",
protocol_version: 827,
protocol_version: CURRENT_BEDROCK_MC_PROTOCOL,
version_name: CURRENT_BEDROCK_MC_VERSION,
player_count,
// A large number looks wreird on the client worlds window

View File

@ -1104,7 +1104,7 @@ impl JavaClient {
player.gameprofile.name,
self.id,
);
player.send_client_information();
player.send_client_information().await;
}
} else {
self.kick(TextComponent::text("Invalid hand or chat type"))
@ -1526,7 +1526,9 @@ impl JavaClient {
server
.item_registry
.use_on_block(&mut stack, player, position, face, block, server)
.use_on_block(
&mut stack, player, position, face, cursor_pos, block, server,
)
.await;
// Check if the item is a block, because not every item can be placed :D

View File

@ -75,7 +75,7 @@ impl Default for PlayerConfig {
fn default() -> Self {
Self {
locale: "en_us".to_string(),
view_distance: NonZeroU8::new(16).unwrap(),
view_distance: NonZeroU8::new(8).unwrap(),
chat_mode: ChatMode::Enabled,
chat_colors: true,
skin_parts: 0,

View File

@ -1,9 +1,15 @@
use std::{num::NonZeroU8, sync::Arc};
use pumpkin_protocol::java::client::play::{CCenterChunk, CUnloadChunk};
use pumpkin_protocol::{
bedrock::client::network_chunk_publisher_update::CNetworkChunkPublisherUpdate,
java::client::play::{CCenterChunk, CUnloadChunk},
};
use pumpkin_world::cylindrical_chunk_iterator::Cylindrical;
use crate::{entity::player::Player, net::ClientPlatform};
use crate::{
entity::{EntityBase, player::Player},
net::ClientPlatform,
};
pub async fn get_view_distance(player: &Player) -> NonZeroU8 {
let server = player.world().server.upgrade().unwrap();
@ -24,12 +30,23 @@ pub async fn update_position(player: &Arc<Player>) {
let new_cylindrical = Cylindrical::new(new_chunk_center, view_distance);
if old_cylindrical != new_cylindrical {
if let ClientPlatform::Java(java) = &player.client {
java.send_packet_now(&CCenterChunk {
chunk_x: new_chunk_center.x.into(),
chunk_z: new_chunk_center.y.into(),
})
.await;
match &player.client {
ClientPlatform::Java(java_client) => {
java_client
.send_packet_now(&CCenterChunk {
chunk_x: new_chunk_center.x.into(),
chunk_z: new_chunk_center.y.into(),
})
.await;
}
ClientPlatform::Bedrock(bedrock_client) => {
bedrock_client
.send_game_packet(&CNetworkChunkPublisherUpdate::new(
player.get_entity().block_pos.load(),
u32::from(view_distance.get()) * 16,
))
.await;
}
}
let mut loading_chunks = Vec::new();
let mut unloading_chunks = Vec::new();

View File

@ -1,4 +1,3 @@
use std::num::NonZeroU32;
use std::pin::Pin;
use std::sync::Weak;
use std::sync::atomic::Ordering::Relaxed;
@ -35,6 +34,7 @@ use crate::{
};
use crate::{block::BlockEvent, entity::item::ItemEntity};
use border::Worldborder;
use bytes::BufMut;
use crossbeam::queue::SegQueue;
use explosion::Explosion;
use pumpkin_config::BasicConfiguration;
@ -42,6 +42,8 @@ use pumpkin_data::data_component_impl::EquipmentSlot;
use pumpkin_data::dimension::Dimension;
use pumpkin_data::entity::MobCategory;
use pumpkin_data::fluid::{Falling, FluidProperties, FluidState};
use pumpkin_data::meta_data_type::MetaDataType;
use pumpkin_data::tracked_data::TrackedData;
use pumpkin_data::{
Block,
entity::{EntityStatus, EntityType},
@ -53,10 +55,9 @@ use pumpkin_data::{
use pumpkin_data::{BlockDirection, BlockState};
use pumpkin_inventory::screen_handler::InventoryPlayer;
use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed};
use pumpkin_protocol::bedrock::client::chunk_radius_update::CChunkRadiusUpdate;
use pumpkin_protocol::bedrock::client::network_chunk_publisher_update::CNetworkChunkPublisherUpdate;
use pumpkin_protocol::bedrock::client::start_game::CStartGame;
use pumpkin_protocol::bedrock::frame_set::FrameSet;
use pumpkin_protocol::java::client::play::{CSetEntityMetadata, Metadata};
use pumpkin_protocol::ser::serializer::Serializer;
use pumpkin_protocol::{
BClientPacket, ClientPacket, IdOr, SoundEvent,
bedrock::{
@ -117,6 +118,7 @@ use pumpkin_world::{world::BlockFlags, world_info::LevelData};
use rand::seq::SliceRandom;
use rand::{Rng, rng};
use scoreboard::Scoreboard;
use serde::Serialize;
use time::LevelTime;
use tokio::sync::Mutex;
use tokio::sync::RwLock;
@ -1247,10 +1249,10 @@ impl World {
yaw: 0.0,
level_settings,
level_id: String::new(),
level_name: "Pumpkin world".to_string(),
level_name: "World".to_string(),
premium_world_template_id: String::new(),
is_trial: false,
rewind_history_size: VarInt(40),
rewind_history_size: VarInt(0),
server_authoritative_block_breaking: false,
current_level_time: self.level_time.lock().await.world_age as _,
enchantment_seed: VarInt(0),
@ -1271,16 +1273,10 @@ impl World {
// TODO The client needs extra biome data for this
enable_clientside_generation: false,
blocknetwork_ids_are_hashed: false,
tick_death_system_enabled: false,
server_auth_sounds: false,
disable_client_sounds: false,
})
.await;
client
.send_game_packet(&CChunkRadiusUpdate {
chunk_radius: VarInt(player.config.read().await.view_distance.get().into()),
})
.await;
chunker::update_position(&player).await;
// chunker::update_position(&player).await;
client
.send_game_packet(&CreativeContent {
groups: &[Group {
@ -1292,48 +1288,29 @@ impl World {
})
.await;
let mut frame_set = FrameSet::default();
client
.write_game_packet_to_set(
&CNetworkChunkPublisherUpdate::new(
BlockPos::new(0, 100, 0),
NonZeroU32::from(player.config.read().await.view_distance).into(),
),
&mut frame_set,
)
.send_game_packet(&CUpdateAttributes {
runtime_id: VarULong(runtime_id),
attributes: vec![Attribute {
min_value: 0.0,
max_value: f32::MAX,
current_value: 0.1,
default_min_value: 0.0,
default_max_value: f32::MAX,
default_value: 0.1,
name: "minecraft:movement".to_string(),
modifiers_list_size: VarUInt(0),
}],
player_tick: VarULong(0),
})
.await;
client
.write_game_packet_to_set(
&CUpdateAttributes {
runtime_id: VarULong(runtime_id),
attributes: vec![Attribute {
min_value: 0.0,
max_value: f32::MAX,
current_value: 0.1,
default_min_value: 0.0,
default_max_value: f32::MAX,
default_value: 0.1,
name: "minecraft:movement".to_string(),
modifiers_list_size: VarUInt(0),
}],
player_tick: VarULong(0),
},
&mut frame_set,
)
.await;
client
.write_game_packet_to_set(&CPlayStatus::PlayerSpawn, &mut frame_set)
.await;
client.send_frame_set(frame_set, 0x84).await;
player.send_abilities_update().await;
{
let mut abilities = player.abilities.lock().await;
abilities.set_for_gamemode(player.gamemode.load());
};
player.send_abilities_update().await;
client.send_game_packet(&CPlayStatus::PlayerSpawn).await;
}
#[expect(clippy::too_many_lines)]
@ -1553,36 +1530,29 @@ impl World {
))
.await;
{
// TODO
// let config = existing_player.config.read().await;
// let mut buf = Vec::new();
// for meta in [
// Metadata::new(
// DATA_PLAYER_MODE_CUSTOMISATION,
// MetaDataType::Byte,
// config.skin_parts,
// ),
// Metadata::new(
// DATA_PLAYER_MAIN_HAND,
// MetaDataType::Byte,
// config.main_hand as u8,
// ),
// ] {
// let mut serializer_buf = Vec::new();
let config = existing_player.config.read().await;
let mut buf = Vec::new();
{
let meta = Metadata::new(
TrackedData::DATA_PLAYER_MODE_CUSTOMIZATION_ID,
MetaDataType::Byte,
config.skin_parts,
);
let mut serializer_buf = Vec::new();
// let mut serializer = Serializer::new(&mut serializer_buf);
// meta.serialize(&mut serializer).unwrap();
// buf.extend(serializer_buf);
// }
// drop(config);
// // END
// buf.put_u8(255);
// client
// .enqueue_packet(&CSetEntityMetadata::new(
// existing_player.get_entity().entity_id.into(),
// buf.into(),
// ))
// .await;
let mut serializer = Serializer::new(&mut serializer_buf);
meta.serialize(&mut serializer).unwrap();
buf.extend(serializer_buf);
};
drop(config);
// END
buf.put_u8(255);
client
.enqueue_packet(&CSetEntityMetadata::new(
existing_player.get_entity().entity_id.into(),
buf.into(),
))
.await;
};
{
@ -1617,7 +1587,7 @@ impl World {
.await;
}
}
player.send_client_information();
player.send_client_information().await;
// Sync selected slot
player
@ -1735,7 +1705,7 @@ impl World {
),
)
.await;
player.send_client_information();
player.send_client_information().await;
chunker::update_position(player).await;
// Update commands