mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
add failing test_set_health_before_login test
This commit is contained in:
parent
4aa5010ea2
commit
87c34e1c33
12 changed files with 272 additions and 59 deletions
|
@ -326,7 +326,7 @@ impl Client {
|
||||||
client_information: crate::ClientInformation::default(),
|
client_information: crate::ClientInformation::default(),
|
||||||
instance_holder,
|
instance_holder,
|
||||||
},
|
},
|
||||||
InConfigurationState,
|
InConfigState,
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok((client, rx))
|
Ok((client, rx))
|
||||||
|
@ -757,8 +757,8 @@ pub struct JoinedClientBundle {
|
||||||
|
|
||||||
/// A marker component for local players that are currently in the
|
/// A marker component for local players that are currently in the
|
||||||
/// `configuration` state.
|
/// `configuration` state.
|
||||||
#[derive(Component)]
|
#[derive(Component, Clone, Debug)]
|
||||||
pub struct InConfigurationState;
|
pub struct InConfigState;
|
||||||
|
|
||||||
pub struct AzaleaPlugin;
|
pub struct AzaleaPlugin;
|
||||||
impl Plugin for AzaleaPlugin {
|
impl Plugin for AzaleaPlugin {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use azalea_protocol::{
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
use crate::{client::InConfigurationState, packet_handling::configuration::SendConfigurationEvent};
|
use crate::{client::InConfigState, packet_handling::configuration::SendConfigurationEvent};
|
||||||
|
|
||||||
pub struct ConfigurationPlugin;
|
pub struct ConfigurationPlugin;
|
||||||
impl Plugin for ConfigurationPlugin {
|
impl Plugin for ConfigurationPlugin {
|
||||||
|
@ -18,13 +18,13 @@ impl Plugin for ConfigurationPlugin {
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
handle_in_configuration_state
|
handle_in_configuration_state
|
||||||
.after(crate::packet_handling::configuration::handle_send_packet_event),
|
.before(crate::packet_handling::configuration::handle_send_packet_event),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_in_configuration_state(
|
fn handle_in_configuration_state(
|
||||||
query: Query<(Entity, &ClientInformation), Added<InConfigurationState>>,
|
query: Query<(Entity, &ClientInformation), Added<InConfigState>>,
|
||||||
mut send_packet_events: EventWriter<SendConfigurationEvent>,
|
mut send_packet_events: EventWriter<SendConfigurationEvent>,
|
||||||
) {
|
) {
|
||||||
for (entity, client_information) in query.iter() {
|
for (entity, client_information) in query.iter() {
|
||||||
|
|
|
@ -13,6 +13,7 @@ use bevy_ecs::{
|
||||||
system::{Commands, Query},
|
system::{Commands, Query},
|
||||||
};
|
};
|
||||||
use derive_more::Deref;
|
use derive_more::Deref;
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
use crate::{client::JoinedClientBundle, events::LocalPlayerEvents, raw_connection::RawConnection};
|
use crate::{client::JoinedClientBundle, events::LocalPlayerEvents, raw_connection::RawConnection};
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ pub fn remove_components_from_disconnected_players(
|
||||||
mut events: EventReader<DisconnectEvent>,
|
mut events: EventReader<DisconnectEvent>,
|
||||||
) {
|
) {
|
||||||
for DisconnectEvent { entity, .. } in events.read() {
|
for DisconnectEvent { entity, .. } in events.read() {
|
||||||
|
trace!("Got DisconnectEvent for {entity:?}");
|
||||||
commands
|
commands
|
||||||
.entity(*entity)
|
.entity(*entity)
|
||||||
.remove::<JoinedClientBundle>()
|
.remove::<JoinedClientBundle>()
|
||||||
|
|
|
@ -33,8 +33,8 @@ pub mod task_pool;
|
||||||
pub use account::{Account, AccountOpts};
|
pub use account::{Account, AccountOpts};
|
||||||
pub use azalea_protocol::common::client_information::ClientInformation;
|
pub use azalea_protocol::common::client_information::ClientInformation;
|
||||||
pub use client::{
|
pub use client::{
|
||||||
start_ecs_runner, Client, DefaultPlugins, JoinError, JoinedClientBundle, StartClientOpts,
|
start_ecs_runner, Client, DefaultPlugins, InConfigState, JoinError, JoinedClientBundle,
|
||||||
TickBroadcast,
|
LocalPlayerBundle, StartClientOpts, TickBroadcast,
|
||||||
};
|
};
|
||||||
pub use events::Event;
|
pub use events::Event;
|
||||||
pub use local_player::{GameProfileComponent, Hunger, InstanceHolder, TabList};
|
pub use local_player::{GameProfileComponent, Hunger, InstanceHolder, TabList};
|
||||||
|
|
|
@ -20,14 +20,23 @@ use crate::{
|
||||||
|
|
||||||
/// A component that keeps strong references to our [`PartialInstance`] and
|
/// A component that keeps strong references to our [`PartialInstance`] and
|
||||||
/// [`Instance`] for local players.
|
/// [`Instance`] for local players.
|
||||||
|
///
|
||||||
|
/// This can also act as a convenience for accessing the player's Instance since
|
||||||
|
/// the alternative is to look up the player's [`InstanceName`] in the
|
||||||
|
/// [`InstanceContainer`].
|
||||||
|
///
|
||||||
|
/// [`InstanceContainer`]: azalea_world::InstanceContainer
|
||||||
|
/// [`InstanceName`]: azalea_world::InstanceName
|
||||||
#[derive(Component, Clone)]
|
#[derive(Component, Clone)]
|
||||||
pub struct InstanceHolder {
|
pub struct InstanceHolder {
|
||||||
/// The partial instance is the world this client currently has loaded. It
|
/// The partial instance is the world this client currently has loaded. It
|
||||||
/// has a limited render distance.
|
/// has a limited render distance.
|
||||||
pub partial_instance: Arc<RwLock<PartialInstance>>,
|
pub partial_instance: Arc<RwLock<PartialInstance>>,
|
||||||
/// The world is the combined [`PartialInstance`]s of all clients in the
|
/// The world is the combined [`PartialInstance`]s of all clients in the
|
||||||
/// same world. (Only relevant if you're using a shared world, i.e. a
|
/// same world.
|
||||||
/// swarm)
|
///
|
||||||
|
/// This is only relevant if you're using a shared world (i.e. a
|
||||||
|
/// swarm).
|
||||||
pub instance: Arc<RwLock<Instance>>,
|
pub instance: Arc<RwLock<Instance>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,12 +123,16 @@ impl Default for Hunger {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstanceHolder {
|
impl InstanceHolder {
|
||||||
/// Create a new `InstanceHolder`.
|
/// Create a new `InstanceHolder` for the given entity.
|
||||||
pub fn new(entity: Entity, world: Arc<RwLock<Instance>>) -> Self {
|
///
|
||||||
|
/// The partial instance will be created for you. The render distance will
|
||||||
|
/// be set to a default value, which you can change by creating a new
|
||||||
|
/// partial_instance.
|
||||||
|
pub fn new(entity: Entity, instance: Arc<RwLock<Instance>>) -> Self {
|
||||||
let client_information = ClientInformation::default();
|
let client_information = ClientInformation::default();
|
||||||
|
|
||||||
InstanceHolder {
|
InstanceHolder {
|
||||||
instance: world,
|
instance,
|
||||||
partial_instance: Arc::new(RwLock::new(PartialInstance::new(
|
partial_instance: Arc::new(RwLock::new(PartialInstance::new(
|
||||||
azalea_world::chunk_storage::calculate_chunk_storage_range(
|
azalea_world::chunk_storage::calculate_chunk_storage_range(
|
||||||
client_information.view_distance.into(),
|
client_information.view_distance.into(),
|
||||||
|
|
|
@ -14,7 +14,7 @@ use bevy_ecs::prelude::*;
|
||||||
use bevy_ecs::system::SystemState;
|
use bevy_ecs::system::SystemState;
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
use crate::client::InConfigurationState;
|
use crate::client::InConfigState;
|
||||||
use crate::disconnect::DisconnectEvent;
|
use crate::disconnect::DisconnectEvent;
|
||||||
use crate::local_player::Hunger;
|
use crate::local_player::Hunger;
|
||||||
use crate::packet_handling::game::KeepAliveEvent;
|
use crate::packet_handling::game::KeepAliveEvent;
|
||||||
|
@ -30,7 +30,7 @@ pub struct ConfigurationEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_packet_events(
|
pub fn send_packet_events(
|
||||||
query: Query<(Entity, &RawConnection), With<InConfigurationState>>,
|
query: Query<(Entity, &RawConnection), With<InConfigState>>,
|
||||||
mut packet_events: ResMut<Events<ConfigurationEvent>>,
|
mut packet_events: ResMut<Events<ConfigurationEvent>>,
|
||||||
) {
|
) {
|
||||||
// we manually clear and send the events at the beginning of each update
|
// we manually clear and send the events at the beginning of each update
|
||||||
|
@ -110,7 +110,7 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
let mut raw_conn = query.get_mut(player_entity).unwrap();
|
let mut raw_conn = query.get_mut(player_entity).unwrap();
|
||||||
|
|
||||||
raw_conn
|
raw_conn
|
||||||
.write_packet(ServerboundFinishConfiguration {})
|
.write_packet(ServerboundFinishConfiguration)
|
||||||
.expect(
|
.expect(
|
||||||
"we should be in the right state and encoding this packet shouldn't fail",
|
"we should be in the right state and encoding this packet shouldn't fail",
|
||||||
);
|
);
|
||||||
|
@ -118,7 +118,7 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
|
|
||||||
// these components are added now that we're going to be in the Game state
|
// these components are added now that we're going to be in the Game state
|
||||||
ecs.entity_mut(player_entity)
|
ecs.entity_mut(player_entity)
|
||||||
.remove::<InConfigurationState>()
|
.remove::<InConfigState>()
|
||||||
.insert(crate::JoinedClientBundle {
|
.insert(crate::JoinedClientBundle {
|
||||||
physics_state: crate::PhysicsState::default(),
|
physics_state: crate::PhysicsState::default(),
|
||||||
inventory: crate::inventory::Inventory::default(),
|
inventory: crate::inventory::Inventory::default(),
|
||||||
|
@ -251,7 +251,7 @@ impl SendConfigurationEvent {
|
||||||
|
|
||||||
pub fn handle_send_packet_event(
|
pub fn handle_send_packet_event(
|
||||||
mut send_packet_events: EventReader<SendConfigurationEvent>,
|
mut send_packet_events: EventReader<SendConfigurationEvent>,
|
||||||
mut query: Query<(&mut RawConnection, Option<&InConfigurationState>)>,
|
mut query: Query<(&mut RawConnection, Option<&InConfigState>)>,
|
||||||
) {
|
) {
|
||||||
for event in send_packet_events.read() {
|
for event in send_packet_events.read() {
|
||||||
if let Ok((raw_conn, in_configuration_state)) = query.get_mut(event.sent_by) {
|
if let Ok((raw_conn, in_configuration_state)) = query.get_mut(event.sent_by) {
|
||||||
|
|
|
@ -1470,7 +1470,7 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.entity(player_entity)
|
.entity(player_entity)
|
||||||
.insert(crate::client::InConfigurationState)
|
.insert(crate::client::InConfigState)
|
||||||
.remove::<crate::JoinedClientBundle>();
|
.remove::<crate::JoinedClientBundle>();
|
||||||
|
|
||||||
system_state.apply(ecs);
|
system_state.apply(ecs);
|
||||||
|
|
|
@ -18,26 +18,26 @@ use tracing::error;
|
||||||
/// yourself. It will do the compression and encryption for you though.
|
/// yourself. It will do the compression and encryption for you though.
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct RawConnection {
|
pub struct RawConnection {
|
||||||
reader: RawConnectionReader,
|
pub reader: RawConnectionReader,
|
||||||
writer: RawConnectionWriter,
|
pub writer: RawConnectionWriter,
|
||||||
|
|
||||||
/// Packets sent to this will be sent to the server.
|
/// Packets sent to this will be sent to the server.
|
||||||
/// A task that reads packets from the server. The client is disconnected
|
/// A task that reads packets from the server. The client is disconnected
|
||||||
/// when this task ends.
|
/// when this task ends.
|
||||||
read_packets_task: tokio::task::JoinHandle<()>,
|
pub read_packets_task: tokio::task::JoinHandle<()>,
|
||||||
/// A task that writes packets from the server.
|
/// A task that writes packets from the server.
|
||||||
write_packets_task: tokio::task::JoinHandle<()>,
|
pub write_packets_task: tokio::task::JoinHandle<()>,
|
||||||
|
|
||||||
connection_protocol: ConnectionProtocol,
|
pub connection_protocol: ConnectionProtocol,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct RawConnectionReader {
|
pub struct RawConnectionReader {
|
||||||
pub incoming_packet_queue: Arc<Mutex<Vec<Box<[u8]>>>>,
|
pub incoming_packet_queue: Arc<Mutex<Vec<Box<[u8]>>>>,
|
||||||
pub run_schedule_sender: mpsc::UnboundedSender<()>,
|
pub run_schedule_sender: mpsc::UnboundedSender<()>,
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct RawConnectionWriter {
|
pub struct RawConnectionWriter {
|
||||||
pub outgoing_packets_sender: mpsc::UnboundedSender<Box<[u8]>>,
|
pub outgoing_packets_sender: mpsc::UnboundedSender<Box<[u8]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
227
azalea-client/tests/simulation.rs
Normal file
227
azalea-client/tests/simulation.rs
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
use std::{fmt::Debug, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use azalea_auth::game_profile::GameProfile;
|
||||||
|
use azalea_client::{
|
||||||
|
events::LocalPlayerEvents,
|
||||||
|
raw_connection::{RawConnection, RawConnectionReader, RawConnectionWriter},
|
||||||
|
ClientInformation, GameProfileComponent, InConfigState, InstanceHolder, LocalPlayerBundle,
|
||||||
|
};
|
||||||
|
use azalea_core::{
|
||||||
|
game_type::{GameMode, OptionalGameType},
|
||||||
|
position::Vec3,
|
||||||
|
resource_location::ResourceLocation,
|
||||||
|
tick::GameTick,
|
||||||
|
};
|
||||||
|
use azalea_entity::{metadata::Health, LocalEntity, Position};
|
||||||
|
use azalea_protocol::packets::{
|
||||||
|
common::CommonPlayerSpawnInfo,
|
||||||
|
config::ClientboundFinishConfiguration,
|
||||||
|
game::{ClientboundLogin, ClientboundSetHealth},
|
||||||
|
ConnectionProtocol, Packet, ProtocolPacket,
|
||||||
|
};
|
||||||
|
use azalea_registry::DimensionType;
|
||||||
|
use azalea_world::Instance;
|
||||||
|
use bevy_app::App;
|
||||||
|
use bevy_app::PluginGroup;
|
||||||
|
use bevy_ecs::{prelude::*, schedule::ExecutorKind};
|
||||||
|
use bevy_log::{tracing_subscriber, LogPlugin};
|
||||||
|
use parking_lot::{Mutex, RwLock};
|
||||||
|
use tokio::{sync::mpsc, time::sleep};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_health_before_login() {
|
||||||
|
let _ = tracing_subscriber::fmt::try_init();
|
||||||
|
|
||||||
|
let mut simulation = Simulation::new(ConnectionProtocol::Configuration);
|
||||||
|
assert!(simulation.has_component::<InConfigState>());
|
||||||
|
|
||||||
|
simulation.receive_packet(ClientboundFinishConfiguration);
|
||||||
|
simulation.tick();
|
||||||
|
|
||||||
|
assert!(!simulation.has_component::<InConfigState>());
|
||||||
|
assert!(simulation.has_component::<LocalEntity>());
|
||||||
|
|
||||||
|
simulation.receive_packet(ClientboundSetHealth {
|
||||||
|
health: 15.,
|
||||||
|
food: 20,
|
||||||
|
saturation: 20.,
|
||||||
|
});
|
||||||
|
simulation.tick();
|
||||||
|
assert_eq!(*simulation.component::<Health>(), 15.);
|
||||||
|
|
||||||
|
simulation.receive_packet(ClientboundLogin {
|
||||||
|
player_id: 0,
|
||||||
|
hardcore: false,
|
||||||
|
levels: vec![],
|
||||||
|
max_players: 20,
|
||||||
|
chunk_radius: 8,
|
||||||
|
simulation_distance: 8,
|
||||||
|
reduced_debug_info: false,
|
||||||
|
show_death_screen: true,
|
||||||
|
do_limited_crafting: false,
|
||||||
|
common: CommonPlayerSpawnInfo {
|
||||||
|
dimension_type: DimensionType::Overworld,
|
||||||
|
dimension: ResourceLocation::new("overworld"),
|
||||||
|
seed: 0,
|
||||||
|
game_type: GameMode::Survival,
|
||||||
|
previous_game_type: OptionalGameType(None),
|
||||||
|
is_debug: false,
|
||||||
|
is_flat: false,
|
||||||
|
last_death_location: None,
|
||||||
|
portal_cooldown: 0,
|
||||||
|
sea_level: 63,
|
||||||
|
},
|
||||||
|
enforces_secure_chat: false,
|
||||||
|
});
|
||||||
|
simulation.tick();
|
||||||
|
|
||||||
|
// health should stay the same
|
||||||
|
assert_eq!(*simulation.component::<Health>(), 15.);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_local_player_bundle(
|
||||||
|
entity: Entity,
|
||||||
|
connection_protocol: ConnectionProtocol,
|
||||||
|
) -> (
|
||||||
|
LocalPlayerBundle,
|
||||||
|
mpsc::UnboundedReceiver<Box<[u8]>>,
|
||||||
|
Arc<Mutex<Vec<Box<[u8]>>>>,
|
||||||
|
tokio::runtime::Runtime,
|
||||||
|
) {
|
||||||
|
// unused since we'll trigger ticks ourselves
|
||||||
|
let (run_schedule_sender, _run_schedule_receiver) = tokio::sync::mpsc::unbounded_channel();
|
||||||
|
|
||||||
|
let (outgoing_packets_sender, outgoing_packets_receiver) = mpsc::unbounded_channel();
|
||||||
|
let incoming_packet_queue = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let reader = RawConnectionReader {
|
||||||
|
incoming_packet_queue: incoming_packet_queue.clone(),
|
||||||
|
run_schedule_sender,
|
||||||
|
};
|
||||||
|
let writer = RawConnectionWriter {
|
||||||
|
outgoing_packets_sender,
|
||||||
|
};
|
||||||
|
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
|
||||||
|
// the tasks can't die since that would make us send a DisconnectEvent
|
||||||
|
let read_packets_task = rt.spawn(async {
|
||||||
|
loop {
|
||||||
|
sleep(Duration::from_secs(60)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let write_packets_task = rt.spawn(async {
|
||||||
|
loop {
|
||||||
|
sleep(Duration::from_secs(60)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let raw_connection = RawConnection {
|
||||||
|
reader,
|
||||||
|
writer,
|
||||||
|
read_packets_task,
|
||||||
|
write_packets_task,
|
||||||
|
connection_protocol,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (local_player_events_sender, local_player_events_receiver) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
|
let instance = Instance::default();
|
||||||
|
let instance_holder = InstanceHolder::new(entity, Arc::new(RwLock::new(instance)));
|
||||||
|
|
||||||
|
let local_player_bundle = LocalPlayerBundle {
|
||||||
|
raw_connection,
|
||||||
|
local_player_events: LocalPlayerEvents(local_player_events_sender),
|
||||||
|
game_profile: GameProfileComponent(GameProfile::new(Uuid::nil(), "azalea".to_owned())),
|
||||||
|
client_information: ClientInformation::default(),
|
||||||
|
instance_holder,
|
||||||
|
};
|
||||||
|
(
|
||||||
|
local_player_bundle,
|
||||||
|
outgoing_packets_receiver,
|
||||||
|
incoming_packet_queue,
|
||||||
|
rt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simulation_instance_name() -> ResourceLocation {
|
||||||
|
ResourceLocation::new("azalea:simulation")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_simulation_app() -> App {
|
||||||
|
let mut app = App::new();
|
||||||
|
app.add_plugins(azalea_client::DefaultPlugins.build().disable::<LogPlugin>());
|
||||||
|
app.edit_schedule(bevy_app::Main, |schedule| {
|
||||||
|
// makes test results more reproducible
|
||||||
|
schedule.set_executor_kind(ExecutorKind::SingleThreaded);
|
||||||
|
});
|
||||||
|
app
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Simulation {
|
||||||
|
pub app: App,
|
||||||
|
pub entity: Entity,
|
||||||
|
|
||||||
|
// the runtime needs to be kept around for the tasks to be considered alive
|
||||||
|
pub rt: tokio::runtime::Runtime,
|
||||||
|
|
||||||
|
pub incoming_packet_queue: Arc<Mutex<Vec<Box<[u8]>>>>,
|
||||||
|
pub outgoing_packets_receiver: mpsc::UnboundedReceiver<Box<[u8]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Simulation {
|
||||||
|
pub fn new(initial_connection_protocol: ConnectionProtocol) -> Self {
|
||||||
|
let mut app = create_simulation_app();
|
||||||
|
let mut entity = app.world_mut().spawn_empty();
|
||||||
|
let (player, outgoing_packets_receiver, incoming_packet_queue, rt) =
|
||||||
|
create_local_player_bundle(entity.id(), initial_connection_protocol);
|
||||||
|
entity.insert(player);
|
||||||
|
|
||||||
|
let entity = entity.id();
|
||||||
|
|
||||||
|
tick_app(&mut app);
|
||||||
|
|
||||||
|
match initial_connection_protocol {
|
||||||
|
ConnectionProtocol::Configuration => {
|
||||||
|
app.world_mut().entity_mut(entity).insert(InConfigState);
|
||||||
|
tick_app(&mut app);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
app,
|
||||||
|
entity,
|
||||||
|
rt,
|
||||||
|
incoming_packet_queue,
|
||||||
|
outgoing_packets_receiver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receive_packet<P: ProtocolPacket + Debug>(&mut self, packet: impl Packet<P>) {
|
||||||
|
let buf = azalea_protocol::write::serialize_packet(&packet.into_variant()).unwrap();
|
||||||
|
self.incoming_packet_queue.lock().push(buf.into());
|
||||||
|
println!("added to incoming_packet_queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
tick_app(&mut self.app);
|
||||||
|
}
|
||||||
|
pub fn component<T: Component + Clone>(&self) -> T {
|
||||||
|
self.app.world().get::<T>(self.entity).unwrap().clone()
|
||||||
|
}
|
||||||
|
pub fn get_component<T: Component + Clone>(&self) -> Option<T> {
|
||||||
|
self.app.world().get::<T>(self.entity).cloned()
|
||||||
|
}
|
||||||
|
pub fn has_component<T: Component>(&self) -> bool {
|
||||||
|
self.app.world().get::<T>(self.entity).is_some()
|
||||||
|
}
|
||||||
|
pub fn position(&self) -> Vec3 {
|
||||||
|
*self.component::<Position>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick_app(app: &mut App) {
|
||||||
|
app.update();
|
||||||
|
app.world_mut().run_schedule(GameTick);
|
||||||
|
}
|
|
@ -489,7 +489,7 @@ pub struct PlayerBundle {
|
||||||
/// be updated by other clients.
|
/// be updated by other clients.
|
||||||
///
|
///
|
||||||
/// If this is for a client then all of our clients will have this.
|
/// If this is for a client then all of our clients will have this.
|
||||||
#[derive(Component, Clone)]
|
#[derive(Component, Clone, Debug)]
|
||||||
pub struct LocalEntity;
|
pub struct LocalEntity;
|
||||||
|
|
||||||
#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
|
#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
use azalea_buf::AzBuf;
|
|
||||||
use azalea_core::{ResourceLocation, Vec3};
|
|
||||||
use azalea_entity::{metadata::PlayerMetadataBundle, EntityBundle, PlayerBundle};
|
|
||||||
use azalea_protocol_macros::ClientboundGamePacket;
|
|
||||||
use azalea_registry::EntityKind;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
/// This packet is sent by the server when a player comes into visible range,
|
|
||||||
/// not when a player joins.
|
|
||||||
#[derive(Clone, Debug, AzBuf, ClientboundGamePacket)]
|
|
||||||
pub struct ClientboundAddPlayer {
|
|
||||||
#[var]
|
|
||||||
pub id: u32,
|
|
||||||
pub uuid: Uuid,
|
|
||||||
pub position: Vec3,
|
|
||||||
pub x_rot: i8,
|
|
||||||
pub y_rot: i8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientboundAddPlayer {
|
|
||||||
pub fn as_player_bundle(&self, world_name: ResourceLocation) -> PlayerBundle {
|
|
||||||
PlayerBundle {
|
|
||||||
entity: EntityBundle::new(self.uuid, self.position, EntityKind::Player, world_name),
|
|
||||||
metadata: PlayerMetadataBundle::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@ use azalea_core::{position::Vec3, resource_location::ResourceLocation, tick::Gam
|
||||||
use azalea_entity::{
|
use azalea_entity::{
|
||||||
attributes::AttributeInstance, Attributes, EntityDimensions, LookDirection, Physics, Position,
|
attributes::AttributeInstance, Attributes, EntityDimensions, LookDirection, Physics, Position,
|
||||||
};
|
};
|
||||||
|
use azalea_registry::EntityKind;
|
||||||
use azalea_world::{ChunkStorage, Instance, InstanceContainer, MinecraftEntityId, PartialInstance};
|
use azalea_world::{ChunkStorage, Instance, InstanceContainer, MinecraftEntityId, PartialInstance};
|
||||||
use bevy_app::App;
|
use bevy_app::App;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
@ -25,16 +26,13 @@ pub struct SimulatedPlayerBundle {
|
||||||
|
|
||||||
impl SimulatedPlayerBundle {
|
impl SimulatedPlayerBundle {
|
||||||
pub fn new(position: Vec3) -> Self {
|
pub fn new(position: Vec3) -> Self {
|
||||||
let dimensions = EntityDimensions {
|
let dimensions = EntityDimensions::from(EntityKind::Player);
|
||||||
width: 0.6,
|
|
||||||
height: 1.8,
|
|
||||||
};
|
|
||||||
|
|
||||||
SimulatedPlayerBundle {
|
SimulatedPlayerBundle {
|
||||||
position: Position::new(position),
|
position: Position::new(position),
|
||||||
physics: Physics::new(dimensions, position),
|
physics: Physics::new(dimensions, position),
|
||||||
physics_state: PhysicsState::default(),
|
physics_state: PhysicsState::default(),
|
||||||
look_direction: LookDirection::new(0.0, 0.0),
|
look_direction: LookDirection::default(),
|
||||||
attributes: Attributes {
|
attributes: Attributes {
|
||||||
speed: AttributeInstance::new(0.1),
|
speed: AttributeInstance::new(0.1),
|
||||||
attack_speed: AttributeInstance::new(4.0),
|
attack_speed: AttributeInstance::new(4.0),
|
||||||
|
|
Loading…
Add table
Reference in a new issue