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>>
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?
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)
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
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
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>
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
pub fn strong_count(&self) -> usize
Returns the number of [UnboundedSender] handles.
pub fn weak_count(&self) -> usize
pub fn weak_count(&self) -> usize
Returns the number of [WeakUnboundedSender] handles.
Trait Implementations§
Source§impl Component for LocalPlayerEvents
impl Component for LocalPlayerEvents
Source§const STORAGE_TYPE: StorageType = bevy_ecs::component::StorageType::Table
const STORAGE_TYPE: StorageType = bevy_ecs::component::StorageType::Table
Source§type Mutability = Mutable
type Mutability = Mutable
Component<Mutability = Mutable>,
while immutable components will instead have Component<Mutability = Immutable>. Read moreSource§fn register_required_components(
_requiree: ComponentId,
required_components: &mut RequiredComponentsRegistrator<'_, '_>,
)
fn register_required_components( _requiree: ComponentId, required_components: &mut RequiredComponentsRegistrator<'_, '_>, )
Source§fn clone_behavior() -> ComponentCloneBehavior
fn clone_behavior() -> ComponentCloneBehavior
Source§fn relationship_accessor() -> Option<ComponentRelationshipAccessor<Self>>
fn relationship_accessor() -> Option<ComponentRelationshipAccessor<Self>>
ComponentRelationshipAccessor] required for working with relationships in dynamic contexts. Read more§fn on_replace() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
fn on_replace() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
§fn on_despawn() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
fn on_despawn() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
§fn map_entities<E>(_this: &mut Self, _mapper: &mut E)where
E: EntityMapper,
fn map_entities<E>(_this: &mut Self, _mapper: &mut E)where
E: EntityMapper,
Source§impl Deref for LocalPlayerEvents
impl Deref for LocalPlayerEvents
Auto Trait Implementations§
impl Freeze for LocalPlayerEvents
impl RefUnwindSafe for LocalPlayerEvents
impl Send for LocalPlayerEvents
impl Sync for LocalPlayerEvents
impl Unpin for LocalPlayerEvents
impl UnsafeUnpin for LocalPlayerEvents
impl UnwindSafe for LocalPlayerEvents
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<C> Bundle for Cwhere
C: Component,
impl<C> Bundle for Cwhere
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>>
fn get_component_ids( components: &Components, ) -> impl Iterator<Item = Option<ComponentId>>
Bundle]’s component ids. This will be None if the component has not been registered.§impl<T> CompatExt for T
impl<T> CompatExt for T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
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)
fn as_any(&self) -> &(dyn Any + 'static)
&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)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.