aboutsummaryrefslogtreecommitdiff
path: root/azalea/src/pathfinder/simulation.rs
blob: ab3b340e856bf3f1ba8cfa94117f21a15d01e0c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! 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, 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 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(),
            attributes: Attributes {
                speed: AttributeInstance::new(0.1),
                attack_speed: AttributeInstance::new(4.0),
            },
            inventory: InventoryComponent::default(),
        }
    }
}

/// 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 instance_name = ResourceLocation::new("azalea:simulation");

        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);
        });

        let mut entity = app.world.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);

        let entity_id = entity.id();

        Self {
            app,
            entity: entity_id,
            _instance: instance,
        }
    }
    pub fn tick(&mut self) {
        self.app.world.run_schedule(GameTick);
        self.app.update();
    }
    pub fn position(&self) -> Vec3 {
        **self.app.world.get::<Position>(self.entity).unwrap()
    }
}