Add Living Entity struct
This commit is contained in:
parent
4076cda942
commit
9ac4bdbd50
@ -394,6 +394,7 @@ impl Player {
|
|||||||
// Also refactor out a better method to get individual advanced state ids
|
// Also refactor out a better method to get individual advanced state ids
|
||||||
|
|
||||||
let players = self
|
let players = self
|
||||||
|
.living_entity
|
||||||
.entity
|
.entity
|
||||||
.world
|
.world
|
||||||
.current_players
|
.current_players
|
||||||
|
|||||||
@ -50,7 +50,9 @@ impl Player {
|
|||||||
if let Some((id, position)) = awaiting_teleport.as_ref() {
|
if let Some((id, position)) = awaiting_teleport.as_ref() {
|
||||||
if id == &confirm_teleport.teleport_id {
|
if id == &confirm_teleport.teleport_id {
|
||||||
// we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported
|
// we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported
|
||||||
self.entity.set_pos(position.x, position.y, position.z);
|
self.living_entity
|
||||||
|
.entity
|
||||||
|
.set_pos(position.x, position.y, position.z);
|
||||||
|
|
||||||
*awaiting_teleport = None;
|
*awaiting_teleport = None;
|
||||||
} else {
|
} else {
|
||||||
@ -76,7 +78,7 @@ impl Player {
|
|||||||
self.kick(TextComponent::text("Invalid movement"));
|
self.kick(TextComponent::text("Invalid movement"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
entity.set_pos(
|
entity.set_pos(
|
||||||
Self::clamp_horizontal(position.x),
|
Self::clamp_horizontal(position.x),
|
||||||
Self::clamp_vertical(position.feet_y),
|
Self::clamp_vertical(position.feet_y),
|
||||||
@ -135,7 +137,7 @@ impl Player {
|
|||||||
self.kick(TextComponent::text("Invalid rotation"));
|
self.kick(TextComponent::text("Invalid rotation"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
|
|
||||||
entity.set_pos(
|
entity.set_pos(
|
||||||
Self::clamp_horizontal(position_rotation.x),
|
Self::clamp_horizontal(position_rotation.x),
|
||||||
@ -200,7 +202,7 @@ impl Player {
|
|||||||
self.kick(TextComponent::text("Invalid rotation"));
|
self.kick(TextComponent::text("Invalid rotation"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
entity
|
entity
|
||||||
.on_ground
|
.on_ground
|
||||||
.store(rotation.ground, std::sync::atomic::Ordering::Relaxed);
|
.store(rotation.ground, std::sync::atomic::Ordering::Relaxed);
|
||||||
@ -228,7 +230,8 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_player_ground(&self, _server: &Arc<Server>, ground: SSetPlayerGround) {
|
pub fn handle_player_ground(&self, _server: &Arc<Server>, ground: SSetPlayerGround) {
|
||||||
self.entity
|
self.living_entity
|
||||||
|
.entity
|
||||||
.on_ground
|
.on_ground
|
||||||
.store(ground.on_ground, std::sync::atomic::Ordering::Relaxed);
|
.store(ground.on_ground, std::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
@ -239,7 +242,7 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(action) = Action::from_i32(command.action.0) {
|
if let Some(action) = Action::from_i32(command.action.0) {
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
match action {
|
match action {
|
||||||
pumpkin_protocol::server::play::Action::StartSneaking => {
|
pumpkin_protocol::server::play::Action::StartSneaking => {
|
||||||
if !entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) {
|
if !entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
@ -289,7 +292,7 @@ impl Player {
|
|||||||
Hand::Off => Animation::SwingOffhand,
|
Hand::Off => Animation::SwingOffhand,
|
||||||
};
|
};
|
||||||
let id = self.entity_id();
|
let id = self.entity_id();
|
||||||
let world = &self.entity.world;
|
let world = &self.living_entity.entity.world;
|
||||||
world.broadcast_packet_expect(
|
world.broadcast_packet_expect(
|
||||||
&[self.client.token],
|
&[self.client.token],
|
||||||
&CEntityAnimation::new(id.into(), animation as u8),
|
&CEntityAnimation::new(id.into(), animation as u8),
|
||||||
@ -313,7 +316,7 @@ impl Player {
|
|||||||
// TODO: filter message & validation
|
// TODO: filter message & validation
|
||||||
let gameprofile = &self.gameprofile;
|
let gameprofile = &self.gameprofile;
|
||||||
|
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
let world = &entity.world;
|
let world = &entity.world;
|
||||||
world.broadcast_packet_all(&CPlayerChatMessage::new(
|
world.broadcast_packet_all(&CPlayerChatMessage::new(
|
||||||
gameprofile.id,
|
gameprofile.id,
|
||||||
@ -367,7 +370,7 @@ impl Player {
|
|||||||
|
|
||||||
pub async fn handle_interact(&self, _: &Arc<Server>, interact: SInteract) {
|
pub async fn handle_interact(&self, _: &Arc<Server>, interact: SInteract) {
|
||||||
let sneaking = interact.sneaking;
|
let sneaking = interact.sneaking;
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
if entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking {
|
if entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking {
|
||||||
entity.set_sneaking(sneaking).await;
|
entity.set_sneaking(sneaking).await;
|
||||||
}
|
}
|
||||||
@ -381,7 +384,7 @@ impl Player {
|
|||||||
let world = &entity.world;
|
let world = &entity.world;
|
||||||
let attacked_player = world.get_player_by_entityid(entity_id.0 as EntityId);
|
let attacked_player = world.get_player_by_entityid(entity_id.0 as EntityId);
|
||||||
if let Some(player) = attacked_player {
|
if let Some(player) = attacked_player {
|
||||||
let victem_entity = &player.entity;
|
let victem_entity = &player.living_entity.entity;
|
||||||
if config.protect_creative
|
if config.protect_creative
|
||||||
&& player.gamemode.load() == GameMode::Creative
|
&& player.gamemode.load() == GameMode::Creative
|
||||||
{
|
{
|
||||||
@ -447,7 +450,7 @@ impl Player {
|
|||||||
let location = player_action.location;
|
let location = player_action.location;
|
||||||
// Block break & block break sound
|
// Block break & block break sound
|
||||||
// TODO: currently this is always dirt replace it
|
// TODO: currently this is always dirt replace it
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
let world = &entity.world;
|
let world = &entity.world;
|
||||||
world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false));
|
world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false));
|
||||||
// AIR
|
// AIR
|
||||||
@ -471,7 +474,7 @@ impl Player {
|
|||||||
}
|
}
|
||||||
// Block break & block break sound
|
// Block break & block break sound
|
||||||
// TODO: currently this is always dirt replace it
|
// TODO: currently this is always dirt replace it
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
let world = &entity.world;
|
let world = &entity.world;
|
||||||
world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false));
|
world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false));
|
||||||
// AIR
|
// AIR
|
||||||
@ -518,7 +521,7 @@ impl Player {
|
|||||||
)
|
)
|
||||||
.expect("All item ids are in the global registry");
|
.expect("All item ids are in the global registry");
|
||||||
if let Ok(block_state_id) = BlockState::new(minecraft_id, None) {
|
if let Ok(block_state_id) = BlockState::new(minecraft_id, None) {
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
let world = &entity.world;
|
let world = &entity.world;
|
||||||
world.broadcast_packet_all(&CBlockUpdate::new(
|
world.broadcast_packet_all(&CBlockUpdate::new(
|
||||||
&location,
|
&location,
|
||||||
|
|||||||
@ -18,7 +18,7 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> {
|
|||||||
CommandTree::new(NAMES, DESCRIPTION).with_child(
|
CommandTree::new(NAMES, DESCRIPTION).with_child(
|
||||||
argument(ARG_TARGET, consume_arg_target).execute(&|sender, server, args| {
|
argument(ARG_TARGET, consume_arg_target).execute(&|sender, server, args| {
|
||||||
let target = parse_arg_player(sender, server, ARG_TARGET, args)?;
|
let target = parse_arg_player(sender, server, ARG_TARGET, args)?;
|
||||||
target.entity.kill();
|
target.living_entity.kill();
|
||||||
|
|
||||||
sender.send_message(
|
sender.send_message(
|
||||||
TextComponent::text("Player has been killed.").color_named(NamedColor::Blue),
|
TextComponent::text("Player has been killed.").color_named(NamedColor::Blue),
|
||||||
|
|||||||
46
pumpkin/src/entity/living.rs
Normal file
46
pumpkin/src/entity/living.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use crossbeam::atomic::AtomicCell;
|
||||||
|
use pumpkin_protocol::client::play::{CEntityStatus, CSetEntityMetadata, Metadata};
|
||||||
|
|
||||||
|
use super::Entity;
|
||||||
|
|
||||||
|
/// Represents a Living Entity (e.g. Player, Zombie, Enderman...)
|
||||||
|
pub struct LivingEntity {
|
||||||
|
pub entity: Entity,
|
||||||
|
/// The entity's current health level.
|
||||||
|
pub health: AtomicCell<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LivingEntity {
|
||||||
|
pub const fn new(entity: Entity) -> Self {
|
||||||
|
Self {
|
||||||
|
entity,
|
||||||
|
health: AtomicCell::new(20.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_health(&self, health: f32) {
|
||||||
|
self.health.store(health);
|
||||||
|
// tell everyone entities health changed
|
||||||
|
self.entity
|
||||||
|
.world
|
||||||
|
.broadcast_packet_all(&CSetEntityMetadata::new(
|
||||||
|
self.entity.entity_id.into(),
|
||||||
|
Metadata::new(9, 3.into(), health),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kills the Entity
|
||||||
|
///
|
||||||
|
/// This is similar to `kill` but Spawn Particles, Animation and plays death sound
|
||||||
|
pub fn kill(&self) {
|
||||||
|
// Spawns death smoke particles
|
||||||
|
self.entity
|
||||||
|
.world
|
||||||
|
.broadcast_packet_all(&CEntityStatus::new(self.entity.entity_id, 60));
|
||||||
|
// Plays the death sound and death animation
|
||||||
|
self.entity
|
||||||
|
.world
|
||||||
|
.broadcast_packet_all(&CEntityStatus::new(self.entity.entity_id, 3));
|
||||||
|
self.entity.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,14 +8,16 @@ use pumpkin_core::math::{
|
|||||||
};
|
};
|
||||||
use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, EntityId};
|
use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, EntityId};
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
client::play::{CEntityStatus, CSetEntityMetadata, Metadata},
|
client::play::{CSetEntityMetadata, Metadata},
|
||||||
VarInt,
|
VarInt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
|
||||||
|
pub mod living;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
|
|
||||||
|
/// Represents a not living Entity (e.g. Item, Egg, Snowball...)
|
||||||
pub struct Entity {
|
pub struct Entity {
|
||||||
/// A unique identifier for the entity
|
/// A unique identifier for the entity
|
||||||
pub entity_id: EntityId,
|
pub entity_id: EntityId,
|
||||||
@ -24,7 +26,6 @@ pub struct Entity {
|
|||||||
/// The world in which the entity exists.
|
/// The world in which the entity exists.
|
||||||
pub world: Arc<World>,
|
pub world: Arc<World>,
|
||||||
/// The entity's current health level.
|
/// The entity's current health level.
|
||||||
pub health: AtomicCell<f32>,
|
|
||||||
|
|
||||||
/// The entity's current position in the world
|
/// The entity's current position in the world
|
||||||
pub pos: AtomicCell<Vector3<f64>>,
|
pub pos: AtomicCell<Vector3<f64>>,
|
||||||
@ -75,7 +76,6 @@ impl Entity {
|
|||||||
sneaking: AtomicBool::new(false),
|
sneaking: AtomicBool::new(false),
|
||||||
world,
|
world,
|
||||||
// TODO: Load this from previous instance
|
// TODO: Load this from previous instance
|
||||||
health: AtomicCell::new(20.0),
|
|
||||||
sprinting: AtomicBool::new(false),
|
sprinting: AtomicBool::new(false),
|
||||||
fall_flying: AtomicBool::new(false),
|
fall_flying: AtomicBool::new(false),
|
||||||
yaw: AtomicCell::new(0.0),
|
yaw: AtomicCell::new(0.0),
|
||||||
@ -121,19 +121,6 @@ impl Entity {
|
|||||||
self.pitch.store(pitch);
|
self.pitch.store(pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kills the Entity
|
|
||||||
///
|
|
||||||
/// This is similar to `kill` but Spawn Particles, Animation and plays death sound
|
|
||||||
pub fn kill(&self) {
|
|
||||||
// Spawns death smoke particles
|
|
||||||
self.world
|
|
||||||
.broadcast_packet_all(&CEntityStatus::new(self.entity_id, 60));
|
|
||||||
// Plays the death sound and death animation
|
|
||||||
self.world
|
|
||||||
.broadcast_packet_all(&CEntityStatus::new(self.entity_id, 3));
|
|
||||||
self.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes the Entity from their current World
|
/// Removes the Entity from their current World
|
||||||
pub fn remove(&self) {
|
pub fn remove(&self) {
|
||||||
self.world.remove_entity(self);
|
self.world.remove_entity(self);
|
||||||
|
|||||||
@ -38,15 +38,14 @@ use crate::{
|
|||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Entity;
|
use super::{living::LivingEntity, Entity};
|
||||||
|
|
||||||
/// Represents a Minecraft player entity.
|
/// Represents a Minecraft player entity.
|
||||||
///
|
///
|
||||||
/// A `Player` is a special type of entity that represents a human player connected to the server.
|
/// A `Player` is a special type of entity that represents a human player connected to the server.
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
/// The underlying entity object that represents the player.
|
/// The underlying living entity object that represents the player.
|
||||||
pub entity: Entity,
|
pub living_entity: LivingEntity,
|
||||||
|
|
||||||
/// The player's game profile information, including their username and UUID.
|
/// The player's game profile information, including their username and UUID.
|
||||||
pub gameprofile: GameProfile,
|
pub gameprofile: GameProfile,
|
||||||
/// The client connection associated with the player.
|
/// The client connection associated with the player.
|
||||||
@ -110,7 +109,12 @@ impl Player {
|
|||||||
);
|
);
|
||||||
let config = client.config.lock().clone().unwrap_or_default();
|
let config = client.config.lock().clone().unwrap_or_default();
|
||||||
Self {
|
Self {
|
||||||
entity: Entity::new(entity_id, world, EntityType::Player, 1.62),
|
living_entity: LivingEntity::new(Entity::new(
|
||||||
|
entity_id,
|
||||||
|
world,
|
||||||
|
EntityType::Player,
|
||||||
|
1.62,
|
||||||
|
)),
|
||||||
config: Mutex::new(config),
|
config: Mutex::new(config),
|
||||||
gameprofile,
|
gameprofile,
|
||||||
client,
|
client,
|
||||||
@ -132,11 +136,11 @@ impl Player {
|
|||||||
|
|
||||||
/// Removes the Player out of the current World
|
/// Removes the Player out of the current World
|
||||||
pub async fn remove(&self) {
|
pub async fn remove(&self) {
|
||||||
self.entity.world.remove_player(self);
|
self.living_entity.entity.world.remove_player(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn entity_id(&self) -> EntityId {
|
pub const fn entity_id(&self) -> EntityId {
|
||||||
self.entity.entity_id
|
self.living_entity.entity.entity_id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the current abilities the Player has
|
/// Updates the current abilities the Player has
|
||||||
@ -174,7 +178,7 @@ impl Player {
|
|||||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
let teleport_id = i + 1;
|
let teleport_id = i + 1;
|
||||||
let entity = &self.entity;
|
let entity = &self.living_entity.entity;
|
||||||
entity.set_pos(x, y, z);
|
entity.set_pos(x, y, z);
|
||||||
entity.set_rotation(yaw, pitch);
|
entity.set_rotation(yaw, pitch);
|
||||||
*self.awaiting_teleport.lock() = Some((teleport_id.into(), Vector3::new(x, y, z)));
|
*self.awaiting_teleport.lock() = Some((teleport_id.into(), Vector3::new(x, y, z)));
|
||||||
@ -200,8 +204,8 @@ impl Player {
|
|||||||
pub fn can_interact_with_block_at(&self, pos: &WorldPosition, additional_range: f64) -> bool {
|
pub fn can_interact_with_block_at(&self, pos: &WorldPosition, additional_range: f64) -> bool {
|
||||||
let d = self.block_interaction_range() + additional_range;
|
let d = self.block_interaction_range() + additional_range;
|
||||||
let box_pos = BoundingBox::from_block(pos);
|
let box_pos = BoundingBox::from_block(pos);
|
||||||
let entity_pos = self.entity.pos.load();
|
let entity_pos = self.living_entity.entity.pos.load();
|
||||||
let standing_eye_height = self.entity.standing_eye_height;
|
let standing_eye_height = self.living_entity.entity.standing_eye_height;
|
||||||
box_pos.squared_magnitude(Vector3 {
|
box_pos.squared_magnitude(Vector3 {
|
||||||
x: entity_pos.x,
|
x: entity_pos.x,
|
||||||
y: entity_pos.y + standing_eye_height as f64,
|
y: entity_pos.y + standing_eye_height as f64,
|
||||||
@ -228,8 +232,8 @@ impl Player {
|
|||||||
self.client.close()
|
self.client.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_health(&self, health: f32, food: i32, food_saturation: f32) {
|
pub fn set_health(&self, health: f32, food: i32, food_saturation: f32) {
|
||||||
self.entity.health.store(health);
|
self.living_entity.set_health(health);
|
||||||
self.food.store(food, std::sync::atomic::Ordering::Relaxed);
|
self.food.store(food, std::sync::atomic::Ordering::Relaxed);
|
||||||
self.food_saturation.store(food_saturation);
|
self.food_saturation.store(food_saturation);
|
||||||
self.client
|
self.client
|
||||||
@ -246,7 +250,8 @@ impl Player {
|
|||||||
self.gamemode.store(gamemode);
|
self.gamemode.store(gamemode);
|
||||||
// So a little story time. I actually made an abilties_from_gamemode function. I looked at vanilla and they always send the abilties from the gamemode. But the funny thing actually is. That the client
|
// So a little story time. I actually made an abilties_from_gamemode function. I looked at vanilla and they always send the abilties from the gamemode. But the funny thing actually is. That the client
|
||||||
// does actually use the same method and set the abilties when receiving the CGameEvent gamemode packet. Just Mojang nonsense
|
// does actually use the same method and set the abilties when receiving the CGameEvent gamemode packet. Just Mojang nonsense
|
||||||
self.entity
|
self.living_entity
|
||||||
|
.entity
|
||||||
.world
|
.world
|
||||||
.broadcast_packet_all(&CPlayerInfoUpdate::new(
|
.broadcast_packet_all(&CPlayerInfoUpdate::new(
|
||||||
0x04,
|
0x04,
|
||||||
|
|||||||
@ -186,7 +186,7 @@ impl World {
|
|||||||
// spawn players for our client
|
// spawn players for our client
|
||||||
let token = player.client.token;
|
let token = player.client.token;
|
||||||
for (_, existing_player) in self.current_players.lock().iter().filter(|c| c.0 != &token) {
|
for (_, existing_player) in self.current_players.lock().iter().filter(|c| c.0 != &token) {
|
||||||
let entity = &existing_player.entity;
|
let entity = &existing_player.living_entity.entity;
|
||||||
let pos = entity.pos.load();
|
let pos = entity.pos.load();
|
||||||
let gameprofile = &existing_player.gameprofile;
|
let gameprofile = &existing_player.gameprofile;
|
||||||
player.client.send_packet(&CSpawnEntity::new(
|
player.client.send_packet(&CSpawnEntity::new(
|
||||||
@ -293,7 +293,7 @@ impl World {
|
|||||||
&[player.client.token],
|
&[player.client.token],
|
||||||
&CRemovePlayerInfo::new(1.into(), &[uuid]),
|
&CRemovePlayerInfo::new(1.into(), &[uuid]),
|
||||||
);
|
);
|
||||||
self.remove_entity(&player.entity);
|
self.remove_entity(&player.living_entity.entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_entity(&self, entity: &Entity) {
|
pub fn remove_entity(&self, entity: &Entity) {
|
||||||
|
|||||||
@ -20,10 +20,10 @@ fn get_view_distance(player: &Player) -> i8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_join(world: &World, player: Arc<Player>) {
|
pub async fn player_join(world: &World, player: Arc<Player>) {
|
||||||
let new_watched = chunk_section_from_pos(&player.entity.block_pos.load());
|
let new_watched = chunk_section_from_pos(&player.living_entity.entity.block_pos.load());
|
||||||
player.watched_section.store(new_watched);
|
player.watched_section.store(new_watched);
|
||||||
let watched_section = new_watched;
|
let watched_section = new_watched;
|
||||||
let chunk_pos = player.entity.chunk_pos.load();
|
let chunk_pos = player.living_entity.entity.chunk_pos.load();
|
||||||
player.client.send_packet(&CCenterChunk {
|
player.client.send_packet(&CCenterChunk {
|
||||||
chunk_x: chunk_pos.x.into(),
|
chunk_x: chunk_pos.x.into(),
|
||||||
chunk_z: chunk_pos.z.into(),
|
chunk_z: chunk_pos.z.into(),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user