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

fix some warnings

This commit is contained in:
Ubuntu 2023-01-25 20:36:23 +00:00
parent 8139246055
commit 4d79ee2817
25 changed files with 188 additions and 310 deletions

View file

@ -257,8 +257,7 @@ async fn interactive_get_ms_auth_token(
log::trace!("Polling to check if user has logged in...");
if let Ok(access_token_response) = client
.post(format!(
"https://login.live.com/oauth20_token.srf?client_id={}",
CLIENT_ID
"https://login.live.com/oauth20_token.srf?client_id={CLIENT_ID}"
))
.form(&vec![
("client_id", CLIENT_ID),

View file

@ -83,17 +83,12 @@ impl fmt::Debug for BuiltInExceptions {
write!(f, "Unclosed quoted string")
}
BuiltInExceptions::ReaderInvalidEscape { character } => {
write!(
f,
"Invalid escape sequence '{}' in quoted string",
character
)
write!(f, "Invalid escape sequence '{character}' in quoted string")
}
BuiltInExceptions::ReaderInvalidBool { value } => {
write!(
f,
"Invalid bool, expected true or false but found '{}'",
value
"Invalid bool, expected true or false but found '{value}'"
)
}
BuiltInExceptions::ReaderInvalidInt { value } => {

View file

@ -69,7 +69,7 @@ pub fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::Tok
variant_discrim = match &d.1 {
syn::Expr::Lit(e) => match &e.lit {
syn::Lit::Int(i) => i.base10_parse().unwrap(),
_ => panic!("Error parsing enum discriminant as int (is {:?})", e),
_ => panic!("Error parsing enum discriminant as int (is {e:?})"),
},
syn::Expr::Unary(_) => {
panic!("Negative enum discriminants are not supported")

View file

@ -71,7 +71,7 @@ pub fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::Tok
syn::Expr::Lit(e) => match &e.lit {
syn::Lit::Int(i) => i.base10_parse().unwrap(),
// syn::Lit::Str(s) => s.value(),
_ => panic!("Error parsing enum discriminant as int (is {:?})", e),
_ => panic!("Error parsing enum discriminant as int (is {e:?})"),
},
syn::Expr::Unary(_) => {
panic!("Negative enum discriminants are not supported")

View file

@ -28,7 +28,7 @@ pub struct Account {
/// The access token for authentication. You can obtain one of these
/// manually from azalea-auth.
///
/// This is an Arc<Mutex> so it can be modified by [`Self::refresh`].
/// This is an `Arc<Mutex>` so it can be modified by [`Self::refresh`].
pub access_token: Option<Arc<Mutex<String>>>,
/// Only required for online-mode accounts.
pub uuid: Option<uuid::Uuid>,

View file

@ -78,8 +78,8 @@ impl ChatPacket {
/// player-sent chat message, this will be None.
pub fn uuid(&self) -> Option<Uuid> {
match self {
ChatPacket::System(_) => return None,
ChatPacket::Player(m) => return Some(m.sender),
ChatPacket::System(_) => None,
ChatPacket::Player(m) => Some(m.sender),
}
}

View file

@ -16,7 +16,6 @@ use azalea_protocol::{
connect::{Connection, ConnectionError},
packets::{
game::{
clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket,
serverbound_client_information_packet::ServerboundClientInformationPacket,
ClientboundGamePacket, ServerboundGamePacket,
},
@ -45,9 +44,10 @@ use bevy_time::TimePlugin;
use iyes_loopless::prelude::*;
use log::{debug, error};
use parking_lot::{Mutex, RwLock};
use std::{fmt::Debug, io, net::SocketAddr, sync::Arc, time::Duration};
use std::{collections::HashMap, fmt::Debug, io, net::SocketAddr, sync::Arc, time::Duration};
use thiserror::Error;
use tokio::{sync::mpsc, time};
use uuid::Uuid;
pub type ClientInformation = ServerboundClientInformationPacket;
@ -371,7 +371,7 @@ impl Client {
/// Get a reference to our (potentially shared) world.
///
/// This gets the [`WeakWorld`] from our world container. If it's a normal
/// This gets the [`World`] from our world container. If it's a normal
/// client, then it'll be the same as the world the client has loaded.
/// If the client using a shared world, then the shared world will be a
/// superset of the client's world.
@ -436,6 +436,11 @@ impl Client {
Ok(())
}
/// Get a HashMap of all the players in the tab list.
pub fn players(&mut self) -> HashMap<Uuid, PlayerInfo> {
self.local_player(&mut self.ecs.lock()).players.clone()
}
}
/// Create the [`App`]. This won't actually run anything yet.
@ -532,7 +537,7 @@ pub async fn tick_run_schedule_loop(run_schedule_sender: mpsc::Sender<()>) {
loop {
game_tick_interval.tick().await;
if let Err(e) = run_schedule_sender.send(()).await {
println!("tick_run_schedule_loop error: {}", e);
println!("tick_run_schedule_loop error: {e}");
// the sender is closed so end the task
return;
}

View file

@ -23,7 +23,7 @@ impl Client {
/// Return a lightweight [`Entity`] for the entity that matches the given
/// predicate function.
///
/// You can then use [`Self::map_entity`] to get components from this
/// You can then use [`Self::entity_component`] to get components from this
/// entity.
///
/// # Example
@ -34,6 +34,7 @@ impl Client {
/// );
/// if let Some(entity) = entity {
/// let position = bot.entity_component::<Position>(entity);
/// // ...
/// }
/// ```
pub fn entity_by<F: ReadOnlyWorldQuery, Q: ReadOnlyWorldQuery>(
@ -58,7 +59,7 @@ impl Client {
pub trait EntityPredicate<Q: ReadOnlyWorldQuery, Filter: ReadOnlyWorldQuery> {
fn find(&self, ecs_lock: Arc<Mutex<bevy_ecs::world::World>>) -> Option<Entity>;
}
impl<'a, F, Q, Filter> EntityPredicate<(Q,), Filter> for F
impl<F, Q, Filter> EntityPredicate<(Q,), Filter> for F
where
F: Fn(&ROQueryItem<Q>) -> bool,
Q: ReadOnlyWorldQuery,
@ -69,7 +70,7 @@ where
let mut query = ecs.query_filtered::<(Entity, Q), Filter>();
let entity = query.iter(&ecs).find(|(_, q)| (self)(q)).map(|(e, _)| e);
entity.clone()
entity
}
}

View file

@ -6,7 +6,7 @@ use std::sync::Arc;
use azalea_protocol::packets::game::{
clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket, ClientboundGamePacket,
};
use azalea_world::entity::{Entity, MinecraftEntityId};
use azalea_world::entity::MinecraftEntityId;
use bevy_app::Plugin;
use bevy_ecs::{
prelude::{Component, EventReader},
@ -14,7 +14,6 @@ use bevy_ecs::{
system::Query,
};
use derive_more::{Deref, DerefMut};
use futures::SinkExt;
use iyes_loopless::prelude::*;
use tokio::sync::mpsc;

View file

@ -73,9 +73,6 @@ pub struct LocalPlayerInLoadedChunk;
impl LocalPlayer {
/// Create a new `LocalPlayer`.
///
/// You should only use this if you want to change these fields from the
/// defaults, otherwise use [`Client::join`].
pub fn new(
entity: Entity,
packet_writer: mpsc::UnboundedSender<ServerboundGamePacket>,

View file

@ -65,6 +65,7 @@ impl Client {
}
}
#[allow(clippy::type_complexity)]
pub(crate) fn send_position(
mut query: Query<
(
@ -243,25 +244,15 @@ impl LocalPlayer {
pub fn local_player_ai_step(
mut query: Query<
(
&mut LocalPlayer,
&mut PhysicsState,
&mut entity::Physics,
&mut entity::Position,
&mut entity::metadata::Sprinting,
&mut entity::Attributes,
),
With<LocalPlayerInLoadedChunk>,
>,
) {
for (
local_player,
mut physics_state,
mut physics,
mut position,
mut sprinting,
mut attributes,
) in query.iter_mut()
{
for (mut physics_state, mut physics, mut sprinting, mut attributes) in query.iter_mut() {
LocalPlayer::tick_controls(None, &mut physics_state);
// server ai step

View file

@ -18,7 +18,7 @@ use azalea_world::{
set_rotation, Dead, EntityBundle, EntityKind, LastSentPosition, MinecraftEntityId, Physics,
PlayerBundle, Position,
},
EntityInfos, LoadedBy, PartialWorld, RelativeEntityUpdate, WorldContainer,
LoadedBy, PartialWorld, RelativeEntityUpdate, WorldContainer,
};
use bevy_app::{App, Plugin};
use bevy_ecs::{
@ -34,7 +34,7 @@ use tokio::sync::mpsc;
use crate::{
local_player::{GameProfileComponent, LocalPlayer},
ChatPacket, ClientInformation, Event, PlayerInfo,
ChatPacket, ClientInformation, PlayerInfo,
};
pub struct PacketHandlerPlugin;
@ -119,14 +119,13 @@ fn handle_packets(ecs: &mut bevy_ecs::world::World) {
ClientboundGamePacket::Login(p) => {
debug!("Got login packet");
#[allow(clippy::type_complexity)]
let mut system_state: SystemState<(
Commands,
Query<(&mut LocalPlayer, &GameProfileComponent)>,
ResMut<WorldContainer>,
ResMut<EntityInfos>,
)> = SystemState::new(ecs);
let (mut commands, mut query, mut world_container, mut entity_infos) =
system_state.get_mut(ecs);
let (mut commands, mut query, mut world_container) = system_state.get_mut(ecs);
let (mut local_player, game_profile) = query.get_mut(player_entity).unwrap();
{
@ -440,7 +439,7 @@ fn handle_packets(ecs: &mut bevy_ecs::world::World) {
let mut system_state: SystemState<Query<&mut LocalPlayer>> =
SystemState::new(ecs);
let mut query = system_state.get_mut(ecs);
let mut local_player = query.get_mut(player_entity).unwrap();
let local_player = query.get_mut(player_entity).unwrap();
let mut partial_world = local_player.partial_world.write();
partial_world.chunks.view_center = ChunkPos::new(p.x, p.z);
@ -496,7 +495,7 @@ fn handle_packets(ecs: &mut bevy_ecs::world::World) {
let mut system_state: SystemState<(Commands, Query<&mut LocalPlayer>)> =
SystemState::new(ecs);
let (mut commands, mut query) = system_state.get_mut(ecs);
let mut local_player = query.get_mut(player_entity).unwrap();
let local_player = query.get_mut(player_entity).unwrap();
if let Some(world_name) = &local_player.world_name {
let bundle = p.as_entity_bundle(world_name.clone());
@ -557,12 +556,10 @@ fn handle_packets(ecs: &mut bevy_ecs::world::World) {
ClientboundGamePacket::AddPlayer(p) => {
debug!("Got add player packet {:?}", p);
let mut system_state: SystemState<(
Commands,
Query<(&mut LocalPlayer, &EntityKind)>,
)> = SystemState::new(ecs);
let mut system_state: SystemState<(Commands, Query<&mut LocalPlayer>)> =
SystemState::new(ecs);
let (mut commands, mut query) = system_state.get_mut(ecs);
let (mut local_player, entity_kind) = query.get_mut(player_entity).unwrap();
let local_player = query.get_mut(player_entity).unwrap();
if let Some(world_name) = &local_player.world_name {
let bundle = p.as_player_bundle(world_name.clone());
@ -628,7 +625,7 @@ fn handle_packets(ecs: &mut bevy_ecs::world::World) {
drop(world);
if let Some(entity) = entity {
let mut new_position = p.position.clone();
let new_position = p.position;
commands.add(RelativeEntityUpdate {
entity,
partial_world: local_player.partial_world.clone(),
@ -725,12 +722,9 @@ fn handle_packets(ecs: &mut bevy_ecs::world::World) {
ClientboundGamePacket::PlayerChat(p) => {
debug!("Got player chat packet {:?}", p);
let mut system_state: SystemState<(
Query<&mut LocalPlayer>,
EventWriter<ChatReceivedEvent>,
)> = SystemState::new(ecs);
let (mut query, mut chat_events) = system_state.get_mut(ecs);
let local_player = query.get_mut(player_entity).unwrap();
let mut system_state: SystemState<EventWriter<ChatReceivedEvent>> =
SystemState::new(ecs);
let mut chat_events = system_state.get_mut(ecs);
chat_events.send(ChatReceivedEvent {
entity: player_entity,
@ -740,12 +734,9 @@ fn handle_packets(ecs: &mut bevy_ecs::world::World) {
ClientboundGamePacket::SystemChat(p) => {
debug!("Got system chat packet {:?}", p);
let mut system_state: SystemState<(
Query<&mut LocalPlayer>,
EventWriter<ChatReceivedEvent>,
)> = SystemState::new(ecs);
let (mut query, mut chat_events) = system_state.get_mut(ecs);
let local_player = query.get_mut(player_entity).unwrap();
let mut system_state: SystemState<EventWriter<ChatReceivedEvent>> =
SystemState::new(ecs);
let mut chat_events = system_state.get_mut(ecs);
chat_events.send(ChatReceivedEvent {
entity: player_entity,
@ -832,13 +823,14 @@ fn handle_packets(ecs: &mut bevy_ecs::world::World) {
ClientboundGamePacket::PlayerCombatKill(p) => {
debug!("Got player kill packet {:?}", p);
#[allow(clippy::type_complexity)]
let mut system_state: SystemState<(
Commands,
Query<(&mut LocalPlayer, &MinecraftEntityId, Option<&Dead>)>,
Query<(&MinecraftEntityId, Option<&Dead>)>,
EventWriter<DeathEvent>,
)> = SystemState::new(ecs);
let (mut commands, mut query, mut death_events) = system_state.get_mut(ecs);
let (local_player, entity_id, dead) = query.get_mut(player_entity).unwrap();
let (entity_id, dead) = query.get_mut(player_entity).unwrap();
if **entity_id == p.player_id && dead.is_none() {
commands.entity(player_entity).insert(Dead);

View file

@ -24,7 +24,7 @@ pub struct WorldContainer {
// If it looks like we're relying on the server giving us unique world names, that's because we
// are. An evil server could give us two worlds with the same name and then we'd have no way of
// telling them apart. We hope most servers are nice and don't do that though. It's only an
// issue when there's multiple clients with the same WeakWorldContainer in different worlds
// issue when there's multiple clients with the same WorldContainer in different worlds
// anyways.
pub worlds: HashMap<ResourceLocation, Weak<RwLock<World>>>,
}

View file

@ -4,7 +4,7 @@ use crate::{
self, add_dead, update_bounding_box, Entity, EntityUuid, MinecraftEntityId, Position,
WorldName,
},
update_entity_by_id_index, update_uuid_index, PartialWorld, World, WorldContainer,
update_entity_by_id_index, update_uuid_index, PartialWorld, WorldContainer,
};
use azalea_core::ChunkPos;
use bevy_app::{App, Plugin};
@ -16,14 +16,12 @@ use bevy_ecs::{
world::EntityMut,
};
use derive_more::{Deref, DerefMut};
use iyes_loopless::prelude::*;
use log::{debug, trace, warn};
use nohash_hasher::{IntMap, IntSet};
use log::{debug, warn};
use nohash_hasher::IntMap;
use parking_lot::RwLock;
use std::{
collections::{HashMap, HashSet},
fmt::Debug,
ops::DerefMut,
sync::Arc,
};
use uuid::Uuid;
@ -77,21 +75,15 @@ fn debug_new_entity(query: Query<Entity, Added<MinecraftEntityId>>) {
// "updates received" if not, then we simply increment our local "updates
// received" and do nothing else
/// Store a map of entities by ID. To get an iterator over all entities, use
/// `storage.shared.read().entities` [`WeakEntityStorage::entities`].
///
/// This is meant to be used with shared worlds.
///
/// You can access the shared storage with `world.shared.read()`.
/// Keep track of certain metadatas that are only relevant for this partial
/// world.
#[derive(Debug, Default)]
pub struct PartialEntityInfos {
// note: using MinecraftEntityId for entity ids is acceptable here since there's no chance of
// collisions here
// note: using MinecraftEntityId for entity ids is acceptable here since
// there's no chance of collisions here
/// The entity id of the player that owns this partial world. This will
/// make [`PartialWorld::entity_mut`] pretend the entity doesn't exist so
/// make [`RelativeEntityUpdate`] pretend the entity doesn't exist so
/// it doesn't get modified from outside sources.
///
/// [`PartialWorld::entity_mut`]: crate::PartialWorld::entity_mut
pub owner_entity: Option<Entity>,
/// A counter for each entity that tracks how many updates we've observed
/// for it.
@ -228,6 +220,7 @@ pub struct LoadedBy(pub HashSet<Entity>);
#[derive(Component, Debug, Deref, DerefMut)]
pub struct UpdatesReceived(u32);
#[allow(clippy::type_complexity)]
pub fn add_updates_received(
mut commands: Commands,
query: Query<
@ -290,7 +283,7 @@ fn remove_despawned_entities_from_indexes(
warn!("Tried to remove entity from chunk {chunk:?} but the chunk was not found.");
}
// remove it from the uuid index
if entity_infos.entity_by_uuid.remove(&*uuid).is_none() {
if entity_infos.entity_by_uuid.remove(uuid).is_none() {
warn!("Tried to remove entity {entity:?} from the uuid index but it was not there.");
}
// and now remove the entity from the ecs

View file

@ -17,11 +17,11 @@ use std::{
};
/// PartialWorlds are usually owned by clients, and hold strong references to
/// chunks and entities in [`WeakWorld`]s.
/// chunks and entities in [`World`]s.
///
/// Basically, they hold the chunks and entities that are within render
/// distance but can still access chunks and entities owned by other
/// `PartialWorld`s that have the same `WeakWorld`.
/// `PartialWorld`s that have the same `World`.
///
/// This is primarily useful for having multiple clients in the same world.
pub struct PartialWorld {

View file

@ -1,4 +1,77 @@
Azalea is a framework for creating Minecraft bots.
Internally, it's just a wrapper over azalea-client, adding useful functions for making bots.
Internally, it's just a wrapper over [`azalea_client`], adding useful
functions for making bots. Because of this, lots of the documentation will
refer to `azalea_client`. You can just replace these with `azalea` in your
code, since everything from azalea_client is re-exported in azalea.
# Installation
First, install Rust nightly with `rustup install nightly` and `rustup
default nightly`.
Then, add one of the following lines to your Cargo.toml:
Latest bleeding-edge version:
`azalea = { git="https://github.com/mat-1/azalea" }`\
Latest "stable" release:
`azalea = "0.5.0"`
## Optimization
For faster compile times, make a `.cargo/config.toml` file in your project
and copy
[this file](https://github.com/mat-1/azalea/blob/main/.cargo/config.toml)
into it. You may have to install the LLD linker.
For faster performance in debug mode, add the following code to your
Cargo.toml:
```toml
[profile.dev]
opt-level = 1
[profile.dev.package."*"]
opt-level = 3
```
# Examples
```rust,no_run
A bot that logs chat messages sent in the server to the console.
use azalea::prelude::*;
use parking_lot::Mutex;
use std::sync::Arc;
#[tokio::main]
async fn main() {
let account = Account::offline("bot");
// or Account::microsoft("example@example.com").await.unwrap();
azalea::start(azalea::Options {
account,
address: "localhost",
state: State::default(),
plugins: plugins![],
handle,
})
.await
.unwrap();
}
#[derive(Default, Clone, Component)]
pub struct State {}
async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
match event {
Event::Chat(m) => {
println!("{}", m.message().to_ansi());
}
_ => {}
}
Ok(())
}
```
[`azalea_client`]: https://docs.rs/azalea-client

View file

@ -7,7 +7,7 @@ async fn main() {
let mut states = Vec::new();
for i in 0..10 {
accounts.push(Account::offline(&format!("bot{}", i)));
accounts.push(Account::offline(&format!("bot{o}")));
states.push(State::default());
}

View file

@ -7,7 +7,7 @@ async fn main() {
let mut states = Vec::new();
for i in 0..10 {
accounts.push(Account::offline(&format!("bot{}", i)));
accounts.push(Account::offline(&format!("bot{i}")));
states.push(State::default());
}

View file

@ -1,4 +1,3 @@
use azalea_client::Account;
use azalea_core::Vec3;
use azalea_world::entity::{set_rotation, Entity, Jumping, Physics, Position};
use bevy_app::App;
@ -85,7 +84,7 @@ fn look_at_listener(
) {
for event in events.iter() {
if let Ok((position, mut physics)) = query.get_mut(event.entity) {
let (y_rot, x_rot) = direction_looking_at(&position, &event.position);
let (y_rot, x_rot) = direction_looking_at(position, &event.position);
set_rotation(&mut physics, y_rot, x_rot);
}
}

View file

@ -1,81 +1,4 @@
//! Azalea is a framework for creating Minecraft bots.
//!
//! Internally, it's just a wrapper over [`azalea_client`], adding useful
//! functions for making bots. Because of this, lots of the documentation will
//! refer to `azalea_client`. You can just replace these with `azalea` in your
//! code, since everything from azalea_client is re-exported in azalea.
//!
//! # Installation
//!
//! First, install Rust nightly with `rustup install nightly` and `rustup
//! default nightly`.
//!
//! Then, add one of the following lines to your Cargo.toml:
//!
//! Latest bleeding-edge version:
//! `azalea = { git="https://github.com/mat-1/azalea" }`\
//! Latest "stable" release:
//! `azalea = "0.5.0"`
//!
//! ## Optimization
//!
//! For faster compile times, make a `.cargo/config.toml` file in your project
//! and copy
//! [this file](https://github.com/mat-1/azalea/blob/main/.cargo/config.toml)
//! into it. You may have to install the LLD linker.
//!
//! For faster performance in debug mode, add the following code to your
//! Cargo.toml:
//! ```toml
//! [profile.dev]
//! opt-level = 1
//! [profile.dev.package."*"]
//! opt-level = 3
//! ```
//!
//!
//! # Examples
//!
//! ```rust,no_run
//! //! A bot that logs chat messages sent in the server to the console.
//!
//! use azalea::prelude::*;
//! use parking_lot::Mutex;
//! use std::sync::Arc;
//!
//! #[tokio::main]
//! async fn main() {
//! let account = Account::offline("bot");
//! // or Account::microsoft("example@example.com").await.unwrap();
//!
//! azalea::start(azalea::Options {
//! account,
//! address: "localhost",
//! state: State::default(),
//! plugins: plugins![],
//! handle,
//! })
//! .await
//! .unwrap();
//! }
//!
//! #[derive(Default, Clone, Component)]
//! pub struct State {}
//!
//! async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
//! match event {
//! Event::Chat(m) => {
//! println!("{}", m.message().to_ansi());
//! }
//! _ => {}
//! }
//!
//! Ok(())
//! }
//! ```
//!
//! [`azalea_client`]: https://docs.rs/azalea-client
#![doc = include_str!("../README.md")]
#![feature(trait_upcasting)]
#![feature(async_closure)]
#![allow(incomplete_features)]
@ -83,7 +6,6 @@
mod bot;
pub mod pathfinder;
pub mod prelude;
mod start;
mod swarm;
pub use azalea_block as blocks;
@ -95,12 +17,19 @@ pub use azalea_world::{entity, World};
use bevy_ecs::prelude::Component;
use futures::Future;
use protocol::ServerAddress;
use start::StartError;
pub use start::{start, Options};
pub use swarm::*;
use thiserror::Error;
pub type HandleFn<Fut, S> = fn(Client, Event, S) -> Fut;
#[derive(Error, Debug)]
pub enum StartError {
#[error("Invalid address")]
InvalidAddress,
#[error("Join error: {0}")]
Join(#[from] azalea_client::JoinError),
}
pub struct ClientBuilder<S, Fut>
where
S: Default + Send + Sync + Clone + 'static,
@ -146,3 +75,12 @@ where
Ok(())
}
}
impl<S, Fut> Default for ClientBuilder<S, Fut>
where
S: Default + Send + Sync + Clone + Component + 'static,
Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
{
fn default() -> Self {
Self::new()
}
}

View file

@ -4,7 +4,7 @@ mod mtdstarlite;
use crate::bot::{JumpEvent, LookAtEvent};
use crate::{SprintDirection, WalkDirection};
use azalea_client::{LocalPlayer, StartSprintEvent, StartWalkEvent};
use azalea_client::{StartSprintEvent, StartWalkEvent};
use azalea_core::{BlockPos, CardinalDirection};
use azalea_world::entity::{Entity, Physics, Position, WorldName};
use azalea_world::WorldContainer;
@ -132,13 +132,13 @@ fn goto_listener(
}
fn tick_execute_path(
mut query: Query<(Entity, &LocalPlayer, &mut Pathfinder, &Position, &Physics)>,
mut query: Query<(Entity, &mut Pathfinder, &Position, &Physics)>,
mut look_at_events: EventWriter<LookAtEvent>,
mut sprint_events: EventWriter<StartSprintEvent>,
mut walk_events: EventWriter<StartWalkEvent>,
mut jump_events: EventWriter<JumpEvent>,
) {
for (entity, local_player, mut pathfinder, position, physics) in &mut query {
for (entity, mut pathfinder, position, physics) in &mut query {
loop {
let target = if let Some(target) = pathfinder.path.front() {
target

View file

@ -1,116 +0,0 @@
use crate::HandleFn;
use azalea_client::{Account, Client};
use azalea_protocol::ServerAddress;
use std::future::Future;
use thiserror::Error;
/// A helper macro that generates a [`Plugins`] struct from a list of objects
/// that implement [`Plugin`].
///
/// ```rust,no_run
/// plugins![azalea_pathfinder::Plugin];
/// ```
///
/// [`Plugin`]: crate::Plugin
#[macro_export]
macro_rules! plugins {
($($plugin:expr),*) => {
{
let mut plugins = azalea::Plugins::new();
$(
plugins.add($plugin);
)*
plugins
}
};
}
/// The options that are passed to [`azalea::start`].
///
/// [`azalea::start`]: crate::start()
pub struct Options<S, A, Fut>
where
A: TryInto<ServerAddress>,
Fut: Future<Output = Result<(), anyhow::Error>>,
{
/// The address of the server that we're connecting to. This can be a
/// `&str`, [`ServerAddress`], or anything that implements
/// `TryInto<ServerAddress>`.
///
/// [`ServerAddress`]: azalea_protocol::ServerAddress
pub address: A,
/// The account that's going to join the server.
pub account: Account,
/// A struct that contains the data that you want your bot to remember
/// across events.
///
/// # Examples
///
/// ```rust
/// use parking_lot::Mutex;
/// use std::sync::Arc;
///
/// #[derive(Default, Clone)]
/// struct State {
/// farming: Arc<Mutex<bool>>,
/// }
/// ```
pub state: S,
/// The function that's called whenever we get an event.
///
/// # Examples
///
/// ```rust
/// use azalea::prelude::*;
///
/// async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
/// Ok(())
/// }
/// ```
pub handle: HandleFn<Fut, S>,
}
#[derive(Error, Debug)]
pub enum StartError {
#[error("Invalid address")]
InvalidAddress,
#[error("Join error: {0}")]
Join(#[from] azalea_client::JoinError),
}
/// Join a server and start handling events. This function will run forever
/// until it gets disconnected from the server.
///
/// # Examples
///
/// ```rust,no_run
/// let error = azalea::start(azalea::Options {
/// account,
/// address: "localhost",
/// state: State::default(),
/// plugins: plugins![azalea_pathfinder::Plugin],
/// handle,
/// }).await;
/// ```
pub async fn start<
S: Send + Sync + Clone + 'static,
A: Send + TryInto<ServerAddress>,
Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
>(
options: Options<S, A, Fut>,
) -> Result<(), StartError> {
let address = match options.address.try_into() {
Ok(address) => address,
Err(_) => return Err(StartError::InvalidAddress),
};
let (bot, mut rx) = Client::join(&options.account, address).await?;
let state = options.state;
while let Some(event) = rx.recv().await {
tokio::spawn((options.handle)(bot.clone(), event.clone(), state.clone()));
}
Ok(())
}

View file

@ -1,7 +1,7 @@
use azalea_client::LocalPlayer;
use azalea_world::entity::MinecraftEntityId;
use bevy_ecs::{
prelude::EventWriter,
prelude::{EventWriter, With},
system::{Query, ResMut, Resource},
};
use derive_more::{Deref, DerefMut};
@ -22,7 +22,7 @@ pub struct SwarmReadyEvent;
struct IsSwarmReady(bool);
fn check_ready(
query: Query<(&LocalPlayer, Option<&MinecraftEntityId>)>,
query: Query<Option<&MinecraftEntityId>, With<LocalPlayer>>,
mut is_swarm_ready: ResMut<IsSwarmReady>,
mut ready_events: EventWriter<SwarmReadyEvent>,
) {
@ -31,7 +31,7 @@ fn check_ready(
return;
}
// if all the players are in the world, we're ready
for (player, entity_id) in query.iter() {
for entity_id in query.iter() {
if entity_id.is_none() {
return;
}

View file

@ -109,12 +109,12 @@ where
}
self
}
/// Add a single new [`Account`] to the swarm. Use [`add_accounts`] to add
/// multiple accounts at a time.
/// Add a single new [`Account`] to the swarm. Use [`Self::add_accounts`] to
/// add multiple accounts at a time.
///
/// This will make the state for this client be the default, use
/// [`Self::add_account_with_state`] to avoid that.
pub fn add_account(mut self, account: Account) -> Self {
pub fn add_account(self, account: Account) -> Self {
self.add_account_with_state(account, S::default())
}
/// Add an account with a custom initial state. Use just
@ -222,7 +222,7 @@ where
// SwarmBuilder (self) isn't Send so we have to take all the things we need out
// of it
let mut swarm_clone = swarm.clone();
let join_delay = self.join_delay.clone();
let join_delay = self.join_delay;
let accounts = self.accounts.clone();
let states = self.states.clone();
@ -284,6 +284,18 @@ where
}
}
impl<S, SS, Fut, SwarmFut> Default for SwarmBuilder<S, SS, Fut, SwarmFut>
where
Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
S: Default + Send + Sync + Clone + Component + 'static,
SS: Default + Send + Sync + Clone + Component + 'static,
{
fn default() -> Self {
Self::new()
}
}
/// An event about something that doesn't have to do with a single bot.
#[derive(Clone, Debug)]
pub enum SwarmEvent {
@ -335,7 +347,7 @@ pub enum SwarmStartError {
/// let mut states = Vec::new();
///
/// for i in 0..10 {
/// accounts.push(Account::offline(&format!("bot{}", i)));
/// accounts.push(Account::offline(&format!("bot{i}")));
/// states.push(State::default());
/// }
///

View file

@ -48,22 +48,22 @@ async fn main() -> anyhow::Result<()> {
let mut states = Vec::new();
for i in 0..1 {
accounts.push(Account::offline(&format!("bot{}", i)));
accounts.push(Account::offline(&format!("bot{i}")));
states.push(State::default());
}
loop {
// let e = azalea::SwarmBuilder::new()
// .add_accounts(accounts.clone())
// .set_handler(handle)
// .set_swarm_handler(swarm_handle)
// .join_delay(Duration::from_millis(1000))
// .start("localhost")
// .await;
let e = azalea::ClientBuilder::new()
let e = azalea::SwarmBuilder::new()
.add_accounts(accounts.clone())
.set_handler(handle)
.start(Account::offline("bot"), "localhost")
.set_swarm_handler(swarm_handle)
.join_delay(Duration::from_millis(1000))
.start("localhost")
.await;
// let e = azalea::ClientBuilder::new()
// .set_handler(handle)
// .start(Account::offline("bot"), "localhost")
// .await;
println!("{e:?}");
}
}
@ -110,7 +110,7 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
"look" => {
let entity_pos = bot.entity_component::<Position>(entity);
let target_pos: BlockPos = entity_pos.into();
println!("target_pos: {:?}", target_pos);
println!("target_pos: {target_pos:?}");
bot.look_at(target_pos.center());
}
"jump" => {
@ -156,10 +156,10 @@ async fn swarm_handle(
println!("swarm chat message: {}", m.message().to_ansi());
if m.message().to_string() == "<py5> world" {
for (name, world) in &swarm.world_container.read().worlds {
println!("world name: {}", name);
println!("world name: {name}");
if let Some(w) = world.upgrade() {
for chunk_pos in w.read().chunks.chunks.values() {
println!("chunk: {:?}", chunk_pos);
println!("chunk: {chunk_pos:?}");
}
} else {
println!("nvm world is gone");