Structures: begin adding Nether Fortress
This commit is contained in:
parent
398ba39233
commit
47254b14ba
@ -112,34 +112,27 @@ impl<V: Hash + Eq + Copy + Default, const DIM: usize> PalettedContainer<V, DIM>
|
||||
debug_assert!(bits_per_entry >= encompassing_bits(data.counts.len()));
|
||||
debug_assert!(bits_per_entry <= 15);
|
||||
|
||||
let palette: Box<[V]> = data.palette.iter().copied().collect();
|
||||
|
||||
let key_to_index_map: HashMap<V, usize> = palette
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, key)| (*key, index))
|
||||
.collect();
|
||||
|
||||
// Don't use HashMap's here, because its slow
|
||||
let blocks_per_i64 = 64 / bits_per_entry;
|
||||
|
||||
let packed_indices = data
|
||||
let packed_indices: Box<[i64]> = data
|
||||
.cube
|
||||
.as_flattened()
|
||||
.as_flattened()
|
||||
.chunks(blocks_per_i64 as usize)
|
||||
.map(|chunk| {
|
||||
chunk.iter().enumerate().fold(0, |acc, (index, key)| {
|
||||
let key_index = key_to_index_map.get(key).unwrap();
|
||||
debug_assert!((1 << bits_per_entry) > *key_index);
|
||||
let key_index = data.palette.iter().position(|&x| x == *key).unwrap();
|
||||
debug_assert!((1 << bits_per_entry) > key_index);
|
||||
|
||||
let packed_offset_index =
|
||||
(*key_index as u64) << (bits_per_entry as u64 * index as u64);
|
||||
(key_index as u64) << (bits_per_entry as u64 * index as u64);
|
||||
acc | packed_offset_index as i64
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
(palette, packed_indices)
|
||||
(data.palette.clone().into_boxed_slice(), packed_indices)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -737,6 +737,7 @@ impl<'a> ProtoChunk<'a> {
|
||||
Xoroshiro::get_population_seed(self.random_config.seed, block_pos.0.x, block_pos.0.z);
|
||||
|
||||
for (_structure, (pos, stype)) in self.structure_starts.clone() {
|
||||
dbg!("generating structure");
|
||||
stype.generate(pos.clone(), self);
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||
|
||||
use crate::ProtoChunk;
|
||||
use crate::{ProtoChunk, generation::height_limit::HeightLimitView};
|
||||
|
||||
pub mod buried_treasure;
|
||||
pub mod nether_fortress;
|
||||
@ -11,6 +12,58 @@ pub trait StructureGenerator {
|
||||
fn generate(&self, position: BlockPos, chunk: &mut crate::ProtoChunk);
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub fn fill_with_outline(
|
||||
min_x: i32,
|
||||
min_y: i32,
|
||||
min_z: i32,
|
||||
max_x: i32,
|
||||
max_y: i32,
|
||||
max_z: i32,
|
||||
outline: &BlockState,
|
||||
inside: &BlockState,
|
||||
chunk: &mut crate::ProtoChunk,
|
||||
) {
|
||||
for y in min_y..=max_y {
|
||||
for x in min_x..=max_x {
|
||||
for z in min_z..=max_z {
|
||||
if y == min_y || y == max_y || x == min_x || x == max_x || z == min_z || z == max_z
|
||||
{
|
||||
chunk.set_block_state(&Vector3::new(x, y, z), outline);
|
||||
} else {
|
||||
chunk.set_block_state(&Vector3::new(x, y, z), inside);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub fn fill(
|
||||
min_x: i32,
|
||||
min_y: i32,
|
||||
min_z: i32,
|
||||
max_x: i32,
|
||||
max_y: i32,
|
||||
max_z: i32,
|
||||
state: &BlockState,
|
||||
chunk: &mut crate::ProtoChunk,
|
||||
) {
|
||||
for y in min_y..=max_y {
|
||||
for x in min_x..=max_x {
|
||||
for z in min_z..=max_z {
|
||||
chunk.set_block_state(&Vector3::new(x, y, z), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_downwards(x: i32, y: i32, z: i32, state: &BlockState, chunk: &mut crate::ProtoChunk) {
|
||||
for y in y..chunk.bottom_y() as i32 {
|
||||
chunk.set_block_state(&Vector3::new(x, y, z), state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StructurePosition {
|
||||
pub position: BlockPos,
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
ProtoChunk,
|
||||
generation::{
|
||||
positions::chunk_pos,
|
||||
structure::structures::{StructureGenerator, StructurePiecesCollector, StructurePosition},
|
||||
structure::structures::{
|
||||
StructureGenerator, StructurePiecesCollector, StructurePosition, fill_with_outline,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -26,5 +29,119 @@ impl StructureGenerator for NetherFortressGenerator {
|
||||
generator,
|
||||
}
|
||||
}
|
||||
fn generate(&self, _position: BlockPos, _chunk: &mut crate::ProtoChunk) {}
|
||||
fn generate(&self, position: BlockPos, chunk: &mut crate::ProtoChunk) {
|
||||
BridgePlatform::generate(&BridgePlatform, position, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BridgePlatform;
|
||||
|
||||
impl BridgePlatform {
|
||||
fn generate(&self, _position: BlockPos, chunk: &mut crate::ProtoChunk) {
|
||||
super::fill(0, 2, 0, 6, 7, 7, Block::AIR.default_state, chunk);
|
||||
super::fill(1, 0, 0, 5, 1, 7, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(1, 2, 1, 5, 2, 7, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(1, 3, 2, 5, 3, 7, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(1, 4, 3, 5, 4, 7, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(1, 2, 0, 1, 4, 2, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(5, 2, 0, 5, 4, 2, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(1, 5, 2, 1, 5, 3, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(5, 5, 2, 5, 5, 3, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(0, 5, 3, 0, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(6, 5, 3, 6, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(1, 5, 8, 5, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
|
||||
// TODO
|
||||
let block_state = Block::NETHER_BRICK_FENCE.default_state;
|
||||
let block_state2 = Block::NETHER_BRICK_FENCE.default_state;
|
||||
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(1, 6, 3),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(5, 6, 3),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(0, 6, 3),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(6, 6, 3),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
|
||||
fill_with_outline(0, 6, 4, 0, 6, 7, block_state2, block_state2, chunk);
|
||||
fill_with_outline(6, 6, 4, 6, 6, 7, block_state2, block_state2, chunk);
|
||||
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(0, 6, 8),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(6, 6, 8),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
|
||||
fill_with_outline(1, 6, 8, 5, 6, 8, block_state, block_state, chunk);
|
||||
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(1, 7, 8),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
|
||||
fill_with_outline(2, 7, 8, 4, 7, 8, block_state, block_state, chunk);
|
||||
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(5, 7, 8),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(2, 8, 8),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
chunk.set_block_state(&Vector3::new(3, 8, 8), block_state);
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(4, 8, 8),
|
||||
Block::NETHER_BRICK_FENCE.default_state,
|
||||
);
|
||||
|
||||
// Blaze Spawner
|
||||
chunk.set_block_state(
|
||||
&Vector3::new(3, 5, 5), // ?
|
||||
Block::SPAWNER.default_state,
|
||||
);
|
||||
|
||||
for x in 0..6 {
|
||||
for z in 0..6 {
|
||||
super::fill_downwards(x, -1, z, Block::NETHER_BRICKS.default_state, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SmallCorridor;
|
||||
|
||||
impl SmallCorridor {
|
||||
fn generate(&self, _position: BlockPos, chunk: &mut crate::ProtoChunk) {
|
||||
super::fill(0, 0, 0, 4, 1, 4, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(0, 2, 0, 4, 5, 4, Block::AIR.default_state, chunk);
|
||||
|
||||
// TODO
|
||||
let block_state = Block::NETHER_BRICK_FENCE.default_state;
|
||||
|
||||
super::fill(0, 2, 0, 0, 5, 4, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(4, 2, 0, 4, 5, 4, Block::NETHER_BRICKS.default_state, chunk);
|
||||
super::fill(0, 3, 1, 0, 4, 1, block_state, chunk);
|
||||
super::fill(0, 3, 3, 0, 4, 3, block_state, chunk);
|
||||
super::fill(4, 3, 1, 4, 4, 1, block_state, chunk);
|
||||
super::fill(4, 3, 3, 4, 4, 3, block_state, chunk);
|
||||
super::fill(0, 6, 0, 4, 6, 4, Block::NETHER_BRICKS.default_state, chunk);
|
||||
|
||||
for i in 0..=4 {
|
||||
for j in 0..=4 {
|
||||
super::fill_downwards(i, -1, j, Block::NETHER_BRICKS.default_state, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,7 +103,8 @@ impl CandleCakeBlock {
|
||||
#[async_trait]
|
||||
impl BlockBehaviour for CandleCakeBlock {
|
||||
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
|
||||
match args.item_stack.lock().await.item.id {
|
||||
let item_id = args.item_stack.lock().await.item.id;
|
||||
match item_id {
|
||||
id if id == Item::FIRE_CHARGE.id || id == Item::FLINT_AND_STEEL.id => {
|
||||
BlockActionResult::Pass
|
||||
} // Item::FIRE_CHARGE | Item::FLINT_AND_STEEL
|
||||
|
||||
@ -36,7 +36,7 @@ struct LocationExecutor;
|
||||
struct EntityExecutor(bool);
|
||||
|
||||
async fn send_damage_result(
|
||||
sender: &mut CommandSender,
|
||||
sender: &CommandSender,
|
||||
success: bool,
|
||||
amount: f32,
|
||||
target_name: TextComponent,
|
||||
|
||||
@ -176,7 +176,7 @@ pub fn snbt_colorful_display(tag: &NbtTag, depth: usize) -> Result<TextComponent
|
||||
NbtTag::IntArray(value) => {
|
||||
let int_array_format = TextComponent::text("I").color_named(NamedColor::Red);
|
||||
let mut content = TextComponent::text("[")
|
||||
.add_child(int_array_format.clone())
|
||||
.add_child(int_array_format)
|
||||
.add_child(TextComponent::text("; "));
|
||||
|
||||
for (index, int) in value.iter().take(128).enumerate() {
|
||||
|
||||
@ -45,7 +45,7 @@ struct Executor {
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
async fn handle_query(&self, sender: &mut CommandSender, target: &Player, exp_type: ExpType) {
|
||||
async fn handle_query(&self, sender: &CommandSender, target: &Player, exp_type: ExpType) {
|
||||
match exp_type {
|
||||
ExpType::Levels => {
|
||||
let level = target.experience_level.load(Ordering::Relaxed);
|
||||
|
||||
@ -93,7 +93,7 @@ impl CommandExecutor for AngleWorldSpawnExecutor {
|
||||
}
|
||||
|
||||
async fn setworldspawn(
|
||||
sender: &mut CommandSender,
|
||||
sender: &CommandSender,
|
||||
server: &Server,
|
||||
block_pos: BlockPos,
|
||||
yaw: f32,
|
||||
|
||||
@ -56,7 +56,7 @@ struct TickExecutor(SubCommand);
|
||||
impl TickExecutor {
|
||||
async fn handle_query(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
sender: &CommandSender,
|
||||
server: &crate::server::Server,
|
||||
manager: &crate::server::tick_rate_manager::ServerTickRateManager,
|
||||
) -> Result<(), CommandError> {
|
||||
@ -102,7 +102,7 @@ impl TickExecutor {
|
||||
}
|
||||
async fn handle_non_sprinting_status(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
sender: &CommandSender,
|
||||
manager: &crate::server::tick_rate_manager::ServerTickRateManager,
|
||||
avg_tick_nanos: i64,
|
||||
) {
|
||||
@ -121,7 +121,7 @@ impl TickExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_percentiles(&self, sender: &mut CommandSender, server: &crate::server::Server) {
|
||||
async fn send_percentiles(&self, sender: &CommandSender, server: &crate::server::Server) {
|
||||
let tick_count = server.tick_count.load(Ordering::Relaxed);
|
||||
let sample_size = (tick_count as usize).min(100);
|
||||
|
||||
@ -149,7 +149,7 @@ impl TickExecutor {
|
||||
}
|
||||
async fn handle_step_command(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
sender: &CommandSender,
|
||||
server: &crate::server::Server,
|
||||
manager: &crate::server::tick_rate_manager::ServerTickRateManager,
|
||||
ticks: i32,
|
||||
@ -173,7 +173,7 @@ impl TickExecutor {
|
||||
}
|
||||
async fn handle_sprint_command(
|
||||
&self,
|
||||
sender: &mut CommandSender,
|
||||
sender: &CommandSender,
|
||||
server: &crate::server::Server,
|
||||
manager: &crate::server::tick_rate_manager::ServerTickRateManager,
|
||||
ticks: i32,
|
||||
|
||||
@ -83,7 +83,7 @@ impl CommandDispatcher {
|
||||
/// - do not query suggestions for the same consumer multiple times just because they are on different paths through the tree
|
||||
pub(crate) async fn find_suggestions<'a>(
|
||||
&'a self,
|
||||
src: &mut CommandSender,
|
||||
src: &CommandSender,
|
||||
server: &'a Server,
|
||||
cmd: &'a str,
|
||||
) -> Vec<CommandSuggestion> {
|
||||
@ -357,7 +357,7 @@ impl CommandDispatcher {
|
||||
}
|
||||
|
||||
async fn try_find_suggestions_on_path<'a>(
|
||||
src: &mut CommandSender,
|
||||
src: &CommandSender,
|
||||
server: &'a Server,
|
||||
path: &[usize],
|
||||
tree: &'a CommandTree,
|
||||
|
||||
@ -83,7 +83,7 @@ mod format {
|
||||
date: &OffsetDateTime,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
let s = date.format(DATE_FORMAT).unwrap().to_string();
|
||||
let s = date.format(DATE_FORMAT).unwrap();
|
||||
serializer.serialize_str(&s)
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ mod format {
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
if let Some(date) = date {
|
||||
let s = date.format(DATE_FORMAT).unwrap().to_string();
|
||||
let s = date.format(DATE_FORMAT).unwrap();
|
||||
serializer.serialize_str(&s)
|
||||
} else {
|
||||
serializer.serialize_str("forever")
|
||||
|
||||
@ -295,7 +295,7 @@ mod test {
|
||||
let path = temp_dir.path().to_path_buf();
|
||||
|
||||
let uuid = Uuid::new_v4();
|
||||
let mut storage = PlayerDataStorage::new(path.clone());
|
||||
let mut storage = PlayerDataStorage::new(path);
|
||||
storage.set_save_enabled(true);
|
||||
|
||||
// Create and save player data
|
||||
|
||||
@ -164,14 +164,15 @@ impl LivingEntity {
|
||||
}
|
||||
|
||||
pub async fn add_effect(&self, effect: Effect) {
|
||||
let mut effects = self.active_effects.lock().await;
|
||||
effects.insert(effect.effect_type, effect);
|
||||
self.active_effects
|
||||
.lock()
|
||||
.await
|
||||
.insert(effect.effect_type, effect);
|
||||
// TODO broadcast metadata
|
||||
}
|
||||
|
||||
pub async fn remove_effect(&self, effect_type: &'static StatusEffect) {
|
||||
let mut effects = self.active_effects.lock().await;
|
||||
effects.remove(&effect_type);
|
||||
self.active_effects.lock().await.remove(&effect_type);
|
||||
self.entity
|
||||
.world
|
||||
.read()
|
||||
|
||||
@ -757,8 +757,6 @@ impl Entity {
|
||||
|
||||
let max = aabb.max_block_pos();
|
||||
|
||||
let world = self.world.read().await;
|
||||
|
||||
let mut eye_level_box = aabb;
|
||||
|
||||
let eye_height = f64::from(self.standing_eye_height);
|
||||
@ -767,6 +765,7 @@ impl Entity {
|
||||
|
||||
eye_level_box.max.y = eye_level_box.min.y;
|
||||
|
||||
let world = self.world.read().await;
|
||||
for x in min.0.x..=max.0.x {
|
||||
for y in min.0.y..=max.0.y {
|
||||
for z in min.0.z..=max.0.z {
|
||||
@ -1126,9 +1125,12 @@ impl Entity {
|
||||
|
||||
#[allow(clippy::float_cmp)]
|
||||
async fn get_velocity_multiplier(&self) -> f32 {
|
||||
let world = self.world.read().await;
|
||||
|
||||
let block = world.get_block(&self.block_pos.load()).await;
|
||||
let block = self
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.get_block(&self.block_pos.load())
|
||||
.await;
|
||||
|
||||
let multiplier = block.velocity_multiplier;
|
||||
|
||||
@ -1143,9 +1145,10 @@ impl Entity {
|
||||
|
||||
#[allow(clippy::float_cmp)]
|
||||
async fn get_jump_velocity_multiplier(&self) -> f32 {
|
||||
let world = self.world.read().await;
|
||||
|
||||
let f = world
|
||||
let f = self
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.get_block(&self.block_pos.load())
|
||||
.await
|
||||
.jump_velocity_multiplier;
|
||||
@ -1302,6 +1305,7 @@ impl Entity {
|
||||
portal_manager.portal_world.clone(),
|
||||
)
|
||||
.await;
|
||||
drop(portal_manager);
|
||||
} else if portal_manager.ticks_in_portal == 0 {
|
||||
should_remove = true;
|
||||
}
|
||||
|
||||
@ -1075,8 +1075,7 @@ impl Player {
|
||||
pitch: Option<f32>,
|
||||
) {
|
||||
let current_world = self.living_entity.entity.world.read().await.clone();
|
||||
let info = &new_world.level_info.read().await;
|
||||
let yaw = yaw.unwrap_or(info.spawn_angle);
|
||||
let yaw = yaw.unwrap_or(new_world.level_info.read().await.spawn_angle);
|
||||
let pitch = pitch.unwrap_or(10.0);
|
||||
|
||||
send_cancellable! {{
|
||||
@ -1212,8 +1211,7 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn add_exhaustion(&self, exhaustion: f32) {
|
||||
let abilities = self.abilities.lock().await;
|
||||
if abilities.invulnerable {
|
||||
if self.abilities.lock().await.invulnerable {
|
||||
return;
|
||||
}
|
||||
self.hunger_manager.add_exhaustion(exhaustion);
|
||||
@ -1456,7 +1454,7 @@ impl Player {
|
||||
|
||||
pub async fn drop_held_item(&self, drop_stack: bool) {
|
||||
// should be locked first otherwise cause deadlock in tick() (this thread lock stack, that thread lock screen_handler)
|
||||
let screen_binding = self.current_screen_handler.lock().await;
|
||||
|
||||
let binding = self.inventory.held_item();
|
||||
let mut item_stack = binding.lock().await;
|
||||
|
||||
@ -1467,6 +1465,7 @@ impl Player {
|
||||
item_stack.decrement(drop_amount);
|
||||
let selected_slot = self.inventory.get_selected_slot();
|
||||
let inv: Arc<dyn Inventory> = self.inventory.clone();
|
||||
let screen_binding = self.current_screen_handler.lock().await;
|
||||
let mut screen_handler = screen_binding.lock().await;
|
||||
let slot_index = screen_handler
|
||||
.get_slot_index(&inv, selected_slot as usize)
|
||||
@ -1838,6 +1837,7 @@ impl Player {
|
||||
screen_handler.update_to_client().await;
|
||||
} else {
|
||||
screen_handler.send_content_updates().await;
|
||||
drop(screen_handler);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1963,18 +1963,19 @@ impl NBTStorage for PlayerInventory {
|
||||
let mut item_compound = NbtCompound::new();
|
||||
item_compound.put_byte("Slot", i as i8);
|
||||
stack.write_item_stack(&mut item_compound);
|
||||
drop(stack);
|
||||
vec.push(NbtTag::Compound(item_compound));
|
||||
}
|
||||
}
|
||||
|
||||
let mut equipment_compound = NbtCompound::new();
|
||||
for slot in self.equipment_slots.values() {
|
||||
let equipment_binding = self.entity_equipment.lock().await;
|
||||
let stack_binding = equipment_binding.get(slot);
|
||||
let stack_binding = self.entity_equipment.lock().await.get(slot);
|
||||
let stack = stack_binding.lock().await;
|
||||
if !stack.is_empty() {
|
||||
let mut item_compound = NbtCompound::new();
|
||||
stack.write_item_stack(&mut item_compound);
|
||||
drop(stack);
|
||||
match slot {
|
||||
EquipmentSlot::OffHand(_) => {
|
||||
equipment_compound.put_component("offhand", item_compound);
|
||||
@ -2067,8 +2068,12 @@ impl EntityBase for Player {
|
||||
if self.abilities.lock().await.invulnerable && damage_type != DamageType::GENERIC_KILL {
|
||||
return false;
|
||||
}
|
||||
let world = self.living_entity.entity.world.read().await;
|
||||
let dyn_self = world
|
||||
let dyn_self = self
|
||||
.living_entity
|
||||
.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.get_entity_by_id(self.living_entity.entity.entity_id)
|
||||
.await
|
||||
.expect("Entity not found in world");
|
||||
@ -2380,14 +2385,14 @@ impl LastSeen {
|
||||
pub async fn indexed_for(&self, recipient: &Arc<Player>) -> Box<[PreviousMessage]> {
|
||||
let mut indexed = Vec::new();
|
||||
for signature in &self.0 {
|
||||
if let Some(index) = recipient
|
||||
let index = recipient
|
||||
.signature_cache
|
||||
.lock()
|
||||
.await
|
||||
.full_cache
|
||||
.iter()
|
||||
.position(|s| s == signature)
|
||||
{
|
||||
.position(|s| s == signature);
|
||||
if let Some(index) = index {
|
||||
indexed.push(PreviousMessage {
|
||||
// Send ID reference to recipient's cache (index + 1 because 0 is reserved for full signature)
|
||||
id: VarInt(1 + index as i32),
|
||||
|
||||
@ -190,6 +190,9 @@ impl PumpkinServer {
|
||||
}
|
||||
|
||||
if rcon.enabled {
|
||||
log::warn!(
|
||||
"RCON is enabled, but it's highly insecure as it transmits passwords and commands in plain text. This makes it vulnerable to interception and exploitation by anyone on the network"
|
||||
);
|
||||
let rcon_server = server.clone();
|
||||
server.spawn_task(async move {
|
||||
RCONServer::run(&rcon, rcon_server).await.unwrap();
|
||||
|
||||
@ -15,6 +15,12 @@
|
||||
#![deny(clippy::branches_sharing_code)]
|
||||
#![deny(clippy::equatable_if_let)]
|
||||
#![deny(clippy::option_if_let_else)]
|
||||
#![deny(clippy::needless_pass_by_ref_mut)]
|
||||
#![deny(clippy::needless_collect)]
|
||||
#![deny(clippy::redundant_clone)]
|
||||
#![deny(clippy::branches_sharing_code)]
|
||||
#![deny(clippy::set_contains_or_insert)]
|
||||
#![deny(clippy::significant_drop_in_scrutinee)]
|
||||
// use log crate
|
||||
#![deny(clippy::print_stdout)]
|
||||
#![deny(clippy::print_stderr)]
|
||||
|
||||
@ -387,7 +387,7 @@ impl BedrockClient {
|
||||
self.tasks.wait().await;
|
||||
self.be_clients.lock().await.remove(&self.address);
|
||||
|
||||
if let Some(player) = self.player.lock().await.clone() {
|
||||
if let Some(player) = self.player.lock().await.as_ref() {
|
||||
player.remove().await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,6 @@ impl BedrockClient {
|
||||
);
|
||||
|
||||
let entity = &player.living_entity.entity;
|
||||
let world = &entity.world.read().await;
|
||||
if BASIC_CONFIG.allow_chat_reports {
|
||||
//TODO Alex help, what is this?
|
||||
//world.broadcast_secure_player_chat(player, &message, decorated_message).await;
|
||||
@ -120,7 +119,7 @@ impl BedrockClient {
|
||||
message, gameprofile.name.clone()
|
||||
);
|
||||
|
||||
world.broadcast_editioned(&je_packet, &be_packet).await;
|
||||
entity.world.read().await.broadcast_editioned(&je_packet, &be_packet).await;
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@ -327,6 +327,7 @@ impl JavaClient {
|
||||
self.finish_login(&profile).await;
|
||||
*self.gameprofile.lock().await = Some(profile);
|
||||
*address = new_address;
|
||||
drop(address);
|
||||
}
|
||||
Err(error) => self.kick(TextComponent::text(error.to_string())).await,
|
||||
}
|
||||
|
||||
@ -342,8 +342,7 @@ impl JavaClient {
|
||||
}
|
||||
ConnectionState::Config => self.handle_config_packet(server, packet).await,
|
||||
ConnectionState::Play => {
|
||||
let player = self.player.lock().await;
|
||||
if let Some(player) = player.as_ref() {
|
||||
if let Some(player) = self.player.lock().await.as_ref() {
|
||||
match self.handle_play_packet(player, server, packet).await {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
|
||||
@ -183,6 +183,7 @@ impl JavaClient {
|
||||
player.living_entity.entity.set_pos(*position);
|
||||
|
||||
*awaiting_teleport = None;
|
||||
drop(awaiting_teleport);
|
||||
} else {
|
||||
self.kick(TextComponent::text("Wrong teleport id")).await;
|
||||
}
|
||||
@ -542,8 +543,7 @@ impl JavaClient {
|
||||
// Some commands can take a long time to execute. If they do, they block packet processing for the player.
|
||||
// That's why we will spawn a task instead.
|
||||
server.spawn_task(async move {
|
||||
let dispatcher = server_clone.command_dispatcher.read().await;
|
||||
dispatcher
|
||||
server_clone.command_dispatcher.read().await
|
||||
.handle_command(
|
||||
&mut CommandSender::Player(player_clone),
|
||||
&server_clone,
|
||||
@ -888,10 +888,7 @@ impl JavaClient {
|
||||
}
|
||||
|
||||
// Update the chat session fields
|
||||
let mut chat_session = player.chat_session.lock().await; // Await the lock
|
||||
|
||||
// Update the chat session fields
|
||||
*chat_session = ChatSession::new(
|
||||
*player.chat_session.lock().await = ChatSession::new(
|
||||
session.session_id,
|
||||
session.expires_at,
|
||||
session.public_key.clone(),
|
||||
@ -1246,8 +1243,10 @@ impl JavaClient {
|
||||
}
|
||||
player.mining.store(false, Ordering::Relaxed);
|
||||
let entity = &player.living_entity.entity;
|
||||
let world = &entity.world.read().await;
|
||||
world
|
||||
entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.set_block_breaking(entity, player_action.position, -1)
|
||||
.await;
|
||||
self.update_sequence(player, player_action.sequence.0);
|
||||
@ -1272,14 +1271,14 @@ impl JavaClient {
|
||||
world.set_block_breaking(entity, location, -1).await;
|
||||
|
||||
let (block, state) = world.get_block_and_state(&location).await;
|
||||
let drop = player.gamemode.load() != GameMode::Creative
|
||||
let block_drop = player.gamemode.load() != GameMode::Creative
|
||||
&& player.can_harvest(state, block).await;
|
||||
|
||||
world
|
||||
.break_block(
|
||||
&location,
|
||||
Some(player.clone()),
|
||||
if drop {
|
||||
if block_drop {
|
||||
BlockFlags::NOTIFY_NEIGHBORS
|
||||
} else {
|
||||
BlockFlags::SKIP_DROPS | BlockFlags::NOTIFY_NEIGHBORS
|
||||
@ -1448,14 +1447,16 @@ impl JavaClient {
|
||||
.await;
|
||||
|
||||
// Check if the item is a block, because not every item can be placed :D
|
||||
if let Some(block) = Block::from_item_id(item.lock().await.item.id) {
|
||||
let item_id = item.lock().await.item.id;
|
||||
if let Some(block) = Block::from_item_id(item_id) {
|
||||
should_try_decrement = self
|
||||
.run_is_block_place(player, block, server, use_item_on, position, face)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Check if the item is a spawn egg
|
||||
if let Some(entity) = entity_from_egg(item.lock().await.item.id) {
|
||||
let item_id = item.lock().await.item.id;
|
||||
if let Some(entity) = entity_from_egg(item_id) {
|
||||
self.spawn_entity_from_egg(player, entity, position, face)
|
||||
.await;
|
||||
should_try_decrement = true;
|
||||
@ -1655,6 +1656,7 @@ impl JavaClient {
|
||||
.await;
|
||||
player_screen_handler.set_received_stack(packet.slot as usize, item_stack);
|
||||
player_screen_handler.send_content_updates().await;
|
||||
drop(player_screen_handler);
|
||||
} else if is_negative && is_legal {
|
||||
// Item drop
|
||||
player.drop_item(item_stack).await;
|
||||
@ -1663,8 +1665,11 @@ impl JavaClient {
|
||||
}
|
||||
|
||||
pub async fn handle_chunk_batch(&self, player: &Player, packet: SChunkBatch) {
|
||||
let mut chunk_manager = player.chunk_manager.lock().await;
|
||||
chunk_manager.handle_acknowledge(packet.chunks_per_tick);
|
||||
player
|
||||
.chunk_manager
|
||||
.lock()
|
||||
.await
|
||||
.handle_acknowledge(packet.chunks_per_tick);
|
||||
log::trace!(
|
||||
"Client requested {} chunks per tick",
|
||||
packet.chunks_per_tick
|
||||
@ -1686,7 +1691,7 @@ impl JavaClient {
|
||||
packet: SCommandSuggestion,
|
||||
server: &Arc<Server>,
|
||||
) {
|
||||
let mut src = CommandSender::Player(player.clone());
|
||||
let src = CommandSender::Player(player.clone());
|
||||
let Some(cmd) = &packet.command.get(1..) else {
|
||||
return;
|
||||
};
|
||||
@ -1696,8 +1701,12 @@ impl JavaClient {
|
||||
return;
|
||||
};
|
||||
|
||||
let dispatcher = server.command_dispatcher.read().await;
|
||||
let suggestions = dispatcher.find_suggestions(&mut src, server, cmd).await;
|
||||
let suggestions = server
|
||||
.command_dispatcher
|
||||
.read()
|
||||
.await
|
||||
.find_suggestions(&src, server, cmd)
|
||||
.await;
|
||||
|
||||
let response = CCommandSuggestions::new(
|
||||
packet.id,
|
||||
@ -1762,7 +1771,6 @@ impl JavaClient {
|
||||
face: BlockDirection,
|
||||
) -> Result<bool, BlockPlacingError> {
|
||||
let entity = &player.living_entity.entity;
|
||||
let world = &entity.world.read().await;
|
||||
|
||||
// Check if the block is under the world
|
||||
if location.0.y + face.to_offset().y < i32::from(Self::WORLD_LOWEST_Y) {
|
||||
@ -1792,6 +1800,8 @@ impl JavaClient {
|
||||
}
|
||||
|
||||
let clicked_block_pos = BlockPos(location.0);
|
||||
let world = &entity.world.read().await;
|
||||
|
||||
let (clicked_block, clicked_block_state) =
|
||||
world.get_block_and_state(&clicked_block_pos).await;
|
||||
|
||||
|
||||
@ -194,7 +194,7 @@ pub async fn can_not_join(
|
||||
Some(expires) => text.add_child(TextComponent::translate(
|
||||
"multiplayer.disconnect.banned.expiration",
|
||||
[TextComponent::text(
|
||||
expires.format(FORMAT_DESCRIPTION).unwrap().to_string(),
|
||||
expires.format(FORMAT_DESCRIPTION).unwrap(),
|
||||
)],
|
||||
)),
|
||||
None => text,
|
||||
@ -214,8 +214,7 @@ pub async fn can_not_join(
|
||||
}
|
||||
}
|
||||
|
||||
let mut banned_ips = BANNED_IP_LIST.write().await;
|
||||
if let Some(entry) = banned_ips.get_entry(&address.ip()) {
|
||||
if let Some(entry) = BANNED_IP_LIST.write().await.get_entry(&address.ip()) {
|
||||
let text = TextComponent::translate(
|
||||
"multiplayer.disconnect.banned_ip.reason",
|
||||
[TextComponent::text(entry.reason.clone())],
|
||||
@ -224,7 +223,7 @@ pub async fn can_not_join(
|
||||
Some(expires) => text.add_child(TextComponent::translate(
|
||||
"multiplayer.disconnect.banned_ip.expiration",
|
||||
[TextComponent::text(
|
||||
expires.format(FORMAT_DESCRIPTION).unwrap().to_string(),
|
||||
expires.format(FORMAT_DESCRIPTION).unwrap(),
|
||||
)],
|
||||
)),
|
||||
None => text,
|
||||
|
||||
@ -126,8 +126,10 @@ impl RCONClient {
|
||||
let output_clone = output.clone();
|
||||
let packet_body = packet.get_body().to_owned();
|
||||
tokio::spawn(async move {
|
||||
let dispatcher = server_clone.command_dispatcher.read().await;
|
||||
dispatcher
|
||||
server_clone
|
||||
.command_dispatcher
|
||||
.read()
|
||||
.await
|
||||
.handle_command(
|
||||
&mut crate::command::CommandSender::Rcon(output_clone),
|
||||
&server_clone,
|
||||
|
||||
@ -187,9 +187,8 @@ impl Context {
|
||||
));
|
||||
}
|
||||
|
||||
let manager = self.permission_manager.read().await;
|
||||
let mut registry = manager.registry.write().await;
|
||||
registry.register_permission(permission)
|
||||
let registry = &self.permission_manager.read().await.registry;
|
||||
registry.write().await.register_permission(permission)
|
||||
}
|
||||
|
||||
/// Check if a player has a permission
|
||||
|
||||
@ -302,16 +302,13 @@ impl PluginManager {
|
||||
}
|
||||
|
||||
/// Start loading a plugin asynchronously
|
||||
#[allow(clippy::too_many_lines)]
|
||||
async fn start_loading_plugin(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<tokio::task::JoinHandle<()>, ManagerError> {
|
||||
let loaders = self.loaders.read().await;
|
||||
for loader in loaders.iter() {
|
||||
for loader in self.loaders.read().await.iter() {
|
||||
if loader.can_load(path) {
|
||||
let server = self.server.read().await;
|
||||
let self_ref = self.self_ref.read().await;
|
||||
|
||||
let (mut instance, metadata, loader_data) = loader.load(path).await?;
|
||||
|
||||
// Mark plugin as loading
|
||||
@ -320,15 +317,25 @@ impl PluginManager {
|
||||
.await
|
||||
.insert(metadata.name.to_string(), PluginState::Loading);
|
||||
|
||||
let self_ref = self_ref
|
||||
.as_ref()
|
||||
let self_ref = self
|
||||
.self_ref
|
||||
.read()
|
||||
.await
|
||||
.clone()
|
||||
.ok_or(ManagerError::ServerNotInitialized)?;
|
||||
|
||||
let context = Arc::new(Context::new(
|
||||
metadata.clone(),
|
||||
Arc::clone(server.as_ref().ok_or(ManagerError::ServerNotInitialized)?),
|
||||
Arc::clone(
|
||||
&self
|
||||
.server
|
||||
.read()
|
||||
.await
|
||||
.clone()
|
||||
.ok_or(ManagerError::ServerNotInitialized)?,
|
||||
),
|
||||
Arc::clone(&self.handlers),
|
||||
Arc::clone(self_ref),
|
||||
Arc::clone(&self_ref),
|
||||
Arc::clone(&PERMISSION_MANAGER),
|
||||
));
|
||||
|
||||
@ -352,7 +359,7 @@ impl PluginManager {
|
||||
self.unloaded_files.write().await.remove(path);
|
||||
|
||||
// Spawn async task for plugin initialization
|
||||
let self_ref_clone = Arc::clone(self_ref);
|
||||
let self_ref_clone = Arc::clone(&self_ref);
|
||||
let state_notify = Arc::clone(&self.state_notify);
|
||||
let plugin_name = metadata.name.to_string();
|
||||
let loader_clone = loader.clone();
|
||||
@ -447,7 +454,7 @@ impl PluginManager {
|
||||
PluginState::Loaded => return Ok(()),
|
||||
PluginState::Failed(error) => {
|
||||
return Err(ManagerError::LoaderError(
|
||||
LoaderError::InitializationFailed(error.clone()),
|
||||
LoaderError::InitializationFailed(error),
|
||||
));
|
||||
}
|
||||
PluginState::Loading => {
|
||||
|
||||
@ -213,10 +213,10 @@ impl Server {
|
||||
log::info!("Loading End: {seed}");
|
||||
let end = World::load(
|
||||
Dimension::End.into_level(world_path.clone(), block_registry.clone(), seed),
|
||||
level_info.clone(),
|
||||
level_info,
|
||||
VanillaDimensionType::TheEnd,
|
||||
block_registry.clone(),
|
||||
weak.clone(),
|
||||
block_registry,
|
||||
weak,
|
||||
);
|
||||
*server
|
||||
.worlds
|
||||
@ -239,13 +239,14 @@ impl Server {
|
||||
pub async fn get_world_from_dimension(&self, dimension: VanillaDimensionType) -> Arc<World> {
|
||||
// TODO: this is really bad
|
||||
let world_guard = self.worlds.read().await;
|
||||
let world = match dimension {
|
||||
match dimension {
|
||||
VanillaDimensionType::Overworld => world_guard.first(),
|
||||
VanillaDimensionType::OverworldCaves => todo!(),
|
||||
VanillaDimensionType::TheEnd => world_guard.get(2),
|
||||
VanillaDimensionType::TheNether => world_guard.get(1),
|
||||
};
|
||||
world.cloned().unwrap()
|
||||
}
|
||||
.cloned()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[allow(clippy::if_then_some_else_none)]
|
||||
@ -291,27 +292,36 @@ impl Server {
|
||||
(world, Some(data))
|
||||
} else {
|
||||
log::warn!("Invalid dimension key in player data: {dimension_key}");
|
||||
let default_world_guard = self.worlds.read().await;
|
||||
let default_world = default_world_guard
|
||||
let default_world = self
|
||||
.worlds
|
||||
.read()
|
||||
.await
|
||||
.first()
|
||||
.expect("Default world should exist");
|
||||
(default_world.clone(), Some(data))
|
||||
.expect("Default world should exist")
|
||||
.clone();
|
||||
(default_world, Some(data))
|
||||
}
|
||||
} else {
|
||||
// Player data exists but doesn't have a "Dimension" key.
|
||||
let default_world_guard = self.worlds.read().await;
|
||||
let default_world = default_world_guard
|
||||
let default_world = self
|
||||
.worlds
|
||||
.read()
|
||||
.await
|
||||
.first()
|
||||
.expect("Default world should exist");
|
||||
(default_world.clone(), Some(data))
|
||||
.expect("Default world should exist")
|
||||
.clone();
|
||||
(default_world, Some(data))
|
||||
}
|
||||
} else {
|
||||
// No player data found or an error occurred, default to the Overworld.
|
||||
let default_world_guard = self.worlds.read().await;
|
||||
let default_world = default_world_guard
|
||||
let default_world = self
|
||||
.worlds
|
||||
.read()
|
||||
.await
|
||||
.first()
|
||||
.expect("Default world should exist");
|
||||
(default_world.clone(), None)
|
||||
.expect("Default world should exist")
|
||||
.clone();
|
||||
(default_world, None)
|
||||
};
|
||||
|
||||
let mut player = Player::new(
|
||||
@ -457,6 +467,7 @@ impl Server {
|
||||
level_info.difficulty_locked,
|
||||
))
|
||||
.await;
|
||||
drop(level_info);
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,15 +719,7 @@ impl Server {
|
||||
EntitySelectorType::AllEntities => {
|
||||
let mut entities = Vec::new();
|
||||
for world in self.worlds.read().await.iter() {
|
||||
entities.extend(
|
||||
world
|
||||
.entities
|
||||
.read()
|
||||
.await
|
||||
.values()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
entities.extend(world.entities.read().await.values().cloned());
|
||||
entities.extend(
|
||||
world
|
||||
.players
|
||||
@ -724,8 +727,7 @@ impl Server {
|
||||
.await
|
||||
.values()
|
||||
.cloned()
|
||||
.map(|p| p as Arc<dyn EntityBase>)
|
||||
.collect::<Vec<_>>(),
|
||||
.map(|p| p as Arc<dyn EntityBase>),
|
||||
);
|
||||
}
|
||||
entities.into_iter()
|
||||
|
||||
@ -283,6 +283,7 @@ impl World {
|
||||
chunk.mark_dirty(true);
|
||||
if old_chunk == current_chunk_coordinate {
|
||||
chunk.data.remove(&entity.entity_uuid);
|
||||
drop(chunk);
|
||||
} else {
|
||||
let chunk = self.level.get_entity_chunk(current_chunk_coordinate).await;
|
||||
let mut chunk = chunk.write().await;
|
||||
@ -433,10 +434,13 @@ impl World {
|
||||
.await
|
||||
.add_seen_signature(&chat_message.signature.clone().unwrap()); // Unwrap is safe because we check for None in validate_chat_message
|
||||
|
||||
let recipient_signature_cache = &mut recipient.signature_cache.lock().await;
|
||||
if recipient.gameprofile.id != sender.gameprofile.id {
|
||||
// Sender may update recipient on signatures recipient hasn't seen
|
||||
recipient_signature_cache.cache_signatures(sender_last_seen.as_ref());
|
||||
recipient
|
||||
.signature_cache
|
||||
.lock()
|
||||
.await
|
||||
.cache_signatures(sender_last_seen.as_ref());
|
||||
}
|
||||
recipient.chat_session.lock().await.messages_received += 1;
|
||||
}
|
||||
@ -587,9 +591,11 @@ impl World {
|
||||
|
||||
if weather.weather_cycle_enabled && (weather.raining || weather.thundering) {
|
||||
weather.reset_weather_cycle(self).await;
|
||||
drop(weather);
|
||||
}
|
||||
} else if level_time.world_age % 20 == 0 {
|
||||
level_time.send_time(self).await;
|
||||
drop(level_time);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1246,6 +1252,8 @@ impl World {
|
||||
scenario_id: String::new(),
|
||||
owner_id: String::new(),
|
||||
};
|
||||
drop(level_info);
|
||||
drop(weather);
|
||||
|
||||
let client = player.client.bedrock();
|
||||
client
|
||||
@ -1436,9 +1444,7 @@ impl World {
|
||||
f64::from(info.spawn_z) + 0.5,
|
||||
);
|
||||
let yaw = info.spawn_angle;
|
||||
let pitch = 0.0;
|
||||
|
||||
(position, yaw, pitch)
|
||||
(position, yaw, 0.0) // Pitch
|
||||
};
|
||||
|
||||
let velocity = player.living_entity.entity.velocity.load();
|
||||
@ -1470,11 +1476,9 @@ impl World {
|
||||
}],
|
||||
))
|
||||
.await;
|
||||
|
||||
let current_players = self.players.read().await;
|
||||
// Here, we send all the infos of players who already joined.
|
||||
{
|
||||
let current_players = self.players.read().await;
|
||||
|
||||
let mut current_player_data = Vec::new();
|
||||
|
||||
for (_, player) in current_players
|
||||
@ -1499,6 +1503,7 @@ impl World {
|
||||
signature: chat_session.signature.clone(),
|
||||
})));
|
||||
}
|
||||
drop(chat_session);
|
||||
|
||||
current_player_data.push((&player.gameprofile.id, player_actions));
|
||||
}
|
||||
@ -1544,7 +1549,7 @@ impl World {
|
||||
|
||||
// Spawn players for our client.
|
||||
let id = player.gameprofile.id;
|
||||
for (_, existing_player) in self.players.read().await.iter().filter(|c| c.0 != &id) {
|
||||
for (_, existing_player) in current_players.iter().filter(|c| c.0 != &id) {
|
||||
let entity = &existing_player.living_entity.entity;
|
||||
let pos = entity.pos.load();
|
||||
let gameprofile = &existing_player.gameprofile;
|
||||
@ -1563,34 +1568,37 @@ impl World {
|
||||
entity.velocity.load(),
|
||||
))
|
||||
.await;
|
||||
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();
|
||||
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 mut serializer = Serializer::new(&mut serializer_buf);
|
||||
meta.serialize(&mut serializer).unwrap();
|
||||
buf.extend(serializer_buf);
|
||||
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;
|
||||
}
|
||||
// END
|
||||
buf.put_u8(255);
|
||||
client
|
||||
.enqueue_packet(&CSetEntityMetadata::new(
|
||||
existing_player.get_entity().entity_id.into(),
|
||||
buf.into(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
player.send_client_information().await;
|
||||
|
||||
@ -1622,6 +1630,7 @@ impl World {
|
||||
// Calculate rain and thunder levels directly from public fields
|
||||
let rain_level = weather.rain_level.clamp(0.0, 1.0);
|
||||
let thunder_level = weather.thunder_level.clamp(0.0, 1.0);
|
||||
drop(weather);
|
||||
|
||||
client
|
||||
.enqueue_packet(&CGameEvent::new(GameEvent::RainLevelChange, rain_level))
|
||||
@ -1658,8 +1667,7 @@ impl World {
|
||||
));
|
||||
|
||||
for (slot, item_arc_mutex) in &from.inventory.entity_equipment.lock().await.equipment {
|
||||
let item_guard = item_arc_mutex.lock().await;
|
||||
let item_stack = item_guard.clone();
|
||||
let item_stack = item_arc_mutex.lock().await.clone();
|
||||
equipment_list.push((slot.discriminant(), item_stack));
|
||||
}
|
||||
|
||||
@ -1829,6 +1837,7 @@ impl World {
|
||||
.is_some_and(|since| since >= 100)
|
||||
})
|
||||
.count();
|
||||
drop(players);
|
||||
|
||||
// TODO: sleep ratio
|
||||
sleeping_player_count == player_count
|
||||
@ -1943,8 +1952,8 @@ impl World {
|
||||
});
|
||||
let mut entity_receiver = self.level.receive_entity_chunks(chunks);
|
||||
let level = self.level.clone();
|
||||
let player = player1.clone();
|
||||
let world = world1.clone();
|
||||
let player = player1;
|
||||
let world = world1;
|
||||
player.clone().spawn_task(async move {
|
||||
'main: loop {
|
||||
let recv_result = tokio::select! {
|
||||
@ -1971,9 +1980,8 @@ impl World {
|
||||
);
|
||||
let mut ids = Vec::new();
|
||||
// Remove all the entities from the world
|
||||
let entity_chunk = chunk.read().await;
|
||||
let mut entities = world.entities.write().await;
|
||||
for (uuid, entity_nbt) in &entity_chunk.data {
|
||||
for (uuid, entity_nbt) in &chunk.read().await.data {
|
||||
let Some(id) = entity_nbt.get_string("id") else {
|
||||
log::warn!("Entity has no ID");
|
||||
continue;
|
||||
@ -2273,9 +2281,7 @@ impl World {
|
||||
let event = PLUGIN_MANAGER.fire(event).await;
|
||||
|
||||
if !event.cancelled {
|
||||
let current_players = current_players.clone();
|
||||
let players = current_players.read().await;
|
||||
for player in players.values() {
|
||||
for player in current_players.read().await.values() {
|
||||
player.send_system_message(&event.join_message).await;
|
||||
}
|
||||
log::info!("{}", event.join_message.to_pretty_console());
|
||||
@ -2330,6 +2336,7 @@ impl World {
|
||||
for player in players.values() {
|
||||
player.send_system_message(&event.leave_message).await;
|
||||
}
|
||||
drop(players);
|
||||
log::info!("{}", event.leave_message.to_pretty_console());
|
||||
}
|
||||
}
|
||||
@ -2352,6 +2359,7 @@ impl World {
|
||||
entity.write_nbt(&mut nbt).await;
|
||||
chunk.data.insert(base_entity.entity_uuid, nbt);
|
||||
chunk.mark_dirty(true);
|
||||
drop(chunk);
|
||||
|
||||
let mut current_entities = self.entities.write().await;
|
||||
current_entities.insert(base_entity.entity_uuid, entity);
|
||||
|
||||
@ -9,6 +9,7 @@ use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::tag::WorldgenBiome::MINECRAFT_REDUCE_WATER_AMBIENT_SPAWNS;
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState};
|
||||
use pumpkin_util::GameMode;
|
||||
use pumpkin_util::math::boundingbox::{BoundingBox, EntityDimensions};
|
||||
use pumpkin_util::math::get_section_cord;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector2::Vector2;
|
||||
@ -505,14 +506,34 @@ pub async fn is_valid_spawn_position_for_type(
|
||||
distance: f64,
|
||||
) -> bool {
|
||||
// TODO !SpawnPlacements.checkSpawnRules(entityType, level, EntitySpawnReason.NATURAL, pos, level.random)
|
||||
// TODO level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5))
|
||||
!(category == &MobCategory::MISC
|
||||
|| (!entity_type.can_spawn_far_from_player
|
||||
&& distance
|
||||
> f64::from(entity_type.category.despawn_distance)
|
||||
* f64::from(entity_type.category.despawn_distance))
|
||||
|| !entity_type.summonable
|
||||
|| !is_spawn_position_ok(world, block_pos, entity_type).await)
|
||||
if category == &MobCategory::MISC {
|
||||
return false;
|
||||
}
|
||||
if !entity_type.can_spawn_far_from_player
|
||||
&& distance
|
||||
> f64::from(entity_type.category.despawn_distance)
|
||||
* f64::from(entity_type.category.despawn_distance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if !entity_type.summonable {
|
||||
return false;
|
||||
}
|
||||
if !is_spawn_position_ok(world, block_pos, entity_type).await {
|
||||
return false;
|
||||
}
|
||||
// TODO: we should use getSpawnBox, but this is only modified for slimes and magma slimes
|
||||
world
|
||||
.is_space_empty(BoundingBox::new_from_pos(
|
||||
f64::from(block_pos.0.x) + 0.5,
|
||||
f64::from(block_pos.0.y),
|
||||
f64::from(block_pos.0.z) + 0.5,
|
||||
&EntityDimensions {
|
||||
width: entity_type.dimension[0],
|
||||
height: entity_type.dimension[1],
|
||||
},
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn is_spawn_position_ok(
|
||||
@ -536,6 +557,7 @@ pub async fn is_spawn_position_ok(
|
||||
let down = world.get_block_state(&block_pos.down()).await;
|
||||
let up = world.get_block_state(&block_pos.up()).await;
|
||||
let cur = world.get_block_state(block_pos).await;
|
||||
// TODO: blockState.allowsSpawning
|
||||
if down.is_side_solid(BlockDirection::Up) {
|
||||
is_valid_empty_spawn_block(cur) && is_valid_empty_spawn_block(up)
|
||||
} else {
|
||||
@ -548,16 +570,10 @@ pub async fn is_spawn_position_ok(
|
||||
|
||||
#[must_use]
|
||||
pub fn is_valid_empty_spawn_block(state: &'static BlockState) -> bool {
|
||||
if state.is_solid() {
|
||||
false
|
||||
} else if false {
|
||||
// TODO isSignalSource
|
||||
false
|
||||
} else if state.is_liquid() {
|
||||
false
|
||||
} else {
|
||||
// TODO !entityType.isBlockDangerous(blockState);
|
||||
!Block::from_state_id(state.id)
|
||||
.is_tagged_with_by_tag(&MINECRAFT_PREVENT_MOB_SPAWNING_INSIDE)
|
||||
// TODO: emitsRedstonePower
|
||||
if state.is_full_cube() || state.is_liquid() {
|
||||
return false;
|
||||
}
|
||||
// TODO !entityType.isBlockDangerous(blockState);
|
||||
!Block::from_state_id(state.id).is_tagged_with_by_tag(&MINECRAFT_PREVENT_MOB_SPAWNING_INSIDE)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user