entity: Arc<RwLock<Arc<World>> to Arc<World>

This removed a bunch of locks and .awaits, now instead of switching the world inside a entity we will just recreate it in a new World
This commit is contained in:
Alexander Medvedev 2025-08-19 13:03:52 +02:00
parent 36d6356532
commit 314547eb5f
43 changed files with 147 additions and 241 deletions

View File

@ -656,7 +656,7 @@ impl Level {
let (sender, receiver) = mpsc::unbounded_channel();
let level = self.clone();
log::debug!("Receiving chunks: {}", chunks.len());
log::trace!("Receiving chunks: {}", chunks.len());
self.spawn_task(async move {
let cancel_notifier = level.shutdown_notifier.notified();
@ -848,8 +848,7 @@ impl Level {
let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position();
let chunk = self.get_chunk(chunk_coordinate).await;
let chunk = chunk.read().await;
let Some(id) = chunk.section.get_block_absolute_y(
let Some(id) = chunk.read().await.section.get_block_absolute_y(
relative.x as usize,
relative.y,
relative.z as usize,
@ -863,8 +862,7 @@ impl Level {
let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position();
let chunk = self.get_chunk(chunk_coordinate).await;
let chunk = chunk.read().await;
let Some(id) = chunk.section.get_rough_biome_absolute_y(
let Some(id) = chunk.read().await.section.get_rough_biome_absolute_y(
relative.x as usize,
relative.y,
relative.z as usize,

View File

@ -48,7 +48,7 @@ impl JukeboxBlock {
#[async_trait]
impl BlockBehaviour for JukeboxBlock {
async fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult {
let world = &args.player.living_entity.entity.world.read().await;
let world = &args.player.living_entity.entity.world;
// if the jukebox already has a record, stop playing
if self.has_record(args.block, args.position, world).await {

View File

@ -55,7 +55,7 @@ impl BlockBehaviour for SignBlock {
}
async fn player_placed(&self, args: PlayerPlacedArgs<'_>) {
match &args.player.client {
match args.player.client.as_ref() {
crate::net::ClientPlatform::Java(java) => {
java.send_sign_packet(*args.position, true).await;
}
@ -100,7 +100,7 @@ impl BlockBehaviour for SignBlock {
let is_facing_front_text =
is_facing_front_text(args.world, args.position, args.block, args.player).await;
match &args.player.client {
match args.player.client.as_ref() {
crate::net::ClientPlatform::Java(java) => {
java.send_sign_packet(*args.position, is_facing_front_text)
.await;

View File

@ -58,8 +58,8 @@ impl BlockBehaviour for TNTBlock {
if item != &Item::FLINT_AND_STEEL || item == &Item::FIRE_CHARGE {
return BlockActionResult::Pass;
}
let world = args.player.world().await;
Self::prime(&world, args.position).await;
let world = args.player.world();
Self::prime(world, args.position).await;
BlockActionResult::Consume
}

View File

@ -74,10 +74,7 @@ impl CommandExecutor for Executor {
let end_z = from.0.z.max(to.0.z);
// TODO: check isInWorldBounds and throw argument.pos.outofbounds
let world = sender
.world()
.await
.ok_or(CommandError::InvalidRequirement)?;
let world = sender.world().ok_or(CommandError::InvalidRequirement)?;
let mut placed_blocks = 0;
let mut to_update = Vec::new();
match mode {

View File

@ -63,7 +63,7 @@ impl CommandExecutor for Executor {
CommandSender::Player(player) => {
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
(player.world().await, pos)
(player.world().clone(), pos)
}
};

View File

@ -22,9 +22,7 @@ impl CommandExecutor for Executor {
_args: &ConsumedArgs<'a>,
) -> Result<(), CommandError> {
let seed = match sender {
CommandSender::Player(player) => {
player.living_entity.entity.world.read().await.level.seed.0
}
CommandSender::Player(player) => player.living_entity.entity.world.level.seed.0,
// TODO: Maybe ask player for world, or get the current world
_ => match server.worlds.read().await.first() {
Some(world) => world.level.seed.0,

View File

@ -51,7 +51,7 @@ impl CommandExecutor for Executor {
.cloned()
.ok_or(CommandError::InvalidRequirement)?
}
CommandSender::Player(player) => player.world().await,
CommandSender::Player(player) => player.world().clone(),
};
let success = match mode {
Mode::Destroy => {

View File

@ -98,7 +98,7 @@ async fn setworldspawn(
block_pos: BlockPos,
yaw: f32,
) -> Result<(), CommandError> {
let Some(world) = sender.world().await else {
let Some(world) = sender.world() else {
return Err(CommandError::CommandFailed(Box::new(TextComponent::text(
"Failed to get world.",
))));

View File

@ -55,7 +55,7 @@ impl CommandExecutor for Executor {
CommandSender::Player(player) => {
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
(player.world().await, pos)
(player.world().clone(), pos)
}
};
let mob = from_type(entity, pos, &world, Uuid::new_v4()).await;

View File

@ -74,7 +74,7 @@ impl CommandExecutor for EntitiesToEntityExecutor {
let base_entity = target.get_entity();
let yaw = base_entity.yaw.load();
let pitch = base_entity.pitch.load();
let world = base_entity.world.read().await.clone();
let world = base_entity.world.clone();
target
.clone()
.teleport(pos, yaw.into(), pitch.into(), world)
@ -110,7 +110,7 @@ impl CommandExecutor for EntitiesToPosFacingPosExecutor {
CommandSender::Rcon(_) | CommandSender::Console => {
server.worlds.read().await.first().unwrap().clone()
}
CommandSender::Player(player) => player.world().await,
CommandSender::Player(player) => player.world().clone(),
};
for target in targets {
@ -152,7 +152,7 @@ impl CommandExecutor for EntitiesToPosFacingEntityExecutor {
pos,
Some(yaw),
Some(pitch),
facing_entity.get_entity().world.read().await.clone(),
facing_entity.get_entity().world.clone(),
)
.await;
}
@ -217,7 +217,7 @@ impl CommandExecutor for EntitiesToPosExecutor {
CommandSender::Rcon(_) | CommandSender::Console => {
server.worlds.read().await.first().unwrap().clone()
}
CommandSender::Player(player) => player.world().await,
CommandSender::Player(player) => player.world().clone(),
};
for target in targets {
let yaw = target.get_entity().yaw.load();
@ -244,7 +244,7 @@ impl CommandExecutor for SelfToEntityExecutor {
) -> Result<(), CommandError> {
let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?;
let pos = destination.get_entity().pos.load();
let world = destination.get_entity().world.read().await.clone();
let world = destination.get_entity().world.clone();
match sender {
CommandSender::Player(player) => {
@ -292,7 +292,7 @@ impl CommandExecutor for SelfToPosExecutor {
}
player
.clone()
.teleport(pos, Some(yaw), Some(pitch), player.world().await)
.teleport(pos, Some(yaw), Some(pitch), player.world().clone())
.await;
}
_ => {

View File

@ -100,11 +100,11 @@ impl CommandSender {
}
#[must_use]
pub async fn world(&self) -> Option<Arc<World>> {
pub fn world(&self) -> Option<Arc<World>> {
match self {
// TODO: maybe return first world when console
Self::Console | Self::Rcon(..) => None,
Self::Player(p) => Some(p.living_entity.entity.world.read().await.clone()),
Self::Player(p) => Some(p.living_entity.entity.world.clone()),
}
}

View File

@ -73,7 +73,7 @@ impl ActiveTargetGoal {
async fn find_closest_target(&self, mob: &MobEntity) {
let mut target = self.target.lock().await;
let world = mob.living_entity.entity.world.read().await;
let world = &mob.living_entity.entity.world;
if self.target_type == &EntityType::PLAYER {
*target = world
.get_closest_player(

View File

@ -95,7 +95,7 @@ impl Goal for LookAtEntityGoal {
}
drop(mob_target);
let world = mob_entity.living_entity.entity.world.read().await;
let world = &mob_entity.living_entity.entity.world;
if self.target_type == &EntityType::PLAYER {
*target = world
.get_closest_player(mob_entity.living_entity.entity.pos.load(), self.range)

View File

@ -49,7 +49,7 @@ impl MeleeAttackGoal {
impl Goal for MeleeAttackGoal {
async fn can_start(&self, mob: &dyn Mob) -> bool {
let time = {
let world = mob.get_entity().world.read().await;
let world = &mob.get_entity().world;
let level_time = world.level_time.lock().await;
level_time.world_age
};

View File

@ -82,7 +82,7 @@ impl MoveToTargetPosGoal {
block_pos_mutable.0.z = block_pos.0.z + n;
// Make sure the world lock is dropped
{
let world = mob.get_entity().world.read().await;
let world = &mob.get_entity().world;
let can_target = if let Some(x) = self.move_to_target_pos.upgrade() {
x.is_target_pos(world.clone(), block_pos_mutable).await
} else {
@ -144,7 +144,7 @@ impl Goal for MoveToTargetPosGoal {
}
async fn should_continue(&self, mob: &dyn Mob) -> bool {
let world = mob.get_entity().world.read().await;
let world = &mob.get_entity().world;
let can_target = if let Some(x) = self.move_to_target_pos.upgrade() {
x.is_target_pos(world.clone(), self.target_pos.load()).await
} else {

View File

@ -93,7 +93,7 @@ pub trait Stepping: Send + Sync {
#[async_trait]
impl Goal for StepAndDestroyBlockGoal {
async fn can_start(&self, mob: &dyn Mob) -> bool {
let world = mob.get_entity().world.read().await;
let world = &mob.get_entity().world;
let level_info = world.level_info.read().await;
if !level_info.game_rules.mob_griefing {
false
@ -129,7 +129,7 @@ impl Goal for StepAndDestroyBlockGoal {
async fn tick(&self, mob: &dyn Mob) {
self.move_to_target_pos_goal.tick(mob).await;
let mob_entity = mob.get_mob_entity();
let world = mob.get_entity().world.read().await;
let world = &mob.get_entity().world;
let block_pos = mob.get_entity().block_pos.load();
let tweak_pos = self.tweak_to_proper_pos(block_pos, world.clone()).await;
// TODO: drop world?

View File

@ -66,25 +66,24 @@ impl TrackTargetGoal {
false
}
pub async fn can_track(
pub fn can_track(
&self,
mob: &dyn Mob,
target: Option<&LivingEntity>,
target_predicate: TargetPredicate,
target_predicate: &TargetPredicate,
) -> bool {
if target.is_none() {
return false;
}
let mob_entity = mob.get_mob_entity();
let target = target.unwrap();
let world = mob_entity.living_entity.entity.world.read().await;
let world = &mob_entity.living_entity.entity.world;
if !target_predicate.test(world.clone(), Some(&mob_entity.living_entity), target) {
return false;
} /*else if (!this.mob.isInPositionTargetRange(target.getBlockPos())) {
return false;
}*/
// TODO: implement this
drop(world); // Drop the lock because is useless now
if self.check_can_navigate {
if self.check_can_navigate_cooldown.fetch_sub(1, Relaxed) - 1 <= 0 {

View File

@ -35,7 +35,7 @@ impl Navigator {
let mut best_move = Vector3::new(0.0, 0.0, 0.0);
let mut lowest_cost = f64::MAX;
let world = entity.entity.world.read().await;
let world = &entity.entity.world;
for x in -1..=1 {
for z in -1..=1 {

View File

@ -89,8 +89,6 @@ impl EntityBase for ExperienceOrbEntity {
let no_clip = !self
.entity
.world
.read()
.await
.is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7))
.await;
// TODO: isSubmergedIn

View File

@ -71,8 +71,6 @@ impl EntityBase for FallingEntity {
entity.velocity.store(velo.multiply(0.7, -0.5, 0.7));
entity
.world
.read()
.await
.set_block_state(
&self.entity.block_pos.load(),
self.block_state_id,

View File

@ -32,7 +32,7 @@ impl HungerManager {
let level = self.level.load();
let exhaustion = self.exhaustion.load();
let health = player.living_entity.health.load();
let difficulty = player.world().await.level_info.read().await.difficulty;
let difficulty = player.world().level_info.read().await.difficulty;
// Decrease hunger level on exhaustion
if level != 0 && exhaustion > 4.0 {
self.exhaustion.store(exhaustion - 4.0);

View File

@ -91,8 +91,6 @@ impl ItemEntity {
let items: Vec<_> = self
.entity
.world
.read()
.await
.entities
.read()
.await
@ -239,8 +237,6 @@ impl EntityBase for ItemEntity {
let no_clip = !self
.entity
.world
.read()
.await
.is_space_empty(bounding_box.expand(-1.0e-7, -1.0e-7, -1.0e-7))
.await;

View File

@ -121,8 +121,6 @@ impl LivingEntity {
.collect();
self.entity
.world
.read()
.await
.broadcast_packet_except(
&[self.entity.entity_uuid],
&CSetEquipment::new(self.entity_id().into(), equipment),
@ -135,8 +133,6 @@ impl LivingEntity {
// TODO: Only nearby
self.entity
.world
.read()
.await
.broadcast_packet_all(&CTakeItemEntity::new(
item.entity_id.into(),
self.entity.entity_id.into(),
@ -175,8 +171,6 @@ impl LivingEntity {
self.active_effects.lock().await.remove(&effect_type);
self.entity
.world
.read()
.await
.send_remove_mob_effect(&self.entity, effect_type)
.await;
}
@ -193,16 +187,14 @@ impl LivingEntity {
// Check if the entity is in water
pub async fn is_in_water(&self) -> bool {
let world = self.entity.world.read().await;
let block_pos = self.entity.block_pos.load();
world.get_block(&block_pos).await == &Block::WATER
self.entity.world.get_block(&block_pos).await == &Block::WATER
}
// Check if the entity is in powder snow
pub async fn is_in_powder_snow(&self) -> bool {
let world = self.entity.world.read().await;
let block_pos = self.entity.block_pos.load();
world.get_block(&block_pos).await == &Block::POWDER_SNOW
self.entity.world.get_block(&block_pos).await == &Block::POWDER_SNOW
}
async fn get_effective_gravity(&self, caller: &Arc<dyn EntityBase>) -> f64 {
@ -462,8 +454,6 @@ impl LivingEntity {
&& !self
.entity
.world
.read()
.await
.check_fluid_collision(self.entity.bounding_box.load().shift(velo))
.await
{
@ -742,7 +732,7 @@ impl LivingEntity {
source: Option<&dyn EntityBase>,
cause: Option<&dyn EntityBase>,
) {
let world = self.entity.world.read().await;
let world = &self.entity.world;
let dyn_self = world
.get_entity_by_id(self.entity.entity_id)
.await
@ -784,10 +774,9 @@ impl LivingEntity {
async fn drop_loot(&self, params: LootContextParameters) {
if let Some(loot_table) = &self.get_entity().entity_type.loot_table {
let world = self.entity.world.read().await;
let pos = self.entity.block_pos.load();
for stack in loot_table.get_loot(params) {
world.drop_stack(&pos, stack).await;
self.entity.world.drop_stack(&pos, stack).await;
}
}
}
@ -906,7 +895,7 @@ impl EntityBase for LivingEntity {
return false; // Fire resistance
}
let world = self.entity.world.read().await;
let world = &self.entity.world;
let last_damage = self.last_damage_taken.load();
let play_sound;
@ -935,8 +924,6 @@ impl EntityBase for LivingEntity {
self.entity
.world
.read()
.await
.broadcast_packet_all(&CDamageEvent::new(
self.entity.entity_id.into(),
damage_type.id.into(),
@ -949,8 +936,6 @@ impl EntityBase for LivingEntity {
if play_sound {
self.entity
.world
.read()
.await
.play_sound(
// Sound::EntityPlayerHurt,
Sound::EntityGenericHurt,
@ -997,8 +982,6 @@ impl EntityBase for LivingEntity {
// Spawn Death particles
self.entity
.world
.read()
.await
.send_entity_status(&self.entity, EntityStatus::AddDeathParticles)
.await;
self.entity.remove().await;

View File

@ -47,7 +47,7 @@ use std::sync::{
Ordering::{self, Relaxed},
},
};
use tokio::sync::{Mutex, RwLock};
use tokio::sync::Mutex;
pub mod ai;
pub mod decoration;
@ -234,7 +234,7 @@ pub struct Entity {
/// The type of entity (e.g., player, zombie, item)
pub entity_type: &'static EntityType,
/// The world in which the entity exists.
pub world: Arc<RwLock<Arc<World>>>,
pub world: Arc<World>,
/// The entity's current position in the world
pub pos: AtomicCell<Vector3<f64>>,
/// The last known position of the entity.
@ -351,7 +351,7 @@ impl Entity {
get_section_cord(floor_z),
)),
sneaking: AtomicBool::new(false),
world: Arc::new(RwLock::new(world)),
world,
sprinting: AtomicBool::new(false),
fall_flying: AtomicBool::new(false),
yaw: AtomicCell::new(0.0),
@ -397,8 +397,6 @@ impl Entity {
pub async fn send_velocity(&self) {
let velocity = self.velocity.load();
self.world
.read()
.await
.broadcast_packet_all(&CEntityVelocity::new(self.entity_id.into(), velocity))
.await;
}
@ -486,8 +484,6 @@ impl Entity {
let pitch = (pitch * 256.0 / 360.0).rem_euclid(256.0);
self.world
.read()
.await
.broadcast_packet_all(&CUpdateEntityRot::new(
self.entity_id.into(),
yaw,
@ -501,8 +497,6 @@ impl Entity {
pub async fn send_head_rot(&self, head_yaw: u8) {
self.world
.read()
.await
.broadcast_packet_all(&CHeadRot::new(self.entity_id.into(), head_yaw))
.await;
}
@ -531,8 +525,6 @@ impl Entity {
let (collisions, block_positions) = self
.world
.read()
.await
.get_block_collisions(bounding_box.stretch(movement))
.await;
@ -765,13 +757,12 @@ 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 {
let pos = BlockPos::new(x, y, z);
let (block, state) = world.get_block_and_state(&pos).await;
let (block, state) = self.world.get_block_and_state(&pos).await;
let collided = World::check_outline(
&bounding_box,
@ -784,11 +775,11 @@ impl Entity {
);
if collided {
world
self.world
.block_registry
.on_entity_collision(
block,
&world,
&self.world,
caller.as_ref(),
&pos,
state,
@ -827,8 +818,6 @@ impl Entity {
let pitch = (pitch * 256.0 / 360.0).rem_euclid(256.0);
self.world
.read()
.await
.broadcast_packet_all(&CUpdateEntityPosRot::new(
self.entity_id.into(),
Vector3::new(converted.x, converted.y, converted.z),
@ -859,8 +848,6 @@ impl Entity {
);
self.world
.read()
.await
.broadcast_packet_all(&CUpdateEntityPos::new(
self.entity_id.into(),
Vector3::new(converted.x, converted.y, converted.z),
@ -900,14 +887,12 @@ impl Entity {
let max = bounding_box.max_block_pos();
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 {
let pos = BlockPos::new(x, y, z);
let (fluid, state) = world.get_fluid_and_fluid_state(&pos).await;
let (fluid, state) = self.world.get_fluid_and_fluid_state(&pos).await;
if fluid.id != Fluid::EMPTY.id {
let marginal_height =
@ -929,7 +914,7 @@ impl Entity {
}
let mut fluid_velo =
world.get_fluid_velocity(pos, &fluid, &state).await;
self.world.get_fluid_velocity(pos, &fluid, &state).await;
if fluid_height[i] < 0.4 {
fluid_velo = fluid_velo * fluid_height[i];
@ -949,18 +934,17 @@ impl Entity {
// BTreeMap auto-sorts water before lava as in vanilla
for (_, fluid) in fluids {
world
self.world
.block_registry
.on_entity_collision_fluid(&fluid, caller.as_ref())
.await;
}
let lava_speed =
if self.world.read().await.dimension_type == VanillaDimensionType::TheNether {
0.007
} else {
0.002_333_333
};
let lava_speed = if self.world.dimension_type == VanillaDimensionType::TheNether {
0.007
} else {
0.002_333_333
};
self.push_by_fluid(0.014, fluid_push[0], fluid_n[0]);
@ -1035,12 +1019,7 @@ impl Entity {
) {
if let Some(mut supporting_block) = self.supporting_block_pos.load() {
if offset > 1.0e-5 {
let (block, state) = self
.world
.read()
.await
.get_block_and_state(&supporting_block)
.await;
let (block, state) = self.world.get_block_and_state(&supporting_block).await;
// if let Some(props) = block.properties(state.id) {
// let name = props.;
@ -1080,7 +1059,7 @@ impl Entity {
if let (Some(b), Some(s)) = (block, state) {
(pos, b, s)
} else {
let (b, s) = self.world.read().await.get_block_and_state(&pos).await;
let (b, s) = self.world.get_block_and_state(&pos).await;
(pos, b, s)
}
@ -1125,12 +1104,7 @@ impl Entity {
#[allow(clippy::float_cmp)]
async fn get_velocity_multiplier(&self) -> f32 {
let block = self
.world
.read()
.await
.get_block(&self.block_pos.load())
.await;
let block = self.world.get_block(&self.block_pos.load()).await;
let multiplier = block.velocity_multiplier;
@ -1147,8 +1121,6 @@ impl Entity {
async fn get_jump_velocity_multiplier(&self) -> f32 {
let f = self
.world
.read()
.await
.get_block(&self.block_pos.load())
.await
.jump_velocity_multiplier;
@ -1225,8 +1197,6 @@ impl Entity {
if self
.world
.read()
.await
.get_block_state(&block_pos.offset(offset))
.await
.is_full_cube()
@ -1288,7 +1258,7 @@ impl Entity {
};
// TODO: this is bad
let scale_factor_current =
if self.world.read().await.dimension_type == VanillaDimensionType::TheNether {
if self.world.dimension_type == VanillaDimensionType::TheNether {
8.0
} else {
1.0
@ -1364,7 +1334,7 @@ impl Entity {
/// Removes the `Entity` from their current `World`
pub async fn remove(&self) {
self.world.read().await.remove_entity(self).await;
self.world.remove_entity(self).await;
}
pub fn create_spawn_packet(&self) -> CSpawnEntity {
@ -1588,8 +1558,6 @@ impl Entity {
/// Plays sound at this entity's position with the entity's sound category
pub async fn play_sound(&self, sound: Sound) {
self.world
.read()
.await
.play_sound(sound, SoundCategory::Neutral, &self.pos.load())
.await;
}
@ -1604,8 +1572,6 @@ impl Entity {
}
buf.put_u8(255);
self.world
.read()
.await
.broadcast_packet_all(&CSetEntityMetadata::new(self.entity_id.into(), buf.into()))
.await;
}
@ -1634,7 +1600,7 @@ impl Entity {
(aabb.max.y - 0.001).floor() as i32,
(aabb.max.z - 0.001).floor() as i32,
);
let world = entity.get_entity().world.read().await;
let world = &entity.get_entity().world;
for x in blockpos.0.x..=blockpos1.0.x {
for y in blockpos.0.y..=blockpos1.0.y {
@ -1647,7 +1613,7 @@ impl Entity {
if outlines.is_empty() {
world
.block_registry
.on_entity_collision(block, &world, entity, &pos, state, server)
.on_entity_collision(block, world, entity, &pos, state, server)
.await;
let fluid = world.get_fluid(&pos).await;
world
@ -1661,7 +1627,7 @@ impl Entity {
if outline_aabb.intersects(&aabb) {
world
.block_registry
.on_entity_collision(block, &world, entity, &pos, state, server)
.on_entity_collision(block, world, entity, &pos, state, server)
.await;
let fluid = world.get_fluid(&pos).await;
world
@ -1674,7 +1640,7 @@ impl Entity {
} else {
world
.block_registry
.on_entity_collision(block, &world, entity, &pos, state, server)
.on_entity_collision(block, world, entity, &pos, state, server)
.await;
let fluid = world.get_fluid(&pos).await;
world
@ -1696,8 +1662,6 @@ impl Entity {
) {
// TODO: handle world change
self.world
.read()
.await
.broadcast_packet_all(&CEntityPositionSync::new(
self.entity_id.into(),
position,
@ -1731,9 +1695,7 @@ impl Entity {
}
pub async fn check_out_of_world(&self, dyn_self: &dyn EntityBase) {
if self.pos.load().y
< f64::from(self.world.read().await.generation_settings().shape.min_y) - 64.0
{
if self.pos.load().y < f64::from(self.world.generation_settings().shape.min_y) - 64.0 {
// Tick out of world damage
dyn_self.damage(4.0, DamageType::OUT_OF_WORLD).await;
}

View File

@ -200,7 +200,7 @@ pub struct Player {
/// The player's game profile information, including their username and UUID.
pub gameprofile: GameProfile,
/// The client connection associated with the player.
pub client: ClientPlatform,
pub client: Arc<ClientPlatform>,
/// The player's inventory.
pub inventory: Arc<PlayerInventory>,
/// The player's configuration settings. Changes when the player changes their settings.
@ -278,7 +278,7 @@ pub struct Player {
impl Player {
pub async fn new(
client: ClientPlatform,
client: Arc<ClientPlatform>,
gameprofile: GameProfile,
config: PlayerConfig,
world: Arc<World>,
@ -397,9 +397,8 @@ impl Player {
}
/// Removes the [`Player`] out of the current [`World`].
#[allow(unused_variables)]
pub async fn remove(self: &Arc<Self>) {
let world = self.world().await;
let world = self.world();
world.remove_player(self, true).await;
let cylindrical = self.watched_section.load();
@ -437,7 +436,7 @@ impl Player {
}
pub async fn attack(&self, victim: Arc<dyn EntityBase>) {
let world = self.world().await;
let world = self.world();
let victim_entity = victim.get_entity();
let attacker_entity = &self.living_entity.entity;
let config = &advanced_config().pvp;
@ -513,22 +512,17 @@ impl Player {
if victim.get_living_entity().is_some() {
let mut knockback_strength = 1.0;
player_attack_sound(&pos, &world, attack_type).await;
player_attack_sound(&pos, world, attack_type).await;
match attack_type {
AttackType::Knockback => knockback_strength += 1.0,
AttackType::Sweeping => {
combat::spawn_sweep_particle(attacker_entity, &world, &pos).await;
combat::spawn_sweep_particle(attacker_entity, world, &pos).await;
}
_ => {}
}
if config.knockback {
combat::handle_knockback(
attacker_entity,
&world,
victim_entity,
knockback_strength,
)
.await;
combat::handle_knockback(attacker_entity, world, victim_entity, knockback_strength)
.await;
}
}
@ -564,7 +558,7 @@ impl Player {
pub async fn get_respawn_point(&self) -> Option<(Vector3<f64>, f32)> {
let respawn_point = self.respawn_point.load()?;
let block = self.world().await.get_block(&respawn_point.position).await;
let block = self.world().get_block(&respawn_point.position).await;
if respawn_point.dimension == VanillaDimensionType::Overworld
&& block.is_tagged_with_by_tag(&tag::Block::MINECRAFT_BEDS)
@ -629,14 +623,14 @@ impl Player {
}
pub async fn wake_up(&self) {
let world = self.world().await;
let world = self.world();
let respawn_point = self
.respawn_point
.load()
.expect("Player waking up should have it's respawn point set on the bed.");
let (bed, bed_state) = world.get_block_and_state_id(&respawn_point.position).await;
BedBlock::set_occupied(false, &world, bed, &respawn_point.position, bed_state).await;
BedBlock::set_occupied(false, world, bed, &respawn_point.position, bed_state).await;
self.living_entity
.entity
@ -760,7 +754,7 @@ impl Player {
let chunk_of_chunks = {
let mut chunk_manager = self.chunk_manager.lock().await;
if let ClientPlatform::Java(_) = &self.client {
if let ClientPlatform::Java(_) = self.client.as_ref() {
// Java clients can only send a limited amount of chunks per tick.
// If we have sent too many chunks without receiving an ack, we stop sending chunks.
chunk_manager
@ -773,7 +767,7 @@ impl Player {
if let Some(chunk_of_chunks) = chunk_of_chunks {
let chunk_count = chunk_of_chunks.len();
match &self.client {
match self.client.as_ref() {
ClientPlatform::Java(java_client) => {
java_client.send_packet_now(&CChunkBatchStart).await;
for chunk in chunk_of_chunks {
@ -811,7 +805,7 @@ impl Player {
if self.mining.load(Ordering::Relaxed) {
let pos = self.mining_pos.lock().await;
let world = self.world().await;
let world = self.world();
let state = world.get_block_state(&pos).await;
// Is the block broken?
if state.is_air() {
@ -824,7 +818,7 @@ impl Player {
} else {
self.continue_mining(
*pos,
&world,
world,
state,
self.start_mining_time.load(Ordering::Relaxed),
)
@ -847,7 +841,7 @@ impl Player {
// TODO This should only be handled by the ClientPlatform
let now = Instant::now();
if now.duration_since(self.last_keep_alive_time.load()) >= Duration::from_secs(15) {
if matches!(self.client, ClientPlatform::Bedrock(_)) {
if matches!(self.client.as_ref(), ClientPlatform::Bedrock(_)) {
return;
}
// We never got a response from the last keep alive we sent.
@ -934,8 +928,8 @@ impl Player {
self.living_entity.entity.entity_id
}
pub async fn world(&self) -> Arc<World> {
self.living_entity.entity.world.read().await.clone()
pub fn world(&self) -> &Arc<World> {
&self.living_entity.entity.world
}
pub fn position(&self) -> Vector3<f64> {
@ -998,14 +992,13 @@ impl Player {
PermissionLvl::Four => EntityStatus::SetOpLevel4,
};
self.world()
.await
.send_entity_status(&self.living_entity.entity, status)
.await;
}
/// Sets the player's difficulty level.
pub async fn send_difficulty_update(&self) {
let world = self.world().await;
let world = self.world();
let level_info = world.level_info.read().await;
self.client
.enqueue_packet(&CChangeDifficulty::new(
@ -1029,7 +1022,7 @@ impl Player {
/// Sends the world time to only this player.
pub async fn send_time(&self, world: &World) {
let l_world = world.level_time.lock().await;
match &self.client {
match self.client.as_ref() {
ClientPlatform::Java(java_client) => {
java_client
.enqueue_packet(&CUpdateTime::new(
@ -1074,7 +1067,7 @@ impl Player {
yaw: Option<f32>,
pitch: Option<f32>,
) {
let current_world = self.living_entity.entity.world.read().await.clone();
let current_world = self.living_entity.entity.world.clone();
let yaw = yaw.unwrap_or(new_world.level_info.read().await.spawn_angle);
let pitch = pitch.unwrap_or(10.0);
@ -1099,12 +1092,18 @@ impl Player {
self.set_client_loaded(false);
let uuid = self.gameprofile.id;
current_world.remove_player(self, false).await;
*self.living_entity.entity.world.write().await = new_world.clone();
new_world.players.write().await.insert(uuid, self.clone());
new_world.players.write().await.insert(uuid, Arc::new(Self::new(
self.client.clone(),
self.gameprofile.clone(),
self.config.read().await.clone(),
new_world.clone(),
self.gamemode.load(),
)
.await));
self.unload_watched_chunks(&current_world).await;
let last_pos = self.living_entity.entity.last_pos.load();
let death_dimension = self.world().await.dimension_type.resource_location();
let death_dimension = self.world().dimension_type.resource_location();
let death_location = BlockPos(Vector3::new(
last_pos.x.round() as i32,
last_pos.y.round() as i32,
@ -1305,8 +1304,6 @@ impl Player {
self.living_entity
.entity
.world
.read()
.await
.broadcast_packet_all(&CPlayerInfoUpdate::new(
PlayerInfoFlags::UPDATE_GAME_MODE.bits(),
&[pumpkin_protocol::java::client::play::Player {
@ -1425,7 +1422,7 @@ impl Player {
+ Vector3::new(0.0, f64::from(EntityType::PLAYER.eye_height) - 0.3, 0.0);
let entity = Entity::new(
Uuid::new_v4(),
self.world().await,
self.world().clone(),
item_pos,
&EntityType::ITEM,
false,
@ -1449,7 +1446,7 @@ impl Player {
// TODO: Merge stacks together
let item_entity =
Arc::new(ItemEntity::new_with_velocity(entity, item_stack, velocity, 40).await);
self.world().await.spawn_entity(item_entity).await;
self.world().spawn_entity(item_entity).await;
}
pub async fn drop_held_item(&self, drop_stack: bool) {
@ -1855,7 +1852,7 @@ impl Player {
/// Swing the hand of the player
pub async fn swing_hand(&self, hand: Hand, all: bool) {
let world = self.world().await;
let world = self.world();
let entity_id = VarInt(self.entity_id());
let animation = match hand {
@ -1906,11 +1903,7 @@ impl NBTStorage for Player {
nbt.put_string(
"Dimension",
self.world()
.await
.dimension_type
.resource_location()
.to_string(),
self.world().dimension_type.resource_location().to_string(),
);
}
@ -2072,8 +2065,6 @@ impl EntityBase for Player {
.living_entity
.entity
.world
.read()
.await
.get_entity_by_id(self.living_entity.entity.entity_id)
.await
.expect("Entity not found in world");
@ -2099,7 +2090,7 @@ impl EntityBase for Player {
pitch: Option<f32>,
world: Arc<World>,
) {
if Arc::ptr_eq(&world, &self.world().await) {
if Arc::ptr_eq(&world, self.world()) {
// Same world
let yaw = yaw.unwrap_or(self.living_entity.entity.yaw.load());
let pitch = pitch.unwrap_or(self.living_entity.entity.pitch.load());
@ -2116,8 +2107,6 @@ impl EntityBase for Player {
self.request_teleport(position, yaw, pitch).await;
entity
.world
.read()
.await
.broadcast_packet_except(&[self.gameprofile.id], &CEntityPositionSync::new(
self.living_entity.entity.entity_id.into(),
position,

View File

@ -65,8 +65,6 @@ impl EntityBase for TNTEntity {
self.entity.remove().await;
self.entity
.world
.read()
.await
.explode(server, self.entity.pos.load(), self.power)
.await;
} else {

View File

@ -32,7 +32,7 @@ impl ItemBehaviour for AxeItem {
_server: &Server,
) {
// I tried to follow mojang order of doing things.
let world = player.world().await;
let world = player.world();
let replacement_block = try_use_axe(block);
// First we try to strip the block. by getting his equivalent and applying it the axis.

View File

@ -97,7 +97,7 @@ fn set_waterlogged(block: &Block, state: u16, waterlogged: bool) -> u16 {
#[async_trait]
impl ItemBehaviour for EmptyBucketItem {
async fn normal_use(&self, _item: &Item, player: &Player) {
let world = player.world().await.clone();
let world = player.world();
let (start_pos, end_pos) = get_start_and_end_pos(player);
let checker = async |pos: &BlockPos, world_inner: &Arc<World>| {
@ -211,7 +211,7 @@ impl ItemBehaviour for EmptyBucketItem {
#[async_trait]
impl ItemBehaviour for FilledBucketItem {
async fn normal_use(&self, item: &Item, player: &Player) {
let world = player.world().await.clone();
let world = player.world();
let (start_pos, end_pos) = get_start_and_end_pos(player);
let checker = async |pos: &BlockPos, world_inner: &Arc<World>| {
let state_id = world_inner.get_block_state_id(pos).await;

View File

@ -24,7 +24,7 @@ const POWER: f32 = 1.5;
impl ItemBehaviour for EggItem {
async fn normal_use(&self, _block: &Item, player: &Player) {
let position = player.position();
let world = player.world().await;
let world = player.world();
world
.play_sound(
Sound::EntityEggThrow,

View File

@ -32,7 +32,7 @@ impl ItemBehaviour for EnderEyeItem {
return;
}
let world = player.world().await;
let world = player.world();
let state_id = world.get_block_state_id(&location).await;
let original_props = block.properties(state_id).unwrap().to_props();
@ -52,13 +52,13 @@ impl ItemBehaviour for EnderEyeItem {
.set_block_state(&location, new_state_id, BlockFlags::empty())
.await;
EndPortal::get_new_portal(&world, location).await;
EndPortal::get_new_portal(world, location).await;
return;
}
async fn normal_use(&self, _item: &Item, player: &Player) {
let world = player.world().await;
let world = player.world();
let (start_pos, end_pos) = self.get_start_and_end_pos(player);
let checker = async |pos: &BlockPos, world_inner: &Arc<World>| {
let state_id = world_inner.get_block_state_id(pos).await;

View File

@ -41,7 +41,7 @@ impl ItemBehaviour for HoeItem {
|| block == &Block::ROOTED_DIRT
{
let mut future_block = block;
let world = player.world().await;
let world = player.world();
//Only rooted can be right-clicked on the bottom of the block
if face == BlockDirection::Down {

View File

@ -38,7 +38,7 @@ impl ItemBehaviour for HoneyCombItem {
block: &Block,
_server: &Server,
) {
let world = player.world().await;
let world = player.world();
// First we try to strip the block. by getting his equivalent and applying it the axis.
let replacement_block = get_waxed_equivalent(block);

View File

@ -24,26 +24,26 @@ impl Ignition {
F: FnOnce(Arc<World>, BlockPos, u16) -> Fut,
Fut: Future<Output = ()>,
{
let world = player.world().await;
let world = player.world();
let pos = location.offset(face.to_offset());
if world.get_fluid(&location).await.name != Fluid::EMPTY.name {
return;
}
let fire_block = FireBlockBase::get_fire_type(&world, &pos).await;
let fire_block = FireBlockBase::get_fire_type(world, &pos).await;
let state_id = world.get_block_state_id(&location).await;
if let Some(new_state_id) = can_be_lit(block, state_id) {
ignite_logic(world, location, new_state_id).await;
ignite_logic(world.clone(), location, new_state_id).await;
return;
}
let state_id = FireBlock
.get_state_for_position(&world, &fire_block, &pos)
.get_state_for_position(world, &fire_block, &pos)
.await;
if FireBlockBase::can_place_at(&world, &pos).await {
ignite_logic(world, pos, state_id).await;
if FireBlockBase::can_place_at(world, &pos).await {
ignite_logic(world.clone(), pos, state_id).await;
}
}
}

View File

@ -58,7 +58,7 @@ impl ItemBehaviour for MinecartItem {
block: &Block,
_server: &Server,
) {
let world = player.world().await;
let world = player.world();
if !block.is_tagged_with_by_tag(&tag::Block::MINECRAFT_RAILS) {
return;

View File

@ -31,7 +31,7 @@ impl ItemBehaviour for ShovelItem {
block: &Block,
_server: &Server,
) {
let world = player.world().await;
let world = player.world();
// Yes, Minecraft does hardcode these
if (block == &Block::GRASS_BLOCK
|| block == &Block::DIRT

View File

@ -24,7 +24,7 @@ const POWER: f32 = 1.5;
impl ItemBehaviour for SnowBallItem {
async fn normal_use(&self, _block: &Item, player: &Player) {
let position = player.position();
let world = player.world().await;
let world = player.world();
world
.play_sound(
Sound::EntitySnowballThrow,

View File

@ -119,7 +119,7 @@ impl BedrockClient {
message, gameprofile.name.clone()
);
entity.world.read().await.broadcast_editioned(&je_packet, &be_packet).await;
entity.world.broadcast_editioned(&je_packet, &be_packet).await;
}
}
}}

View File

@ -303,7 +303,7 @@ impl JavaClient {
}
entity.on_ground.store(packet.collision & FLAG_ON_GROUND != 0, Ordering::Relaxed);
let world = &player.world().await;
let world = &player.world();
// TODO: Warn when player moves to quickly
if !self.sync_position(player, world, pos, last_pos, entity.yaw.load(), entity.pitch.load(), packet.collision & FLAG_ON_GROUND != 0).await {
@ -416,7 +416,7 @@ impl JavaClient {
let yaw = (entity.yaw.load() * 256.0 / 360.0).rem_euclid(256.0);
let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0);
// let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor();
let world = &entity.world.read().await;
let world = &entity.world;
// TODO: Warn when player moves to quickly
if !self
@ -510,7 +510,7 @@ impl JavaClient {
let pitch = (entity.pitch.load() * 256.0 / 360.0).rem_euclid(256.0);
// let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0);
let world = &entity.world.read().await;
let world = &entity.world;
let packet =
CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, rotation.ground);
world
@ -580,7 +580,7 @@ impl JavaClient {
return;
}
let world = player.world().await;
let world = player.world();
let block = world.get_block(&pick_item.pos).await;
if block.item_id == 0 {
@ -626,7 +626,7 @@ impl JavaClient {
pub async fn handle_set_command_block(&self, player: &Arc<Player>, command: SSetCommandBlock) {
// TODO: check things
let pos = command.pos;
if let Some(block_entity) = player.world().await.get_block_entity(&pos).await {
if let Some(block_entity) = player.world().get_block_entity(&pos).await {
if block_entity.resource_location() != CommandBlockEntity::ID {
log::warn!(
"Client tried to change Command block but not Command block entity found"
@ -706,7 +706,6 @@ impl JavaClient {
let (yaw, pitch) = player.rotation();
let hit_result = player
.world()
.await
.raycast(
player.eye_position(),
player
@ -724,7 +723,7 @@ impl JavaClient {
player,
InteractAction::LeftClickBlock,
&item,
player.world().await.get_block(&hit_pos).await,
player.world().get_block(&hit_pos).await,
Some(hit_pos),
)
} else {
@ -784,7 +783,7 @@ impl JavaClient {
);
let entity = &player.living_entity.entity;
let world = &entity.world.read().await;
let world = &entity.world;
if BASIC_CONFIG.allow_chat_reports {
world.broadcast_secure_player_chat(player, &chat_message, decorated_message).await;
} else {
@ -1040,7 +1039,7 @@ impl JavaClient {
if player.living_entity.health.load() > 0.0 {
return;
}
player.world().await.respawn_player(player, false).await;
player.world().respawn_player(player, false).await;
let screen_handler = player.current_screen_handler.lock().await;
let mut screen_handler = screen_handler.lock().await;
@ -1090,7 +1089,7 @@ impl JavaClient {
// TODO: set as camera entity when spectator
let world = &entity.world.read().await;
let world = &entity.world;
let player_victim = world.get_player_by_id(entity_id.0).await;
if entity_id.0 == player.entity_id() {
// This can't be triggered from a non-modded client.
@ -1164,7 +1163,7 @@ impl JavaClient {
}
let position = player_action.position;
let entity = &player.living_entity.entity;
let world = &entity.world.read().await;
let world = &entity.world;
let (block, state) = world.get_block_and_state(&position).await;
let inventory = player.inventory();
@ -1245,8 +1244,6 @@ impl JavaClient {
let entity = &player.living_entity.entity;
entity
.world
.read()
.await
.set_block_breaking(entity, player_action.position, -1)
.await;
self.update_sequence(player, player_action.sequence.0);
@ -1265,7 +1262,7 @@ impl JavaClient {
// Block break & play sound
let entity = &player.living_entity.entity;
let world = &entity.world.read().await;
let world = &entity.world;
player.mining.store(false, Ordering::Relaxed);
world.set_block_breaking(entity, location, -1).await;
@ -1398,7 +1395,7 @@ impl JavaClient {
};
let entity = &player.living_entity.entity;
let world = entity.world.read().await;
let world = &entity.world;
let block = world.get_block(&position).await;
let sneaking = player.living_entity.entity.sneaking.load(Ordering::Relaxed);
@ -1412,7 +1409,7 @@ impl JavaClient {
&cursor_pos,
&face,
&item,
&world,
world,
block,
server,
)
@ -1421,7 +1418,6 @@ impl JavaClient {
// TODO: Trigger ANY_BLOCK_USE Criteria
if matches!(result, BlockActionResult::SuccessServer) {
drop(world); // IMPORTANT: We need to drop this to prevent a deadlock
player.swing_hand(hand, true).await;
}
return Ok(());
@ -1526,7 +1522,7 @@ impl JavaClient {
}
pub async fn handle_sign_update(&self, player: &Player, sign_data: SUpdateSign) {
let world = &player.living_entity.entity.world.read().await;
let world = &player.living_entity.entity.world;
let Some(block_entity) = world.get_block_entity(&sign_data.location).await else {
return;
};
@ -1577,7 +1573,6 @@ impl JavaClient {
let hit_result = player
.world()
.await
.raycast(
player.eye_position(),
player.eye_position().add(
@ -1596,7 +1591,7 @@ impl JavaClient {
player,
InteractAction::RightClickBlock,
&item_in_hand,
player.world().await.get_block(&hit_pos).await,
player.world().get_block(&hit_pos).await,
Some(hit_pos),
)
} else {
@ -1744,9 +1739,9 @@ impl JavaClient {
// Create rotation like Vanilla
let yaw = wrap_degrees(rand::random::<f32>() * 360.0) % 360.0;
let world = player.world().await;
let world = player.world();
// Create a new mob and UUID based on the spawn egg id
let mob = from_type(entity_type, pos, &world, Uuid::new_v4()).await;
let mob = from_type(entity_type, pos, world, Uuid::new_v4()).await;
// Set the rotation
mob.get_entity().set_rotation(yaw, 0.0);
@ -1800,7 +1795,7 @@ impl JavaClient {
}
let clicked_block_pos = BlockPos(location.0);
let world = &entity.world.read().await;
let world = &entity.world;
let (clicked_block, clicked_block_state) =
world.get_block_and_state(&clicked_block_pos).await;

View File

@ -35,12 +35,9 @@ use rsa::RsaPublicKey;
use std::collections::HashSet;
use std::fs;
use std::net::IpAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32};
use std::{
future::Future,
sync::{Arc, atomic::Ordering},
time::Duration,
};
use std::{future::Future, sync::atomic::Ordering, time::Duration};
use tokio::sync::{Mutex, RwLock};
use tokio::task::JoinHandle;
use tokio_util::task::TaskTracker;
@ -325,7 +322,7 @@ impl Server {
};
let mut player = Player::new(
client,
Arc::new(client),
profile,
config.clone().unwrap_or_default(),
world.clone(),

View File

@ -33,7 +33,7 @@ pub async fn update_position(player: &Arc<Player>) {
let new_cylindrical = Cylindrical::new(new_chunk_center, view_distance);
if old_cylindrical != new_cylindrical {
match &player.client {
match player.client.as_ref() {
ClientPlatform::Java(client) => {
client
.send_packet_now(&CCenterChunk {
@ -66,7 +66,7 @@ pub async fn update_position(player: &Arc<Player>) {
// Make sure the watched section and the chunk watcher updates are async atomic. We want to
// ensure what we unload when the player disconnects is correct.
let level = &entity.world.read().await.level;
let level = &entity.world.level;
level.mark_chunks_as_newly_watched(&loading_chunks).await;
let chunks_to_clean = level.mark_chunks_as_not_watched(&unloading_chunks).await;
@ -90,11 +90,9 @@ pub async fn update_position(player: &Arc<Player>) {
}
if !loading_chunks.is_empty() {
entity.world.read().await.spawn_world_chunks(
player.clone(),
loading_chunks,
new_chunk_center,
);
entity
.world
.spawn_world_chunks(player.clone(), loading_chunks, new_chunk_center);
}
}
}

View File

@ -390,7 +390,7 @@ impl World {
let current_players = self.players.read().await;
for (_, player) in current_players.iter() {
match &player.client {
match player.client.as_ref() {
ClientPlatform::Java(client) => client.enqueue_packet(je_packet).await,
ClientPlatform::Bedrock(client) => client.send_game_packet(be_packet).await,
}
@ -1689,7 +1689,7 @@ impl World {
yaw: f32,
pitch: f32,
) {
if let ClientPlatform::Java(client) = &player.client {
if let ClientPlatform::Java(client) = player.client.as_ref() {
self.worldborder.lock().await.init_client(client).await;
}
@ -1753,7 +1753,7 @@ impl World {
pub async fn respawn_player(&self, player: &Arc<Player>, alive: bool) {
let last_pos = player.living_entity.entity.last_pos.load();
let death_dimension = player.world().await.dimension_type.resource_location();
let death_dimension = player.world().dimension_type.resource_location();
let death_location = BlockPos(Vector3::new(
last_pos.x.round() as i32,
last_pos.y.round() as i32,

View File

@ -35,7 +35,7 @@ impl LevelTime {
pub async fn send_time(&self, world: &World) {
let current_players = world.players.read().await;
for player in current_players.values() {
match &player.client {
match player.client.as_ref() {
ClientPlatform::Java(java_client) => {
java_client
.enqueue_packet(&CUpdateTime::new(self.world_age, self.time_of_day, true))