Refactor Schedule (#289)

## Description

- Tweak the system schedule to be more parallel-friendly and explicit.
Dependencies between systems is clearer.
- System and resource configuration is better encapsulated in each
module by using plugins.

The schedule graph for `Main` now looks like this (using
`bevy_mod_debugdump`):


![graph](https://user-images.githubusercontent.com/31678482/225321113-0ec4f4dd-86f6-45fe-8158-12a451536770.svg)

### Unresolved

- What to do about ambiguous systems? Many systems here are ambiguous
because they take `&mut Client`, but the order they write packets isn't
really important. Marking them as ambiguous explicitly with
`.ambiguous_with*` across module boundaries seems difficult and
cumbersome.

## Test Plan

Steps:
1. Run examples. Check that I didn't screw up the system order.
This commit is contained in:
Ryan Johnson 2023-03-15 16:12:59 -07:00 committed by GitHub
parent 255b78e02c
commit 2c0fb2d8c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 294 additions and 174 deletions

View file

@ -1,6 +1,7 @@
[workspace] [workspace]
members = ["crates/*"] members = ["crates/*"]
exclude = ["rust-mc-bot"] exclude = ["rust-mc-bot"]
resolver = "2"
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 3 opt-level = 3

View file

@ -3,9 +3,9 @@ use std::net::IpAddr;
use std::num::Wrapping; use std::num::Wrapping;
use std::time::Instant; use std::time::Instant;
use bevy_app::{CoreSet, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_ecs::query::WorldQuery; use bevy_ecs::query::WorldQuery;
use bevy_ecs::schedule::SystemConfigs;
use bevy_ecs::system::Command; use bevy_ecs::system::Command;
use bytes::BytesMut; use bytes::BytesMut;
use glam::{DVec3, Vec3}; use glam::{DVec3, Vec3};
@ -38,10 +38,10 @@ use crate::component::{
}; };
use crate::dimension::DimensionId; use crate::dimension::DimensionId;
use crate::entity::{velocity_to_packet_units, EntityStatus, McEntity, TrackedData}; use crate::entity::{velocity_to_packet_units, EntityStatus, McEntity, TrackedData};
use crate::instance::Instance; use crate::instance::{Instance, UpdateInstancesPreClientSet};
use crate::inventory::{Inventory, InventoryKind}; use crate::inventory::{Inventory, InventoryKind};
use crate::packet::WritePacket; use crate::packet::WritePacket;
use crate::prelude::ScratchBuffer; use crate::prelude::ScratchBuf;
use crate::server::{NewClientInfo, Server}; use crate::server::{NewClientInfo, Server};
use crate::view::{ChunkPos, ChunkView}; use crate::view::{ChunkPos, ChunkView};
@ -52,8 +52,8 @@ pub mod event;
#[derive(Bundle)] #[derive(Bundle)]
pub(crate) struct ClientBundle { pub(crate) struct ClientBundle {
client: Client, client: Client,
scratch: ScratchBuffer, scratch: ScratchBuf,
entity_remove_buffer: EntityRemoveBuffer, entity_remove_buffer: EntityRemoveBuf,
username: Username, username: Username,
uuid: UniqueId, uuid: UniqueId,
ip: Ip, ip: Ip,
@ -95,8 +95,8 @@ impl ClientBundle {
) -> Self { ) -> Self {
Self { Self {
client: Client { conn, enc, dec }, client: Client { conn, enc, dec },
scratch: ScratchBuffer::default(), scratch: ScratchBuf::default(),
entity_remove_buffer: EntityRemoveBuffer(vec![]), entity_remove_buffer: EntityRemoveBuf(vec![]),
username: Username(info.username), username: Username(info.username),
uuid: UniqueId(info.uuid), uuid: UniqueId(info.uuid),
ip: Ip(info.ip), ip: Ip(info.ip),
@ -396,9 +396,9 @@ impl Command for DisconnectClient {
/// ///
/// You should not need to use this directly under normal circumstances. /// You should not need to use this directly under normal circumstances.
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub struct EntityRemoveBuffer(Vec<VarInt>); pub struct EntityRemoveBuf(Vec<VarInt>);
impl EntityRemoveBuffer { impl EntityRemoveBuf {
pub fn push(&mut self, entity_id: i32) { pub fn push(&mut self, entity_id: i32) {
debug_assert!( debug_assert!(
entity_id != 0, entity_id != 0,
@ -599,25 +599,45 @@ pub fn despawn_disconnected_clients(
} }
} }
pub(crate) fn update_clients() -> SystemConfigs { pub(crate) struct ClientPlugin;
/// When clients have their packet buffer flushed. Any system that writes
/// packets to clients should happen before this. Otherwise, the data
/// will arrive one tick late.
#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct FlushPacketsSet;
impl Plugin for ClientPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.add_systems(
( (
initial_join, initial_join,
update_chunk_load_dist, update_chunk_load_dist,
respawn, read_data_in_view
send_keepalive, .after(UpdateInstancesPreClientSet)
read_data_in_view, .after(update_chunk_load_dist),
update_view, update_view.after(initial_join).after(read_data_in_view),
remove_entities, respawn.after(update_view),
remove_entities.after(update_view),
update_spawn_position.after(update_view),
update_old_view_dist.after(update_view),
teleport.after(update_view),
update_game_mode, update_game_mode,
teleport, send_keepalive,
update_view_dist,
update_compass_pos,
update_tracked_data, update_tracked_data,
update_op_level, update_op_level,
acknowledge_player_actions, acknowledge_player_actions,
flush_packets,
) )
.chain() .in_base_set(CoreSet::PostUpdate)
.before(FlushPacketsSet),
)
.configure_set(
FlushPacketsSet
.in_base_set(CoreSet::PostUpdate)
.after(UpdateInstancesPreClientSet),
)
.add_system(flush_packets.in_set(FlushPacketsSet));
}
} }
#[derive(WorldQuery)] #[derive(WorldQuery)]
@ -766,8 +786,8 @@ fn update_chunk_load_dist(
fn read_data_in_view( fn read_data_in_view(
mut clients: Query<( mut clients: Query<(
&mut Client, &mut Client,
&mut ScratchBuffer, &mut ScratchBuf,
&mut EntityRemoveBuffer, &mut EntityRemoveBuf,
&OldLocation, &OldLocation,
&OldPosition, &OldPosition,
&OldViewDistance, &OldViewDistance,
@ -845,12 +865,15 @@ fn read_data_in_view(
/// Updates the clients' view, i.e. the set of chunks that are visible from the /// Updates the clients' view, i.e. the set of chunks that are visible from the
/// client's chunk position. /// client's chunk position.
///
/// This handles the situation when a client changes instances or chunk
/// position. It must run after [`read_data_in_view`].
fn update_view( fn update_view(
mut clients: Query< mut clients: Query<
( (
&mut Client, &mut Client,
&mut ScratchBuffer, &mut ScratchBuf,
&mut EntityRemoveBuffer, &mut EntityRemoveBuf,
&Location, &Location,
&OldLocation, &OldLocation,
&Position, &Position,
@ -1004,8 +1027,9 @@ fn update_view(
); );
} }
/// Removes all the entities that are queued to be removed for each client.
fn remove_entities( fn remove_entities(
mut clients: Query<(&mut Client, &mut EntityRemoveBuffer), Changed<EntityRemoveBuffer>>, mut clients: Query<(&mut Client, &mut EntityRemoveBuf), Changed<EntityRemoveBuf>>,
) { ) {
for (mut client, mut buf) in &mut clients { for (mut client, mut buf) in &mut clients {
if !buf.0.is_empty() { if !buf.0.is_empty() {
@ -1032,6 +1056,10 @@ fn update_game_mode(mut clients: Query<(&mut Client, &GameMode), Changed<GameMod
} }
} }
/// Syncs the client's position and look with the server.
///
/// This should happen after chunks are loaded so the client doesn't fall though
/// the floor.
fn teleport( fn teleport(
mut clients: Query< mut clients: Query<
(&mut Client, &mut TeleportState, &Position, &Look), (&mut Client, &mut TeleportState, &Position, &Look),
@ -1069,7 +1097,7 @@ fn teleport(
} }
} }
fn update_view_dist( fn update_old_view_dist(
mut clients: Query<(&mut OldViewDistance, &ViewDistance), Changed<ViewDistance>>, mut clients: Query<(&mut OldViewDistance, &ViewDistance), Changed<ViewDistance>>,
) { ) {
for (mut old_dist, dist) in &mut clients { for (mut old_dist, dist) in &mut clients {
@ -1077,9 +1105,11 @@ fn update_view_dist(
} }
} }
fn update_compass_pos(mut clients: Query<(&mut Client, &CompassPos), Changed<CompassPos>>) { /// Sets the client's compass position.
// This also closes the "downloading terrain" screen when first joining, so ///
// we should do this after the initial chunks are written. /// This also closes the "downloading terrain" screen when first joining, so
/// it should happen after the initial chunks are written.
fn update_spawn_position(mut clients: Query<(&mut Client, &CompassPos), Changed<CompassPos>>) {
for (mut client, compass_pos) in &mut clients { for (mut client, compass_pos) in &mut clients {
client.write_packet(&PlayerSpawnPositionS2c { client.write_packet(&PlayerSpawnPositionS2c {
position: compass_pos.0, position: compass_pos.0,

View file

@ -1,8 +1,10 @@
use std::cmp; use std::cmp;
use anyhow::bail; use anyhow::bail;
use bevy_app::{CoreSet, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_ecs::query::WorldQuery; use bevy_ecs::query::WorldQuery;
use bevy_ecs::schedule::ScheduleLabel;
use bevy_ecs::system::{SystemParam, SystemState}; use bevy_ecs::system::{SystemParam, SystemState};
use glam::{DVec3, Vec3}; use glam::{DVec3, Vec3};
use paste::paste; use paste::paste;
@ -39,7 +41,6 @@ use crate::client::Client;
use crate::component::{Look, OnGround, Ping, Position}; use crate::component::{Look, OnGround, Ping, Position};
use crate::entity::{EntityAnimation, EntityKind, McEntity, TrackedData}; use crate::entity::{EntityAnimation, EntityKind, McEntity, TrackedData};
use crate::inventory::Inventory; use crate::inventory::Inventory;
use crate::server::EventLoopSchedule;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct QueryBlockNbt { pub struct QueryBlockNbt {
@ -527,7 +528,7 @@ macro_rules! events {
)* )*
) => { ) => {
/// Inserts [`Events`] resources into the world for each client event. /// Inserts [`Events`] resources into the world for each client event.
pub(crate) fn register_client_events(world: &mut World) { fn register_client_events(world: &mut World) {
$( $(
$( $(
world.insert_resource(Events::<$name>::default()); world.insert_resource(Events::<$name>::default());
@ -641,6 +642,31 @@ events! {
} }
} }
pub(crate) struct ClientEventPlugin;
/// The [`ScheduleLabel`] for the event loop [`Schedule`].
#[derive(ScheduleLabel, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct EventLoopSchedule;
/// The default base set for [`EventLoopSchedule`].
#[derive(SystemSet, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct EventLoopSet;
impl Plugin for ClientEventPlugin {
fn build(&self, app: &mut bevy_app::App) {
register_client_events(&mut app.world);
app.configure_set(EventLoopSet.in_base_set(CoreSet::PreUpdate))
.add_system(run_event_loop.in_set(EventLoopSet));
// Add the event loop schedule.
let mut event_loop = Schedule::new();
event_loop.set_default_base_set(EventLoopSet);
app.add_schedule(EventLoopSchedule, event_loop);
}
}
#[derive(WorldQuery)] #[derive(WorldQuery)]
#[world_query(mutable)] #[world_query(mutable)]
pub(crate) struct EventLoopQuery { pub(crate) struct EventLoopQuery {
@ -659,7 +685,7 @@ pub(crate) struct EventLoopQuery {
} }
/// An exclusive system for running the event loop schedule. /// An exclusive system for running the event loop schedule.
pub(crate) fn run_event_loop( fn run_event_loop(
world: &mut World, world: &mut World,
state: &mut SystemState<(Query<EventLoopQuery>, ClientEvents, Commands)>, state: &mut SystemState<(Query<EventLoopQuery>, ClientEvents, Commands)>,
mut clients_to_check: Local<Vec<Entity>>, mut clients_to_check: Local<Vec<Entity>>,
@ -1384,7 +1410,7 @@ fn handle_one_packet(
/// is subject to change. /// is subject to change.
/// ///
/// This system must be scheduled to run in the /// This system must be scheduled to run in the
/// [`EventLoopSchedule`](crate::server::EventLoopSchedule). Otherwise, it may /// [`EventLoopSchedule`]. Otherwise, it may
/// not function correctly. /// not function correctly.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn default_event_handler( pub fn default_event_handler(

View file

@ -1,11 +1,13 @@
use std::fmt; use std::fmt;
use bevy_app::{CoreSet, Plugin};
/// Contains shared components and world queries. /// Contains shared components and world queries.
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use glam::{DVec3, Vec3}; use glam::{DVec3, Vec3};
use uuid::Uuid; use uuid::Uuid;
use valence_protocol::types::{GameMode as ProtocolGameMode, Property}; use valence_protocol::types::{GameMode as ProtocolGameMode, Property};
use crate::prelude::FlushPacketsSet;
use crate::util::{from_yaw_and_pitch, to_yaw_and_pitch}; use crate::util::{from_yaw_and_pitch, to_yaw_and_pitch};
use crate::view::ChunkPos; use crate::view::ChunkPos;
use crate::NULL_ENTITY; use crate::NULL_ENTITY;
@ -111,12 +113,6 @@ impl Default for Location {
pub struct OldLocation(Entity); pub struct OldLocation(Entity);
impl OldLocation { impl OldLocation {
pub(crate) fn update(mut query: Query<(&Location, &mut OldLocation), Changed<Location>>) {
for (loc, mut old_loc) in &mut query {
old_loc.0 = loc.0;
}
}
pub fn new(instance: Entity) -> Self { pub fn new(instance: Entity) -> Self {
Self(instance) Self(instance)
} }
@ -153,12 +149,6 @@ impl Position {
pub struct OldPosition(DVec3); pub struct OldPosition(DVec3);
impl OldPosition { impl OldPosition {
pub(crate) fn update(mut query: Query<(&Position, &mut OldPosition), Changed<Position>>) {
for (pos, mut old_pos) in &mut query {
old_pos.0 = pos.0;
}
}
pub fn new(pos: DVec3) -> Self { pub fn new(pos: DVec3) -> Self {
Self(pos) Self(pos)
} }
@ -201,4 +191,39 @@ impl Look {
pub struct OnGround(pub bool); pub struct OnGround(pub bool);
#[derive(Component, Default, Debug)] #[derive(Component, Default, Debug)]
pub struct ScratchBuffer(pub Vec<u8>); pub struct ScratchBuf(pub Vec<u8>);
pub(crate) struct ComponentPlugin;
impl Plugin for ComponentPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.add_systems(
(update_old_position, update_old_location)
.in_base_set(CoreSet::PostUpdate)
.after(FlushPacketsSet),
)
// This is fine because we're applying system buffers later.
.add_system(despawn_marked_entities.in_base_set(CoreSet::PostUpdate
));
}
}
fn update_old_position(mut query: Query<(&Position, &mut OldPosition), Changed<Position>>) {
for (pos, mut old_pos) in &mut query {
old_pos.0 = pos.0;
}
}
fn update_old_location(mut query: Query<(&Location, &mut OldLocation), Changed<Location>>) {
for (loc, mut old_loc) in &mut query {
old_loc.0 = loc.0;
}
}
/// Despawns all the entities marked as despawned with the [`Despawned`]
/// component.
fn despawn_marked_entities(mut commands: Commands, entities: Query<Entity, With<Despawned>>) {
for entity in &entities {
commands.entity(entity).despawn();
}
}

View file

@ -3,6 +3,7 @@ use std::fmt;
use std::fmt::Formatter; use std::fmt::Formatter;
use std::ops::Range; use std::ops::Range;
use bevy_app::{App, CoreSet, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
pub use data::{EntityKind, TrackedData}; pub use data::{EntityKind, TrackedData};
use glam::{DVec3, UVec3, Vec3}; use glam::{DVec3, UVec3, Vec3};
@ -21,6 +22,7 @@ use valence_protocol::var_int::VarInt;
use crate::component::Despawned; use crate::component::Despawned;
use crate::config::DEFAULT_TPS; use crate::config::DEFAULT_TPS;
use crate::packet::WritePacket; use crate::packet::WritePacket;
use crate::prelude::FlushPacketsSet;
use crate::util::Aabb; use crate::util::Aabb;
use crate::NULL_ENTITY; use crate::NULL_ENTITY;
@ -28,30 +30,32 @@ pub mod data;
include!(concat!(env!("OUT_DIR"), "/entity_event.rs")); include!(concat!(env!("OUT_DIR"), "/entity_event.rs"));
/// A [`Resource`] which maintains information about all the [`McEntity`] pub(crate) struct EntityPlugin;
/// components on the server.
#[derive(Resource, Debug)]
pub struct McEntityManager {
protocol_id_to_mcentity: FxHashMap<i32, Entity>,
next_protocol_id: i32,
}
impl McEntityManager { /// When new Minecraft entities are initialized and added to
pub(crate) fn new() -> Self { /// [`McEntityManager`]. Systems that need all Minecraft entities to be in a
Self { /// valid state should run after this.
protocol_id_to_mcentity: HashMap::default(), #[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
next_protocol_id: 1, pub(crate) struct InitEntitiesSet;
impl Plugin for EntityPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(McEntityManager::new())
.configure_set(InitEntitiesSet.in_base_set(CoreSet::PostUpdate))
.add_system(init_mcentities.in_set(InitEntitiesSet))
.add_systems(
(
remove_despawned_from_manager.after(init_mcentities),
update_mcentities.after(FlushPacketsSet),
)
.in_base_set(CoreSet::PostUpdate),
);
} }
} }
/// Gets the [`Entity`] of the [`McEntity`] with the given protocol ID. /// Sets the protocol ID of new mcentities and adds them to the
pub fn get_with_protocol_id(&self, id: i32) -> Option<Entity> { /// [`McEntityManager`].
self.protocol_id_to_mcentity.get(&id).cloned() fn init_mcentities(
}
}
/// Sets the protocol ID of new mcentities.
pub(crate) fn init_mcentities(
mut entities: Query<(Entity, &mut McEntity), Added<McEntity>>, mut entities: Query<(Entity, &mut McEntity), Added<McEntity>>,
mut manager: ResMut<McEntityManager>, mut manager: ResMut<McEntityManager>,
) { ) {
@ -72,7 +76,7 @@ pub(crate) fn init_mcentities(
} }
/// Removes despawned mcentities from the mcentity manager. /// Removes despawned mcentities from the mcentity manager.
pub(crate) fn deinit_despawned_mcentities( fn remove_despawned_from_manager(
entities: Query<&mut McEntity, With<Despawned>>, entities: Query<&mut McEntity, With<Despawned>>,
mut manager: ResMut<McEntityManager>, mut manager: ResMut<McEntityManager>,
) { ) {
@ -81,7 +85,7 @@ pub(crate) fn deinit_despawned_mcentities(
} }
} }
pub(crate) fn update_mcentities(mut mcentities: Query<&mut McEntity, Changed<McEntity>>) { fn update_mcentities(mut mcentities: Query<&mut McEntity, Changed<McEntity>>) {
for mut ent in &mut mcentities { for mut ent in &mut mcentities {
ent.data.clear_modifications(); ent.data.clear_modifications();
ent.old_position = ent.position; ent.old_position = ent.position;
@ -94,6 +98,28 @@ pub(crate) fn update_mcentities(mut mcentities: Query<&mut McEntity, Changed<McE
} }
} }
/// A [`Resource`] which maintains information about all the [`McEntity`]
/// components on the server.
#[derive(Resource, Debug)]
pub struct McEntityManager {
protocol_id_to_mcentity: FxHashMap<i32, Entity>,
next_protocol_id: i32,
}
impl McEntityManager {
fn new() -> Self {
Self {
protocol_id_to_mcentity: HashMap::default(),
next_protocol_id: 1,
}
}
/// Gets the [`Entity`] of the [`McEntity`] with the given protocol ID.
pub fn get_with_protocol_id(&self, id: i32) -> Option<Entity> {
self.protocol_id_to_mcentity.get(&id).cloned()
}
}
/// A component for Minecraft entities. For Valence to recognize a /// A component for Minecraft entities. For Valence to recognize a
/// Minecraft entity, it must have this component attached. /// Minecraft entity, it must have this component attached.
/// ///

View file

@ -3,6 +3,7 @@ use std::collections::hash_map::Entry;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::iter::FusedIterator; use std::iter::FusedIterator;
use bevy_app::{CoreSet, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
pub use chunk::{Block, BlockEntity, BlockMut, BlockRef, Chunk}; pub use chunk::{Block, BlockEntity, BlockMut, BlockRef, Chunk};
pub use chunk_entry::*; pub use chunk_entry::*;
@ -20,8 +21,9 @@ use valence_protocol::Packet;
use crate::component::Despawned; use crate::component::Despawned;
use crate::dimension::DimensionId; use crate::dimension::DimensionId;
use crate::entity::McEntity; use crate::entity::{InitEntitiesSet, McEntity};
use crate::packet::{PacketWriter, WritePacket}; use crate::packet::{PacketWriter, WritePacket};
use crate::prelude::FlushPacketsSet;
use crate::server::{Server, SharedServer}; use crate::server::{Server, SharedServer};
use crate::view::ChunkPos; use crate::view::ChunkPos;
@ -433,7 +435,31 @@ impl Instance {
} }
} }
pub(crate) fn update_instances_pre_client( pub(crate) struct InstancePlugin;
#[derive(SystemSet, Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct UpdateInstancesPreClientSet;
impl Plugin for InstancePlugin {
fn build(&self, app: &mut bevy_app::App) {
app.configure_set(
UpdateInstancesPreClientSet
.after(InitEntitiesSet)
.in_base_set(CoreSet::PostUpdate),
)
.add_system(update_instances_pre_client.in_set(UpdateInstancesPreClientSet))
.add_system(
update_instances_post_client
.after(FlushPacketsSet)
.in_base_set(CoreSet::PostUpdate),
);
#[cfg(debug_assertions)]
app.add_system(check_instance_invariants.in_base_set(CoreSet::PostUpdate));
}
}
fn update_instances_pre_client(
mut instances: Query<&mut Instance>, mut instances: Query<&mut Instance>,
mut entities: Query<(Entity, &mut McEntity, Option<&Despawned>)>, mut entities: Query<(Entity, &mut McEntity, Option<&Despawned>)>,
server: Res<Server>, server: Res<Server>,
@ -575,7 +601,7 @@ pub(crate) fn update_instances_pre_client(
} }
} }
pub(crate) fn update_instances_post_client(mut instances: Query<&mut Instance>) { fn update_instances_post_client(mut instances: Query<&mut Instance>) {
for mut instance in &mut instances { for mut instance in &mut instances {
instance.partition.retain(|_, cell| { instance.partition.retain(|_, cell| {
cell.packet_buf.clear(); cell.packet_buf.clear();
@ -594,8 +620,8 @@ pub(crate) fn update_instances_post_client(mut instances: Query<&mut Instance>)
} }
} }
pub(crate) fn check_instance_invariants(instances: Query<&Instance>, entities: Query<&McEntity>) {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fn check_instance_invariants(instances: Query<&Instance>, entities: Query<&McEntity>) {
for instance in &instances { for instance in &instances {
for (pos, cell) in &instance.partition { for (pos, cell) in &instance.partition {
for &id in &cell.entities { for &id in &cell.entities {
@ -606,7 +632,4 @@ pub(crate) fn check_instance_invariants(instances: Query<&Instance>, entities: Q
} }
} }
} }
let _ = instances;
let _ = entities;
} }

View file

@ -30,8 +30,8 @@ use std::borrow::Cow;
use std::iter::FusedIterator; use std::iter::FusedIterator;
use std::ops::Range; use std::ops::Range;
use bevy_app::{CoreSet, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_ecs::schedule::SystemConfigs;
use tracing::{debug, warn}; use tracing::{debug, warn};
use valence_protocol::item::ItemStack; use valence_protocol::item::ItemStack;
use valence_protocol::packet::s2c::play::{ use valence_protocol::packet::s2c::play::{
@ -47,6 +47,7 @@ use crate::client::event::{
use crate::client::{Client, CursorItem, PlayerInventoryState}; use crate::client::{Client, CursorItem, PlayerInventoryState};
use crate::component::GameMode; use crate::component::GameMode;
use crate::packet::WritePacket; use crate::packet::WritePacket;
use crate::prelude::FlushPacketsSet;
#[derive(Debug, Clone, Component)] #[derive(Debug, Clone, Component)]
pub struct Inventory { pub struct Inventory {
@ -302,8 +303,11 @@ impl OpenInventory {
} }
} }
/// The systems needed for updating the inventories. pub(crate) struct InventoryPlugin;
pub(crate) fn update_inventories() -> SystemConfigs {
impl Plugin for InventoryPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.add_systems(
( (
handle_set_held_item, handle_set_held_item,
handle_click_container handle_click_container
@ -317,7 +321,10 @@ pub(crate) fn update_inventories() -> SystemConfigs {
update_client_on_close_inventory.after(update_open_inventories), update_client_on_close_inventory.after(update_open_inventories),
update_player_inventories, update_player_inventories,
) )
.into_configs() .in_base_set(CoreSet::PostUpdate)
.before(FlushPacketsSet),
);
}
} }
/// Send updates for each client's player inventory. /// Send updates for each client's player inventory.

View file

@ -49,6 +49,7 @@ pub mod prelude {
pub use bevy_app::prelude::*; pub use bevy_app::prelude::*;
pub use bevy_ecs::prelude::*; pub use bevy_ecs::prelude::*;
pub use biome::{Biome, BiomeId}; pub use biome::{Biome, BiomeId};
pub use client::event::{EventLoopSchedule, EventLoopSet};
pub use client::*; pub use client::*;
pub use component::*; pub use component::*;
pub use config::{ pub use config::{
@ -66,7 +67,7 @@ pub mod prelude {
pub use protocol::ident::Ident; pub use protocol::ident::Ident;
pub use protocol::item::{ItemKind, ItemStack}; pub use protocol::item::{ItemKind, ItemStack};
pub use protocol::text::{Color, Text, TextFormat}; pub use protocol::text::{Color, Text, TextFormat};
pub use server::{EventLoopSchedule, EventLoopSet, NewClientInfo, Server, SharedServer}; pub use server::{NewClientInfo, Server, SharedServer};
pub use uuid::Uuid; pub use uuid::Uuid;
pub use valence_nbt::Compound; pub use valence_nbt::Compound;
pub use valence_protocol::block::BlockKind; pub use valence_protocol::block::BlockKind;

View file

@ -4,6 +4,7 @@ use std::collections::HashMap;
use std::iter::FusedIterator; use std::iter::FusedIterator;
use std::mem; use std::mem;
use bevy_app::{CoreSet, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_ecs::schedule::SystemConfigs; use bevy_ecs::schedule::SystemConfigs;
use tracing::warn; use tracing::warn;
@ -18,6 +19,7 @@ use valence_protocol::types::Property;
use crate::client::Client; use crate::client::Client;
use crate::component::{GameMode, Ping, Properties, UniqueId, Username}; use crate::component::{GameMode, Ping, Properties, UniqueId, Username};
use crate::packet::{PacketWriter, WritePacket}; use crate::packet::{PacketWriter, WritePacket};
use crate::prelude::FlushPacketsSet;
use crate::server::Server; use crate::server::Server;
/// The global list of players on a server visible by pressing the tab key by /// The global list of players on a server visible by pressing the tab key by
@ -92,7 +94,7 @@ impl PlayerList {
impl PlayerList { impl PlayerList {
/// Create a new empty player list. /// Create a new empty player list.
pub(crate) fn new() -> Self { fn new() -> Self {
Self { Self {
cached_update_packets: vec![], cached_update_packets: vec![],
entries: HashMap::new(), entries: HashMap::new(),
@ -551,8 +553,20 @@ impl<'a> VacantEntry<'a> {
} }
} }
pub(crate) struct PlayerListPlugin;
impl Plugin for PlayerListPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.insert_resource(PlayerList::new()).add_system(
update_player_list
.before(FlushPacketsSet)
.in_base_set(CoreSet::PostUpdate),
);
}
}
/// Manage all player lists on the server and send updates to clients. /// Manage all player lists on the server and send updates to clients.
pub(crate) fn update_player_list( fn update_player_list(
player_list: ResMut<PlayerList>, player_list: ResMut<PlayerList>,
server: Res<Server>, server: Res<Server>,
mut clients: Query<&mut Client>, mut clients: Query<&mut Client>,

View file

@ -8,7 +8,6 @@ use anyhow::ensure;
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_app::{ScheduleRunnerPlugin, ScheduleRunnerSettings}; use bevy_app::{ScheduleRunnerPlugin, ScheduleRunnerSettings};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_ecs::schedule::ScheduleLabel;
use flume::{Receiver, Sender}; use flume::{Receiver, Sender};
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rsa::{PublicKeyParts, RsaPrivateKey}; use rsa::{PublicKeyParts, RsaPrivateKey};
@ -20,19 +19,16 @@ use valence_protocol::ident;
use valence_protocol::types::Property; use valence_protocol::types::Property;
use crate::biome::{validate_biomes, Biome, BiomeId}; use crate::biome::{validate_biomes, Biome, BiomeId};
use crate::client::event::{register_client_events, run_event_loop}; use crate::client::event::EventLoopSet;
use crate::client::{update_clients, ClientBundle}; use crate::client::{ClientBundle, ClientPlugin};
use crate::component::{Despawned, OldLocation, OldPosition};
use crate::config::{AsyncCallbacks, ConnectionMode, ServerPlugin}; use crate::config::{AsyncCallbacks, ConnectionMode, ServerPlugin};
use crate::dimension::{validate_dimensions, Dimension, DimensionId}; use crate::dimension::{validate_dimensions, Dimension, DimensionId};
use crate::entity::{ use crate::entity::EntityPlugin;
deinit_despawned_mcentities, init_mcentities, update_mcentities, McEntityManager, use crate::instance::{Instance, InstancePlugin};
}; use crate::inventory::InventoryPlugin;
use crate::instance::{ use crate::player_list::PlayerListPlugin;
check_instance_invariants, update_instances_post_client, update_instances_pre_client, Instance, use crate::prelude::event::ClientEventPlugin;
}; use crate::prelude::ComponentPlugin;
use crate::inventory::update_inventories;
use crate::player_list::{update_player_list, PlayerList};
use crate::server::connect::do_accept_loop; use crate::server::connect::do_accept_loop;
mod byte_channel; mod byte_channel;
@ -305,17 +301,7 @@ pub fn build_plugin(
let shared = server.shared.clone(); let shared = server.shared.clone();
// Insert resources. // Insert resources.
app.insert_resource(server) app.insert_resource(server);
.insert_resource(McEntityManager::new())
.insert_resource(PlayerList::new());
register_client_events(&mut app.world);
// Add the event loop schedule.
let mut event_loop = Schedule::new();
event_loop.configure_set(EventLoopSet);
event_loop.set_default_base_set(EventLoopSet);
app.add_schedule(EventLoopSchedule, event_loop);
// Make the app loop forever at the configured TPS. // Make the app loop forever at the configured TPS.
{ {
@ -333,59 +319,41 @@ pub fn build_plugin(
.in_base_set(StartupSet::PostStartup), .in_base_set(StartupSet::PostStartup),
); );
// Add `CoreSet:::PreUpdate` systems. // Spawn new clients before the event loop starts.
app.add_systems( app.add_system(
(spawn_new_clients.before(run_event_loop), run_event_loop).in_base_set(CoreSet::PreUpdate), spawn_new_clients
.in_base_set(CoreSet::PreUpdate)
.before(EventLoopSet),
); );
// Add internal valence systems that run after `CoreSet::Update`. app.add_system(increment_tick_counter.in_base_set(CoreSet::Last));
app.add_systems(
( // Add internal plugins.
init_mcentities, app.add_plugin(ComponentPlugin)
check_instance_invariants, .add_plugin(ClientPlugin)
update_player_list.before(update_instances_pre_client), .add_plugin(ClientEventPlugin)
update_instances_pre_client.after(init_mcentities), .add_plugin(EntityPlugin)
update_instances_post_client.after(update_instances_pre_client), .add_plugin(InstancePlugin)
deinit_despawned_mcentities.after(update_instances_post_client), .add_plugin(InventoryPlugin)
despawn_marked_entities.after(deinit_despawned_mcentities), .add_plugin(PlayerListPlugin);
update_mcentities.after(despawn_marked_entities),
OldPosition::update.after(despawn_marked_entities), /*
OldLocation::update.after(despawn_marked_entities), println!(
"{}",
bevy_mod_debugdump::schedule_graph_dot(
app,
CoreSchedule::Main,
&bevy_mod_debugdump::schedule_graph::Settings {
ambiguity_enable: false,
..Default::default()
},
) )
.in_base_set(CoreSet::PostUpdate), );
) */
.add_systems(
update_inventories()
.in_base_set(CoreSet::PostUpdate)
.before(init_mcentities),
)
.add_systems(
update_clients()
.in_base_set(CoreSet::PostUpdate)
.after(update_instances_pre_client)
.before(update_instances_post_client),
)
.add_system(increment_tick_counter.in_base_set(CoreSet::Last));
Ok(()) Ok(())
} }
/// The [`ScheduleLabel`] for the event loop [`Schedule`].
#[derive(ScheduleLabel, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct EventLoopSchedule;
/// The default base set for [`EventLoopSchedule`].
#[derive(SystemSet, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct EventLoopSet;
/// Despawns all the entities marked as despawned with the [`Despawned`]
/// component.
fn despawn_marked_entities(mut commands: Commands, entities: Query<Entity, With<Despawned>>) {
for entity in &entities {
commands.entity(entity).despawn();
}
}
fn increment_tick_counter(mut server: ResMut<Server>) { fn increment_tick_counter(mut server: ResMut<Server>) {
server.current_tick += 1; server.current_tick += 1;
} }

View file

@ -1,6 +1,5 @@
use crate::{Decode, Encode};
use super::team::TeamColor; use super::team::TeamColor;
use crate::{Decode, Encode};
#[derive(Copy, Clone, Debug, Encode, Decode)] #[derive(Copy, Clone, Debug, Encode, Decode)]
pub struct ScoreboardDisplayS2c<'a> { pub struct ScoreboardDisplayS2c<'a> {