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

Add Client::set_client_information (#33)

* start adding options

* add default options

* send options packet by default

* mention set_options in Client::join doc

* make TranslatableComponent::read return TextComponent

* change set_options to set_client_information

* clean up some code

* Add `Initialize` event

* fix some clippy warnings

* change `Client::options` to `client_information`
This commit is contained in:
mat 2022-10-23 19:00:24 -05:00 committed by GitHub
parent 587001724a
commit 65da123631
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 97 additions and 23 deletions

View file

@ -56,11 +56,7 @@ pub enum AuthError {
/// though, and in case the Microsoft API does start providing the real email.
pub async fn auth(email: &str, opts: AuthOpts) -> Result<AuthResult, AuthError> {
let cached_account = if let Some(cache_file) = &opts.cache_file {
if let Some(account) = cache::get_account_in_cache(cache_file, email).await {
Some(account)
} else {
None
}
cache::get_account_in_cache(cache_file, email).await
} else {
None
};

View file

@ -110,7 +110,6 @@ impl McBufWritable for String {
}
}
impl McBufWritable for &str {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
write_utf_with_len(buf, self, MAX_STRING_LENGTH.into())

0
azalea-chat/src/text_component.rs Executable file → Normal file
View file

6
azalea-chat/src/translatable_component.rs Executable file → Normal file
View file

@ -59,7 +59,7 @@ impl TranslatableComponent {
.args
.get(matched)
.cloned()
.unwrap_or(StringOrComponent::String("".to_string()));
.unwrap_or_else(|| StringOrComponent::String("".to_string()));
components.push(TextComponent::new(built_text.clone()));
built_text.clear();
@ -107,7 +107,7 @@ impl TranslatableComponent {
Ok(TextComponent {
base: BaseComponent {
siblings: components.into_iter().map(|c| Component::Text(c)).collect(),
siblings: components.into_iter().map(Component::Text).collect(),
style: Style::default(),
},
text: "".to_string(),
@ -135,7 +135,7 @@ impl Display for StringOrComponent {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
match self {
StringOrComponent::String(s) => write!(f, "{}", s),
StringOrComponent::Component(c) => write!(f, "{}", c.to_string()),
StringOrComponent::Component(c) => write!(f, "{}", c),
}
}
}

View file

@ -84,8 +84,8 @@ impl Client {
/// # }
/// ```
pub async fn chat(&self, message: &str) -> Result<(), std::io::Error> {
if message.starts_with('/') {
self.send_command_packet(&message[1..]).await
if let Some(command) = message.strip_prefix('/') {
self.send_command_packet(command).await
} else {
self.send_chat_packet(message).await
}

View file

@ -9,6 +9,7 @@ use azalea_protocol::{
clientbound_player_chat_packet::ClientboundPlayerChatPacket,
clientbound_system_chat_packet::ClientboundSystemChatPacket,
serverbound_accept_teleportation_packet::ServerboundAcceptTeleportationPacket,
serverbound_client_information_packet::ServerboundClientInformationPacket,
serverbound_custom_payload_packet::ServerboundCustomPayloadPacket,
serverbound_keep_alive_packet::ServerboundKeepAlivePacket,
serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket,
@ -30,7 +31,7 @@ use azalea_world::{
Dimension,
};
use log::{debug, error, warn};
use parking_lot::Mutex;
use parking_lot::{Mutex, RwLock};
use std::{
fmt::Debug,
io::{self, Cursor},
@ -43,10 +44,17 @@ use tokio::{
time::{self},
};
pub type ClientInformation = ServerboundClientInformationPacket;
/// Events are sent before they're processed, so for example game ticks happen
/// at the beginning of a tick before anything has happened.
#[derive(Debug, Clone)]
pub enum Event {
/// Happens right after the bot switches into the Game state, but before
/// it's actually spawned. This can be useful for setting the client
/// information with `Client::set_client_information`, so the packet
/// doesn't have to be sent twice.
Initialize,
Login,
Chat(ChatPacket),
/// Happens 20 times per second, but only when the world is loaded.
@ -78,6 +86,7 @@ pub struct Client {
pub player: Arc<Mutex<Player>>,
pub dimension: Arc<Mutex<Dimension>>,
pub physics_state: Arc<Mutex<PhysicsState>>,
pub client_information: Arc<RwLock<ClientInformation>>,
tasks: Arc<Mutex<Vec<JoinHandle<()>>>>,
}
@ -123,6 +132,8 @@ pub enum HandleError {
impl Client {
/// Connect to a Minecraft server.
///
/// To change the render distance and other settings, use [`Client::set_client_information`].
///
/// # Examples
///
/// ```rust,no_run
@ -240,8 +251,11 @@ impl Client {
dimension: Arc::new(Mutex::new(Dimension::default())),
physics_state: Arc::new(Mutex::new(PhysicsState::default())),
tasks: Arc::new(Mutex::new(Vec::new())),
client_information: Arc::new(RwLock::new(ClientInformation::default())),
};
tx.send(Event::Initialize).unwrap();
// just start up the game loop and we're ready!
// if you get an error right here that means you're doing something with locks wrong
@ -389,6 +403,12 @@ impl Client {
player_lock.set_entity_id(p.player_id);
}
// send the client information that we have set
let client_information_packet: ClientInformation =
client.client_information.read().clone();
client.write_packet(client_information_packet.get()).await?;
// brand
client
.write_packet(
ServerboundCustomPayloadPacket {
@ -806,6 +826,35 @@ impl Client {
.entity(entity_id)
.expect("Player entity should be in the given dimension")
}
/// Returns whether we have a received the login packet yet.
pub fn logged_in(&self) -> bool {
let dimension = self.dimension.lock();
let player = self.player.lock();
player.entity(&dimension).is_some()
}
/// Tell the server we changed our game options (i.e. render distance, main hand).
/// If this is not set before the login packet, the default will be sent.
pub async fn set_client_information(
&self,
client_information: ServerboundClientInformationPacket,
) -> Result<(), std::io::Error> {
{
let mut client_information_lock = self.client_information.write();
*client_information_lock = client_information;
}
if self.logged_in() {
let client_information_packet = {
let client_information = self.client_information.read();
client_information.clone().get()
};
self.write_packet(client_information_packet).await?;
}
Ok(())
}
}
impl<T> From<std::sync::PoisonError<T>> for HandleError {

View file

@ -14,7 +14,7 @@ pub mod ping;
mod player;
pub use account::Account;
pub use client::{Client, Event};
pub use client::{Client, ClientInformation, Event};
pub use movement::MoveDirection;
pub use player::Player;

View file

@ -166,7 +166,7 @@ impl Connection<ClientboundLoginPacket, ServerboundLoginPacket> {
/// `ClientboundLoginPacket::Hello` packet.
///
/// # Examples
///
///
/// ```rust,no_run
/// let token = azalea_auth::auth(azalea_auth::AuthOpts {
/// ..Default::default()

View file

@ -3,20 +3,48 @@ use azalea_protocol_macros::ServerboundGamePacket;
#[derive(Clone, Debug, McBuf, ServerboundGamePacket)]
pub struct ServerboundClientInformationPacket {
/// The locale of the client.
pub language: String,
/// The view distance of the client in chunks, same as the render distance
/// in-game.
pub view_distance: u8,
/// The types of chat messages the client wants to receive. Note that many
/// servers ignore this.
pub chat_visibility: ChatVisibility,
/// Whether the messages sent from the server should have colors. Note that
/// many servers ignore this and always send colored messages.
pub chat_colors: bool,
pub model_customisation: u8,
pub main_hand: HumanoidArm,
pub text_filtering_enabled: bool,
/// Whether the client should show up as "Anonymous Player" in the server
/// list.
pub allows_listing: bool,
}
impl Default for ServerboundClientInformationPacket {
fn default() -> Self {
Self {
language: "en_us".to_string(),
view_distance: 8,
chat_visibility: ChatVisibility::Full,
chat_colors: true,
model_customisation: 0,
main_hand: HumanoidArm::Right,
text_filtering_enabled: false,
allows_listing: false,
}
}
}
#[derive(McBuf, Clone, Copy, Debug)]
pub enum ChatVisibility {
/// All chat messages should be sent to the client.
Full = 0,
/// Chat messages from other players should be not sent to the client, only
/// messages from the server like "Player joined the game" should be sent.
System = 1,
/// No chat messages should be sent to the client.
Hidden = 2,
}

View file

@ -106,7 +106,8 @@ impl ChunkStorage {
if !self.in_range(pos) {
log::trace!(
"Ignoring chunk since it's not in the view range: {}, {}",
pos.x, pos.z
pos.x,
pos.z
);
return Ok(());
}

View file

@ -1,5 +1,5 @@
use azalea::{pathfinder, Account};
use azalea::{Bot, Client, Event};
use azalea::pathfinder;
use azalea::prelude::*;
use parking_lot::Mutex;
use std::sync::Arc;

View file

@ -1,8 +1,6 @@
//! A simple bot that repeats chat messages sent by other players.
use azalea::{Account, Client, Event};
use parking_lot::Mutex;
use std::sync::Arc;
use azalea::prelude::*;
#[tokio::main]
async fn main() {

View file

@ -1,7 +1,7 @@
mod autoeat;
use azalea::prelude::*;
use azalea::{pathfinder, Account, BlockPos, Client, Event, ItemKind, MoveDirection, Plugin, Vec3};
use azalea::{pathfinder, BlockPos, ItemKind, Vec3};
#[derive(Default, Clone)]
struct State {}

View file

@ -129,7 +129,7 @@ pub enum Error {
/// it gets disconnected from the server.
///
/// # Examples
///
///
/// ```rust,no_run
/// let error = azalea::start(azalea::Options {
/// account,

View file

@ -1,4 +1,4 @@
//! The Azalea prelude.
//! The Azalea prelude. Things that are necessary for a bare-bones bot are re-exported here.
pub use crate::bot::BotTrait;
pub use azalea_client::{Account, Client, Event};

View file

@ -28,6 +28,9 @@ async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()>
Event::Login => {
bot.chat("Hello world").await?;
}
Event::Initialize => {
println!("initialized");
}
Event::Tick => {
bot.jump();
}