mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
Compare commits
10 commits
4fe7124371
...
daab2e08dc
Author | SHA1 | Date | |
---|---|---|---|
|
daab2e08dc | ||
e7bf124ed5 | |||
9719d00526 | |||
302752860c | |||
45f89b48e4 | |||
6984a2b9e6 | |||
a5c67d2eee | |||
004741b781 | |||
63348dbbea | |||
|
30cc0ed8bf |
20 changed files with 317 additions and 101 deletions
|
@ -12,10 +12,11 @@ is breaking anyways, semantic versioning is not followed.
|
|||
|
||||
- `HitResult` now contains the entity that's being looked at.
|
||||
- A `QueuedServerBlockUpdates` component that keeps track of block updates per `Update`.
|
||||
- Local clients now have a `TicksConnected` component. (@Kumpelinus)
|
||||
|
||||
### Changed
|
||||
|
||||
- Update to Minecraft 1.21.6.
|
||||
- Update to Minecraft 1.21.8.
|
||||
- Renamed `azalea_entity::EntityKind` to `EntityKindComponent` to disambiguate with `azalea_registry::EntityKind`.
|
||||
- Moved functions and types related to hit results from `azalea::interact` to `azalea::interact::pick`.
|
||||
- `Client::attack` now takes `Entity` instead of `MinecraftEntityId`.
|
||||
|
@ -24,6 +25,8 @@ is breaking anyways, semantic versioning is not followed.
|
|||
|
||||
- Fix packet order for loading (`PlayerLoaded`/`MovePlayerPos`) and sprinting (`PlayerInput`/`PlayerCommand`).
|
||||
- Clients no longer send invalid look directions if the server teleports us with one.
|
||||
- Movement code was updated with the changes from 1.21.5, so it no longer flags Grim.
|
||||
- `azalea-chat` now correctly handles arrays of integers in the `with` field. (@qwqawawow)
|
||||
|
||||
## [0.13.0+mc1.21.5] - 2025-06-15
|
||||
|
||||
|
|
42
Cargo.lock
generated
42
Cargo.lock
generated
|
@ -209,7 +209,7 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|||
|
||||
[[package]]
|
||||
name = "azalea"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"azalea-auth",
|
||||
|
@ -249,7 +249,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-auth"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-crypto",
|
||||
|
@ -269,7 +269,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-block"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-block-macros",
|
||||
"azalea-buf",
|
||||
|
@ -278,7 +278,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-block-macros"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -287,7 +287,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-brigadier"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-chat",
|
||||
|
@ -298,7 +298,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-buf"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-buf-macros",
|
||||
"byteorder",
|
||||
|
@ -311,7 +311,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-buf-macros"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -320,7 +320,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-chat"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-language",
|
||||
|
@ -333,7 +333,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-client"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compat",
|
||||
|
@ -370,7 +370,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-core"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-chat",
|
||||
|
@ -386,7 +386,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-crypto"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"azalea-buf",
|
||||
|
@ -403,7 +403,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-entity"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-block",
|
||||
"azalea-buf",
|
||||
|
@ -426,7 +426,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-inventory"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-chat",
|
||||
|
@ -441,7 +441,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-inventory-macros"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -450,7 +450,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-language"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"compact_str",
|
||||
"serde",
|
||||
|
@ -459,7 +459,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-physics"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-block",
|
||||
"azalea-core",
|
||||
|
@ -477,7 +477,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-protocol"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
|
@ -513,7 +513,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-protocol-macros"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -522,7 +522,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-registry"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-registry-macros",
|
||||
|
@ -532,7 +532,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-registry-macros"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -540,7 +540,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-world"
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
dependencies = [
|
||||
"azalea-block",
|
||||
"azalea-buf",
|
||||
|
|
|
@ -22,7 +22,7 @@ resolver = "2"
|
|||
# --- Workspace Settings ---
|
||||
|
||||
[workspace.package]
|
||||
version = "0.13.0+mc1.21.7"
|
||||
version = "0.13.0+mc1.21.8"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/azalea-rs/azalea"
|
||||
|
|
|
@ -10,7 +10,7 @@ A collection of Rust crates for making Minecraft bots, clients, and tools.
|
|||
|
||||
<!-- The line below is automatically read and updated by the migrate script, so don't change it manually. -->
|
||||
|
||||
_Currently supported Minecraft version: `1.21.7`._
|
||||
_Currently supported Minecraft version: `1.21.8`._
|
||||
|
||||
> [!WARNING]
|
||||
> Azalea is still unfinished, though most crates are in a useable state.
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
enum ClickAction {
|
||||
OPEN_URL = Action::new("open_url", true),
|
||||
OPEN_FILE = Action::new("open_file", false),
|
||||
RUN_COMMAND = Action::new("run_command", true),
|
||||
SUGGEST_COMMAND = Action::new("suggest_command", true),
|
||||
CHANGE_PAGE = Action::new("change_page", true),
|
||||
COPY_TO_CLIPBOARD = Action::new("copy_to_clipboard", true),
|
||||
}
|
||||
|
||||
struct ClickAction {
|
||||
pub name: String,
|
||||
pub allow_from_server: bool,
|
||||
}
|
||||
|
||||
impl ClickAction {
|
||||
fn new(name: &str, allow_from_server: bool) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
allow_from_server,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ClickEvent {
|
||||
action: ClickAction,
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
use std::{backtrace::Backtrace, io};
|
||||
|
||||
use azalea_core::{position::Vec3, tick::GameTick};
|
||||
use azalea_core::{
|
||||
position::{Vec2, Vec3},
|
||||
tick::GameTick,
|
||||
};
|
||||
use azalea_entity::{
|
||||
Attributes, InLoadedChunk, Jumping, LastSentPosition, LookDirection, Physics, Position,
|
||||
metadata::Sprinting,
|
||||
|
@ -137,8 +140,7 @@ pub struct PhysicsState {
|
|||
pub trying_to_sprint: bool,
|
||||
|
||||
pub move_direction: WalkDirection,
|
||||
pub forward_impulse: f32,
|
||||
pub left_impulse: f32,
|
||||
pub move_vector: Vec2,
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
|
@ -308,12 +310,10 @@ pub fn send_sprinting_if_needed(
|
|||
}
|
||||
}
|
||||
|
||||
/// Update the impulse from self.move_direction. The multiplier is used for
|
||||
/// sneaking.
|
||||
/// Updates the [`PhysicsState::move_vector`] based on the
|
||||
/// [`PhysicsState::move_direction`].
|
||||
pub(crate) fn tick_controls(mut query: Query<&mut PhysicsState>) {
|
||||
for mut physics_state in query.iter_mut() {
|
||||
let multiplier: Option<f32> = None;
|
||||
|
||||
let mut forward_impulse: f32 = 0.;
|
||||
let mut left_impulse: f32 = 0.;
|
||||
let move_direction = physics_state.move_direction;
|
||||
|
@ -337,13 +337,9 @@ pub(crate) fn tick_controls(mut query: Query<&mut PhysicsState>) {
|
|||
}
|
||||
_ => {}
|
||||
};
|
||||
physics_state.forward_impulse = forward_impulse;
|
||||
physics_state.left_impulse = left_impulse;
|
||||
|
||||
if let Some(multiplier) = multiplier {
|
||||
physics_state.forward_impulse *= multiplier;
|
||||
physics_state.left_impulse *= multiplier;
|
||||
}
|
||||
let move_vector = Vec2::new(left_impulse, forward_impulse).normalized();
|
||||
physics_state.move_vector = move_vector;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,8 +353,12 @@ pub fn local_player_ai_step(
|
|||
) {
|
||||
for (physics_state, mut physics, mut sprinting, mut attributes) in query.iter_mut() {
|
||||
// server ai step
|
||||
physics.x_acceleration = physics_state.left_impulse;
|
||||
physics.z_acceleration = physics_state.forward_impulse;
|
||||
|
||||
// TODO: replace those booleans when using items, passengers, and sneaking are
|
||||
// properly implemented
|
||||
let move_vector = modify_input(physics_state.move_vector, false, false, false, &attributes);
|
||||
physics.x_acceleration = move_vector.x;
|
||||
physics.z_acceleration = move_vector.y;
|
||||
|
||||
// TODO: food data and abilities
|
||||
// let has_enough_food_to_sprint = self.food_data().food_level ||
|
||||
|
@ -385,6 +385,47 @@ pub fn local_player_ai_step(
|
|||
}
|
||||
}
|
||||
|
||||
// LocalPlayer.modifyInput
|
||||
fn modify_input(
|
||||
mut move_vector: Vec2,
|
||||
is_using_item: bool,
|
||||
is_passenger: bool,
|
||||
moving_slowly: bool,
|
||||
attributes: &Attributes,
|
||||
) -> Vec2 {
|
||||
if move_vector.length_squared() == 0. {
|
||||
return move_vector;
|
||||
}
|
||||
|
||||
move_vector *= 0.98;
|
||||
if is_using_item && !is_passenger {
|
||||
move_vector *= 0.2;
|
||||
}
|
||||
|
||||
if moving_slowly {
|
||||
let sneaking_speed = attributes.sneaking_speed.calculate() as f32;
|
||||
move_vector *= sneaking_speed;
|
||||
}
|
||||
|
||||
modify_input_speed_for_square_movement(move_vector)
|
||||
}
|
||||
fn modify_input_speed_for_square_movement(move_vector: Vec2) -> Vec2 {
|
||||
let length = move_vector.length();
|
||||
if length == 0. {
|
||||
return move_vector;
|
||||
}
|
||||
let scaled_to_inverse_length = move_vector * (1. / length);
|
||||
let dist = distance_to_unit_square(scaled_to_inverse_length);
|
||||
let scale = (length * dist).min(1.);
|
||||
scaled_to_inverse_length * scale
|
||||
}
|
||||
fn distance_to_unit_square(v: Vec2) -> f32 {
|
||||
let x = v.x.abs();
|
||||
let y = v.y.abs();
|
||||
let ratio = if y > x { x / y } else { y / x };
|
||||
(1.0 + ratio * ratio).sqrt()
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Start walking in the given direction. To sprint, use
|
||||
/// [`Client::sprint`]. To stop walking, call walk with
|
||||
|
@ -508,7 +549,7 @@ fn has_enough_impulse_to_start_sprinting(physics_state: &PhysicsState) -> bool {
|
|||
// if self.underwater() {
|
||||
// self.has_forward_impulse()
|
||||
// } else {
|
||||
physics_state.forward_impulse > 0.8
|
||||
physics_state.move_vector.y > 0.8
|
||||
// }
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,23 @@ pub use events::*;
|
|||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use crate::{
|
||||
block_update::QueuedServerBlockUpdates, chat::{ChatPacket, ChatReceivedEvent}, chunks, connection::RawConnection, declare_packet_handlers, disconnect::DisconnectEvent, interact::BlockStatePredictionHandler, inventory::{
|
||||
ClientInformation,
|
||||
block_update::QueuedServerBlockUpdates,
|
||||
chat::{ChatPacket, ChatReceivedEvent},
|
||||
chunks,
|
||||
connection::RawConnection,
|
||||
declare_packet_handlers,
|
||||
disconnect::DisconnectEvent,
|
||||
interact::BlockStatePredictionHandler,
|
||||
inventory::{
|
||||
ClientSideCloseContainerEvent, Inventory, MenuOpenedEvent, SetContainerContentEvent,
|
||||
}, loading::HasClientLoaded, local_player::{Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, TabList}, movement::{KnockbackEvent, KnockbackType}, packet::as_system, player::{GameProfileComponent, PlayerInfo}, tick_counter::TicksConnected, ClientInformation
|
||||
},
|
||||
loading::HasClientLoaded,
|
||||
local_player::{Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, TabList},
|
||||
movement::{KnockbackEvent, KnockbackType},
|
||||
packet::as_system,
|
||||
player::{GameProfileComponent, PlayerInfo},
|
||||
tick_counter::TicksConnected,
|
||||
};
|
||||
|
||||
pub fn process_packet(ecs: &mut World, player: Entity, packet: &ClientboundGamePacket) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use azalea_core::tick::GameTick;
|
||||
use azalea_physics::PhysicsSet;
|
||||
use azalea_world::InstanceName;
|
||||
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_ecs::prelude::*;
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use azalea_client::{test_utils::prelude::*, tick_counter::TicksConnected};
|
||||
use azalea_core::resource_location::ResourceLocation;
|
||||
use azalea_protocol::packets::{config::{ClientboundFinishConfiguration, ClientboundRegistryData}, ConnectionProtocol};
|
||||
use azalea_protocol::packets::{
|
||||
ConnectionProtocol,
|
||||
config::{ClientboundFinishConfiguration, ClientboundRegistryData},
|
||||
};
|
||||
use azalea_registry::{DataRegistry, DimensionType};
|
||||
use simdnbt::owned::{NbtCompound, NbtTag};
|
||||
|
||||
|
|
|
@ -131,8 +131,8 @@ impl From<Vec<u8>> for BitSet {
|
|||
///
|
||||
/// Note that this is optimized for fast serialization and deserialization for
|
||||
/// Minecraft, and may not be as performant as it could be for other purposes.
|
||||
/// Notably, the internal representation is an array of `u8`s even though
|
||||
/// `usize` would be slightly faster.
|
||||
/// Consider using [`FastFixedBitSet`] if you don't need the
|
||||
/// `AzaleaRead`/`AzaleaWrite` implementation.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FixedBitSet<const N: usize>
|
||||
where
|
||||
|
@ -151,6 +151,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub const fn new_with_data(data: [u8; bits_to_bytes(N)]) -> Self {
|
||||
FixedBitSet { data }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn index(&self, index: usize) -> bool {
|
||||
(self.data[index / 8] & (1u8 << (index % 8))) != 0
|
||||
|
@ -198,6 +202,52 @@ pub const fn bits_to_bytes(n: usize) -> usize {
|
|||
n.div_ceil(8)
|
||||
}
|
||||
|
||||
/// A slightly faster compact fixed-size array of bits.
|
||||
///
|
||||
/// The `N` is the number of bits reserved for the bitset. You're encouraged to
|
||||
/// use it like `FastFixedBitSet<20>` if you need 20 bits.
|
||||
///
|
||||
/// This is almost identical to [`FixedBitSet`], but more efficient (~20% faster
|
||||
/// access) and doesn't implement `AzaleaRead`/`AzaleaWrite`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FastFixedBitSet<const N: usize>
|
||||
where
|
||||
[u64; bits_to_longs(N)]: Sized,
|
||||
{
|
||||
data: [u64; bits_to_longs(N)],
|
||||
}
|
||||
impl<const N: usize> FastFixedBitSet<N>
|
||||
where
|
||||
[u64; bits_to_longs(N)]: Sized,
|
||||
{
|
||||
pub const fn new() -> Self {
|
||||
FastFixedBitSet {
|
||||
data: [0; bits_to_longs(N)],
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn index(&self, index: usize) -> bool {
|
||||
(self.data[index / 64] & (1u64 << (index % 64))) != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set(&mut self, bit_index: usize) {
|
||||
self.data[bit_index / 64] |= 1u64 << (bit_index % 64);
|
||||
}
|
||||
}
|
||||
impl<const N: usize> Default for FastFixedBitSet<N>
|
||||
where
|
||||
[u64; bits_to_longs(N)]: Sized,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
pub const fn bits_to_longs(n: usize) -> usize {
|
||||
n.div_ceil(64)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -818,6 +818,66 @@ impl fmt::Display for Vec3 {
|
|||
}
|
||||
}
|
||||
|
||||
/// A 2D vector.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, AzBuf)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct Vec2 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
impl Vec2 {
|
||||
const ZERO: Vec2 = Vec2 { x: 0.0, y: 0.0 };
|
||||
|
||||
#[inline]
|
||||
pub fn new(x: f32, y: f32) -> Self {
|
||||
Vec2 { x, y }
|
||||
}
|
||||
#[inline]
|
||||
pub fn scale(&self, amount: f32) -> Self {
|
||||
Vec2 {
|
||||
x: self.x * amount,
|
||||
y: self.y * amount,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn dot(&self, other: Vec2) -> f32 {
|
||||
self.x * other.x + self.y * other.y
|
||||
}
|
||||
#[inline]
|
||||
pub fn normalized(&self) -> Self {
|
||||
let length = (self.x * self.x + self.y * self.y).sqrt();
|
||||
if length < 1e-4 {
|
||||
return Vec2::ZERO;
|
||||
}
|
||||
Vec2 {
|
||||
x: self.x / length,
|
||||
y: self.y / length,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn length_squared(&self) -> f32 {
|
||||
self.x * self.x + self.y * self.y
|
||||
}
|
||||
#[inline]
|
||||
pub fn length(&self) -> f32 {
|
||||
self.length_squared().sqrt()
|
||||
}
|
||||
}
|
||||
impl Mul<f32> for Vec2 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: f32) -> Self::Output {
|
||||
self.scale(rhs)
|
||||
}
|
||||
}
|
||||
impl MulAssign<f32> for Vec2 {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: f32) {
|
||||
*self = self.scale(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
const PACKED_X_LENGTH: u64 = 1 + 25; // minecraft does something a bit more complicated to get this 25
|
||||
const PACKED_Z_LENGTH: u64 = PACKED_X_LENGTH;
|
||||
const PACKED_Y_LENGTH: u64 = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH;
|
||||
|
|
|
@ -10,6 +10,7 @@ use thiserror::Error;
|
|||
#[derive(Clone, Debug, Component)]
|
||||
pub struct Attributes {
|
||||
pub speed: AttributeInstance,
|
||||
pub sneaking_speed: AttributeInstance,
|
||||
pub attack_speed: AttributeInstance,
|
||||
pub water_movement_efficiency: AttributeInstance,
|
||||
|
||||
|
@ -92,7 +93,7 @@ pub enum AttributeModifierOperation {
|
|||
pub fn sprinting_modifier() -> AttributeModifier {
|
||||
AttributeModifier {
|
||||
id: ResourceLocation::new("sprinting"),
|
||||
amount: 0.30000001192092896,
|
||||
amount: 0.3f32 as f64,
|
||||
operation: AttributeModifierOperation::MultiplyTotal,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,10 +50,10 @@ pub fn move_relative(
|
|||
|
||||
pub fn input_vector(direction: LookDirection, speed: f32, acceleration: Vec3) -> Vec3 {
|
||||
let distance = acceleration.length_squared();
|
||||
if distance < 1.0E-7 {
|
||||
if distance < 1.0e-7 {
|
||||
return Vec3::ZERO;
|
||||
}
|
||||
let acceleration = if distance > 1.0 {
|
||||
let acceleration = if distance > 1. {
|
||||
acceleration.normalize()
|
||||
} else {
|
||||
acceleration
|
||||
|
@ -492,6 +492,7 @@ pub fn default_attributes(_entity_kind: EntityKind) -> Attributes {
|
|||
// entities have different defaults
|
||||
Attributes {
|
||||
speed: AttributeInstance::new(0.1),
|
||||
sneaking_speed: AttributeInstance::new(0.3),
|
||||
attack_speed: AttributeInstance::new(4.0),
|
||||
water_movement_efficiency: AttributeInstance::new(0.0),
|
||||
block_interaction_range: AttributeInstance::new(4.5),
|
||||
|
|
|
@ -15,10 +15,10 @@ use azalea_core::{
|
|||
tick::GameTick,
|
||||
};
|
||||
use azalea_entity::{
|
||||
Attributes, InLoadedChunk, Jumping, LocalEntity, LookDirection, OnClimbable, Physics, Pose,
|
||||
Position, metadata::Sprinting, move_relative,
|
||||
Attributes, EntityKindComponent, InLoadedChunk, Jumping, LocalEntity, LookDirection,
|
||||
OnClimbable, Physics, Pose, Position, metadata::Sprinting, move_relative,
|
||||
};
|
||||
use azalea_registry::Block;
|
||||
use azalea_registry::{Block, EntityKind};
|
||||
use azalea_world::{Instance, InstanceContainer, InstanceName};
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_ecs::prelude::*;
|
||||
|
@ -66,12 +66,17 @@ pub fn ai_step(
|
|||
&LookDirection,
|
||||
&Sprinting,
|
||||
&InstanceName,
|
||||
&EntityKindComponent,
|
||||
),
|
||||
(With<LocalEntity>, With<InLoadedChunk>),
|
||||
>,
|
||||
instance_container: Res<InstanceContainer>,
|
||||
) {
|
||||
for (mut physics, jumping, position, look_direction, sprinting, instance_name) in &mut query {
|
||||
for (mut physics, jumping, position, look_direction, sprinting, instance_name, entity_kind) in
|
||||
&mut query
|
||||
{
|
||||
let is_player = **entity_kind == EntityKind::Player;
|
||||
|
||||
// vanilla does movement interpolation here, doesn't really matter much for a
|
||||
// bot though
|
||||
|
||||
|
@ -79,14 +84,29 @@ pub fn ai_step(
|
|||
physics.no_jump_delay -= 1;
|
||||
}
|
||||
|
||||
if physics.velocity.x.abs() < 0.003 {
|
||||
physics.velocity.x = 0.;
|
||||
if is_player {
|
||||
if physics.velocity.horizontal_distance_squared() < 9.0e-6 {
|
||||
physics.velocity.x = 0.;
|
||||
physics.velocity.z = 0.;
|
||||
}
|
||||
} else {
|
||||
if physics.velocity.x.abs() < 0.003 {
|
||||
physics.velocity.x = 0.;
|
||||
}
|
||||
if physics.velocity.z.abs() < 0.003 {
|
||||
physics.velocity.z = 0.;
|
||||
}
|
||||
}
|
||||
|
||||
if physics.velocity.y.abs() < 0.003 {
|
||||
physics.velocity.y = 0.;
|
||||
}
|
||||
if physics.velocity.z.abs() < 0.003 {
|
||||
physics.velocity.z = 0.;
|
||||
|
||||
if is_player {
|
||||
// handled in local_player_ai_step
|
||||
} else {
|
||||
physics.x_acceleration *= 0.98;
|
||||
physics.z_acceleration *= 0.98;
|
||||
}
|
||||
|
||||
if jumping == Some(&Jumping(true)) {
|
||||
|
@ -128,9 +148,6 @@ pub fn ai_step(
|
|||
physics.no_jump_delay = 0;
|
||||
}
|
||||
|
||||
physics.x_acceleration *= 0.98;
|
||||
physics.z_acceleration *= 0.98;
|
||||
|
||||
// TODO: freezing, pushEntities, drowning damage (in their own systems,
|
||||
// after `travel`)
|
||||
}
|
||||
|
|
|
@ -94,7 +94,8 @@ fn apply_change<T: Add<Output = T>>(base: T, condition: bool, change: T) -> T {
|
|||
impl AzaleaRead for RelativeMovements {
|
||||
fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
// yes minecraft seriously wastes that many bits, smh
|
||||
let set = FixedBitSet::<32>::azalea_read(buf)?;
|
||||
let set = u32::azalea_read(buf)?;
|
||||
let set = FixedBitSet::<32>::new_with_data(set.swap_bytes().to_be_bytes());
|
||||
Ok(RelativeMovements {
|
||||
x: set.index(0),
|
||||
y: set.index(1),
|
||||
|
|
|
@ -12,7 +12,7 @@ use azalea_buf::{AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
|
|||
use crate::read::ReadPacketError;
|
||||
|
||||
pub const PROTOCOL_VERSION: i32 = 772;
|
||||
pub const VERSION_NAME: &str = "1.21.7";
|
||||
pub const VERSION_NAME: &str = "1.21.8";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ConnectionProtocol {
|
||||
|
|
39
azalea/examples/resource_pack.rs
Normal file
39
azalea/examples/resource_pack.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
//! A simple bot that repeats chat messages sent by other players.
|
||||
|
||||
use azalea::prelude::*;
|
||||
use azalea_protocol::packets::game::ClientboundGamePacket;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let account = Account::offline("bot");
|
||||
// or let account = Account::microsoft("email").await.unwrap();
|
||||
|
||||
ClientBuilder::new()
|
||||
.set_handler(handle)
|
||||
.reconnect_after(None)
|
||||
.start(account, "localhost")
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Component)]
|
||||
pub struct State {}
|
||||
|
||||
async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()> {
|
||||
match event {
|
||||
Event::Packet(packet) => {
|
||||
if let ClientboundGamePacket::ResourcePackPush(push_pack) = packet.as_ref() {
|
||||
println!("Resource Pack URL: {}", push_pack.url);
|
||||
|
||||
bot.disconnect();
|
||||
}
|
||||
}
|
||||
Event::Disconnect(_) => {
|
||||
std::process::exit(0);
|
||||
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -65,7 +65,7 @@ where
|
|||
let mut best_paths: [usize; 7] = [0; 7];
|
||||
let mut best_path_scores: [f32; 7] = [heuristic(start); 7];
|
||||
|
||||
let mut num_nodes = 0;
|
||||
let mut num_nodes = 0_usize;
|
||||
let mut num_movements = 0;
|
||||
|
||||
while let Some(WeightedNode { index, g_score, .. }) = open_set.pop() {
|
||||
|
@ -91,10 +91,6 @@ where
|
|||
let neighbor_heuristic;
|
||||
let neighbor_index;
|
||||
|
||||
// skip neighbors that don't result in a big enough improvement
|
||||
if tentative_g_score - g_score < MIN_IMPROVEMENT {
|
||||
continue;
|
||||
}
|
||||
num_movements += 1;
|
||||
|
||||
match nodes.entry(neighbor.movement.target) {
|
||||
|
@ -136,7 +132,7 @@ where
|
|||
}
|
||||
|
||||
// check for timeout every ~10ms
|
||||
if num_nodes % 10000 == 0 {
|
||||
if num_nodes.is_multiple_of(10_000) {
|
||||
let min_timeout_reached = match min_timeout {
|
||||
PathfinderTimeout::Time(max_duration) => start_time.elapsed() >= max_duration,
|
||||
PathfinderTimeout::Nodes(max_nodes) => num_nodes >= max_nodes,
|
||||
|
|
|
@ -167,6 +167,7 @@ pub trait PathfinderClientExt {
|
|||
fn start_goto(&self, goal: impl Goal + 'static);
|
||||
fn start_goto_without_mining(&self, goal: impl Goal + 'static);
|
||||
fn stop_pathfinding(&self);
|
||||
fn force_stop_pathfinding(&self);
|
||||
fn wait_until_goto_target_reached(&self) -> impl Future<Output = ()>;
|
||||
fn is_goto_target_reached(&self) -> bool;
|
||||
}
|
||||
|
@ -212,6 +213,13 @@ impl PathfinderClientExt for azalea_client::Client {
|
|||
.send_event(GotoEvent::new(self.entity, goal).with_allow_mining(false));
|
||||
}
|
||||
|
||||
/// Stop calculating a path, and stop moving once the current movement is
|
||||
/// finished.
|
||||
///
|
||||
/// This behavior exists to prevent the bot from taking damage if
|
||||
/// `stop_pathfinding` was called while executing a parkour jump, but if
|
||||
/// it's undesirable then you may want to consider using
|
||||
/// [`Self::force_stop_pathfinding`] instead.
|
||||
fn stop_pathfinding(&self) {
|
||||
self.ecs.lock().send_event(StopPathfindingEvent {
|
||||
entity: self.entity,
|
||||
|
@ -219,6 +227,15 @@ impl PathfinderClientExt for azalea_client::Client {
|
|||
});
|
||||
}
|
||||
|
||||
/// Stop calculating a path and stop executing the current movement
|
||||
/// immediately.
|
||||
fn force_stop_pathfinding(&self) {
|
||||
self.ecs.lock().send_event(StopPathfindingEvent {
|
||||
entity: self.entity,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
/// Waits forever until the bot no longer has a pathfinder goal.
|
||||
async fn wait_until_goto_target_reached(&self) {
|
||||
// we do this to make sure the event got handled before we start checking
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
|
||||
use azalea_block::{BlockState, properties};
|
||||
use azalea_core::{
|
||||
bitset::FixedBitSet,
|
||||
bitset::FastFixedBitSet,
|
||||
position::{BlockPos, ChunkPos, ChunkSectionBlockPos, ChunkSectionPos},
|
||||
};
|
||||
use azalea_physics::collision::BlockWithShape;
|
||||
|
@ -87,11 +87,11 @@ impl CachedSections {
|
|||
pub struct CachedSection {
|
||||
pub pos: ChunkSectionPos,
|
||||
/// Blocks that we can fully pass through (like air).
|
||||
pub passable_bitset: FixedBitSet<4096>,
|
||||
pub passable_bitset: FastFixedBitSet<4096>,
|
||||
/// Blocks that we can stand on and do parkour from.
|
||||
pub solid_bitset: FixedBitSet<4096>,
|
||||
pub solid_bitset: FastFixedBitSet<4096>,
|
||||
/// Blocks that we can stand on but might not be able to parkour from.
|
||||
pub standable_bitset: FixedBitSet<4096>,
|
||||
pub standable_bitset: FastFixedBitSet<4096>,
|
||||
}
|
||||
|
||||
impl CachedWorld {
|
||||
|
@ -200,9 +200,9 @@ impl CachedWorld {
|
|||
|
||||
fn calculate_bitsets_for_section(&self, section_pos: ChunkSectionPos) -> Option<CachedSection> {
|
||||
self.with_section(section_pos, |section| {
|
||||
let mut passable_bitset = FixedBitSet::<4096>::new();
|
||||
let mut solid_bitset = FixedBitSet::<4096>::new();
|
||||
let mut standable_bitset = FixedBitSet::<4096>::new();
|
||||
let mut passable_bitset = FastFixedBitSet::<4096>::new();
|
||||
let mut solid_bitset = FastFixedBitSet::<4096>::new();
|
||||
let mut standable_bitset = FastFixedBitSet::<4096>::new();
|
||||
for i in 0..4096 {
|
||||
let block_state = section.get_at_index(i);
|
||||
if is_block_state_passable(block_state) {
|
||||
|
|
Loading…
Add table
Reference in a new issue