Skip to main content

LocalPlayerEvents

Struct LocalPlayerEvents 

Source
pub struct LocalPlayerEvents(pub UnboundedSender<Event>);
Expand description

A component that contains an event sender for events that are only received by local players.

The receiver for this is returned by Client::start_client.

Tuple Fields§

§0: UnboundedSender<Event>

Methods from Deref<Target = UnboundedSender<Event>>§

pub fn send(&self, message: T) -> Result<(), SendError<T>>

Attempts to send a message on this UnboundedSender without blocking.

This method is not marked as async because sending a message to an unbounded channel never requires any form of waiting. This is due to the channel’s infinite capacity, allowing the send operation to complete immediately. As a result, the send method can be used in both synchronous and asynchronous code without issues.

If the receive half of the channel is closed, either due to close being called or the UnboundedReceiver having been dropped, this function returns an error. The error includes the value passed to send.

Examples found in repository?
azalea-protocol/examples/packet_logger.rs (line 429)
205async fn proxy_conn(
206    mut client_conn: Connection<ServerboundLoginPacket, ClientboundLoginPacket>,
207) -> Result<(), Box<dyn Error>> {
208    // resolve TARGET_ADDR
209    let parsed_target_addr = ServerAddr::try_from(TARGET_ADDR).unwrap();
210    let resolved_target_addr = resolve_address(&parsed_target_addr).await?;
211
212    let mut server_conn = Connection::new(&resolved_target_addr).await?;
213
214    let account = if ACCOUNT.contains('@') {
215        Account::microsoft(ACCOUNT).await?
216    } else {
217        Account::offline(ACCOUNT)
218    };
219    println!("got account: {:?}", account);
220
221    server_conn
222        .write(ServerboundIntention {
223            protocol_version: PROTOCOL_VERSION,
224            hostname: parsed_target_addr.host,
225            port: parsed_target_addr.port,
226            intention: ClientIntention::Login,
227        })
228        .await?;
229    let mut server_conn = server_conn.login();
230
231    // login
232    server_conn
233        .write(ServerboundHello {
234            name: account.username().to_owned(),
235            profile_id: account.uuid(),
236        })
237        .await?;
238
239    let (server_conn, login_finished) = loop {
240        let packet = server_conn.read().await?;
241
242        println!("got packet: {:?}", packet);
243
244        match packet {
245            ClientboundLoginPacket::Hello(p) => {
246                debug!("Got encryption request");
247                let e = azalea_crypto::encrypt(&p.public_key, &p.challenge).unwrap();
248
249                if let Some(access_token) = account.access_token() {
250                    // keep track of the number of times we tried
251                    // authenticating so we can give up after too many
252                    let mut attempts: usize = 1;
253
254                    while let Err(e) = {
255                        server_conn
256                            .authenticate(&access_token, &account.uuid(), e.secret_key, &p, None)
257                            .await
258                    } {
259                        if attempts >= 2 {
260                            // if this is the second attempt and we failed
261                            // both times, give up
262                            return Err(e.into());
263                        }
264                        if matches!(
265                            e,
266                            ClientSessionServerError::InvalidSession
267                                | ClientSessionServerError::ForbiddenOperation
268                        ) {
269                            // uh oh, we got an invalid session and have
270                            // to reauthenticate now
271                            account.refresh().await?;
272                        } else {
273                            return Err(e.into());
274                        }
275                        attempts += 1;
276                    }
277                }
278
279                server_conn
280                    .write(ServerboundKey {
281                        key_bytes: e.encrypted_public_key,
282                        encrypted_challenge: e.encrypted_challenge,
283                    })
284                    .await?;
285
286                server_conn.set_encryption_key(e.secret_key);
287            }
288            ClientboundLoginPacket::LoginCompression(p) => {
289                debug!("Got compression request {:?}", p.compression_threshold);
290                server_conn.set_compression_threshold(p.compression_threshold);
291            }
292            ClientboundLoginPacket::LoginFinished(p) => {
293                debug!(
294                    "Got profile {:?}. handshake is finished and we're now switching to the configuration state",
295                    p.game_profile
296                );
297                // server_conn.write(ServerboundLoginAcknowledged {}).await?;
298                break (server_conn.config(), p);
299            }
300            ClientboundLoginPacket::LoginDisconnect(p) => {
301                error!("Got disconnect {p:?}");
302                return Err("Disconnected".into());
303            }
304            ClientboundLoginPacket::CustomQuery(p) => {
305                debug!("Got custom query {:?}", p);
306                // replying to custom query is done in
307                // packet_handling::login::process_packet_events
308            }
309            ClientboundLoginPacket::CookieRequest(p) => {
310                debug!("Got cookie request {:?}", p);
311
312                server_conn
313                    .write(packets::login::ServerboundCookieResponse {
314                        key: p.key,
315                        // cookies aren't implemented
316                        payload: None,
317                    })
318                    .await?;
319            }
320        }
321    };
322
323    // give the client the login_finished
324    println!("got the login_finished: {:?}", login_finished);
325    client_conn.write(login_finished).await?;
326    let client_conn = client_conn.config();
327
328    info!("started direct bridging");
329
330    // bridge packets
331    let listen_raw_reader = client_conn.reader.raw;
332    let listen_raw_writer = client_conn.writer.raw;
333
334    let target_raw_reader = server_conn.reader.raw;
335    let target_raw_writer = server_conn.writer.raw;
336
337    let packet_logs_txt = Arc::new(tokio::sync::Mutex::new(
338        File::create("combined.txt").await.unwrap(),
339    ));
340
341    let packet_logs_txt_clone = packet_logs_txt.clone();
342    let copy_listen_to_target = tokio::spawn(async move {
343        let mut listen_raw_reader = listen_raw_reader;
344        let mut target_raw_writer = target_raw_writer;
345
346        let packet_logs_txt = packet_logs_txt_clone;
347
348        let mut serverbound_parsed_txt = File::create("serverbound.txt").await.unwrap();
349
350        loop {
351            let packet = match listen_raw_reader.read().await {
352                Ok(p) => p,
353                Err(e) => {
354                    error!("Error reading packet from listen: {e}");
355                    return;
356                }
357            };
358
359            // decode as a game packet
360            let decoded_packet = azalea_protocol::read::deserialize_packet::<ServerboundGamePacket>(
361                &mut Cursor::new(&packet),
362            );
363
364            if let Ok(decoded_packet) = decoded_packet {
365                let timestamp = chrono::Utc::now();
366                let _ = serverbound_parsed_txt
367                    .write_all(format!("{timestamp} {:?}\n", decoded_packet).as_bytes())
368                    .await;
369                let _ = packet_logs_txt
370                    .lock()
371                    .await
372                    .write_all(format!("{timestamp} <- {:?}\n", decoded_packet).as_bytes())
373                    .await;
374            }
375
376            match target_raw_writer.write(&packet).await {
377                Ok(_) => {}
378                Err(e) => {
379                    error!("Error writing packet to target: {e}");
380                    return;
381                }
382            }
383        }
384    });
385
386    // write to clientbound.txt in a separate task so it doesn't block receiving
387    // packets
388    let (clientbound_tx, mut clientbound_rx) = tokio::sync::mpsc::unbounded_channel::<Box<[u8]>>();
389    let copy_clientbound_to_file = tokio::spawn(async move {
390        let mut clientbound_parsed_txt = File::create("clientbound.txt").await.unwrap();
391
392        loop {
393            let Some(packet) = clientbound_rx.recv().await else {
394                return;
395            };
396
397            // decode as a game packet
398            let decoded_packet = azalea_protocol::read::deserialize_packet::<ClientboundGamePacket>(
399                &mut Cursor::new(&packet),
400            );
401
402            if let Ok(decoded_packet) = decoded_packet {
403                let timestamp = chrono::Utc::now();
404                let _ = clientbound_parsed_txt
405                    .write_all(format!("{timestamp} {decoded_packet:?}\n").as_bytes())
406                    .await;
407                let _ = packet_logs_txt
408                    .lock()
409                    .await
410                    .write_all(format!("{timestamp} -> {decoded_packet:?}\n").as_bytes())
411                    .await;
412            }
413        }
414    });
415
416    let copy_remote_to_local = tokio::spawn(async move {
417        let mut target_raw_reader = target_raw_reader;
418        let mut listen_raw_writer = listen_raw_writer;
419
420        loop {
421            let packet = match target_raw_reader.read().await {
422                Ok(p) => p,
423                Err(e) => {
424                    error!("Error reading packet from target: {e}");
425                    return;
426                }
427            };
428
429            clientbound_tx.send(packet.clone()).unwrap();
430
431            match listen_raw_writer.write(&packet).await {
432                Ok(_) => {}
433                Err(e) => {
434                    error!("Error writing packet to listen: {e}");
435                    return;
436                }
437            }
438        }
439    });
440
441    tokio::try_join!(
442        copy_listen_to_target,
443        copy_remote_to_local,
444        copy_clientbound_to_file
445    )?;
446
447    Ok(())
448}

pub async fn closed(&self)

Completes when the receiver has dropped.

This allows the producers to get notified when interest in the produced values is canceled and immediately stop doing work.

§Cancel safety

This method is cancel safe. Once the channel is closed, it stays closed forever and all future calls to closed will return immediately.

§Examples
use tokio::sync::mpsc;

let (tx1, rx) = mpsc::unbounded_channel::<()>();
let tx2 = tx1.clone();
let tx3 = tx1.clone();
let tx4 = tx1.clone();
let tx5 = tx1.clone();
tokio::spawn(async move {
    drop(rx);
});

futures::join!(
    tx1.closed(),
    tx2.closed(),
    tx3.closed(),
    tx4.closed(),
    tx5.closed()
);
println!("Receiver dropped");

pub fn is_closed(&self) -> bool

Checks if the channel has been closed. This happens when the UnboundedReceiver is dropped, or when the UnboundedReceiver::close method is called.

let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<()>();
assert!(!tx.is_closed());

let tx2 = tx.clone();
assert!(!tx2.is_closed());

drop(rx);
assert!(tx.is_closed());
assert!(tx2.is_closed());

pub fn same_channel(&self, other: &UnboundedSender<T>) -> bool

Returns true if senders belong to the same channel.

§Examples
let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<()>();
let  tx2 = tx.clone();
assert!(tx.same_channel(&tx2));

let (tx3, rx3) = tokio::sync::mpsc::unbounded_channel::<()>();
assert!(!tx3.same_channel(&tx2));

pub fn downgrade(&self) -> WeakUnboundedSender<T>

Converts the UnboundedSender to a [WeakUnboundedSender] that does not count towards RAII semantics, i.e. if all UnboundedSender instances of the channel were dropped and only WeakUnboundedSender instances remain, the channel is closed.

pub fn strong_count(&self) -> usize

Returns the number of [UnboundedSender] handles.

pub fn weak_count(&self) -> usize

Returns the number of [WeakUnboundedSender] handles.

Trait Implementations§

Source§

impl Component for LocalPlayerEvents
where Self: Send + Sync + 'static,

Source§

const STORAGE_TYPE: StorageType = bevy_ecs::component::StorageType::Table

A constant indicating the storage type used for this component.
Source§

type Mutability = Mutable

A marker type to assist Bevy with determining if this component is mutable, or immutable. Mutable components will have Component<Mutability = Mutable>, while immutable components will instead have Component<Mutability = Immutable>. Read more
Source§

fn register_required_components( _requiree: ComponentId, required_components: &mut RequiredComponentsRegistrator<'_, '_>, )

Registers required components. Read more
Source§

fn clone_behavior() -> ComponentCloneBehavior

Called when registering this component, allowing to override clone function (or disable cloning altogether) for this component. Read more
Source§

fn relationship_accessor() -> Option<ComponentRelationshipAccessor<Self>>

Returns [ComponentRelationshipAccessor] required for working with relationships in dynamic contexts. Read more
§

fn on_add() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>

Gets the on_add [ComponentHook] for this Component if one is defined.
§

fn on_insert() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>

Gets the on_insert [ComponentHook] for this Component if one is defined.
§

fn on_replace() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>

Gets the on_replace [ComponentHook] for this Component if one is defined.
§

fn on_remove() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>

Gets the on_remove [ComponentHook] for this Component if one is defined.
§

fn on_despawn() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>

Gets the on_despawn [ComponentHook] for this Component if one is defined.
§

fn map_entities<E>(_this: &mut Self, _mapper: &mut E)
where E: EntityMapper,

Maps the entities on this component using the given [EntityMapper]. This is used to remap entities in contexts like scenes and entity cloning. When deriving Component, this is populated by annotating fields containing entities with #[entities] Read more
Source§

impl Deref for LocalPlayerEvents

Source§

type Target = UnboundedSender<Event>

The resulting type after dereferencing.
Source§

fn deref(&self) -> &Self::Target

Dereferences the value.
Source§

impl DerefMut for LocalPlayerEvents

Source§

fn deref_mut(&mut self) -> &mut Self::Target

Mutably dereferences the value.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<C> Bundle for C
where C: Component,

§

fn component_ids( components: &mut ComponentsRegistrator<'_>, ) -> impl Iterator<Item = ComponentId> + use<C>

§

fn get_component_ids( components: &Components, ) -> impl Iterator<Item = Option<ComponentId>>

Return a iterator over this [Bundle]’s component ids. This will be None if the component has not been registered.
§

impl<C> BundleFromComponents for C
where C: Component,

§

unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> C
where F: for<'a> FnMut(&'a mut T) -> OwningPtr<'a>,

§

impl<T> CompatExt for T

§

fn compat(self) -> Compat<T>

Applies the [Compat] adapter by value. Read more
§

fn compat_ref(&self) -> Compat<&T>

Applies the [Compat] adapter by shared reference. Read more
§

fn compat_mut(&mut self) -> Compat<&mut T>

Applies the [Compat] adapter by mutable reference. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSend for T
where T: Any + Send,

§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

impl<C> DynamicBundle for C
where C: Component,

§

type Effect = ()

An operation on the entity that happens after inserting this bundle.
§

unsafe fn get_components( ptr: MovingPtr<'_, C>, func: &mut impl FnMut(StorageType, OwningPtr<'_>), ) -> <C as DynamicBundle>::Effect

Moves the components out of the bundle. Read more
§

unsafe fn apply_effect( _ptr: MovingPtr<'_, MaybeUninit<C>>, _entity: &mut EntityWorldMut<'_>, )

Applies the after-effects of spawning this bundle. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> IntoResult<T> for T

§

fn into_result(self) -> Result<T, RunSystemError>

Converts this type into the system output type.
§

impl<A> Is for A
where A: Any,

§

fn is<T>() -> bool
where T: Any,

Checks if the current type “is” another type, using a TypeId equality comparison. This is most useful in the context of generic logic. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<T> ConditionalSend for T
where T: Send,