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:
parent
8139246055
commit
4d79ee2817
25 changed files with 188 additions and 310 deletions
|
@ -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),
|
||||
|
|
|
@ -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 } => {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>>>,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(())
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
/// }
|
||||
///
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Add table
Reference in a new issue