mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
instanceloadedevent and a few fixes
This commit is contained in:
parent
856a3252f6
commit
e6941b6a24
7 changed files with 100 additions and 26 deletions
|
@ -1,4 +1,8 @@
|
||||||
use std::{collections::HashSet, io::Cursor, sync::Arc};
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
io::Cursor,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
use azalea_buf::McBufWritable;
|
use azalea_buf::McBufWritable;
|
||||||
use azalea_chat::FormattedText;
|
use azalea_chat::FormattedText;
|
||||||
|
@ -23,7 +27,7 @@ use azalea_protocol::{
|
||||||
},
|
},
|
||||||
read::ReadPacketError,
|
read::ReadPacketError,
|
||||||
};
|
};
|
||||||
use azalea_world::{InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance};
|
use azalea_world::{Instance, InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance};
|
||||||
use bevy_app::{App, First, Plugin, PreUpdate, Update};
|
use bevy_app::{App, First, Plugin, PreUpdate, Update};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
|
@ -36,7 +40,7 @@ use bevy_ecs::{
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use log::{debug, error, trace, warn};
|
use log::{debug, error, trace, warn};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::{Mutex, RwLock};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -100,7 +104,8 @@ impl Plugin for PacketHandlerPlugin {
|
||||||
.add_event::<ChatReceivedEvent>()
|
.add_event::<ChatReceivedEvent>()
|
||||||
.add_event::<DeathEvent>()
|
.add_event::<DeathEvent>()
|
||||||
.add_event::<KeepAliveEvent>()
|
.add_event::<KeepAliveEvent>()
|
||||||
.add_event::<ResourcePackEvent>();
|
.add_event::<ResourcePackEvent>()
|
||||||
|
.add_event::<InstanceLoadedEvent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +176,17 @@ pub struct ResourcePackEvent {
|
||||||
pub prompt: Option<FormattedText>,
|
pub prompt: Option<FormattedText>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An instance (aka world, dimension) was loaded by a client.
|
||||||
|
///
|
||||||
|
/// Since the instance is given to you as a weak reference, it won't be able to
|
||||||
|
/// be `upgrade`d if all local players leave it.
|
||||||
|
#[derive(Event, Debug, Clone)]
|
||||||
|
pub struct InstanceLoadedEvent {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub name: ResourceLocation,
|
||||||
|
pub instance: Weak<RwLock<Instance>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Something that receives packets from the server.
|
/// Something that receives packets from the server.
|
||||||
#[derive(Event, Component, Clone)]
|
#[derive(Event, Component, Clone)]
|
||||||
pub struct PacketReceiver {
|
pub struct PacketReceiver {
|
||||||
|
@ -227,9 +243,11 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
&GameProfileComponent,
|
&GameProfileComponent,
|
||||||
&ClientInformation,
|
&ClientInformation,
|
||||||
)>,
|
)>,
|
||||||
|
EventWriter<InstanceLoadedEvent>,
|
||||||
ResMut<InstanceContainer>,
|
ResMut<InstanceContainer>,
|
||||||
)> = SystemState::new(ecs);
|
)> = SystemState::new(ecs);
|
||||||
let (mut commands, mut query, mut instance_container) = system_state.get_mut(ecs);
|
let (mut commands, mut query, mut instance_loaded_events, mut instance_container) =
|
||||||
|
system_state.get_mut(ecs);
|
||||||
let (mut local_player, mut entity_id_index, game_profile, client_information) =
|
let (mut local_player, mut entity_id_index, game_profile, client_information) =
|
||||||
query.get_mut(player_entity).unwrap();
|
query.get_mut(player_entity).unwrap();
|
||||||
|
|
||||||
|
@ -246,15 +264,21 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
})
|
})
|
||||||
.element;
|
.element;
|
||||||
|
|
||||||
let new_world_name = p.dimension.clone();
|
let new_instance_name = p.dimension.clone();
|
||||||
|
|
||||||
// add this world to the instance_container (or don't if it's already
|
// add this world to the instance_container (or don't if it's already
|
||||||
// there)
|
// there)
|
||||||
let weak_world = instance_container.insert(
|
let instance = instance_container.insert(
|
||||||
new_world_name.clone(),
|
new_instance_name.clone(),
|
||||||
dimension.height,
|
dimension.height,
|
||||||
dimension.min_y,
|
dimension.min_y,
|
||||||
);
|
);
|
||||||
|
instance_loaded_events.send(InstanceLoadedEvent {
|
||||||
|
entity: player_entity,
|
||||||
|
name: new_instance_name.clone(),
|
||||||
|
instance: Arc::downgrade(&instance),
|
||||||
|
});
|
||||||
|
|
||||||
// set the partial_world to an empty world
|
// set the partial_world to an empty world
|
||||||
// (when we add chunks or entities those will be in the
|
// (when we add chunks or entities those will be in the
|
||||||
// instance_container)
|
// instance_container)
|
||||||
|
@ -267,14 +291,14 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
// in a shared world
|
// in a shared world
|
||||||
Some(player_entity),
|
Some(player_entity),
|
||||||
);
|
);
|
||||||
local_player.world = weak_world;
|
local_player.world = instance;
|
||||||
|
|
||||||
let player_bundle = PlayerBundle {
|
let player_bundle = PlayerBundle {
|
||||||
entity: EntityBundle::new(
|
entity: EntityBundle::new(
|
||||||
game_profile.uuid,
|
game_profile.uuid,
|
||||||
Vec3::default(),
|
Vec3::default(),
|
||||||
azalea_registry::EntityKind::Player,
|
azalea_registry::EntityKind::Player,
|
||||||
new_world_name,
|
new_instance_name,
|
||||||
),
|
),
|
||||||
metadata: PlayerMetadataBundle::default(),
|
metadata: PlayerMetadataBundle::default(),
|
||||||
};
|
};
|
||||||
|
@ -1161,9 +1185,11 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
&ClientInformation,
|
&ClientInformation,
|
||||||
&ReceivedRegistries,
|
&ReceivedRegistries,
|
||||||
)>,
|
)>,
|
||||||
|
EventWriter<InstanceLoadedEvent>,
|
||||||
ResMut<InstanceContainer>,
|
ResMut<InstanceContainer>,
|
||||||
)> = SystemState::new(ecs);
|
)> = SystemState::new(ecs);
|
||||||
let (mut commands, mut query, mut instance_container) = system_state.get_mut(ecs);
|
let (mut commands, mut query, mut instance_loaded_events, mut instance_container) =
|
||||||
|
system_state.get_mut(ecs);
|
||||||
let (mut local_player, game_profile, client_information, received_registries) =
|
let (mut local_player, game_profile, client_information, received_registries) =
|
||||||
query.get_mut(player_entity).unwrap();
|
query.get_mut(player_entity).unwrap();
|
||||||
|
|
||||||
|
@ -1178,15 +1204,20 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
})
|
})
|
||||||
.element;
|
.element;
|
||||||
|
|
||||||
let new_world_name = p.dimension.clone();
|
let new_instance_name = p.dimension.clone();
|
||||||
|
|
||||||
// add this world to the instance_container (or don't if it's already
|
// add this world to the instance_container (or don't if it's already
|
||||||
// there)
|
// there)
|
||||||
let weak_world = instance_container.insert(
|
let instance = instance_container.insert(
|
||||||
new_world_name.clone(),
|
new_instance_name.clone(),
|
||||||
dimension.height,
|
dimension.height,
|
||||||
dimension.min_y,
|
dimension.min_y,
|
||||||
);
|
);
|
||||||
|
instance_loaded_events.send(InstanceLoadedEvent {
|
||||||
|
entity: player_entity,
|
||||||
|
name: new_instance_name.clone(),
|
||||||
|
instance: Arc::downgrade(&instance),
|
||||||
|
});
|
||||||
|
|
||||||
// set the partial_world to an empty world
|
// set the partial_world to an empty world
|
||||||
// (when we add chunks or entities those will be in the
|
// (when we add chunks or entities those will be in the
|
||||||
|
@ -1197,7 +1228,7 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
),
|
),
|
||||||
Some(player_entity),
|
Some(player_entity),
|
||||||
);
|
);
|
||||||
local_player.world = weak_world;
|
local_player.world = instance;
|
||||||
|
|
||||||
// this resets a bunch of our components like physics and stuff
|
// this resets a bunch of our components like physics and stuff
|
||||||
let player_bundle = PlayerBundle {
|
let player_bundle = PlayerBundle {
|
||||||
|
@ -1205,7 +1236,7 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
game_profile.uuid,
|
game_profile.uuid,
|
||||||
Vec3::default(),
|
Vec3::default(),
|
||||||
azalea_registry::EntityKind::Player,
|
azalea_registry::EntityKind::Player,
|
||||||
new_world_name,
|
new_instance_name,
|
||||||
),
|
),
|
||||||
metadata: PlayerMetadataBundle::default(),
|
metadata: PlayerMetadataBundle::default(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -218,6 +218,32 @@ impl BitStorage {
|
||||||
pub fn size(&self) -> usize {
|
pub fn size(&self) -> usize {
|
||||||
self.size
|
self.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> BitStorageIter {
|
||||||
|
BitStorageIter {
|
||||||
|
storage: self,
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BitStorageIter<'a> {
|
||||||
|
storage: &'a BitStorage,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for BitStorageIter<'a> {
|
||||||
|
type Item = u64;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.index >= self.storage.size {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = self.storage.get(self.index);
|
||||||
|
self.index += 1;
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -27,20 +27,18 @@ pub struct InstanceContainer {
|
||||||
// telling them apart. We hope most servers are nice and don't do that though. It's only an
|
// telling them apart. We hope most servers are nice and don't do that though. It's only an
|
||||||
// issue when there's multiple clients with the same WorldContainer in different worlds
|
// issue when there's multiple clients with the same WorldContainer in different worlds
|
||||||
// anyways.
|
// anyways.
|
||||||
pub worlds: HashMap<ResourceLocation, Weak<RwLock<Instance>>>,
|
pub instances: HashMap<ResourceLocation, Weak<RwLock<Instance>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstanceContainer {
|
impl InstanceContainer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
InstanceContainer {
|
InstanceContainer::default()
|
||||||
worlds: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a world from the container. Returns `None` if none of the clients
|
/// Get a world from the container. Returns `None` if none of the clients
|
||||||
/// are in this world.
|
/// are in this world.
|
||||||
pub fn get(&self, name: &InstanceName) -> Option<Arc<RwLock<Instance>>> {
|
pub fn get(&self, name: &InstanceName) -> Option<Arc<RwLock<Instance>>> {
|
||||||
self.worlds.get(name).and_then(|world| world.upgrade())
|
self.instances.get(name).and_then(|world| world.upgrade())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an empty world to the container (or not if it already exists) and
|
/// Add an empty world to the container (or not if it already exists) and
|
||||||
|
@ -52,7 +50,7 @@ impl InstanceContainer {
|
||||||
height: u32,
|
height: u32,
|
||||||
min_y: i32,
|
min_y: i32,
|
||||||
) -> Arc<RwLock<Instance>> {
|
) -> Arc<RwLock<Instance>> {
|
||||||
if let Some(existing_lock) = self.worlds.get(&name).and_then(|world| world.upgrade()) {
|
if let Some(existing_lock) = self.instances.get(&name).and_then(|world| world.upgrade()) {
|
||||||
let existing = existing_lock.read();
|
let existing = existing_lock.read();
|
||||||
if existing.chunks.height != height {
|
if existing.chunks.height != height {
|
||||||
error!(
|
error!(
|
||||||
|
@ -73,7 +71,7 @@ impl InstanceContainer {
|
||||||
entities_by_chunk: HashMap::new(),
|
entities_by_chunk: HashMap::new(),
|
||||||
entity_by_id: IntMap::default(),
|
entity_by_id: IntMap::default(),
|
||||||
}));
|
}));
|
||||||
self.worlds.insert(name, Arc::downgrade(&world));
|
self.instances.insert(name, Arc::downgrade(&world));
|
||||||
world
|
world
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,25 @@ impl Heightmap {
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an iterator over the top available block positions in this
|
||||||
|
/// heightmap.
|
||||||
|
pub fn iter_first_available<'a>(&'a self) -> impl Iterator<Item = ChunkBlockPos> + 'a {
|
||||||
|
self.data.iter().enumerate().map(move |(index, height)| {
|
||||||
|
let x = (index % 16) as u8;
|
||||||
|
let z = (index / 16) as u8;
|
||||||
|
ChunkBlockPos::new(x, height as i32 + self.min_y, z)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an iterator over the top block positions in this heightmap.
|
||||||
|
pub fn iter_highest_taken<'a>(&'a self) -> impl Iterator<Item = ChunkBlockPos> + 'a {
|
||||||
|
self.data.iter().enumerate().map(move |(index, height)| {
|
||||||
|
let x = (index % 16) as u8;
|
||||||
|
let z = (index / 16) as u8;
|
||||||
|
ChunkBlockPos::new(x, height as i32 + self.min_y - 1, z)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for HeightmapKind {
|
impl FromStr for HeightmapKind {
|
||||||
|
|
|
@ -339,7 +339,7 @@ async fn swarm_handle(
|
||||||
SwarmEvent::Chat(m) => {
|
SwarmEvent::Chat(m) => {
|
||||||
println!("swarm chat message: {}", m.message().to_ansi());
|
println!("swarm chat message: {}", m.message().to_ansi());
|
||||||
if m.message().to_string() == "<py5> world" {
|
if m.message().to_string() == "<py5> world" {
|
||||||
for (name, world) in &swarm.instance_container.read().worlds {
|
for (name, world) in &swarm.instance_container.read().instances {
|
||||||
println!("world name: {name}");
|
println!("world name: {name}");
|
||||||
if let Some(w) = world.upgrade() {
|
if let Some(w) = world.upgrade() {
|
||||||
for chunk_pos in w.read().chunks.map.values() {
|
for chunk_pos in w.read().chunks.map.values() {
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub use azalea_block as blocks;
|
||||||
pub use azalea_brigadier as brigadier;
|
pub use azalea_brigadier as brigadier;
|
||||||
pub use azalea_chat::FormattedText;
|
pub use azalea_chat::FormattedText;
|
||||||
pub use azalea_client::*;
|
pub use azalea_client::*;
|
||||||
pub use azalea_core::{BlockPos, ChunkPos, Vec3};
|
pub use azalea_core::{BlockPos, ChunkPos, ResourceLocation, Vec3};
|
||||||
pub use azalea_entity as entity;
|
pub use azalea_entity as entity;
|
||||||
pub use azalea_protocol as protocol;
|
pub use azalea_protocol as protocol;
|
||||||
pub use azalea_registry::{Block, EntityKind, Item};
|
pub use azalea_registry::{Block, EntityKind, Item};
|
||||||
|
|
|
@ -70,7 +70,7 @@ impl Simulation {
|
||||||
// make sure it doesn't do fixed ticks without us telling it to
|
// make sure it doesn't do fixed ticks without us telling it to
|
||||||
.insert_resource(FixedTime::new(Duration::from_secs(60)))
|
.insert_resource(FixedTime::new(Duration::from_secs(60)))
|
||||||
.insert_resource(InstanceContainer {
|
.insert_resource(InstanceContainer {
|
||||||
worlds: [(instance_name.clone(), Arc::downgrade(&instance.clone()))]
|
instances: [(instance_name.clone(), Arc::downgrade(&instance.clone()))]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue