mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
make packet handler work
i still haven't actually tested any of this yet lol but in theory it should all work i'll probably either actually test az-client and fix all the remaining issues or update the azalea crate next ok also one thing that i'm not particularly happy with is how the packet handlers are doing ugly queries like ```rs let local_player = ecs .query::<&LocalPlayer>() .get_mut(ecs, player_entity) .unwrap(); ``` i think the right way to solve it would be by putting every packet handler in its own system but i haven't come up with a way to make that not be really annoying yet
This commit is contained in:
parent
109418de21
commit
a928d83ade
12 changed files with 640 additions and 448 deletions
|
@ -1,6 +1,6 @@
|
|||
pub use crate::chat::ChatPacket;
|
||||
use crate::{
|
||||
local_player::{send_tick_event, update_in_loaded_chunk, LocalPlayer},
|
||||
local_player::{death_event, send_tick_event, update_in_loaded_chunk, LocalPlayer},
|
||||
movement::{local_player_ai_step, send_position},
|
||||
packet_handling,
|
||||
plugins::PluginStates,
|
||||
|
@ -93,10 +93,6 @@ pub struct Client {
|
|||
pub profile: GameProfile,
|
||||
/// The entity for this client in the ECS.
|
||||
pub entity: Entity,
|
||||
/// A container of world names to worlds. If we're not using a shared world
|
||||
/// (i.e. not a swarm), then this will only contain data about the world
|
||||
/// we're currently in.
|
||||
world_container: Arc<RwLock<WorldContainer>>,
|
||||
/// The world that this client is in.
|
||||
pub world: Arc<RwLock<PartialWorld>>,
|
||||
|
||||
|
@ -104,7 +100,6 @@ pub struct Client {
|
|||
/// client and keep state. If you're not making a plugin and you're using
|
||||
/// the `azalea` crate. you can ignore this field.
|
||||
pub plugins: Arc<PluginStates>,
|
||||
tasks: Arc<Mutex<Vec<JoinHandle<()>>>>,
|
||||
|
||||
/// The entity component system. You probably don't need to access this
|
||||
/// directly. Note that if you're using a shared world (i.e. a swarm), this
|
||||
|
@ -140,7 +135,6 @@ impl Client {
|
|||
/// defaults, otherwise use [`Client::join`].
|
||||
pub fn new(
|
||||
profile: GameProfile,
|
||||
world_container: Option<Arc<RwLock<WorldContainer>>>,
|
||||
entity: Entity,
|
||||
ecs: Arc<Mutex<bevy_ecs::world::World>>,
|
||||
) -> Self {
|
||||
|
@ -149,12 +143,9 @@ impl Client {
|
|||
// default our id to 0, it'll be set later
|
||||
entity,
|
||||
world: Arc::new(RwLock::new(PartialWorld::default())),
|
||||
world_container: world_container
|
||||
.unwrap_or_else(|| Arc::new(RwLock::new(WorldContainer::new()))),
|
||||
// The plugins can be modified by the user by replacing the plugins
|
||||
// field right after this. No Mutex so the user doesn't need to .lock().
|
||||
plugins: Arc::new(PluginStates::default()),
|
||||
tasks: Arc::new(Mutex::new(Vec::new())),
|
||||
|
||||
ecs,
|
||||
}
|
||||
|
@ -209,13 +200,13 @@ impl Client {
|
|||
let entity = entity_mut.id();
|
||||
|
||||
// we got the GameConnection, so the server is now connected :)
|
||||
let client = Client::new(game_profile.clone(), None, entity, ecs_lock.clone());
|
||||
let client = Client::new(game_profile.clone(), entity, ecs_lock.clone());
|
||||
|
||||
let world = client.world();
|
||||
|
||||
let (packet_writer_sender, packet_writer_receiver) = mpsc::unbounded_channel();
|
||||
|
||||
let local_player = crate::local_player::LocalPlayer::new(
|
||||
let mut local_player = crate::local_player::LocalPlayer::new(
|
||||
entity,
|
||||
game_profile,
|
||||
packet_writer_sender,
|
||||
|
@ -236,8 +227,8 @@ impl Client {
|
|||
.clone()
|
||||
.write_task(write_conn, packet_writer_receiver),
|
||||
);
|
||||
client.tasks.lock().push(read_packets_task);
|
||||
client.tasks.lock().push(write_packets_task);
|
||||
local_player.tasks.push(read_packets_task);
|
||||
local_player.tasks.push(write_packets_task);
|
||||
|
||||
ecs.entity_mut(entity)
|
||||
.insert((local_player, packet_receiver));
|
||||
|
@ -379,12 +370,8 @@ impl Client {
|
|||
///
|
||||
/// The OwnedReadHalf for the TCP connection is in one of the tasks, so it
|
||||
/// automatically closes the connection when that's dropped.
|
||||
pub async fn disconnect(&self) -> Result<(), std::io::Error> {
|
||||
let tasks = self.tasks.lock();
|
||||
for task in tasks.iter() {
|
||||
task.abort();
|
||||
}
|
||||
Ok(())
|
||||
pub fn disconnect(&self) {
|
||||
self.local_player_mut(&mut self.ecs.lock()).disconnect();
|
||||
}
|
||||
|
||||
pub fn local_player<'a>(&'a self, ecs: &'a mut bevy_ecs::world::World) -> &'a LocalPlayer {
|
||||
|
@ -405,12 +392,17 @@ impl Client {
|
|||
/// superset of the client's world.
|
||||
pub fn world(&self) -> Arc<RwLock<World>> {
|
||||
let mut ecs = self.ecs.lock();
|
||||
let world_name = self
|
||||
.local_player(&mut ecs)
|
||||
.world_name
|
||||
.as_ref()
|
||||
.expect("World name must be known if we're doing Client::world");
|
||||
let world_container = self.world_container.read();
|
||||
|
||||
let world_name = {
|
||||
let local_player = self.local_player(&mut ecs);
|
||||
local_player
|
||||
.world_name
|
||||
.as_ref()
|
||||
.expect("World name must be known if we're doing Client::world")
|
||||
.clone()
|
||||
};
|
||||
|
||||
let world_container = ecs.resource::<WorldContainer>();
|
||||
world_container.get(&world_name).unwrap()
|
||||
}
|
||||
|
||||
|
@ -491,10 +483,12 @@ pub async fn start_ecs(
|
|||
.with_system(update_in_loaded_chunk)
|
||||
.with_system(local_player_ai_step)
|
||||
.with_system(send_tick_event),
|
||||
)
|
||||
.add_system(packet_handling::handle_packets.label("handle_packets"))
|
||||
// should happen last
|
||||
.add_system(packet_handling::clear_packets.after("handle_packets"));
|
||||
);
|
||||
|
||||
// fire the Death event when the player dies.
|
||||
app.add_system(death_event.after("tick").after("packet"));
|
||||
|
||||
app.init_resource::<WorldContainer>();
|
||||
|
||||
// all resources should have been added by now so we can take the ecs from the
|
||||
// app
|
||||
|
|
|
@ -9,14 +9,14 @@ use azalea_auth::game_profile::GameProfile;
|
|||
use azalea_core::{ChunkPos, ResourceLocation};
|
||||
use azalea_protocol::{connect::WriteConnection, packets::game::ServerboundGamePacket};
|
||||
use azalea_world::{
|
||||
entity::{self, Entity},
|
||||
EntityInfos, PartialWorld, World,
|
||||
entity::{self, Dead, Entity},
|
||||
EntityInfos, PartialWorld, World, WorldContainer,
|
||||
};
|
||||
use bevy_ecs::{component::Component, system::Query};
|
||||
use bevy_ecs::{component::Component, query::Added, system::Query};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use parking_lot::RwLock;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::{sync::mpsc, task::JoinHandle};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{ClientInformation, Event, PlayerInfo, WalkDirection};
|
||||
|
@ -34,16 +34,20 @@ pub struct LocalPlayer {
|
|||
/// A map of player uuids to their information in the tab list
|
||||
pub players: HashMap<Uuid, PlayerInfo>,
|
||||
|
||||
/// The partial world is the world this client currently has loaded. It has
|
||||
/// a limited render distance.
|
||||
pub partial_world: Arc<RwLock<PartialWorld>>,
|
||||
/// The world is the combined [`PartialWorld`]s of all clients in the same
|
||||
/// world. (Only relevant if you're using a shared world, i.e. a swarm)
|
||||
pub world: Arc<RwLock<World>>,
|
||||
pub world_name: Option<ResourceLocation>,
|
||||
|
||||
pub tx: mpsc::UnboundedSender<Event>,
|
||||
}
|
||||
|
||||
/// Present if the player can be dead.
|
||||
#[derive(Component, Copy, Clone, Default, Deref, DerefMut)]
|
||||
pub struct Dead(bool);
|
||||
/// A list of async tasks that are running and will stop running when this
|
||||
/// LocalPlayer is dropped or disconnected with [`Self::disconnect`]
|
||||
pub(crate) tasks: Vec<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PhysicsState {
|
||||
|
@ -87,7 +91,6 @@ impl LocalPlayer {
|
|||
|
||||
physics_state: PhysicsState::default(),
|
||||
client_information: ClientInformation::default(),
|
||||
dead: false,
|
||||
players: HashMap::new(),
|
||||
|
||||
world,
|
||||
|
@ -99,6 +102,8 @@ impl LocalPlayer {
|
|||
world_name: None,
|
||||
|
||||
tx,
|
||||
|
||||
tasks: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,6 +113,16 @@ impl LocalPlayer {
|
|||
.send(packet)
|
||||
.expect("write_packet shouldn't be able to be called if the connection is closed");
|
||||
}
|
||||
|
||||
/// Disconnect this client from the server by ending all tasks.
|
||||
///
|
||||
/// The OwnedReadHalf for the TCP connection is in one of the tasks, so it
|
||||
/// automatically closes the connection when that's dropped.
|
||||
pub fn disconnect(&self) {
|
||||
for task in self.tasks.iter() {
|
||||
task.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_tick_event(query: Query<&LocalPlayer>) {
|
||||
|
@ -141,6 +156,13 @@ pub fn update_in_loaded_chunk(
|
|||
}
|
||||
}
|
||||
|
||||
/// Send the "Death" event for [`LocalPlayer`]s that died with no reason.
|
||||
pub fn death_event(query: Query<&LocalPlayer, Added<Dead>>) {
|
||||
for local_player in &query {
|
||||
local_player.tx.send(Event::Death(None));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum HandlePacketError {
|
||||
#[error("{0}")]
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -125,7 +125,7 @@ pub fn move_colliding(
|
|||
|
||||
// TODO: minecraft checks for a "minor" horizontal collision here
|
||||
|
||||
let _block_pos_below = entity::on_pos_legacy(&world.chunks, position, physics);
|
||||
let _block_pos_below = entity::on_pos_legacy(&world.chunks, position);
|
||||
// let _block_state_below = self
|
||||
// .world
|
||||
// .get_block_state(&block_pos_below)
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use azalea_buf::McBuf;
|
||||
use azalea_core::{ResourceLocation, Vec3};
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use azalea_world::entity::{
|
||||
metadata::{apply_default_metadata, PlayerMetadataBundle, UpdateMetadataError},
|
||||
EntityBundle,
|
||||
};
|
||||
use azalea_world::entity::{metadata::apply_default_metadata, EntityBundle};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
|
||||
|
@ -48,6 +45,7 @@ impl ClientboundAddEntityPacket {
|
|||
EntityBundle::new(self.uuid, self.position, self.entity_type, world_name)
|
||||
}
|
||||
|
||||
/// Apply the default metadata for the given entity.
|
||||
pub fn apply_metadata(&self, entity: &mut bevy_ecs::world::EntityMut) {
|
||||
apply_default_metadata(entity, self.entity_type);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use azalea_buf::McBuf;
|
|||
use azalea_core::{ResourceLocation, Vec3};
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use azalea_registry::EntityKind;
|
||||
use azalea_world::entity::{metadata::PlayerMetadataBundle, EntityBundle, PlayerBundle};
|
||||
use azalea_world::entity::{metadata::PlayerMetadataBundle, Dead, EntityBundle, PlayerBundle};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// This packet is sent by the server when a player comes into visible range,
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use azalea_buf::McBuf;
|
||||
use azalea_core::Vec3;
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
|
||||
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
|
||||
pub struct ClientboundTeleportEntityPacket {
|
||||
#[var]
|
||||
pub id: u32,
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub z: f64,
|
||||
pub position: Vec3,
|
||||
pub y_rot: i8,
|
||||
pub x_rot: i8,
|
||||
pub on_ground: bool,
|
||||
|
|
|
@ -17,6 +17,8 @@ const SECTION_HEIGHT: u32 = 16;
|
|||
/// An efficient storage of chunks for a client that has a limited render
|
||||
/// distance. This has support for using a shared [`ChunkStorage`].
|
||||
pub struct PartialChunkStorage {
|
||||
/// The center of the view, i.e. the chunk the player is currently in. You
|
||||
/// can safely modify this.
|
||||
pub view_center: ChunkPos,
|
||||
chunk_radius: u32,
|
||||
view_range: u32,
|
||||
|
@ -135,7 +137,7 @@ impl PartialChunkStorage {
|
|||
}
|
||||
|
||||
/// Get a [`Chunk`] within render distance, or `None` if it's not loaded.
|
||||
/// Use [`PartialChunkStorage::get`] to get a chunk from the shared storage.
|
||||
/// Use [`ChunkStorage::get`] to get a chunk from the shared storage.
|
||||
pub fn limited_get(&self, pos: &ChunkPos) -> Option<&Arc<RwLock<Chunk>>> {
|
||||
if !self.in_range(pos) {
|
||||
warn!(
|
||||
|
@ -149,7 +151,7 @@ impl PartialChunkStorage {
|
|||
self.chunks[index].as_ref()
|
||||
}
|
||||
/// Get a mutable reference to a [`Chunk`] within render distance, or
|
||||
/// `None` if it's not loaded. Use [`PartialChunkStorage::get`] to get
|
||||
/// `None` if it's not loaded. Use [`ChunkStorage::get`] to get
|
||||
/// a chunk from the shared storage.
|
||||
pub fn limited_get_mut(&mut self, pos: &ChunkPos) -> Option<&mut Option<Arc<RwLock<Chunk>>>> {
|
||||
if !self.in_range(pos) {
|
||||
|
|
|
@ -6,13 +6,14 @@ use azalea_buf::{McBuf, McBufReadable, McBufWritable};
|
|||
use azalea_chat::FormattedText;
|
||||
use azalea_core::{BlockPos, Direction, GlobalPos, Particle, Slot};
|
||||
use bevy_ecs::component::Component;
|
||||
use derive_more::Deref;
|
||||
use enum_as_inner::EnumAsInner;
|
||||
use nohash_hasher::IntSet;
|
||||
use std::io::{Cursor, Write};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EntityMetadataItems(pub Vec<EntityDataItem>);
|
||||
#[derive(Clone, Debug, Deref)]
|
||||
pub struct EntityMetadataItems(Vec<EntityDataItem>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EntityDataItem {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use azalea_core::{Vec3, AABB};
|
||||
use bevy_ecs::{query::Changed, system::Query};
|
||||
|
||||
use super::{Physics, Position};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EntityDimensions {
|
||||
|
@ -21,3 +24,19 @@ impl EntityDimensions {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the position of the entity. This doesn't update the cache in
|
||||
/// azalea-world, and should only be used within azalea-world!
|
||||
///
|
||||
/// # Safety
|
||||
/// Cached position in the world must be updated.
|
||||
pub fn update_bounding_box(mut query: Query<(&Position, &mut Physics), Changed<Position>>) {
|
||||
for (position, mut physics) in query.iter_mut() {
|
||||
let bounding_box = physics.dimensions.make_bounding_box(&position);
|
||||
physics.bounding_box = bounding_box;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_bounding_box(pos: &Position, physics: &Physics) -> AABB {
|
||||
physics.dimensions.make_bounding_box(&pos)
|
||||
}
|
||||
|
|
|
@ -5,14 +5,22 @@ pub mod metadata;
|
|||
|
||||
use crate::ChunkStorage;
|
||||
|
||||
use self::{attributes::AttributeInstance, metadata::UpdateMetadataError};
|
||||
use self::{
|
||||
attributes::AttributeInstance,
|
||||
metadata::{Health, UpdateMetadataError},
|
||||
};
|
||||
pub use attributes::Attributes;
|
||||
use azalea_block::BlockState;
|
||||
use azalea_core::{BlockPos, ChunkPos, ResourceLocation, Vec3, AABB};
|
||||
use bevy_ecs::{bundle::Bundle, component::Component, query::Changed, system::Query};
|
||||
use bevy_ecs::{
|
||||
bundle::Bundle,
|
||||
component::Component,
|
||||
query::{Changed, Without},
|
||||
system::{Commands, Query},
|
||||
};
|
||||
pub use data::*;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
pub use dimensions::EntityDimensions;
|
||||
pub use dimensions::{update_bounding_box, EntityDimensions};
|
||||
use std::fmt::Debug;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -36,18 +44,6 @@ impl std::hash::Hash for MinecraftEntityId {
|
|||
}
|
||||
impl nohash_hasher::IsEnabled for MinecraftEntityId {}
|
||||
|
||||
/// Sets the position of the entity. This doesn't update the cache in
|
||||
/// azalea-world, and should only be used within azalea-world!
|
||||
///
|
||||
/// # Safety
|
||||
/// Cached position in the world must be updated.
|
||||
pub fn update_bounding_box(mut query: Query<(&Position, &mut Physics), Changed<Position>>) {
|
||||
for (position, mut physics) in query.iter_mut() {
|
||||
let bounding_box = physics.dimensions.make_bounding_box(&position);
|
||||
physics.bounding_box = bounding_box;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_rotation(physics: &mut Physics, y_rot: f32, x_rot: f32) {
|
||||
physics.y_rot = y_rot % 360.0;
|
||||
physics.x_rot = x_rot.clamp(-90.0, 90.0) % 360.0;
|
||||
|
@ -80,27 +76,9 @@ pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> V
|
|||
}
|
||||
}
|
||||
|
||||
/// Apply the given metadata items to the entity. Everything that isn't
|
||||
/// included in items will be left unchanged.
|
||||
pub fn apply_metadata(
|
||||
ecs: bevy_ecs::world::World,
|
||||
entity: &mut bevy_ecs::world::EntityMut,
|
||||
items: Vec<EntityDataItem>,
|
||||
) -> Result<(), UpdateMetadataError> {
|
||||
metadata::apply_metadata(entity, items)
|
||||
}
|
||||
|
||||
pub fn make_bounding_box(pos: &Position, physics: &Physics) -> AABB {
|
||||
physics.dimensions.make_bounding_box(&pos)
|
||||
}
|
||||
|
||||
/// Get the position of the block below the entity, but a little lower.
|
||||
pub fn on_pos_legacy(
|
||||
chunk_storage: &ChunkStorage,
|
||||
position: &Position,
|
||||
physics: &Physics,
|
||||
) -> BlockPos {
|
||||
on_pos(0.2, chunk_storage, position, physics)
|
||||
pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: &Position) -> BlockPos {
|
||||
on_pos(0.2, chunk_storage, position)
|
||||
}
|
||||
|
||||
// int x = Mth.floor(this.position.x);
|
||||
|
@ -115,12 +93,7 @@ pub fn on_pos_legacy(
|
|||
// }
|
||||
// }
|
||||
// return var5;
|
||||
pub fn on_pos(
|
||||
offset: f32,
|
||||
chunk_storage: &ChunkStorage,
|
||||
pos: &Position,
|
||||
physics: &Physics,
|
||||
) -> BlockPos {
|
||||
pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: &Position) -> BlockPos {
|
||||
let x = pos.x.floor() as i32;
|
||||
let y = (pos.y - offset as f64).floor() as i32;
|
||||
let z = pos.z.floor() as i32;
|
||||
|
@ -145,6 +118,8 @@ pub fn on_pos(
|
|||
pos
|
||||
}
|
||||
|
||||
/// The Minecraft UUID of the entity. For players, this is their actual player
|
||||
/// UUID, and for other entities it's just random.
|
||||
#[derive(Component, Deref, DerefMut, Clone, Copy)]
|
||||
pub struct EntityUuid(Uuid);
|
||||
|
||||
|
@ -239,6 +214,27 @@ pub struct Physics {
|
|||
pub has_impulse: bool,
|
||||
}
|
||||
|
||||
/// Marker component for entities that are dead.
|
||||
///
|
||||
/// "Dead" means that the entity has 0 health.
|
||||
#[derive(Component, Copy, Clone, Default)]
|
||||
pub struct Dead;
|
||||
|
||||
/// System that adds the [`Dead`] marker component if an entity's health is set
|
||||
/// to 0 (or less than 0). This will be present if an entity is doing the death
|
||||
/// animation.
|
||||
///
|
||||
/// Entities that are dead can not be revived.
|
||||
/// TODO: fact check this in-game by setting an entity's health to 0 and then
|
||||
/// not 0
|
||||
pub fn add_dead(mut commands: Commands, query: Query<(Entity, &Health), Changed<Health>>) {
|
||||
for (entity, health) in query.iter() {
|
||||
if **health <= 0.0 {
|
||||
commands.entity(entity).insert(Dead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A component NewType for [`azalea_registry::EntityKind`].
|
||||
///
|
||||
/// Most of the time, you should be using `azalea_registry::EntityKind`
|
||||
|
@ -266,6 +262,7 @@ impl EntityBundle {
|
|||
kind: azalea_registry::EntityKind,
|
||||
world_name: ResourceLocation,
|
||||
) -> Self {
|
||||
// TODO: get correct entity dimensions by having them codegened somewhere
|
||||
let dimensions = EntityDimensions {
|
||||
width: 0.6,
|
||||
height: 1.8,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
entity::{self, update_bounding_box, Entity, MinecraftEntityId},
|
||||
entity::{self, add_dead, update_bounding_box, Entity, MinecraftEntityId},
|
||||
MaybeRemovedEntity, World, WorldContainer,
|
||||
};
|
||||
use azalea_core::ChunkPos;
|
||||
|
@ -18,12 +18,15 @@ use uuid::Uuid;
|
|||
pub struct EntityPlugin;
|
||||
impl Plugin for EntityPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_system_set_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
// Since it's PostUpdate, these will run after every tick or packet
|
||||
app.add_system_set(
|
||||
SystemSet::new()
|
||||
.after("tick")
|
||||
.after("packet")
|
||||
.with_system(update_entity_chunk_positions)
|
||||
.with_system(remove_despawned_entities_from_indexes)
|
||||
.with_system(update_bounding_box),
|
||||
.with_system(update_bounding_box)
|
||||
.with_system(add_dead),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue