1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00
azalea/azalea/src/pathfinder/simulation.rs
2024-02-25 16:53:08 -06:00

171 lines
5.2 KiB
Rust

//! Simulate the Minecraft world, currently only used for tests.
use std::sync::Arc;
use azalea_client::{
inventory::InventoryComponent, packet_handling::game::SendPacketEvent, PhysicsState,
};
use azalea_core::{position::Vec3, resource_location::ResourceLocation, tick::GameTick};
use azalea_entity::{
attributes::AttributeInstance, Attributes, EntityDimensions, LookDirection, Physics, Position,
};
use azalea_world::{ChunkStorage, Instance, InstanceContainer, MinecraftEntityId, PartialInstance};
use bevy_app::App;
use bevy_ecs::prelude::*;
use parking_lot::RwLock;
use uuid::Uuid;
#[derive(Bundle, Clone)]
pub struct SimulatedPlayerBundle {
pub position: Position,
pub physics: Physics,
pub physics_state: PhysicsState,
pub look_direction: LookDirection,
pub attributes: Attributes,
pub inventory: InventoryComponent,
}
impl SimulatedPlayerBundle {
pub fn new(position: Vec3) -> Self {
let dimensions = EntityDimensions {
width: 0.6,
height: 1.8,
};
SimulatedPlayerBundle {
position: Position::new(position),
physics: Physics::new(dimensions, &position),
physics_state: PhysicsState::default(),
look_direction: LookDirection::new(0.0, 0.0),
attributes: Attributes {
speed: AttributeInstance::new(0.1),
attack_speed: AttributeInstance::new(4.0),
},
inventory: InventoryComponent::default(),
}
}
}
fn simulation_instance_name() -> ResourceLocation {
ResourceLocation::new("azalea:simulation")
}
fn create_simulation_instance(chunks: ChunkStorage) -> (App, Arc<RwLock<Instance>>) {
let instance_name = simulation_instance_name();
let instance = Arc::new(RwLock::new(Instance {
chunks,
..Default::default()
}));
let mut app = App::new();
// we don't use all the default azalea plugins because we don't need all of them
app.add_plugins((
azalea_physics::PhysicsPlugin,
azalea_entity::EntityPlugin,
azalea_client::movement::PlayerMovePlugin,
super::PathfinderPlugin,
crate::BotPlugin,
azalea_client::task_pool::TaskPoolPlugin::default(),
// for mining
azalea_client::inventory::InventoryPlugin,
azalea_client::mining::MinePlugin,
azalea_client::interact::InteractPlugin,
))
.insert_resource(InstanceContainer {
instances: [(instance_name.clone(), Arc::downgrade(&instance.clone()))]
.iter()
.cloned()
.collect(),
})
.add_event::<SendPacketEvent>();
app.edit_schedule(bevy_app::Main, |schedule| {
schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded);
});
(app, instance)
}
fn create_simulation_player(
ecs: &mut World,
instance: Arc<RwLock<Instance>>,
player: SimulatedPlayerBundle,
) -> Entity {
let instance_name = simulation_instance_name();
let mut entity = ecs.spawn((
MinecraftEntityId(0),
azalea_entity::LocalEntity,
azalea_entity::metadata::PlayerMetadataBundle::default(),
azalea_entity::EntityBundle::new(
Uuid::nil(),
*player.position,
azalea_registry::EntityKind::Player,
instance_name,
),
azalea_client::InstanceHolder {
// partial_instance is never actually used by the pathfinder so
partial_instance: Arc::new(RwLock::new(PartialInstance::default())),
instance: instance.clone(),
},
InventoryComponent::default(),
));
entity.insert(player);
entity.id()
}
/// Simulate the Minecraft world to see if certain movements would be possible.
pub struct Simulation {
pub app: App,
pub entity: Entity,
_instance: Arc<RwLock<Instance>>,
}
impl Simulation {
pub fn new(chunks: ChunkStorage, player: SimulatedPlayerBundle) -> Self {
let (mut app, instance) = create_simulation_instance(chunks);
let entity = create_simulation_player(&mut app.world, instance.clone(), player);
Self {
app,
entity,
_instance: instance,
}
}
pub fn tick(&mut self) {
self.app.update();
self.app.world.run_schedule(GameTick);
}
pub fn position(&self) -> Vec3 {
**self.app.world.get::<Position>(self.entity).unwrap()
}
}
/// A set of simulations, useful for efficiently doing multiple simulations.
pub struct SimulationSet {
pub app: App,
instance: Arc<RwLock<Instance>>,
}
impl SimulationSet {
pub fn new(chunks: ChunkStorage) -> Self {
let (app, instance) = create_simulation_instance(chunks);
Self { app, instance }
}
pub fn tick(&mut self) {
self.app.update();
self.app.world.run_schedule(GameTick);
}
pub fn spawn(&mut self, player: SimulatedPlayerBundle) -> Entity {
create_simulation_player(&mut self.app.world, self.instance.clone(), player)
}
pub fn despawn(&mut self, entity: Entity) {
self.app.world.despawn(entity);
}
pub fn position(&self, entity: Entity) -> Vec3 {
**self.app.world.get::<Position>(entity).unwrap()
}
}