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

PongPlugin

This commit is contained in:
mat 2025-03-25 05:16:10 +00:00
parent 4c53498f07
commit 8af265e48b
9 changed files with 113 additions and 17 deletions

View file

@ -84,6 +84,7 @@ use crate::{
login::{self, InLoginState, LoginSendPacketQueue},
},
player::retroactively_add_game_profile_component,
pong::PongPlugin,
raw_connection::RawConnection,
respawn::RespawnPlugin,
task_pool::TaskPoolPlugin,
@ -1035,7 +1036,8 @@ impl PluginGroup for DefaultPlugins {
.add(ChunksPlugin)
.add(TickEndPlugin)
.add(BrandPlugin)
.add(TickBroadcastPlugin);
.add(TickBroadcastPlugin)
.add(PongPlugin);
#[cfg(feature = "log")]
{
group = group.add(bevy_log::LogPlugin::default());

View file

@ -191,12 +191,11 @@ pub fn spawn_listener(
pub fn chat_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<ChatReceivedEvent>) {
for event in events.read() {
let local_player_events = query
.get(event.entity)
.expect("Non-local entities shouldn't be able to receive chat events");
if let Ok(local_player_events) = query.get(event.entity) {
let _ = local_player_events.send(Event::Chat(event.packet.clone()));
}
}
}
// only tick if we're in a world
pub fn tick_listener(query: Query<&LocalPlayerEvents, With<InstanceName>>) {
@ -210,12 +209,11 @@ pub fn packet_listener(
mut events: EventReader<ReceivePacketEvent>,
) {
for event in events.read() {
let local_player_events = query
.get(event.entity)
.expect("Non-local entities shouldn't be able to receive packet events");
if let Ok(local_player_events) = query.get(event.entity) {
let _ = local_player_events.send(Event::Packet(event.packet.clone()));
}
}
}
pub fn add_player_listener(
query: Query<&LocalPlayerEvents>,

View file

@ -9,6 +9,7 @@ pub mod inventory;
pub mod mining;
pub mod movement;
pub mod packet;
pub mod pong;
pub mod respawn;
pub mod task_pool;
pub mod tick_end;

View file

@ -109,3 +109,13 @@ fn packet_interrupts(packet: &ClientboundConfigPacket) -> bool {
| ClientboundConfigPacket::Transfer(_)
)
}
/// A Bevy trigger that's sent when our client receives a [`ClientboundPing`]
/// packet in the config state.
///
/// See [`PingEvent`] for more information.
///
/// [`ClientboundPing`]: azalea_protocol::packets::config::ClientboundPing
/// [`PingEvent`]: crate::packet::game::PingEvent
#[derive(Event, Debug, Clone)]
pub struct ConfigPingEvent(pub azalea_protocol::packets::config::ClientboundPing);

View file

@ -142,10 +142,8 @@ impl ConfigPacketHandler<'_> {
pub fn ping(&mut self, p: ClientboundPing) {
debug!("Got ping packet (in configuration) {p:?}");
as_system::<Query<&RawConnection>>(self.ecs, |query| {
let raw_conn = query.get(self.player).unwrap();
raw_conn.write_packet(ServerboundPong { id: p.id }).unwrap();
as_system::<Commands>(self.ecs, |mut commands| {
commands.trigger_targets(ConfigPingEvent(p), self.player);
});
}

View file

@ -212,3 +212,21 @@ pub struct InstanceLoadedEvent {
pub name: ResourceLocation,
pub instance: Weak<RwLock<Instance>>,
}
/// A Bevy trigger that's sent when our client receives a [`ClientboundPing`]
/// packet in the game state.
///
/// Also see [`ConfigPingEvent`] which is used for the config state.
///
/// This is not an event and can't be listened to from a normal system,
///so `EventReader<PingEvent>` will not work.
///
/// To use it, add your "system" with `add_observer` instead of `add_systems`
/// and use `Trigger<PingEvent>` instead of `EventReader`.
///
/// The client Entity that received the packet will be attached to the trigger.
///
/// [`ClientboundPing`]: azalea_protocol::packets::game::ClientboundPing
/// [`ConfigPingEvent`]: crate::packet::config::ConfigPingEvent
#[derive(Event, Debug, Clone)]
pub struct PingEvent(pub azalea_protocol::packets::game::ClientboundPing);

View file

@ -1363,10 +1363,7 @@ impl GamePacketHandler<'_> {
debug!("Got ping packet {p:?}");
as_system::<Commands>(self.ecs, |mut commands| {
commands.trigger(SendPacketEvent::new(
self.player,
ServerboundPong { id: p.id },
));
commands.trigger_targets(PingEvent(p.clone()), self.player);
});
}

View file

@ -0,0 +1,37 @@
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use super::packet::{
config::{ConfigPingEvent, SendConfigPacketEvent},
game::PingEvent,
};
use crate::packet::game::SendPacketEvent;
/// A plugin that replies to [`ClientboundPing`] packets with
/// [`ServerboundPong`].
///
/// This works in both the `game` and `config` states.
///
/// [`ClientboundPing`]: azalea_protocol::packets::game::ClientboundPing
/// [`ServerboundPong`]: azalea_protocol::packets::game::ServerboundPong
pub struct PongPlugin;
impl Plugin for PongPlugin {
fn build(&self, app: &mut App) {
app.add_observer(reply_to_game_ping)
.add_observer(reply_to_config_ping);
}
}
pub fn reply_to_game_ping(trigger: Trigger<PingEvent>, mut commands: Commands) {
commands.trigger(SendPacketEvent::new(
trigger.entity(),
azalea_protocol::packets::game::ServerboundPong { id: trigger.0.id },
));
}
pub fn reply_to_config_ping(trigger: Trigger<ConfigPingEvent>, mut commands: Commands) {
commands.trigger(SendConfigPacketEvent::new(
trigger.entity(),
azalea_protocol::packets::config::ServerboundPong { id: trigger.0.id },
));
}

View file

@ -0,0 +1,35 @@
use std::sync::Arc;
use azalea_client::{packet::game::SendPacketEvent, test_simulation::*};
use azalea_protocol::packets::{
ConnectionProtocol,
game::{ClientboundPing, ServerboundGamePacket},
};
use bevy_ecs::observer::Trigger;
use bevy_log::tracing_subscriber;
use parking_lot::Mutex;
#[test]
fn reply_to_ping_with_pong() {
let _ = tracing_subscriber::fmt::try_init();
let mut simulation = Simulation::new(ConnectionProtocol::Game);
let reply_count = Arc::new(Mutex::new(0));
let reply_count_clone = reply_count.clone();
simulation
.app
.add_observer(move |trigger: Trigger<SendPacketEvent>| {
if trigger.sent_by == simulation.entity {
if let ServerboundGamePacket::Pong(packet) = &trigger.packet {
assert_eq!(packet.id, 123);
*reply_count_clone.lock() += 1;
}
}
});
simulation.tick();
simulation.receive_packet(ClientboundPing { id: 123 });
simulation.tick();
assert_eq!(*reply_count.lock(), 1);
}