1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00
This commit is contained in:
Ubuntu 2022-11-18 16:14:46 +00:00
commit 8397fc8445
23 changed files with 295 additions and 254 deletions

View file

@ -1,13 +1,32 @@
use std::time::{SystemTime, UNIX_EPOCH};
//! Implementations of chat-related features.
use crate::Client;
use azalea_chat::Component;
use azalea_crypto::MessageSignature;
use azalea_protocol::packets::game::{
clientbound_player_chat_packet::LastSeenMessagesUpdate,
clientbound_player_chat_packet::{ClientboundPlayerChatPacket, LastSeenMessagesUpdate},
clientbound_system_chat_packet::ClientboundSystemChatPacket,
serverbound_chat_command_packet::ServerboundChatCommandPacket,
serverbound_chat_packet::ServerboundChatPacket,
};
use std::time::{SystemTime, UNIX_EPOCH};
use crate::Client;
/// A chat packet, either a system message or a chat message.
#[derive(Debug, Clone)]
pub enum ChatPacket {
System(ClientboundSystemChatPacket),
Player(Box<ClientboundPlayerChatPacket>),
}
impl ChatPacket {
/// Get the message shown in chat for this packet.
pub fn message(&self) -> Component {
match self {
ChatPacket::System(p) => p.content.clone(),
ChatPacket::Player(p) => p.message(false),
}
}
}
impl Client {
/// Sends chat message to the server. This only sends the chat packet and

View file

@ -1,13 +1,11 @@
pub use crate::chat::ChatPacket;
use crate::{movement::WalkDirection, plugins::Plugins, Account, Player};
use azalea_auth::game_profile::GameProfile;
use azalea_chat::Component;
use azalea_core::{ChunkPos, ResourceLocation, Vec3};
use azalea_protocol::{
connect::{Connection, ConnectionError, ReadConnection, WriteConnection},
packets::{
game::{
clientbound_player_chat_packet::ClientboundPlayerChatPacket,
clientbound_system_chat_packet::ClientboundSystemChatPacket,
serverbound_accept_teleportation_packet::ServerboundAcceptTeleportationPacket,
serverbound_client_information_packet::ServerboundClientInformationPacket,
serverbound_custom_payload_packet::ServerboundCustomPayloadPacket,
@ -32,7 +30,7 @@ use azalea_protocol::{
};
use azalea_world::{
entity::{metadata, Entity, EntityData, EntityMetadata},
Dimension,
World,
};
use log::{debug, error, info, warn};
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
@ -66,23 +64,6 @@ pub enum Event {
Packet(Box<ClientboundGamePacket>),
}
/// A chat packet, either a system message or a chat message.
#[derive(Debug, Clone)]
pub enum ChatPacket {
System(ClientboundSystemChatPacket),
Player(Box<ClientboundPlayerChatPacket>),
}
impl ChatPacket {
/// Get the message shown in chat for this packet.
pub fn message(&self) -> Component {
match self {
ChatPacket::System(p) => p.content.clone(),
ChatPacket::Player(p) => p.message(false),
}
}
}
/// A player that you control that is currently in a Minecraft server.
#[derive(Clone)]
pub struct Client {
@ -90,9 +71,9 @@ pub struct Client {
pub read_conn: Arc<tokio::sync::Mutex<ReadConnection<ClientboundGamePacket>>>,
pub write_conn: Arc<tokio::sync::Mutex<WriteConnection<ServerboundGamePacket>>>,
pub player: Arc<RwLock<Player>>,
pub dimension: Arc<RwLock<Dimension>>,
/// Whether there's multiple clients sharing the same dimension.
pub dimension_shared: Arc<RwLock<bool>>,
pub world: Arc<RwLock<World>>,
/// Whether there's multiple clients sharing the same world.
pub world_shared: Arc<RwLock<bool>>,
pub physics_state: Arc<Mutex<PhysicsState>>,
pub client_information: Arc<RwLock<ClientInformation>>,
/// Plugins are a way for other crates to add custom functionality to the
@ -146,13 +127,13 @@ pub enum HandleError {
}
impl Client {
/// Create a new client from the given game profile, conn in the game
/// state, and dimension. You should only use this if you want to change
/// these fields from the defaults, otherwise use `Client::join`.
/// Create a new client from the given GameProfile, Connection, and World.
/// You should only use this if you want to change these fields from the
/// defaults, otherwise use [`Client::join`].
pub fn new(
game_profile: GameProfile,
conn: Connection<ClientboundGamePacket, ServerboundGamePacket>,
dimension: Option<Arc<RwLock<Dimension>>>,
world: Option<Arc<RwLock<World>>>,
) -> Self {
let (read_conn, write_conn) = conn.into_split();
let (read_conn, write_conn) = (
@ -165,8 +146,8 @@ impl Client {
read_conn,
write_conn,
player: Arc::new(RwLock::new(Player::default())),
dimension_shared: Arc::new(RwLock::new(dimension.is_some())),
dimension: dimension.unwrap_or_else(|| Arc::new(RwLock::new(Dimension::default()))),
world_shared: Arc::new(RwLock::new(world.is_some())),
world: world.unwrap_or_else(|| Arc::new(RwLock::new(World::default()))),
physics_state: Arc::new(Mutex::new(PhysicsState::default())),
client_information: Arc::new(
RwLock::new(ServerboundClientInformationPacket::default()),
@ -450,27 +431,27 @@ impl Client {
.as_int()
.expect("min_y tag is not an int");
let mut dimension_lock = client.dimension.write();
let mut world_lock = client.world.write();
if *client.dimension_shared.read() {
if *client.world_shared.read() {
// we can't clear the dimension if it's shared, so just
// make sure the height and stuff is correct
if dimension_lock.height() != height {
if world_lock.height() != height {
error!(
"Shared dimension height mismatch: {} != {}",
dimension_lock.height(),
world_lock.height(),
height
);
}
if dimension_lock.min_y() != min_y {
if world_lock.min_y() != min_y {
error!(
"Shared dimension min_y mismatch: {} != {}",
dimension_lock.min_y(),
"Shared world min_y mismatch: {} != {}",
world_lock.min_y(),
min_y
);
}
} else {
*dimension_lock = Dimension::new(
*world_lock = World::new(
client.client_information.read().view_distance.into(),
height,
min_y,
@ -482,7 +463,7 @@ impl Client {
Vec3::default(),
EntityMetadata::Player(metadata::Player::default()),
);
dimension_lock.add_entity(p.player_id, entity);
world_lock.add_entity(p.player_id, entity);
let mut player_lock = client.player.write();
@ -555,9 +536,9 @@ impl Client {
player_lock.entity_id
};
let mut dimension_lock = client.dimension.write();
let mut world_lock = client.world.write();
let mut player_entity = dimension_lock
let mut player_entity = world_lock
.entity_mut(player_entity_id)
.expect("Player entity doesn't exist");
@ -611,7 +592,7 @@ impl Client {
y: new_pos_y,
z: new_pos_z,
};
dimension_lock
world_lock
.set_entity_pos(player_entity_id, new_pos)
.expect("The player entity should always exist");
@ -642,7 +623,7 @@ impl Client {
ClientboundGamePacket::SetChunkCacheCenter(p) => {
debug!("Got chunk cache center packet {:?}", p);
client
.dimension
.world
.write()
.update_view_center(&ChunkPos::new(p.x, p.z));
}
@ -652,7 +633,7 @@ impl Client {
// let chunk = Chunk::read_with_world_height(&mut p.chunk_data);
// debug("chunk {:?}")
if let Err(e) = client
.dimension
.world
.write()
.replace_with_packet_data(&pos, &mut Cursor::new(&p.chunk_data.data))
{
@ -665,12 +646,12 @@ impl Client {
ClientboundGamePacket::AddEntity(p) => {
debug!("Got add entity packet {:?}", p);
let entity = EntityData::from(p);
client.dimension.write().add_entity(p.id, entity);
client.world.write().add_entity(p.id, entity);
}
ClientboundGamePacket::SetEntityData(p) => {
debug!("Got set entity data packet {:?}", p);
let mut dimension = client.dimension.write();
if let Some(mut entity) = dimension.entity_mut(p.id) {
let mut world = client.world.write();
if let Some(mut entity) = world.entity_mut(p.id) {
entity.apply_metadata(&p.packed_items.0);
} else {
warn!("Server sent an entity data packet for an entity id ({}) that we don't know about", p.id);
@ -688,7 +669,7 @@ impl Client {
ClientboundGamePacket::AddPlayer(p) => {
debug!("Got add player packet {:?}", p);
let entity = EntityData::from(p);
client.dimension.write().add_entity(p.id, entity);
client.world.write().add_entity(p.id, entity);
}
ClientboundGamePacket::InitializeBorder(p) => {
debug!("Got initialize border packet {:?}", p);
@ -709,9 +690,9 @@ impl Client {
debug!("Got set experience packet {:?}", p);
}
ClientboundGamePacket::TeleportEntity(p) => {
let mut dimension_lock = client.dimension.write();
let mut world_lock = client.world.write();
dimension_lock
world_lock
.set_entity_pos(
p.id,
Vec3 {
@ -729,16 +710,16 @@ impl Client {
// debug!("Got rotate head packet {:?}", p);
}
ClientboundGamePacket::MoveEntityPos(p) => {
let mut dimension_lock = client.dimension.write();
let mut world_lock = client.world.write();
dimension_lock
world_lock
.move_entity_with_delta(p.entity_id, &p.delta)
.map_err(|e| HandleError::Other(e.into()))?;
}
ClientboundGamePacket::MoveEntityPosRot(p) => {
let mut dimension_lock = client.dimension.write();
let mut world_lock = client.world.write();
dimension_lock
world_lock
.move_entity_with_delta(p.entity_id, &p.delta)
.map_err(|e| HandleError::Other(e.into()))?;
}
@ -771,17 +752,17 @@ impl Client {
}
ClientboundGamePacket::BlockUpdate(p) => {
debug!("Got block update packet {:?}", p);
let mut dimension = client.dimension.write();
dimension.set_block_state(&p.pos, p.block_state);
let mut world = client.world.write();
world.set_block_state(&p.pos, p.block_state);
}
ClientboundGamePacket::Animate(p) => {
debug!("Got animate packet {:?}", p);
}
ClientboundGamePacket::SectionBlocksUpdate(p) => {
debug!("Got section blocks update packet {:?}", p);
let mut dimension = client.dimension.write();
let mut world = client.world.write();
for state in &p.states {
dimension.set_block_state(&(p.section_pos + state.pos.clone()), state.state);
world.set_block_state(&(p.section_pos + state.pos.clone()), state.state);
}
}
ClientboundGamePacket::GameEvent(p) => {
@ -877,16 +858,16 @@ impl Client {
async fn game_tick(client: &mut Client, tx: &UnboundedSender<Event>) {
// return if there's no chunk at the player's position
{
let dimension_lock = client.dimension.write();
let world_lock = client.world.write();
let player_lock = client.player.write();
let player_entity = player_lock.entity(&dimension_lock);
let player_entity = player_lock.entity(&world_lock);
let player_entity = if let Some(player_entity) = player_entity {
player_entity
} else {
return;
};
let player_chunk_pos: ChunkPos = player_entity.pos().into();
if dimension_lock[&player_chunk_pos].is_none() {
if world_lock[&player_chunk_pos].is_none() {
return;
}
}
@ -904,43 +885,43 @@ impl Client {
}
/// Returns the entity associated to the player.
pub fn entity_mut(&self) -> Entity<RwLockWriteGuard<Dimension>> {
pub fn entity_mut(&self) -> Entity<RwLockWriteGuard<World>> {
let entity_id = {
let player_lock = self.player.write();
player_lock.entity_id
};
let mut dimension = self.dimension.write();
let mut world = self.world.write();
let entity_data = dimension
let entity_data = world
.entity_storage
.get_mut_by_id(entity_id)
.expect("Player entity should exist");
let entity_ptr = unsafe { entity_data.as_ptr() };
Entity::new(dimension, entity_id, entity_ptr)
Entity::new(world, entity_id, entity_ptr)
}
/// Returns the entity associated to the player.
pub fn entity(&self) -> Entity<RwLockReadGuard<Dimension>> {
pub fn entity(&self) -> Entity<RwLockReadGuard<World>> {
let entity_id = {
let player_lock = self.player.read();
player_lock.entity_id
};
let dimension = self.dimension.read();
let world = self.world.read();
let entity_data = dimension
let entity_data = world
.entity_storage
.get_by_id(entity_id)
.expect("Player entity should be in the given dimension");
.expect("Player entity should be in the given world");
let entity_ptr = unsafe { entity_data.as_const_ptr() };
Entity::new(dimension, entity_id, entity_ptr)
Entity::new(world, entity_id, entity_ptr)
}
/// Returns whether we have a received the login packet yet.
pub fn logged_in(&self) -> bool {
let dimension = self.dimension.read();
let world = self.world.read();
let player = self.player.write();
player.entity(&dimension).is_some()
player.entity(&world).is_some()
}
/// Tell the server we changed our game options (i.e. render distance, main hand).
@ -968,6 +949,12 @@ impl Client {
Ok(())
}
/// Get your player entity's metadata. You can use this to get your health,
/// xp score, and other useful information.
pub fn metadata(&self) -> metadata::Player {
self.entity().metadata.clone().into_player().unwrap()
}
}
impl<T> From<std::sync::PoisonError<T>> for HandleError {

View file

@ -154,19 +154,19 @@ impl Client {
// Set our current position to the provided Vec3, potentially clipping through blocks.
pub async fn set_pos(&mut self, new_pos: Vec3) -> Result<(), MovePlayerError> {
let player_lock = self.player.write();
let mut dimension_lock = self.dimension.write();
let mut world_lock = self.world.write();
dimension_lock.set_entity_pos(player_lock.entity_id, new_pos)?;
world_lock.set_entity_pos(player_lock.entity_id, new_pos)?;
Ok(())
}
pub async fn move_entity(&mut self, movement: &Vec3) -> Result<(), MovePlayerError> {
let mut dimension_lock = self.dimension.write();
let mut world_lock = self.world.write();
let player = self.player.write();
let mut entity = player
.entity_mut(&mut dimension_lock)
.entity_mut(&mut world_lock)
.ok_or(MovePlayerError::PlayerNotInWorld)?;
log::trace!(
"move entity bounding box: {} {:?}",

View file

@ -1,13 +1,13 @@
use azalea_world::entity::Entity;
use azalea_world::Dimension;
use azalea_world::World;
use uuid::Uuid;
/// Something that has a dimension associated to it. Usually, this is a `Client`.
pub trait DimensionHaver {
fn dimension(&self) -> &Dimension;
/// Something that has a world associated to it. Usually, this is a `Client`.
pub trait WorldHaver {
fn world(&self) -> &World;
}
/// A player in the dimension or tab list.
/// A player in the world or tab list.
#[derive(Default, Debug)]
pub struct Player {
/// The player's uuid.
@ -18,13 +18,13 @@ pub struct Player {
impl Player {
/// Get a reference to the entity of the player in the world.
pub fn entity<'d>(&'d self, dimension: &'d Dimension) -> Option<Entity<&Dimension>> {
dimension.entity(self.entity_id)
pub fn entity<'d>(&'d self, world: &'d World) -> Option<Entity<&World>> {
world.entity(self.entity_id)
}
/// Get a mutable reference to the entity of the player in the world.
pub fn entity_mut<'d>(&'d self, dimension: &'d mut Dimension) -> Option<Entity> {
dimension.entity_mut(self.entity_id)
pub fn entity_mut<'d>(&'d self, world: &'d mut World) -> Option<Entity> {
world.entity_mut(self.entity_id)
}
pub fn set_uuid(&mut self, uuid: Uuid) {

View file

@ -7,19 +7,21 @@ use std::{
hash::BuildHasherDefault,
};
type U64Hasher = BuildHasherDefault<NoHashHasher<u64>>;
// kind of based on https://docs.rs/http/latest/src/http/extensions.rs.html
/// A map of plugin ids to Plugin trait objects. The client stores this so we
/// can keep the state for our plugins.
///
/// If you're using azalea, you should generate this from the `plugins!` macro.
#[derive(Clone)]
#[derive(Clone, Default)]
pub struct Plugins {
map: Option<HashMap<TypeId, Box<dyn Plugin>, BuildHasherDefault<NoHashHasher<u64>>>>,
map: Option<HashMap<TypeId, Box<dyn Plugin>, U64Hasher>>,
}
impl Plugins {
pub fn new() -> Self {
Self { map: None }
Self::default()
}
pub fn add<T: Plugin>(&mut self, plugin: T) {
@ -46,7 +48,7 @@ impl IntoIterator for Plugins {
fn into_iter(self) -> Self::IntoIter {
self.map
.map(|map| map.into_iter().map(|(_, v)| v).collect::<Vec<_>>())
.map(|map| map.into_values().collect::<Vec<_>>())
.unwrap_or_default()
.into_iter()
}

View file

@ -197,12 +197,12 @@ impl Add<ChunkSectionBlockPos> for ChunkSectionPos {
}
}
/// A block pos with an attached dimension
/// A block pos with an attached world
#[derive(Debug, Clone)]
pub struct GlobalPos {
pub pos: BlockPos,
// this is actually a ResourceKey in Minecraft, but i don't think it matters?
pub dimension: ResourceLocation,
pub world: ResourceLocation,
}
impl From<&BlockPos> for ChunkPos {
@ -297,7 +297,7 @@ impl McBufReadable for BlockPos {
impl McBufReadable for GlobalPos {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
Ok(GlobalPos {
dimension: ResourceLocation::read_from(buf)?,
world: ResourceLocation::read_from(buf)?,
pos: BlockPos::read_from(buf)?,
})
}
@ -326,7 +326,7 @@ impl McBufWritable for BlockPos {
impl McBufWritable for GlobalPos {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
ResourceLocation::write_into(&self.dimension, buf)?;
ResourceLocation::write_into(&self.world, buf)?;
BlockPos::write_into(&self.pos, buf)?;
Ok(())

View file

@ -1,17 +1,17 @@
mod blocks;
mod dimension_collisions;
mod discrete_voxel_shape;
mod mergers;
mod shape;
mod world_collisions;
use azalea_core::{Axis, Vec3, AABB, EPSILON};
use azalea_world::entity::{Entity, EntityData};
use azalea_world::{Dimension, MoveEntityError};
use azalea_world::{MoveEntityError, World};
pub use blocks::BlockWithShape;
use dimension_collisions::CollisionGetter;
pub use discrete_voxel_shape::*;
pub use shape::*;
use std::ops::DerefMut;
use world_collisions::CollisionGetter;
pub enum MoverType {
Own,
@ -33,7 +33,7 @@ pub trait MovableEntity {
) -> Result<(), MoveEntityError>;
}
impl HasCollision for Dimension {
impl HasCollision for World {
// private Vec3 collide(Vec3 var1) {
// AABB var2 = this.getBoundingBox();
// List var3 = this.level.getEntityCollisions(this, var2.expandTowards(var1));
@ -62,7 +62,7 @@ impl HasCollision for Dimension {
fn collide(&self, movement: &Vec3, entity: &EntityData) -> Vec3 {
let entity_bounding_box = entity.bounding_box;
// TODO: get_entity_collisions
// let entity_collisions = dimension.get_entity_collisions(self, entity_bounding_box.expand_towards(movement));
// let entity_collisions = world.get_entity_collisions(self, entity_bounding_box.expand_towards(movement));
let entity_collisions = Vec::new();
if movement.length_sqr() == 0.0 {
*movement
@ -82,7 +82,7 @@ impl HasCollision for Dimension {
}
}
impl<D: DerefMut<Target = Dimension>> MovableEntity for Entity<'_, D> {
impl<D: DerefMut<Target = World>> MovableEntity for Entity<'_, D> {
/// Move an entity by a given delta, checking for collisions.
fn move_colliding(
&mut self,
@ -110,7 +110,7 @@ impl<D: DerefMut<Target = Dimension>> MovableEntity for Entity<'_, D> {
// movement = this.maybeBackOffFromEdge(movement, moverType);
let collide_result = { self.dimension.collide(movement, self) };
let collide_result = { self.world.collide(movement, self) };
let move_distance = collide_result.length_sqr();
@ -126,7 +126,7 @@ impl<D: DerefMut<Target = Dimension>> MovableEntity for Entity<'_, D> {
}
};
self.dimension.set_entity_pos(self.id, new_pos)?;
self.world.set_entity_pos(self.id, new_pos)?;
}
let x_collision = movement.x != collide_result.x;
@ -140,7 +140,7 @@ impl<D: DerefMut<Target = Dimension>> MovableEntity for Entity<'_, D> {
let _block_pos_below = self.on_pos_legacy();
// let _block_state_below = self
// .dimension
// .world
// .get_block_state(&block_pos_below)
// .expect("Couldn't get block state below");
@ -197,7 +197,7 @@ fn collide_bounding_box(
entity: Option<&EntityData>,
movement: &Vec3,
entity_bounding_box: &AABB,
dimension: &Dimension,
world: &World,
entity_collisions: Vec<VoxelShape>,
) -> Vec3 {
let mut collision_boxes: Vec<VoxelShape> = Vec::with_capacity(entity_collisions.len() + 1);
@ -209,7 +209,7 @@ fn collide_bounding_box(
// TODO: world border
let block_collisions =
dimension.get_block_collisions(entity, entity_bounding_box.expand_towards(movement));
world.get_block_collisions(entity, entity_bounding_box.expand_towards(movement));
let block_collisions = block_collisions.collect::<Vec<_>>();
collision_boxes.extend(block_collisions);
collide_with_shapes(movement, *entity_bounding_box, &collision_boxes)

View file

@ -2,7 +2,7 @@ use crate::collision::{BlockWithShape, VoxelShape, AABB};
use azalea_block::BlockState;
use azalea_core::{ChunkPos, ChunkSectionPos, Cursor3d, CursorIterationType, EPSILON};
use azalea_world::entity::EntityData;
use azalea_world::{Chunk, Dimension};
use azalea_world::{Chunk, World};
use parking_lot::Mutex;
use std::sync::Arc;
@ -16,7 +16,7 @@ pub trait CollisionGetter {
) -> BlockCollisions<'a>;
}
impl CollisionGetter for Dimension {
impl CollisionGetter for World {
fn get_block_collisions<'a>(
&'a self,
entity: Option<&EntityData>,
@ -27,7 +27,7 @@ impl CollisionGetter for Dimension {
}
pub struct BlockCollisions<'a> {
pub dimension: &'a Dimension,
pub world: &'a World,
// context: CollisionContext,
pub aabb: AABB,
pub entity_shape: VoxelShape,
@ -37,7 +37,7 @@ pub struct BlockCollisions<'a> {
impl<'a> BlockCollisions<'a> {
// TODO: the entity is stored in the context
pub fn new(dimension: &'a Dimension, _entity: Option<&EntityData>, aabb: AABB) -> Self {
pub fn new(world: &'a World, _entity: Option<&EntityData>, aabb: AABB) -> Self {
let origin_x = (aabb.min_x - EPSILON) as i32 - 1;
let origin_y = (aabb.min_y - EPSILON) as i32 - 1;
let origin_z = (aabb.min_z - EPSILON) as i32 - 1;
@ -49,7 +49,7 @@ impl<'a> BlockCollisions<'a> {
let cursor = Cursor3d::new(origin_x, origin_y, origin_z, end_x, end_y, end_z);
Self {
dimension,
world,
aabb,
entity_shape: VoxelShape::from(aabb),
cursor,
@ -75,7 +75,7 @@ impl<'a> BlockCollisions<'a> {
// return var7;
// }
self.dimension[&chunk_pos].as_ref()
self.world[&chunk_pos].as_ref()
}
}
@ -97,7 +97,7 @@ impl<'a> Iterator for BlockCollisions<'a> {
let pos = item.pos;
let block_state: BlockState = chunk
.lock()
.get(&(&pos).into(), self.dimension.min_y())
.get(&(&pos).into(), self.world.min_y())
.unwrap_or(BlockState::Air);
// TODO: continue if self.only_suffocating_blocks and the block is not suffocating

View file

@ -8,7 +8,7 @@ use azalea_block::{Block, BlockState};
use azalea_core::{BlockPos, Vec3};
use azalea_world::{
entity::{Entity, EntityData},
Dimension,
World,
};
use collision::{MovableEntity, MoverType};
@ -19,7 +19,7 @@ pub trait HasPhysics {
fn jump_from_ground(&mut self);
}
impl<D: DerefMut<Target = Dimension>> HasPhysics for Entity<'_, D> {
impl<D: DerefMut<Target = World>> HasPhysics for Entity<'_, D> {
/// Move the entity with the given acceleration while handling friction,
/// gravity, collisions, and some other stuff.
fn travel(&mut self, acceleration: &Vec3) {
@ -40,7 +40,7 @@ impl<D: DerefMut<Target = Dimension>> HasPhysics for Entity<'_, D> {
let block_pos_below = get_block_pos_below_that_affects_movement(self);
let block_state_below = self
.dimension
.world
.get_block_state(&block_pos_below)
.unwrap_or(BlockState::Air);
let block_below: Box<dyn Block> = block_state_below.into();
@ -122,12 +122,11 @@ impl<D: DerefMut<Target = Dimension>> HasPhysics for Entity<'_, D> {
};
if self.metadata.sprinting {
let y_rot = self.y_rot * 0.017453292;
self.delta = self.delta
+ Vec3 {
x: (-f32::sin(y_rot) * 0.2) as f64,
y: 0.,
z: (f32::cos(y_rot) * 0.2) as f64,
};
self.delta += Vec3 {
x: (-f32::sin(y_rot) * 0.2) as f64,
y: 0.,
z: (f32::cos(y_rot) * 0.2) as f64,
};
}
self.has_impulse = true;
@ -143,7 +142,7 @@ fn get_block_pos_below_that_affects_movement(entity: &EntityData) -> BlockPos {
)
}
fn handle_relative_friction_and_calculate_movement<D: DerefMut<Target = Dimension>>(
fn handle_relative_friction_and_calculate_movement<D: DerefMut<Target = World>>(
entity: &mut Entity<D>,
acceleration: &Vec3,
block_friction: f32,
@ -182,10 +181,10 @@ fn get_friction_influenced_speed(entity: &EntityData, friction: f32) -> f32 {
/// Returns the what the entity's jump should be multiplied by based on the
/// block they're standing on.
fn block_jump_factor<D: DerefMut<Target = Dimension>>(entity: &Entity<D>) -> f32 {
let block_at_pos = entity.dimension.get_block_state(&entity.pos().into());
fn block_jump_factor<D: DerefMut<Target = World>>(entity: &Entity<D>) -> f32 {
let block_at_pos = entity.world.get_block_state(&entity.pos().into());
let block_below = entity
.dimension
.world
.get_block_state(&get_block_pos_below_that_affects_movement(entity));
let block_at_pos_jump_factor = if let Some(block) = block_at_pos {
@ -210,11 +209,11 @@ fn block_jump_factor<D: DerefMut<Target = Dimension>>(entity: &Entity<D>) -> f32
// public double getJumpBoostPower() {
// return this.hasEffect(MobEffects.JUMP) ? (double)(0.1F * (float)(this.getEffect(MobEffects.JUMP).getAmplifier() + 1)) : 0.0D;
// }
fn jump_power<D: DerefMut<Target = Dimension>>(entity: &Entity<D>) -> f32 {
fn jump_power<D: DerefMut<Target = World>>(entity: &Entity<D>) -> f32 {
0.42 * block_jump_factor(entity)
}
fn jump_boost_power<D: DerefMut<Target = Dimension>>(_entity: &Entity<D>) -> f64 {
fn jump_boost_power<D: DerefMut<Target = World>>(_entity: &Entity<D>) -> f64 {
// TODO: potion effects
// if let Some(effects) = entity.effects() {
// if let Some(jump_effect) = effects.get(&Effect::Jump) {
@ -232,14 +231,14 @@ fn jump_boost_power<D: DerefMut<Target = Dimension>>(_entity: &Entity<D>) -> f64
mod tests {
use super::*;
use azalea_core::ChunkPos;
use azalea_world::{Chunk, Dimension};
use azalea_world::{Chunk, World};
use uuid::Uuid;
#[test]
fn test_gravity() {
let mut dim = Dimension::default();
let mut world = World::default();
dim.add_entity(
world.add_entity(
0,
EntityData::new(
Uuid::from_u128(0),
@ -250,7 +249,7 @@ mod tests {
},
),
);
let mut entity = dim.entity_mut(0).unwrap();
let mut entity = world.entity_mut(0).unwrap();
// y should start at 70
assert_eq!(entity.pos().y, 70.);
entity.ai_step();
@ -267,10 +266,11 @@ mod tests {
}
#[test]
fn test_collision() {
let mut dim = Dimension::default();
dim.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
let mut world = World::default();
world
.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
.unwrap();
dim.add_entity(
world.add_entity(
0,
EntityData::new(
Uuid::from_u128(0),
@ -281,12 +281,12 @@ mod tests {
},
),
);
let block_state = dim.set_block_state(&BlockPos { x: 0, y: 69, z: 0 }, BlockState::Stone);
let block_state = world.set_block_state(&BlockPos { x: 0, y: 69, z: 0 }, BlockState::Stone);
assert!(
block_state.is_some(),
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
);
let mut entity = dim.entity_mut(0).unwrap();
let mut entity = world.entity_mut(0).unwrap();
entity.ai_step();
// delta will change, but it won't move until next tick
assert_eq!(entity.pos().y, 70.);
@ -298,10 +298,11 @@ mod tests {
#[test]
fn test_slab_collision() {
let mut dim = Dimension::default();
dim.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
let mut world = World::default();
world
.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
.unwrap();
dim.add_entity(
world.add_entity(
0,
EntityData::new(
Uuid::from_u128(0),
@ -312,7 +313,7 @@ mod tests {
},
),
);
let block_state = dim.set_block_state(
let block_state = world.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::StoneSlab_BottomFalse,
);
@ -320,7 +321,7 @@ mod tests {
block_state.is_some(),
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
);
let mut entity = dim.entity_mut(0).unwrap();
let mut entity = world.entity_mut(0).unwrap();
// do a few steps so we fall on the slab
for _ in 0..20 {
entity.ai_step();
@ -330,10 +331,11 @@ mod tests {
#[test]
fn test_top_slab_collision() {
let mut dim = Dimension::default();
dim.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
let mut world = World::default();
world
.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
.unwrap();
dim.add_entity(
world.add_entity(
0,
EntityData::new(
Uuid::from_u128(0),
@ -344,7 +346,7 @@ mod tests {
},
),
);
let block_state = dim.set_block_state(
let block_state = world.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::StoneSlab_TopFalse,
);
@ -352,7 +354,7 @@ mod tests {
block_state.is_some(),
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
);
let mut entity = dim.entity_mut(0).unwrap();
let mut entity = world.entity_mut(0).unwrap();
// do a few steps so we fall on the slab
for _ in 0..20 {
entity.ai_step();
@ -362,10 +364,11 @@ mod tests {
#[test]
fn test_weird_wall_collision() {
let mut dim = Dimension::default();
dim.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
let mut world = World::default();
world
.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
.unwrap();
dim.add_entity(
world.add_entity(
0,
EntityData::new(
Uuid::from_u128(0),
@ -376,7 +379,7 @@ mod tests {
},
),
);
let block_state = dim.set_block_state(
let block_state = world.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::CobblestoneWall_LowLowLowFalseFalseLow,
);
@ -384,7 +387,7 @@ mod tests {
block_state.is_some(),
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
);
let mut entity = dim.entity_mut(0).unwrap();
let mut entity = world.entity_mut(0).unwrap();
// do a few steps so we fall on the slab
for _ in 0..20 {
entity.ai_step();

View file

@ -302,7 +302,8 @@ where
R1: ProtocolPacket + Debug,
W1: ProtocolPacket + Debug,
{
fn from<R2, W2>(connection: Connection<R1, W1>) -> Connection<R2, W2>
/// Creates a `Connection` of a type from a `Connection` of another type. Useful for servers or custom packets.
pub fn from<R2, W2>(connection: Connection<R1, W1>) -> Connection<R2, W2>
where
R2: ProtocolPacket + Debug,
W2: ProtocolPacket + Debug,
@ -323,4 +324,25 @@ where
},
}
}
/// Convert an existing `TcpStream` into a `Connection`. Useful for servers.
pub fn wrap(stream: TcpStream) -> Connection<R1, W1> {
let (read_stream, write_stream) = stream.into_split();
Connection {
reader: ReadConnection {
read_stream,
buffer: BytesMut::new(),
compression_threshold: None,
dec_cipher: None,
_reading: PhantomData,
},
writer: WriteConnection {
write_stream,
compression_threshold: None,
enc_cipher: None,
_writing: PhantomData,
},
}
}
}

View file

@ -1,6 +1,6 @@
use crate::palette::PalettedContainer;
use crate::palette::PalettedContainerType;
use crate::Dimension;
use crate::World;
use azalea_block::BlockState;
use azalea_buf::BufReadError;
use azalea_buf::{McBufReadable, McBufWritable};
@ -143,7 +143,7 @@ impl IndexMut<&ChunkPos> for ChunkStorage {
impl Chunk {
pub fn read_with_dimension(
buf: &mut Cursor<&[u8]>,
data: &Dimension,
data: &World,
) -> Result<Self, BufReadError> {
Self::read_with_dimension_height(buf, data.height())
}

View file

@ -40,9 +40,8 @@ impl AttributeInstance {
AttributeModifierOperation::MultiplyBase => total += self.base * modifier.amount,
_ => {}
}
match modifier.operation {
AttributeModifierOperation::MultiplyTotal => total *= 1.0 + modifier.amount,
_ => {}
if let AttributeModifierOperation::MultiplyTotal = modifier.operation {
total *= 1.0 + modifier.amount
}
}
total

View file

@ -6,6 +6,7 @@ use super::{EntityDataValue, Pose, Rotations, VillagerData};
use azalea_block::BlockState;
use azalea_chat::Component;
use azalea_core::{BlockPos, Direction, Particle, Slot};
use enum_as_inner::EnumAsInner;
use std::{
collections::VecDeque,
ops::{Deref, DerefMut},
@ -7908,7 +7909,7 @@ impl DerefMut for AbstractTameable {
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, EnumAsInner)]
pub enum EntityMetadata {
Allay(Allay),
AreaEffectCloud(AreaEffectCloud),

View file

@ -5,7 +5,7 @@ pub mod metadata;
use self::attributes::{AttributeInstance, AttributeModifiers};
pub use self::metadata::EntityMetadata;
use crate::Dimension;
use crate::World;
use azalea_block::BlockState;
use azalea_core::{BlockPos, Vec3, AABB};
pub use data::*;
@ -15,22 +15,22 @@ use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;
use uuid::Uuid;
/// A reference to an entity in a dimension.
/// A reference to an entity in a world.
#[derive(Debug)]
pub struct Entity<'d, D = &'d mut Dimension> {
/// The dimension this entity is in.
pub dimension: D,
pub struct Entity<'d, D = &'d mut World> {
/// The world this entity is in.
pub world: D,
/// The incrementing numerical id of the entity.
pub id: u32,
pub data: NonNull<EntityData>,
_marker: PhantomData<&'d ()>,
}
impl<'d, D: Deref<Target = Dimension>> Entity<'d, D> {
pub fn new(dimension: D, id: u32, data: NonNull<EntityData>) -> Self {
impl<'d, D: Deref<Target = World>> Entity<'d, D> {
pub fn new(world: D, id: u32, data: NonNull<EntityData>) -> Self {
// TODO: have this be based on the entity type
Self {
dimension,
world,
id,
data,
_marker: PhantomData,
@ -38,12 +38,12 @@ impl<'d, D: Deref<Target = Dimension>> Entity<'d, D> {
}
}
impl<'d, D: DerefMut<Target = Dimension>> Entity<'d, D> {
impl<'d, D: DerefMut<Target = World>> Entity<'d, D> {
/// Sets the position of the entity. This doesn't update the cache in
/// azalea-world, and should only be used within azalea-world!
///
/// # Safety
/// Cached position in the dimension must be updated.
/// Cached position in the world must be updated.
pub unsafe fn move_unchecked(&mut self, new_pos: Vec3) {
self.pos = new_pos;
let bounding_box = self.make_bounding_box();
@ -94,7 +94,7 @@ impl<'d, D: DerefMut<Target = Dimension>> Entity<'d, D> {
}
}
impl<'d, D: Deref<Target = Dimension>> Entity<'d, D> {
impl<'d, D: Deref<Target = World>> Entity<'d, D> {
#[inline]
pub fn pos(&self) -> &Vec3 {
&self.pos
@ -129,10 +129,10 @@ impl<'d, D: Deref<Target = Dimension>> Entity<'d, D> {
// TODO: check if block below is a fence, wall, or fence gate
let block_pos = pos.down(1);
let block_state = self.dimension.get_block_state(&block_pos);
let block_state = self.world.get_block_state(&block_pos);
if block_state == Some(BlockState::Air) {
let block_pos_below = block_pos.down(1);
let block_state_below = self.dimension.get_block_state(&block_pos_below);
let block_state_below = self.world.get_block_state(&block_pos_below);
if let Some(_block_state_below) = block_state_below {
// if block_state_below.is_fence()
// || block_state_below.is_wall()
@ -149,13 +149,13 @@ impl<'d, D: Deref<Target = Dimension>> Entity<'d, D> {
// impl<
// 'd,
// D: DerefMut<Target = Dimension> + Deref<Target = Dimension>,
// D2: Deref<Target = Dimension>,
// D: DerefMut<Target = World> + Deref<Target = World>,
// D2: Deref<Target = World>,
// > From<Entity<'d, D>> for Entity<'d, D2>
// {
// fn from(entity: Entity<'d, D>) -> Entity<'d, D> {
// Entity {
// dimension: entity.dimension,
// world: entity.world,
// id: entity.id,
// data: entity.data,
// _marker: PhantomData,
@ -163,13 +163,13 @@ impl<'d, D: Deref<Target = Dimension>> Entity<'d, D> {
// }
// }
impl<D: DerefMut<Target = Dimension>> DerefMut for Entity<'_, D> {
impl<D: DerefMut<Target = World>> DerefMut for Entity<'_, D> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.data.as_mut() }
}
}
impl<D: Deref<Target = Dimension>> Deref for Entity<'_, D> {
impl<D: Deref<Target = World>> Deref for Entity<'_, D> {
type Target = EntityData;
fn deref(&self) -> &Self::Target {
@ -181,7 +181,7 @@ impl<D: Deref<Target = Dimension>> Deref for Entity<'_, D> {
pub struct EntityData {
pub uuid: Uuid,
/// The position of the entity right now.
/// This can be changde with unsafe_move, but the correct way is with dimension.move_entity
/// This can be changde with unsafe_move, but the correct way is with world.move_entity
pos: Vec3,
/// The position of the entity last tick.
pub last_pos: Vec3,
@ -264,16 +264,25 @@ impl EntityData {
}
}
/// Get the position of the entity in the dimension.
/// Get the position of the entity in the world.
#[inline]
pub fn pos(&self) -> &Vec3 {
&self.pos
}
/// Convert this &mut self into a (mutable) pointer.
///
/// # Safety
/// The entity MUST exist while this pointer exists.
pub unsafe fn as_ptr(&mut self) -> NonNull<EntityData> {
NonNull::new_unchecked(self as *mut EntityData)
}
/// Convert this &self into a (mutable) pointer.
///
/// # Safety
/// The entity MUST exist while this pointer exists. You also must not
/// modify the data inside the pointer.
pub unsafe fn as_const_ptr(&self) -> NonNull<EntityData> {
// this is cursed
NonNull::new_unchecked(self as *const EntityData as *mut EntityData)
@ -286,9 +295,9 @@ mod tests {
#[test]
fn from_mut_entity_to_ref_entity() {
let mut dim = Dimension::default();
let mut world = World::default();
let uuid = Uuid::from_u128(100);
dim.add_entity(
world.add_entity(
0,
EntityData::new(
uuid,
@ -296,7 +305,7 @@ mod tests {
EntityMetadata::Player(metadata::Player::default()),
),
);
let entity: Entity = dim.entity_mut(0).unwrap();
let entity: Entity = world.entity_mut(0).unwrap();
let entity_ref = Entity::from(entity);
assert_eq!(entity_ref.uuid, uuid);
}

View file

@ -22,10 +22,9 @@ use std::{
use thiserror::Error;
use uuid::Uuid;
/// A dimension is a collection of chunks and entities.
/// Minecraft calls these "Levels", Fabric calls them "Worlds", Minestom calls them "Instances".
/// A world is a collection of chunks and entities. They're called "levels" in Minecraft's source code.
#[derive(Debug, Default)]
pub struct Dimension {
pub struct World {
pub chunk_storage: ChunkStorage,
pub entity_storage: EntityStorage,
}
@ -36,9 +35,9 @@ pub enum MoveEntityError {
EntityDoesNotExist,
}
impl Dimension {
impl World {
pub fn new(chunk_radius: u32, height: u32, min_y: i32) -> Self {
Dimension {
World {
chunk_storage: ChunkStorage::new(chunk_radius, height, min_y),
entity_storage: EntityStorage::new(),
}
@ -127,13 +126,13 @@ impl Dimension {
self.entity_storage.get_mut_by_id(id)
}
pub fn entity(&self, id: u32) -> Option<Entity<&Dimension>> {
pub fn entity(&self, id: u32) -> Option<Entity<&World>> {
let entity_data = self.entity_storage.get_by_id(id)?;
let entity_ptr = unsafe { entity_data.as_const_ptr() };
Some(Entity::new(self, id, entity_ptr))
}
pub fn entity_mut(&mut self, id: u32) -> Option<Entity<'_, &mut Dimension>> {
pub fn entity_mut(&mut self, id: u32) -> Option<Entity<'_, &mut World>> {
let entity_data = self.entity_storage.get_mut_by_id(id)?;
let entity_ptr = unsafe { entity_data.as_ptr() };
Some(Entity::new(self, id, entity_ptr))
@ -157,14 +156,14 @@ impl Dimension {
}
}
impl Index<&ChunkPos> for Dimension {
impl Index<&ChunkPos> for World {
type Output = Option<Arc<Mutex<Chunk>>>;
fn index(&self, pos: &ChunkPos) -> &Self::Output {
&self.chunk_storage[pos]
}
}
impl IndexMut<&ChunkPos> for Dimension {
impl IndexMut<&ChunkPos> for World {
fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
&mut self.chunk_storage[pos]
}

View file

@ -49,7 +49,7 @@ async fn deposit(bot: &mut Client, state: State) -> anyhow::Result<()> {
bot.goto(Vec3::new(0, 70, 0)).await?;
let chest = bot
.open_container(&bot.dimension.block_at(BlockPos::new(0, 70, 0)))
.open_container(&bot.world.block_at(BlockPos::new(0, 70, 0)))
.await
.unwrap();

View file

@ -36,10 +36,7 @@ async fn swarm_handle(swarm: Swarm, event: Event, state: State) {
match event {
Event::Tick => {
// choose an arbitrary player within render distance to target
if let Some(target) = swarm
.dimension
.find_one_entity(|e| e.id == "minecraft:player")
{
if let Some(target) = swarm.world.find_one_entity(|e| e.id == "minecraft:player") {
for bot in swarm {
bot.tick_goto_goal(pathfinder::Goals::Reach(target.bounding_box));
// if target.bounding_box.distance(bot.eyes) < bot.reach_distance() {

View file

@ -38,11 +38,9 @@ impl BotTrait for azalea_client::Client {
impl crate::Plugin for Plugin {
async fn handle(self: Box<Self>, event: Event, mut bot: Client) {
if let Event::Tick = event {
if *self.state.jumping_once.lock() {
if bot.jumping() {
*self.state.jumping_once.lock() = false;
bot.set_jumping(false);
}
if *self.state.jumping_once.lock() && bot.jumping() {
*self.state.jumping_once.lock() = false;
bot.set_jumping(false);
}
}
}

View file

@ -74,11 +74,11 @@ impl Trait for azalea_client::Client {
let successors = |node: &Node| {
let mut edges = Vec::new();
let dimension = self.dimension.read();
let world = self.world.read();
for possible_move in possible_moves.iter() {
edges.push(Edge {
target: possible_move.next_node(&node),
cost: possible_move.cost(&dimension, node),
target: possible_move.next_node(node),
cost: possible_move.cost(&world, node),
});
}
edges

View file

@ -1,11 +1,11 @@
use super::{Node, VerticalVel};
use azalea_core::{BlockPos, CardinalDirection};
use azalea_physics::collision::{self, BlockWithShape};
use azalea_world::Dimension;
use azalea_world::World;
/// whether this block is passable
fn is_block_passable(pos: &BlockPos, dim: &Dimension) -> bool {
if let Some(block) = dim.get_block_state(pos) {
fn is_block_passable(pos: &BlockPos, world: &World) -> bool {
if let Some(block) = world.get_block_state(pos) {
block.shape() == &collision::empty_shape()
} else {
false
@ -13,8 +13,8 @@ fn is_block_passable(pos: &BlockPos, dim: &Dimension) -> bool {
}
/// whether this block has a solid hitbox (i.e. we can stand on it)
fn is_block_solid(pos: &BlockPos, dim: &Dimension) -> bool {
if let Some(block) = dim.get_block_state(pos) {
fn is_block_solid(pos: &BlockPos, world: &World) -> bool {
if let Some(block) = world.get_block_state(pos) {
block.shape() == &collision::block_shape()
} else {
false
@ -22,21 +22,22 @@ fn is_block_solid(pos: &BlockPos, dim: &Dimension) -> bool {
}
/// Whether this block and the block above are passable
fn is_passable(pos: &BlockPos, dim: &Dimension) -> bool {
is_block_passable(pos, dim) && is_block_passable(&pos.up(1), dim)
fn is_passable(pos: &BlockPos, world: &World) -> bool {
is_block_passable(pos, world) && is_block_passable(&pos.up(1), world)
}
/// Whether we can stand in this position. Checks if the block below is solid,
/// and that the two blocks above that are passable.
fn is_standable(pos: &BlockPos, dim: &Dimension) -> bool {
is_block_solid(&pos.down(1), dim) && is_passable(&pos, dim)
fn is_standable(pos: &BlockPos, world: &World) -> bool {
is_block_solid(&pos.down(1), world) && is_passable(pos, world)
}
const JUMP_COST: f32 = 0.5;
const WALK_ONE_BLOCK_COST: f32 = 1.0;
pub trait Move {
fn cost(&self, dim: &Dimension, node: &Node) -> f32;
fn cost(&self, world: &World, node: &Node) -> f32;
/// Returns by how much the entity's position should be changed when this move is executed.
fn offset(&self) -> BlockPos;
fn next_node(&self, node: &Node) -> Node {
@ -49,8 +50,9 @@ pub trait Move {
pub struct ForwardMove(pub CardinalDirection);
impl Move for ForwardMove {
fn cost(&self, dim: &Dimension, node: &Node) -> f32 {
if is_standable(&(node.pos + self.offset()), dim) && node.vertical_vel == VerticalVel::None
fn cost(&self, world: &World, node: &Node) -> f32 {
if is_standable(&(node.pos + self.offset()), world)
&& node.vertical_vel == VerticalVel::None
{
WALK_ONE_BLOCK_COST
} else {
@ -64,10 +66,10 @@ impl Move for ForwardMove {
pub struct AscendMove(pub CardinalDirection);
impl Move for AscendMove {
fn cost(&self, dim: &Dimension, node: &Node) -> f32 {
fn cost(&self, world: &World, node: &Node) -> f32 {
if node.vertical_vel == VerticalVel::None
&& is_block_passable(&node.pos.up(2), dim)
&& is_standable(&(node.pos + self.offset()), dim)
&& is_block_passable(&node.pos.up(2), world)
&& is_standable(&(node.pos + self.offset()), world)
{
WALK_ONE_BLOCK_COST + JUMP_COST
} else {
@ -86,11 +88,11 @@ impl Move for AscendMove {
}
pub struct DescendMove(pub CardinalDirection);
impl Move for DescendMove {
fn cost(&self, dim: &Dimension, node: &Node) -> f32 {
fn cost(&self, world: &World, node: &Node) -> f32 {
// check whether 3 blocks vertically forward are passable
if node.vertical_vel == VerticalVel::None
&& is_standable(&(node.pos + self.offset()), dim)
&& is_block_passable(&(node.pos + self.offset().up(2)), dim)
&& is_standable(&(node.pos + self.offset()), world)
&& is_block_passable(&(node.pos + self.offset().up(2)), world)
{
WALK_ONE_BLOCK_COST
} else {
@ -109,24 +111,24 @@ impl Move for DescendMove {
}
pub struct DiagonalMove(pub CardinalDirection);
impl Move for DiagonalMove {
fn cost(&self, dim: &Dimension, node: &Node) -> f32 {
fn cost(&self, world: &World, node: &Node) -> f32 {
if node.vertical_vel != VerticalVel::None {
return f32::INFINITY;
}
if !is_passable(
&BlockPos::new(node.pos.x + self.0.x(), node.pos.y, node.pos.z + self.0.z()),
dim,
world,
) && !is_passable(
&BlockPos::new(
node.pos.x + self.0.right().x(),
node.pos.y,
node.pos.z + self.0.right().z(),
),
dim,
world,
) {
return f32::INFINITY;
}
if !is_standable(&(node.pos + self.offset()), dim) {
if !is_standable(&(node.pos + self.offset()), world) {
return f32::INFINITY;
}
WALK_ONE_BLOCK_COST * 1.4
@ -152,40 +154,43 @@ mod tests {
#[test]
fn test_is_passable() {
let mut dim = Dimension::default();
dim.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
let mut world = World::default();
world
.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
.unwrap();
dim.set_block_state(&BlockPos::new(0, 0, 0), BlockState::Stone);
dim.set_block_state(&BlockPos::new(0, 1, 0), BlockState::Air);
world.set_block_state(&BlockPos::new(0, 0, 0), BlockState::Stone);
world.set_block_state(&BlockPos::new(0, 1, 0), BlockState::Air);
assert_eq!(is_block_passable(&BlockPos::new(0, 0, 0), &dim), false);
assert_eq!(is_block_passable(&BlockPos::new(0, 1, 0), &dim), true);
assert_eq!(is_block_passable(&BlockPos::new(0, 0, 0), &world), false);
assert_eq!(is_block_passable(&BlockPos::new(0, 1, 0), &world), true);
}
#[test]
fn test_is_solid() {
let mut dim = Dimension::default();
dim.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
let mut world = World::default();
world
.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
.unwrap();
dim.set_block_state(&BlockPos::new(0, 0, 0), BlockState::Stone);
dim.set_block_state(&BlockPos::new(0, 1, 0), BlockState::Air);
world.set_block_state(&BlockPos::new(0, 0, 0), BlockState::Stone);
world.set_block_state(&BlockPos::new(0, 1, 0), BlockState::Air);
assert_eq!(is_block_solid(&BlockPos::new(0, 0, 0), &dim), true);
assert_eq!(is_block_solid(&BlockPos::new(0, 1, 0), &dim), false);
assert_eq!(is_block_solid(&BlockPos::new(0, 0, 0), &world), true);
assert_eq!(is_block_solid(&BlockPos::new(0, 1, 0), &world), false);
}
#[test]
fn test_is_standable() {
let mut dim = Dimension::default();
dim.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
let mut world = World::default();
world
.set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
.unwrap();
dim.set_block_state(&BlockPos::new(0, 0, 0), BlockState::Stone);
dim.set_block_state(&BlockPos::new(0, 1, 0), BlockState::Air);
dim.set_block_state(&BlockPos::new(0, 2, 0), BlockState::Air);
dim.set_block_state(&BlockPos::new(0, 3, 0), BlockState::Air);
world.set_block_state(&BlockPos::new(0, 0, 0), BlockState::Stone);
world.set_block_state(&BlockPos::new(0, 1, 0), BlockState::Air);
world.set_block_state(&BlockPos::new(0, 2, 0), BlockState::Air);
world.set_block_state(&BlockPos::new(0, 3, 0), BlockState::Air);
assert!(is_standable(&BlockPos::new(0, 1, 0), &dim));
assert!(!is_standable(&BlockPos::new(0, 0, 0), &dim));
assert!(!is_standable(&BlockPos::new(0, 2, 0), &dim));
assert!(is_standable(&BlockPos::new(0, 1, 0), &world));
assert!(!is_standable(&BlockPos::new(0, 0, 0), &world));
assert!(!is_standable(&BlockPos::new(0, 2, 0), &world));
}
}

View file

@ -105,7 +105,7 @@ impl<
for n in &known_nodes {
*pf.state_mut(n) = NodeState::default();
}
(*pf.state_mut(&start)).rhs = W::default();
pf.state_mut(&start).rhs = W::default();
pf.open.push(start, pf.calculate_key(&start));
pf

View file

@ -11,7 +11,7 @@ use azalea_protocol::{
resolver::{self, ResolverError},
ServerAddress,
};
use azalea_world::Dimension;
use azalea_world::World;
use futures::{
future::{select_all, try_join_all},
FutureExt,
@ -121,8 +121,8 @@ pub async fn start_swarm<
let resolved_address = resolver::resolve_address(&address).await?;
let address_borrow = &address;
let shared_dimension = Arc::new(RwLock::new(Dimension::default()));
let shared_dimension_borrow = &shared_dimension;
let shared_world = Arc::new(RwLock::new(World::default()));
let shared_world_borrow = &shared_world;
let bots: Vec<(Client, UnboundedReceiver<Event>)> = try_join_all(options.accounts.iter().map(
async move |account| -> Result<(Client, UnboundedReceiver<Event>), JoinError> {
@ -132,7 +132,7 @@ pub async fn start_swarm<
let (tx, rx) = mpsc::unbounded_channel();
let client = Client::new(game_profile, conn, Some(shared_dimension_borrow.clone()));
let client = Client::new(game_profile, conn, Some(shared_world_borrow.clone()));
tx.send(Event::Initialize).unwrap();

View file

@ -73,7 +73,7 @@ async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()>
// println!("{}", m.message().to_ansi(None));
// if m.message().to_string() == "<py5> goto" {
// let target_pos_vec3 = bot
// .dimension
// .world
// .read()
// .entity_by_uuid(&uuid::uuid!("6536bfed869548fd83a1ecd24cf2a0fd"))
// .unwrap()