1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

start adding HitResultComponent

This commit is contained in:
mat 2023-03-09 21:02:05 +00:00
parent c5237daf51
commit 0468379e59
20 changed files with 354 additions and 122 deletions

1
Cargo.lock generated
View file

@ -316,6 +316,7 @@ dependencies = [
"azalea-nbt",
"azalea-registry",
"bevy_ecs",
"num-traits",
"uuid",
]

View file

@ -404,7 +404,10 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
&to_pascal_case(&block.name.to_string()),
proc_macro2::Span::call_site(),
);
let block_struct_name = Ident::new(&block_name_pascal_case.to_string(), proc_macro2::Span::call_site());
let block_struct_name = Ident::new(
&block_name_pascal_case.to_string(),
proc_macro2::Span::call_site(),
);
let mut from_block_to_state_match_inner = quote! {};

View file

@ -8,7 +8,7 @@ use crate::{
death_event, handle_send_packet_event, update_in_loaded_chunk, GameProfileComponent,
LocalPlayer, PhysicsState, SendPacketEvent,
},
movement::PlayerMovePlugin,
movement::{LastSentLookDirection, PlayerMovePlugin},
packet_handling::{self, PacketHandlerPlugin, PacketReceiver},
player::retroactively_add_game_profile_component,
task_pool::TaskPoolPlugin,
@ -100,7 +100,16 @@ pub struct Client {
pub struct ClientInformation(ServerboundClientInformationPacket);
/// A component that contains a map of player UUIDs to their information in the
/// tab list
/// tab list.
///
/// ```
/// # fn example(client: &azalea_client::Client) {
/// let tab_list = client.component::<TabList>();
/// println!("Online players:");
/// for (uuid, player_info) in tab_list {
/// println!("- {} ({}ms)", player_info.profile.name, player_info.latency);
/// }
/// # }
#[derive(Component, Clone, Debug, Deref, DerefMut, Default)]
pub struct TabList(HashMap<Uuid, PlayerInfo>);
@ -253,6 +262,7 @@ impl Client {
client_information: ClientInformation::default(),
tab_list: TabList::default(),
current_sequence_number: CurrentSequenceNumber::default(),
last_sent_direction: LastSentLookDirection::default(),
_local: Local,
});
@ -496,6 +506,7 @@ pub struct JoinedClientBundle {
pub client_information: ClientInformation,
pub tab_list: TabList,
pub current_sequence_number: CurrentSequenceNumber,
pub last_sent_direction: LastSentLookDirection,
pub _local: Local,
}

View file

@ -1,21 +1,22 @@
use azalea_core::{BlockPos, Direction};
use azalea_core::{BlockPos, Direction, GameMode};
use azalea_protocol::packets::game::{
serverbound_interact_packet::InteractionHand,
serverbound_use_item_on_packet::{BlockHitResult, ServerboundUseItemOnPacket},
};
use azalea_world::entity::{view_vector, EyeHeight, LookDirection, Position};
use bevy_app::{App, Plugin};
use bevy_ecs::{component::Component, entity::Entity, event::EventReader, system::Query};
use derive_more::{Deref, DerefMut};
use log::warn;
use crate::{Client, LocalPlayer};
use crate::{local_player::LocalGameMode, Client, LocalPlayer, PlayerInfo};
/// A plugin that allows clients to interact with blocks in the world.
pub struct InteractPlugin;
impl Plugin for InteractPlugin {
fn build(&self, app: &mut App) {
app.add_event::<BlockInteractEvent>()
.add_system(handle_block_interact_event);
.add_systems((handle_block_interact_event, update_hit_result_component));
}
}
@ -44,10 +45,14 @@ pub struct BlockInteractEvent {
pub position: BlockPos,
}
/// The number of changes this client has made to blocks.
/// A component that contains the number of changes this client has made to
/// blocks.
#[derive(Component, Copy, Clone, Debug, Default, Deref, DerefMut)]
pub struct CurrentSequenceNumber(u32);
#[derive(Component, Clone, Debug, Deref, DerefMut)]
pub struct HitResultComponent(BlockHitResult);
fn handle_block_interact_event(
mut events: EventReader<BlockInteractEvent>,
mut query: Query<(&LocalPlayer, &mut CurrentSequenceNumber)>,
@ -80,3 +85,23 @@ fn handle_block_interact_event(
)
}
}
fn update_hit_result_component(
mut query: Query<(
&mut HitResultComponent,
&LocalGameMode,
&Position,
&EyeHeight,
&LookDirection,
)>,
) {
for (hit_result, game_mode, position, eye_height, look_direction) in &mut query {
let pick_range = if game_mode.current == GameMode::Creative {
6.
} else {
4.5
};
let view_vector = view_vector(look_direction);
let end_position = **position + view_vector * pick_range;
}
}

View file

@ -1,7 +1,7 @@
use std::{io, sync::Arc};
use azalea_auth::game_profile::GameProfile;
use azalea_core::ChunkPos;
use azalea_core::{ChunkPos, GameMode};
use azalea_protocol::packets::game::ServerboundGamePacket;
use azalea_world::{
entity::{self, Dead},
@ -74,9 +74,16 @@ pub struct GameProfileComponent(pub GameProfile);
/// Marks a [`LocalPlayer`] that's in a loaded chunk. This is updated at the
/// beginning of every tick.
#[derive(Component)]
#[derive(Component, Clone, Debug, Copy)]
pub struct LocalPlayerInLoadedChunk;
/// The gamemode of a local player. For a non-local player, you can look up the
/// player in the [`TabList`].
#[derive(Component, Clone, Debug, Copy)]
pub struct LocalGameMode {
pub current: GameMode,
}
impl LocalPlayer {
/// Create a new `LocalPlayer`.
pub fn new(

View file

@ -16,6 +16,7 @@ use azalea_world::{
};
use bevy_app::{App, CoreSchedule, IntoSystemAppConfigs, Plugin};
use bevy_ecs::{
component::Component,
entity::Entity,
event::EventReader,
query::With,
@ -84,18 +85,26 @@ impl Client {
**jumping_ref
}
/// Sets your rotation. `y_rot` is yaw (looking to the side), `x_rot` is
/// pitch (looking up and down). You can get these numbers from the vanilla
/// f3 screen.
/// Sets the direction the client is looking. `y_rot` is yaw (looking to the
/// side), `x_rot` is pitch (looking up and down). You can get these
/// numbers from the vanilla f3 screen.
/// `y_rot` goes from -180 to 180, and `x_rot` goes from -90 to 90.
pub fn set_rotation(&mut self, y_rot: f32, x_rot: f32) {
pub fn set_direction(&mut self, y_rot: f32, x_rot: f32) {
let mut ecs = self.ecs.lock();
let mut physics = self.query::<&mut entity::Physics>(&mut ecs);
let mut look_direction = self.query::<&mut entity::LookDirection>(&mut ecs);
entity::set_rotation(&mut physics, y_rot, x_rot);
(look_direction.y_rot, look_direction.x_rot) = (y_rot, x_rot);
}
}
/// A component that contains the look direction that was last sent over the
/// network.
#[derive(Debug, Component, Clone, Default)]
pub struct LastSentLookDirection {
pub x_rot: f32,
pub y_rot: f32,
}
#[allow(clippy::type_complexity)]
pub(crate) fn send_position(
mut query: Query<
@ -106,6 +115,8 @@ pub(crate) fn send_position(
&entity::Position,
&mut entity::LastSentPosition,
&mut entity::Physics,
&entity::LookDirection,
&mut LastSentLookDirection,
&entity::metadata::Sprinting,
),
&LocalPlayerInLoadedChunk,
@ -118,6 +129,8 @@ pub(crate) fn send_position(
position,
mut last_sent_position,
mut physics,
direction,
mut last_direction,
sprinting,
) in query.iter_mut()
{
@ -130,8 +143,8 @@ pub(crate) fn send_position(
let x_delta = position.x - last_sent_position.x;
let y_delta = position.y - last_sent_position.y;
let z_delta = position.z - last_sent_position.z;
let y_rot_delta = (physics.y_rot - physics.y_rot_last) as f64;
let x_rot_delta = (physics.x_rot - physics.x_rot_last) as f64;
let y_rot_delta = (direction.y_rot - last_direction.y_rot) as f64;
let x_rot_delta = (direction.x_rot - last_direction.x_rot) as f64;
physics_state.position_remainder += 1;
@ -140,19 +153,19 @@ pub(crate) fn send_position(
let sending_position = ((x_delta.powi(2) + y_delta.powi(2) + z_delta.powi(2))
> 2.0e-4f64.powi(2))
|| physics_state.position_remainder >= 20;
let sending_rotation = y_rot_delta != 0.0 || x_rot_delta != 0.0;
let sending_direction = y_rot_delta != 0.0 || x_rot_delta != 0.0;
// if self.is_passenger() {
// TODO: posrot packet for being a passenger
// }
let packet = if sending_position && sending_rotation {
let packet = if sending_position && sending_direction {
Some(
ServerboundMovePlayerPosRotPacket {
x: position.x,
y: position.y,
z: position.z,
x_rot: physics.x_rot,
y_rot: physics.y_rot,
x_rot: direction.x_rot,
y_rot: direction.y_rot,
on_ground: physics.on_ground,
}
.get(),
@ -167,11 +180,11 @@ pub(crate) fn send_position(
}
.get(),
)
} else if sending_rotation {
} else if sending_direction {
Some(
ServerboundMovePlayerRotPacket {
x_rot: physics.x_rot,
y_rot: physics.y_rot,
x_rot: direction.x_rot,
y_rot: direction.y_rot,
on_ground: physics.on_ground,
}
.get(),
@ -191,9 +204,9 @@ pub(crate) fn send_position(
**last_sent_position = **position;
physics_state.position_remainder = 0;
}
if sending_rotation {
physics.y_rot_last = physics.y_rot;
physics.x_rot_last = physics.x_rot;
if sending_direction {
last_direction.y_rot = direction.y_rot;
last_direction.x_rot = direction.x_rot;
}
physics.last_on_ground = physics.on_ground;

View file

@ -1,6 +1,6 @@
use std::{collections::HashSet, io::Cursor, sync::Arc};
use azalea_core::{ChunkPos, ResourceLocation, Vec3};
use azalea_core::{ChunkPos, GameMode, ResourceLocation, Vec3};
use azalea_protocol::{
connect::{ReadConnection, WriteConnection},
packets::game::{
@ -16,7 +16,7 @@ use azalea_protocol::{
use azalea_world::{
entity::{
metadata::{apply_metadata, Health, PlayerMetadataBundle},
set_rotation, Dead, EntityBundle, EntityKind, EntityUpdateSet, LastSentPosition,
Dead, EntityBundle, EntityKind, EntityUpdateSet, LastSentPosition, LookDirection,
MinecraftEntityId, Physics, PlayerBundle, Position, WorldName,
},
entity::{LoadedBy, RelativeEntityUpdate},
@ -40,7 +40,7 @@ use crate::{
client::TabList,
disconnect::DisconnectEvent,
inventory::{ClientSideCloseContainerEvent, InventoryComponent},
local_player::{GameProfileComponent, LocalPlayer},
local_player::{GameProfileComponent, LocalGameMode, LocalPlayer},
ClientInformation, PlayerInfo,
};
@ -362,12 +362,13 @@ fn process_packet_events(ecs: &mut World) {
Query<(
&mut LocalPlayer,
&mut Physics,
&mut LookDirection,
&mut Position,
&mut LastSentPosition,
)>,
> = SystemState::new(ecs);
let mut query = system_state.get_mut(ecs);
let Ok(( local_player, mut physics, mut position, mut last_sent_position)) =
let Ok((local_player, mut physics, mut direction, mut position, mut last_sent_position)) =
query.get_mut(player_entity) else {
continue;
};
@ -403,10 +404,10 @@ fn process_packet_events(ecs: &mut World) {
let mut y_rot = p.y_rot;
let mut x_rot = p.x_rot;
if p.relative_arguments.x_rot {
x_rot += physics.x_rot;
x_rot += direction.x_rot;
}
if p.relative_arguments.y_rot {
y_rot += physics.y_rot;
y_rot += direction.y_rot;
}
physics.delta = Vec3 {
@ -417,7 +418,7 @@ fn process_packet_events(ecs: &mut World) {
// we call a function instead of setting the fields ourself since the
// function makes sure the rotations stay in their
// ranges
set_rotation(&mut physics, y_rot, x_rot);
(direction.y_rot, direction.x_rot) = (y_rot, x_rot);
// TODO: minecraft sets "xo", "yo", and "zo" here but idk what that means
// so investigate that ig
let new_pos = Vec3 {
@ -866,7 +867,22 @@ fn process_packet_events(ecs: &mut World) {
}
}
ClientboundGamePacket::GameEvent(p) => {
use azalea_protocol::packets::game::clientbound_game_event_packet::EventType;
debug!("Got game event packet {:?}", p);
match p.event {
EventType::ChangeGameMode => {
let mut system_state: SystemState<Query<&mut LocalGameMode>> =
SystemState::new(ecs);
let mut query = system_state.get_mut(ecs);
let mut local_game_mode = query.get_mut(player_entity).unwrap();
if let Some(new_game_mode) = GameMode::from_id(p.param as u8) {
local_game_mode.current = new_game_mode;
}
}
_ => {}
}
}
ClientboundGamePacket::LevelParticles(p) => {
debug!("Got level particles packet {:?}", p);

View file

@ -1,6 +1,6 @@
use azalea_auth::game_profile::GameProfile;
use azalea_chat::FormattedText;
use azalea_core::GameType;
use azalea_core::GameMode;
use azalea_world::entity::EntityInfos;
use bevy_ecs::{
event::EventReader,
@ -18,7 +18,10 @@ pub struct PlayerInfo {
pub profile: GameProfile,
/// The player's UUID.
pub uuid: Uuid,
pub gamemode: GameType,
/// The current gamemode of the player, like survival or creative.
pub gamemode: GameMode,
/// The player's latency in milliseconds. The bars in the tab screen depend
/// on this.
pub latency: i32,
/// The player's display name in the tab list, but only if it's different
/// from the player's normal username. Use `player_info.profile.name` to get

View file

@ -14,6 +14,7 @@ azalea-chat = { path = "../azalea-chat", version = "^0.6.0" }
azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0" }
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
bevy_ecs = { version = "0.10.0", default-features = false, optional = true }
num-traits = "0.2.15"
uuid = "^1.1.2"
[features]

View file

@ -1,8 +1,9 @@
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
use std::io::{Cursor, Write};
#[derive(Hash, Copy, Clone, Debug, Default)]
pub enum GameType {
/// A Minecraft gamemode, like survival or creative.
#[derive(Hash, Copy, Clone, Debug, Default, Eq, PartialEq)]
pub enum GameMode {
#[default]
Survival,
Creative,
@ -10,30 +11,30 @@ pub enum GameType {
Spectator,
}
impl GameType {
impl GameMode {
pub fn to_id(&self) -> u8 {
match self {
GameType::Survival => 0,
GameType::Creative => 1,
GameType::Adventure => 2,
GameType::Spectator => 3,
GameMode::Survival => 0,
GameMode::Creative => 1,
GameMode::Adventure => 2,
GameMode::Spectator => 3,
}
}
/// Get the id of the game type, but return -1 if the game type is invalid.
pub fn to_optional_id<T: Into<Option<GameType>>>(game_type: T) -> i8 {
pub fn to_optional_id<T: Into<Option<GameMode>>>(game_type: T) -> i8 {
match game_type.into() {
Some(game_type) => game_type.to_id() as i8,
None => -1,
}
}
pub fn from_id(id: u8) -> Option<GameType> {
pub fn from_id(id: u8) -> Option<GameMode> {
Some(match id {
0 => GameType::Survival,
1 => GameType::Creative,
2 => GameType::Adventure,
3 => GameType::Spectator,
0 => GameMode::Survival,
1 => GameMode::Creative,
2 => GameMode::Adventure,
3 => GameMode::Spectator,
_ => return None,
})
}
@ -42,7 +43,7 @@ impl GameType {
Some(
match id {
-1 => None,
id => Some(GameType::from_id(id as u8)?),
id => Some(GameMode::from_id(id as u8)?),
}
.into(),
)
@ -52,10 +53,10 @@ impl GameType {
// TODO: these should be translated
// TranslatableComponent("selectWorld.gameMode." + string2)
match self {
GameType::Survival => "Survival",
GameType::Creative => "Creative",
GameType::Adventure => "Adventure",
GameType::Spectator => "Spectator",
GameMode::Survival => "Survival",
GameMode::Creative => "Creative",
GameMode::Adventure => "Adventure",
GameMode::Spectator => "Spectator",
}
}
@ -63,32 +64,32 @@ impl GameType {
// TODO: These should be translated TranslatableComponent("gameMode." +
// string2);
match self {
GameType::Survival => "Survival Mode",
GameType::Creative => "Creative Mode",
GameType::Adventure => "Adventure Mode",
GameType::Spectator => "Spectator Mode",
GameMode::Survival => "Survival Mode",
GameMode::Creative => "Creative Mode",
GameMode::Adventure => "Adventure Mode",
GameMode::Spectator => "Spectator Mode",
}
}
pub fn from_name(name: &str) -> GameType {
pub fn from_name(name: &str) -> GameMode {
match name {
"survival" => GameType::Survival,
"creative" => GameType::Creative,
"adventure" => GameType::Adventure,
"spectator" => GameType::Spectator,
"survival" => GameMode::Survival,
"creative" => GameMode::Creative,
"adventure" => GameMode::Adventure,
"spectator" => GameMode::Spectator,
_ => panic!("Unknown game type name: {name}"),
}
}
}
impl McBufReadable for GameType {
impl McBufReadable for GameMode {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
let id = u8::read_from(buf)?;
GameType::from_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 })
GameMode::from_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 })
}
}
impl McBufWritable for GameType {
impl McBufWritable for GameMode {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
u8::write_into(&self.to_id(), buf)
}
@ -97,15 +98,15 @@ impl McBufWritable for GameType {
/// Rust doesn't let us `impl McBufReadable for Option<GameType>` so we have to
/// make a new type :(
#[derive(Hash, Copy, Clone, Debug)]
pub struct OptionalGameType(pub Option<GameType>);
pub struct OptionalGameType(pub Option<GameMode>);
impl From<Option<GameType>> for OptionalGameType {
fn from(game_type: Option<GameType>) -> Self {
impl From<Option<GameMode>> for OptionalGameType {
fn from(game_type: Option<GameMode>) -> Self {
OptionalGameType(game_type)
}
}
impl From<OptionalGameType> for Option<GameType> {
impl From<OptionalGameType> for Option<GameMode> {
fn from(optional_game_type: OptionalGameType) -> Self {
optional_game_type.0
}
@ -114,12 +115,12 @@ impl From<OptionalGameType> for Option<GameType> {
impl McBufReadable for OptionalGameType {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
let id = i8::read_from(buf)?;
GameType::from_optional_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 })
GameMode::from_optional_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 })
}
}
impl McBufWritable for OptionalGameType {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
GameType::to_optional_id(*self).write_into(buf)
GameMode::to_optional_id(*self).write_into(buf)
}
}

View file

@ -39,6 +39,8 @@ pub use aabb::*;
mod block_hit_result;
pub use block_hit_result::*;
// some random math things used in minecraft are defined down here
// TODO: make this generic
pub fn binary_search(mut min: i32, max: i32, predicate: &dyn Fn(i32) -> bool) -> i32 {
let mut diff = max - min;
@ -69,6 +71,10 @@ pub fn gcd(mut a: u32, mut b: u32) -> u32 {
a
}
pub fn lerp<T: num_traits::Float>(amount: T, a: T, b: T) -> T {
a + amount * (b - a)
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -0,0 +1,97 @@
use azalea_core::{BlockPos, Vec3};
// static <T, C> T traverseBlocks(Vec3 from, Vec3 to, C context,
// BiFunction<C, BlockPos, T> getHitResult, Function<C, T> getMissResult) {
// if (from.equals(to)) {
// return getMissResult.apply(context);
// } else {
// double rightAfterEndX = Mth.lerp(-1.0E-7D, to.x, from.x); // var5
// double rightAfterEndY = Mth.lerp(-1.0E-7D, to.y, from.y); // var7
// double rightAfterEndZ = Mth.lerp(-1.0E-7D, to.z, from.z); // var9
// double rightBeforeStartX = Mth.lerp(-1.0E-7D, from.x, to.x); // var11
// double rightBeforeStartY = Mth.lerp(-1.0E-7D, from.y, to.y); // var13
// double rightBeforeStartZ = Mth.lerp(-1.0E-7D, from.z, to.z); // var15
// int currentBlockX = Mth.floor(rightBeforeStartX); // var17
// int currentBlockY = Mth.floor(rightBeforeStartY); // var18
// int currentBlockZ = Mth.floor(rightBeforeStartZ); // var19
// BlockPos.MutableBlockPos blockPos = new
// BlockPos.MutableBlockPos(currentBlockX, currentBlockY, currentBlockZ);
// Object data = getHitResult.apply(context, blockPos);
// if (data != null) {
// return data;
// } else {
// double vectorX = rightAfterEndX - rightBeforeStartX; // var22
// double vectorY = rightAfterEndY - rightBeforeStartX; // var24
// double vectorZ = rightAfterEndZ - rightBeforeStartX; // var26
// int vectorXSign = Mth.sign(vectorX); // var28
// int vectorYSign = Mth.sign(vectorY); // var29
// int vectorZSign = Mth.sign(vectorZ); // var30
// double percentageStepX = vectorXSign == 0 ? 1.7976931348623157E308D :
// ((double) vectorXSign / vectorX); // var31 double percentageStepY =
// vectorYSign == 0 ? 1.7976931348623157E308D : ((double) vectorYSign /
// vectorY); // var33 double percentageStepZ = vectorZSign == 0 ?
// 1.7976931348623157E308D : ((double) vectorZSign / vectorZ); // var35
// double xPercentage = percentageStepX * (vectorXSign > 0 ? 1.0D -
// Mth.frac(rightBeforeStartX) : Mth.frac(rightBeforeStartY)); // var37
// double yPercentage = percentageStepY * (vectorYSign > 0 ? 1.0D -
// Mth.frac(rightBeforeStartY) : Mth.frac(rightBeforeStartX)); // var39
// double zPercentage = percentageStepZ * (vectorZSign > 0 ? 1.0D -
// Mth.frac(rightBeforeStartZ) : Mth.frac(rightBeforeStartZ)); // var41
// Object data;
// do {
// if (xPercentage > 1.0D && yPercentage > 1.0D && zPercentage > 1.0D)
// { return getMissResult.apply(context);
// }
// if (xPercentage < yPercentage) {
// if (xPercentage < zPercentage) {
// currentBlockX += vectorXSign;
// xPercentage += percentageStepX;
// } else {
// currentBlockZ += vectorZSign;
// zPercentage += percentageStepZ;
// }
// } else if (posY < posZ) {
// currentBlockY += vectorYSign;
// yPercentage += percentageStepY;
// } else {
// currentBlockZ += vectorZSign;
// zPercentage += percentageStepZ;
// }
// data = getHitResult.apply(context, blockPos.set(currentBlockX,
// currentBlockY, currentBlockZ)); } while (data == null);
// return data;
// }
// }
// }
// static <T, C> T traverseBlocks(Vec3 from, Vec3 to, C context,
// BiFunction<C, BlockPos, T> getHitResult, Function<C, T> getMissResult) {
pub fn traverse_blocks<C, T>(
from: Vec3,
to: Vec3,
context: C,
get_hit_result: fn(C, BlockPos) -> T,
get_miss_result: fn(C) -> T,
) {
// if (from.equals(to)) {
// return getHitResult.apply(context);
// } else {
// double rightAfterEndX = Mth.lerp(-1.0E-7D, to.x, from.x); // var5
// double rightAfterEndY = Mth.lerp(-1.0E-7D, to.y, from.y); // var7
// double rightAfterEndZ = Mth.lerp(-1.0E-7D, to.z, from.z); // var9
// double rightBeforeStartX = Mth.lerp(-1.0E-7D, from.x, to.x); // var11
// double rightBeforeStartY = Mth.lerp(-1.0E-7D, from.y, to.y); // var13
// double rightBeforeStartZ = Mth.lerp(-1.0E-7D, from.z, to.z); // var15
if from == to {
return get_miss_result(context);
}
let right_after_end = Vec3 {
};
}

View file

@ -5,10 +5,7 @@ mod shape;
mod world_collisions;
use azalea_core::{Axis, Vec3, AABB, EPSILON};
use azalea_world::{
entity::{self},
Instance, MoveEntityError,
};
use azalea_world::{entity, Instance, MoveEntityError};
pub use blocks::BlockWithShape;
pub use discrete_voxel_shape::*;
pub use shape::*;

View file

@ -1,14 +1,15 @@
#![doc = include_str!("../README.md")]
#![feature(trait_alias)]
pub mod clip;
pub mod collision;
use azalea_block::{Block, BlockState};
use azalea_core::{BlockPos, Vec3};
use azalea_world::{
entity::{
metadata::Sprinting, move_relative, Attributes, Jumping, Local, Physics, Position,
WorldName,
metadata::Sprinting, move_relative, Attributes, Jumping, Local, LookDirection, Physics,
Position, WorldName,
},
Instance, WorldContainer,
};
@ -43,10 +44,19 @@ impl Plugin for PhysicsPlugin {
/// Move the entity with the given acceleration while handling friction,
/// gravity, collisions, and some other stuff.
fn travel(
mut query: Query<(&mut Physics, &mut Position, &Attributes, &WorldName), With<Local>>,
mut query: Query<
(
&mut Physics,
&mut LookDirection,
&mut Position,
&Attributes,
&WorldName,
),
With<Local>,
>,
world_container: Res<WorldContainer>,
) {
for (mut physics, mut position, attributes, world_name) in &mut query {
for (mut physics, direction, mut position, attributes, world_name) in &mut query {
let world_lock = world_container
.get(world_name)
.expect("All entities should be in a valid world");
@ -85,6 +95,7 @@ fn travel(
block_friction,
&world,
&mut physics,
&direction,
&mut position,
attributes,
);
@ -158,12 +169,20 @@ pub fn ai_step(
pub struct ForceJumpEvent(pub Entity);
pub fn force_jump_listener(
mut query: Query<(&mut Physics, &Position, &Sprinting, &WorldName)>,
mut query: Query<(
&mut Physics,
&Position,
&LookDirection,
&Sprinting,
&WorldName,
)>,
world_container: Res<WorldContainer>,
mut events: EventReader<ForceJumpEvent>,
) {
for event in events.iter() {
if let Ok((mut physics, position, sprinting, world_name)) = query.get_mut(event.0) {
if let Ok((mut physics, position, direction, sprinting, world_name)) =
query.get_mut(event.0)
{
let world_lock = world_container
.get(world_name)
.expect("All entities should be in a valid world");
@ -178,7 +197,7 @@ pub fn force_jump_listener(
};
if **sprinting {
// sprint jumping gives some extra velocity
let y_rot = physics.y_rot * 0.017453292;
let y_rot = direction.y_rot * 0.017453292;
physics.delta += Vec3 {
x: (-f32::sin(y_rot) * 0.2) as f64,
y: 0.,
@ -204,11 +223,13 @@ fn handle_relative_friction_and_calculate_movement(
block_friction: f32,
world: &Instance,
physics: &mut Physics,
direction: &LookDirection,
position: &mut Position,
attributes: &Attributes,
) -> Vec3 {
move_relative(
physics,
direction,
get_friction_influenced_speed(physics, attributes, block_friction),
&Vec3 {
x: physics.xxa as f64,

View file

@ -1,12 +1,12 @@
use azalea_buf::McBuf;
use azalea_core::{GameType, GlobalPos, OptionalGameType, ResourceLocation};
use azalea_core::{GameMode, GlobalPos, OptionalGameType, ResourceLocation};
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundLoginPacket {
pub player_id: u32,
pub hardcore: bool,
pub game_type: GameType,
pub game_type: GameMode,
pub previous_game_type: OptionalGameType,
pub levels: Vec<ResourceLocation>,
pub registry_holder: azalea_nbt::Tag,

View file

@ -3,7 +3,7 @@ use azalea_buf::{
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
};
use azalea_chat::FormattedText;
use azalea_core::{FixedBitSet, GameType};
use azalea_core::{FixedBitSet, GameMode};
use azalea_protocol_macros::ClientboundGamePacket;
use std::{
collections::HashMap,
@ -24,7 +24,7 @@ pub struct PlayerInfoEntry {
pub profile: GameProfile,
pub listed: bool,
pub latency: i32,
pub game_mode: GameType,
pub game_mode: GameMode,
pub display_name: Option<FormattedText>,
pub chat_session: Option<RemoteChatSessionData>,
}
@ -40,7 +40,7 @@ pub struct InitializeChatAction {
}
#[derive(Clone, Debug, McBuf)]
pub struct UpdateGameModeAction {
pub game_mode: GameType,
pub game_mode: GameMode,
}
#[derive(Clone, Debug, McBuf)]
pub struct UpdateListedAction {

View file

@ -1,5 +1,5 @@
use azalea_buf::McBuf;
use azalea_core::{GameType, GlobalPos, OptionalGameType, ResourceLocation};
use azalea_core::{GameMode, GlobalPos, OptionalGameType, ResourceLocation};
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
@ -7,7 +7,7 @@ pub struct ClientboundRespawnPacket {
pub dimension_type: ResourceLocation,
pub dimension: ResourceLocation,
pub seed: u64,
pub player_game_type: GameType,
pub player_game_type: GameMode,
pub previous_player_game_type: OptionalGameType,
pub is_debug: bool,
pub is_flat: bool,

View file

@ -29,7 +29,7 @@ use std::{
};
use uuid::Uuid;
use super::Local;
use super::{Local, LookDirection};
/// A Bevy [`SystemSet`] for various types of entity updates.
#[derive(SystemSet, Debug, Hash, Eq, PartialEq, Clone)]
@ -75,6 +75,7 @@ impl Plugin for EntityPlugin {
debug_detect_updates_received_on_local_entities,
add_dead,
update_bounding_box,
clamp_look_direction,
))
.init_resource::<EntityInfos>();
}
@ -322,6 +323,13 @@ fn remove_despawned_entities_from_indexes(
}
}
fn clamp_look_direction(mut query: Query<&mut LookDirection>) {
for mut look_direction in &mut query {
look_direction.y_rot = look_direction.y_rot % 360.0;
look_direction.x_rot = look_direction.x_rot.clamp(-90.0, 90.0) % 360.0;
}
}
impl Debug for EntityInfos {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EntityInfos").finish()

View file

@ -38,19 +38,18 @@ impl std::hash::Hash for MinecraftEntityId {
}
}
impl nohash_hasher::IsEnabled for MinecraftEntityId {}
pub fn set_rotation(physics: &mut Physics, y_rot: f32, x_rot: f32) {
physics.y_rot = y_rot % 360.0;
physics.x_rot = x_rot.clamp(-90.0, 90.0) % 360.0;
// TODO: minecraft also sets yRotO and xRotO to xRot and yRot ... but
// idk what they're used for so
}
pub fn move_relative(physics: &mut Physics, speed: f32, acceleration: &Vec3) {
let input_vector = input_vector(physics, speed, acceleration);
pub fn move_relative(
physics: &mut Physics,
direction: &LookDirection,
speed: f32,
acceleration: &Vec3,
) {
let input_vector = input_vector(direction, speed, acceleration);
physics.delta += input_vector;
}
pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> Vec3 {
pub fn input_vector(direction: &LookDirection, speed: f32, acceleration: &Vec3) -> Vec3 {
let distance = acceleration.length_squared();
if distance < 1.0E-7 {
return Vec3::default();
@ -61,8 +60,8 @@ pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> V
*acceleration
}
.scale(speed as f64);
let y_rot = f32::sin(physics.y_rot * 0.017453292f32);
let x_rot = f32::cos(physics.y_rot * 0.017453292f32);
let y_rot = f32::sin(direction.y_rot * 0.017453292f32);
let x_rot = f32::cos(direction.y_rot * 0.017453292f32);
Vec3 {
x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
y: acceleration.y,
@ -70,6 +69,20 @@ pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> V
}
}
pub fn view_vector(look_direction: &LookDirection) -> Vec3 {
let y_rot = look_direction.y_rot * 0.017453292;
let x_rot = -look_direction.x_rot * 0.017453292;
let x_rot_cos = f32::cos(x_rot);
let x_rot_sin = f32::sin(x_rot);
let y_rot_cos = f32::cos(y_rot);
let y_rot_sin = f32::sin(y_rot);
Vec3 {
x: (x_rot_sin * y_rot_cos) as f64,
y: (-y_rot_sin) as f64,
z: (x_rot_cos * y_rot_cos) as f64,
}
}
/// Get the position of the block below the entity, but a little lower.
pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: &Position) -> BlockPos {
on_pos(0.2, chunk_storage, position)
@ -149,7 +162,7 @@ impl From<&Position> for BlockPos {
}
}
/// The last position of the entity that was sent to the network.
/// The last position of the entity that was sent over the network.
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
pub struct LastSentPosition(Vec3);
impl From<LastSentPosition> for ChunkPos {
@ -182,9 +195,16 @@ pub struct WorldName(pub ResourceLocation);
///
/// If this is true, the entity will try to jump every tick. (It's equivalent to
/// the space key being held in vanilla.)
#[derive(Debug, Component, Deref, DerefMut)]
#[derive(Debug, Component, Clone, Deref, DerefMut)]
pub struct Jumping(bool);
/// A component that contains the direction an entity is looking.
#[derive(Debug, Component, Clone, Default)]
pub struct LookDirection {
pub x_rot: f32,
pub y_rot: f32,
}
/// The physics data relating to the entity, such as position, velocity, and
/// bounding box.
#[derive(Debug, Component)]
@ -198,12 +218,6 @@ pub struct Physics {
/// Z acceleration.
pub zza: f32,
pub x_rot: f32,
pub y_rot: f32,
pub x_rot_last: f32,
pub y_rot_last: f32,
pub on_ground: bool,
pub last_on_ground: bool,
@ -237,10 +251,18 @@ pub fn add_dead(mut commands: Commands, query: Query<(Entity, &Health), Changed<
}
}
/// A component that contains the offset of the entity's eyes from the entity
/// coordinates.
///
/// This is used to calculate the camera position for players, when spectating
/// an entity, and when raytracing from the entity.
#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
pub struct EyeHeight(f32);
/// A component NewType for [`azalea_registry::EntityKind`].
///
/// Most of the time, you should be using `azalea_registry::EntityKind`
/// instead.
/// directly instead.
#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)]
pub struct EntityKind(azalea_registry::EntityKind);
@ -254,6 +276,8 @@ pub struct EntityBundle {
pub position: Position,
pub last_sent_position: LastSentPosition,
pub physics: Physics,
pub direction: LookDirection,
pub eye_height: EyeHeight,
pub attributes: Attributes,
pub jumping: Jumping,
}
@ -265,11 +289,12 @@ impl EntityBundle {
kind: azalea_registry::EntityKind,
world_name: ResourceLocation,
) -> Self {
// TODO: get correct entity dimensions by having them codegened somewhere
// TODO: get correct entity dimensions by having them codegen'd somewhere
let dimensions = EntityDimensions {
width: 0.6,
height: 1.8,
};
let eye_height = dimensions.height * 0.85;
Self {
kind: EntityKind(kind),
@ -284,12 +309,6 @@ impl EntityBundle {
yya: 0.,
zza: 0.,
x_rot: 0.,
y_rot: 0.,
y_rot_last: 0.,
x_rot_last: 0.,
on_ground: false,
last_on_ground: false,
@ -299,6 +318,8 @@ impl EntityBundle {
has_impulse: false,
},
eye_height: EyeHeight(eye_height),
direction: LookDirection::default(),
attributes: Attributes {
// TODO: do the correct defaults for everything, some

View file

@ -9,7 +9,8 @@ use crate::ecs::{
};
use azalea_core::Vec3;
use azalea_physics::{force_jump_listener, PhysicsSet};
use azalea_world::entity::{metadata::Player, set_rotation, Jumping, Local, Physics, Position};
use azalea_world::entity::LookDirection;
use azalea_world::entity::{metadata::Player, Jumping, Local, Position};
use std::f64::consts::PI;
use crate::pathfinder::PathfinderPlugin;
@ -99,12 +100,12 @@ pub struct LookAtEvent {
}
fn look_at_listener(
mut events: EventReader<LookAtEvent>,
mut query: Query<(&Position, &mut Physics)>,
mut query: Query<(&Position, &mut LookDirection)>,
) {
for event in events.iter() {
if let Ok((position, mut physics)) = query.get_mut(event.entity) {
if let Ok((position, mut look_direction)) = query.get_mut(event.entity) {
let (y_rot, x_rot) = direction_looking_at(position, &event.position);
set_rotation(&mut physics, y_rot, x_rot);
(look_direction.y_rot, look_direction.x_rot) = (y_rot, x_rot);
}
}
}