azalea/pathfinder/
simulation.rs1use std::sync::Arc;
4
5use azalea_client::{inventory::Inventory, packet_handling::game::SendPacketEvent, PhysicsState};
6use azalea_core::{position::Vec3, resource_location::ResourceLocation, tick::GameTick};
7use azalea_entity::{
8 attributes::AttributeInstance, Attributes, EntityDimensions, LookDirection, Physics, Position,
9};
10use azalea_registry::EntityKind;
11use azalea_world::{ChunkStorage, Instance, InstanceContainer, MinecraftEntityId, PartialInstance};
12use bevy_app::App;
13use bevy_ecs::prelude::*;
14use parking_lot::RwLock;
15use uuid::Uuid;
16
17#[derive(Bundle, Clone)]
18pub struct SimulatedPlayerBundle {
19 pub position: Position,
20 pub physics: Physics,
21 pub physics_state: PhysicsState,
22 pub look_direction: LookDirection,
23 pub attributes: Attributes,
24 pub inventory: Inventory,
25}
26
27impl SimulatedPlayerBundle {
28 pub fn new(position: Vec3) -> Self {
29 let dimensions = EntityDimensions::from(EntityKind::Player);
30
31 SimulatedPlayerBundle {
32 position: Position::new(position),
33 physics: Physics::new(dimensions, position),
34 physics_state: PhysicsState::default(),
35 look_direction: LookDirection::default(),
36 attributes: Attributes {
37 speed: AttributeInstance::new(0.1),
38 attack_speed: AttributeInstance::new(4.0),
39 water_movement_efficiency: AttributeInstance::new(0.0),
40 },
41 inventory: Inventory::default(),
42 }
43 }
44}
45
46fn simulation_instance_name() -> ResourceLocation {
47 ResourceLocation::new("azalea:simulation")
48}
49
50fn create_simulation_instance(chunks: ChunkStorage) -> (App, Arc<RwLock<Instance>>) {
51 let instance_name = simulation_instance_name();
52
53 let instance = Arc::new(RwLock::new(Instance {
54 chunks,
55 ..Default::default()
56 }));
57
58 let mut app = App::new();
59 app.add_plugins((
61 azalea_physics::PhysicsPlugin,
62 azalea_entity::EntityPlugin,
63 azalea_client::movement::PlayerMovePlugin,
64 super::PathfinderPlugin,
65 crate::BotPlugin,
66 azalea_client::task_pool::TaskPoolPlugin::default(),
67 azalea_client::inventory::InventoryPlugin,
69 azalea_client::mining::MinePlugin,
70 azalea_client::interact::InteractPlugin,
71 ))
72 .insert_resource(InstanceContainer {
73 instances: [(instance_name.clone(), Arc::downgrade(&instance.clone()))]
74 .iter()
75 .cloned()
76 .collect(),
77 })
78 .add_event::<SendPacketEvent>();
79
80 app.edit_schedule(bevy_app::Main, |schedule| {
81 schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded);
82 });
83
84 (app, instance)
85}
86
87fn create_simulation_player_complete_bundle(
88 instance: Arc<RwLock<Instance>>,
89 player: &SimulatedPlayerBundle,
90) -> impl Bundle {
91 let instance_name = simulation_instance_name();
92
93 (
94 MinecraftEntityId(0),
95 azalea_entity::LocalEntity,
96 azalea_entity::metadata::PlayerMetadataBundle::default(),
97 azalea_entity::EntityBundle::new(
98 Uuid::nil(),
99 *player.position,
100 azalea_registry::EntityKind::Player,
101 instance_name,
102 ),
103 azalea_client::InstanceHolder {
104 partial_instance: Arc::new(RwLock::new(PartialInstance::default())),
106 instance: instance.clone(),
107 },
108 Inventory::default(),
109 )
110}
111
112fn create_simulation_player(
113 ecs: &mut World,
114 instance: Arc<RwLock<Instance>>,
115 player: SimulatedPlayerBundle,
116) -> Entity {
117 let mut entity = ecs.spawn(create_simulation_player_complete_bundle(instance, &player));
118 entity.insert(player);
119 entity.id()
120}
121
122pub struct Simulation {
124 pub app: App,
125 pub entity: Entity,
126 _instance: Arc<RwLock<Instance>>,
127}
128
129impl Simulation {
130 pub fn new(chunks: ChunkStorage, player: SimulatedPlayerBundle) -> Self {
131 let (mut app, instance) = create_simulation_instance(chunks);
132 let entity = create_simulation_player(app.world_mut(), instance.clone(), player);
133 Self {
134 app,
135 entity,
136 _instance: instance,
137 }
138 }
139
140 pub fn tick(&mut self) {
141 self.app.update();
142 self.app.world_mut().run_schedule(GameTick);
143 }
144 pub fn component<T: Component + Clone>(&self) -> T {
145 self.app.world().get::<T>(self.entity).unwrap().clone()
146 }
147 pub fn get_component<T: Component + Clone>(&self) -> Option<T> {
148 self.app.world().get::<T>(self.entity).cloned()
149 }
150 pub fn position(&self) -> Vec3 {
151 *self.component::<Position>()
152 }
153 pub fn is_mining(&self) -> bool {
154 self.get_component::<azalea_client::mining::MineBlockPos>()
156 .and_then(|c| *c)
157 .is_some()
158 }
159}
160
161pub struct SimulationSet {
163 pub app: App,
164 instance: Arc<RwLock<Instance>>,
165}
166impl SimulationSet {
167 pub fn new(chunks: ChunkStorage) -> Self {
168 let (app, instance) = create_simulation_instance(chunks);
169 Self { app, instance }
170 }
171 pub fn tick(&mut self) {
172 self.app.update();
173 self.app.world_mut().run_schedule(GameTick);
174 }
175
176 pub fn spawn(&mut self, player: SimulatedPlayerBundle) -> Entity {
177 create_simulation_player(self.app.world_mut(), self.instance.clone(), player)
178 }
179 pub fn despawn(&mut self, entity: Entity) {
180 self.app.world_mut().despawn(entity);
181 }
182
183 pub fn position(&self, entity: Entity) -> Vec3 {
184 **self.app.world().get::<Position>(entity).unwrap()
185 }
186}