valence/crates/valence_client/src/event_loop.rs
Ryan Johnson c5557e744d
Move packets out of valence_core. (#335)
## Description

- Move all packets out of `valence_core` and into the places where
they're actually used. This has a few benefits:
- Avoids compiling code for packets that go unused when feature flags
are disabled.
- Code is distributed more uniformly across crates, improving
compilation times.
- Improves local reasoning when everything relevant to a module is
defined in the same place.
  - Easier to share code between the packet consumer and the packet.
- Tweak `Packet` macro syntax.
- Update `syn` to 2.0.
- Reorganize some code in `valence_client` (needs further work).
- Impl `WritePacket` for `Instance`.
- Remove packet enums such as `S2cPlayPacket` and `C2sPlayPacket`.
- Replace `assert_packet_count` and `assert_packet_order` macros with
non-macro methods.
To prevent this PR from getting out of hand, I've disabled the packet
inspector and stresser until they have been rewritten to account for
these changes.
2023-05-29 01:36:11 -07:00

181 lines
5.5 KiB
Rust

use std::time::Instant;
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_ecs::schedule::ScheduleLabel;
use bevy_ecs::system::SystemState;
use bytes::Bytes;
use tracing::{debug, warn};
use valence_core::protocol::{Decode, Packet};
use valence_entity::hitbox::HitboxUpdateSet;
use crate::{Client, SpawnClientsSet};
pub(super) fn build(app: &mut App) {
app.configure_set(
RunEventLoopSet
.in_base_set(CoreSet::PreUpdate)
.after(SpawnClientsSet)
.after(HitboxUpdateSet),
)
.add_system(run_event_loop.in_set(RunEventLoopSet))
.add_event::<PacketEvent>();
// Add the event loop schedule.
let mut event_loop = Schedule::new();
event_loop.set_default_base_set(EventLoopSet::Update);
event_loop.configure_sets((
EventLoopSet::PreUpdate.before(EventLoopSet::Update),
EventLoopSet::Update.before(EventLoopSet::PostUpdate),
EventLoopSet::PostUpdate,
));
app.add_schedule(EventLoopSchedule, event_loop);
}
/// The [`ScheduleLabel`] for the event loop [`Schedule`].
#[derive(ScheduleLabel, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct EventLoopSchedule;
#[derive(SystemSet, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
#[system_set(base)]
pub enum EventLoopSet {
PreUpdate,
#[default]
Update,
PostUpdate,
}
#[derive(SystemSet, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct RunEventLoopSet;
#[derive(Clone, Debug)]
pub struct PacketEvent {
/// The client this packet originated from.
pub client: Entity,
/// The moment in time this packet arrived.
pub timestamp: Instant,
/// This packet's ID.
pub id: i32,
/// The content of the packet, excluding the leading varint packet ID.
pub data: Bytes,
}
impl PacketEvent {
/// Attempts to decode this packet as the packet `P`.
///
/// If the packet ID is mismatched or an error occurs, `None` is returned.
/// Otherwise, `Some` is returned containing the decoded packet.
#[inline]
pub fn decode<'a, P>(&'a self) -> Option<P>
where
P: Packet + Decode<'a>,
{
if self.id == P::ID {
let mut r = &self.data[..];
match P::decode(&mut r) {
Ok(pkt) => {
if r.is_empty() {
return Some(pkt);
}
warn!(
"missed {} bytes while decoding packet {} (ID = {})",
r.len(),
P::NAME,
P::ID
);
debug!("complete packet after partial decode: {pkt:?}");
}
Err(e) => {
warn!("failed to decode packet with ID of {}: {e:#}", P::ID);
}
}
}
None
}
}
/// An exclusive system for running the event loop schedule.
#[allow(clippy::type_complexity)]
fn run_event_loop(
world: &mut World,
state: &mut SystemState<(
Query<(Entity, &mut Client)>,
EventWriter<PacketEvent>,
Commands,
)>,
mut check_again: Local<Vec<(Entity, usize)>>,
) {
debug_assert!(check_again.is_empty());
let (mut clients, mut event_writer, mut commands) = state.get_mut(world);
for (entity, mut client) in &mut clients {
match client.connection_mut().try_recv() {
Ok(Some(pkt)) => {
event_writer.send(PacketEvent {
client: entity,
timestamp: pkt.timestamp,
id: pkt.id,
data: pkt.body,
});
let remaining = client.connection().len();
if remaining > 0 {
check_again.push((entity, remaining));
}
}
Ok(None) => {}
Err(e) => {
// Client is disconnected.
debug!("disconnecting client: {e:#}");
commands.entity(entity).remove::<Client>();
}
}
}
state.apply(world);
world.run_schedule(EventLoopSchedule);
while !check_again.is_empty() {
let (mut clients, mut event_writer, mut commands) = state.get_mut(world);
check_again.retain_mut(|(entity, remaining)| {
debug_assert!(*remaining > 0);
if let Ok((_, mut client)) = clients.get_mut(*entity) {
match client.connection_mut().try_recv() {
Ok(Some(pkt)) => {
event_writer.send(PacketEvent {
client: *entity,
timestamp: pkt.timestamp,
id: pkt.id,
data: pkt.body,
});
*remaining -= 1;
// Keep looping as long as there are packets to process this tick.
*remaining > 0
}
Ok(None) => false,
Err(e) => {
// Client is disconnected.
debug!("disconnecting client: {e:#}");
commands.entity(*entity).remove::<Client>();
false
}
}
} else {
// Client must have been deleted in the last run of the schedule.
false
}
});
state.apply(world);
world.run_schedule(EventLoopSchedule);
}
}