1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

i am being trolled by rust

for some reason some stuff is really slow for literally no reason and it makes no sense i am going insane
This commit is contained in:
mat 2022-11-23 02:21:34 -06:00
parent d41d6480ea
commit 2d1fd83f9f
10 changed files with 285 additions and 122 deletions

View file

@ -35,12 +35,14 @@ use azalea_world::{
WeakWorld, WeakWorldContainer, World,
};
use log::{debug, error, info, trace, warn};
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
use parking_lot::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::time::Duration;
use std::{
collections::HashMap,
fmt::Debug,
io::{self, Cursor},
sync::Arc,
time::Instant,
};
use thiserror::Error;
use tokio::{
@ -106,7 +108,7 @@ pub struct Client {
pub write_conn: Arc<tokio::sync::Mutex<WriteConnection<ServerboundGamePacket>>>,
pub entity_id: Arc<RwLock<u32>>,
/// The world that this client has access to. This supports shared worlds.
pub world: Arc<RwLock<World>>,
pub world: Arc<Mutex<World>>,
/// A container of world names to worlds. If we're not using a shared world
/// (i.e. not a swarm), then this will only contain data about the world
/// we're currently in.
@ -188,7 +190,7 @@ impl Client {
write_conn,
// default our id to 0, it'll be set later
entity_id: Arc::new(RwLock::new(0)),
world: Arc::new(RwLock::new(World::default())),
world: Arc::new(Mutex::new(World::default())),
world_container: world_container
.unwrap_or_else(|| Arc::new(RwLock::new(WeakWorldContainer::new()))),
world_name: Arc::new(RwLock::new(None)),
@ -489,10 +491,11 @@ impl Client {
.insert(p.dimension.clone(), height, min_y);
// set the loaded_world to an empty world
// (when we add chunks or entities those will be in the world_container)
let mut world_lock = client.world.write();
let mut world_lock = client.world.lock();
*world_lock = World::new(
client.client_information.read().view_distance.into(),
weak_world,
p.player_id,
);
let entity = EntityData::new(
@ -500,6 +503,7 @@ impl Client {
Vec3::default(),
EntityMetadata::Player(metadata::Player::default()),
);
// make it so other entities don't update this entity in a shared world
world_lock.add_entity(p.player_id, entity);
*client.entity_id.write() = p.player_id;
@ -569,11 +573,9 @@ impl Client {
let (new_pos, y_rot, x_rot) = {
let player_entity_id = *client.entity_id.read();
let mut world_lock = client.world.write();
let mut world_lock = client.world.lock();
let mut player_entity = world_lock
.entity_mut(player_entity_id)
.expect("Player entity doesn't exist");
let mut player_entity = world_lock.entity_mut(player_entity_id).unwrap();
let delta_movement = player_entity.delta;
@ -747,7 +749,7 @@ impl Client {
debug!("Got chunk cache center packet {:?}", p);
client
.world
.write()
.lock()
.update_view_center(&ChunkPos::new(p.x, p.z));
}
ClientboundGamePacket::LevelChunkWithLight(p) => {
@ -759,10 +761,10 @@ impl Client {
// parse it again. This is only used when we have a shared
// world, since we check that the chunk isn't currently owned
// by this client.
let shared_has_chunk = client.world.read().get_chunk(&pos).is_some();
let shared_has_chunk = client.world.lock().get_chunk(&pos).is_some();
let this_client_has_chunk = client
.world
.read()
.lock()
.chunk_storage
.limited_get(&pos)
.is_some();
@ -778,7 +780,7 @@ impl Client {
// debug("chunk {:?}")
if let Err(e) = client
.world
.write()
.lock()
.replace_with_packet_data(&pos, &mut Cursor::new(&p.chunk_data.data))
{
error!("Couldn't set chunk data: {}", e);
@ -790,15 +792,15 @@ impl Client {
ClientboundGamePacket::AddEntity(p) => {
debug!("Got add entity packet {:?}", p);
let entity = EntityData::from(p);
client.world.write().add_entity(p.id, entity);
client.world.lock().add_entity(p.id, entity);
}
ClientboundGamePacket::SetEntityData(p) => {
debug!("Got set entity data packet {:?}", p);
let mut world = client.world.write();
let mut world = client.world.lock();
if let Some(mut entity) = world.entity_mut(p.id) {
entity.apply_metadata(&p.packed_items.0);
} else {
warn!("Server sent an entity data packet for an entity id ({}) that we don't know about", p.id);
// warn!("Server sent an entity data packet for an entity id ({}) that we don't know about", p.id);
}
}
ClientboundGamePacket::UpdateAttributes(_p) => {
@ -813,13 +815,13 @@ impl Client {
ClientboundGamePacket::AddPlayer(p) => {
debug!("Got add player packet {:?}", p);
let entity = EntityData::from(p);
client.world.write().add_entity(p.id, entity);
client.world.lock().add_entity(p.id, entity);
}
ClientboundGamePacket::InitializeBorder(p) => {
debug!("Got initialize border packet {:?}", p);
}
ClientboundGamePacket::SetTime(p) => {
debug!("Got set time packet {:?}", p);
// debug!("Got set time packet {:?}", p);
}
ClientboundGamePacket::SetDefaultSpawnPosition(p) => {
debug!("Got set default spawn position packet {:?}", p);
@ -841,18 +843,26 @@ impl Client {
debug!("Got set experience packet {:?}", p);
}
ClientboundGamePacket::TeleportEntity(p) => {
let mut world_lock = client.world.write();
let a_start = Instant::now();
let mut world_lock = client.world.lock();
let a_elapsed = a_start.elapsed();
let b_start = Instant::now();
let _ = world_lock.set_entity_pos(
p.id,
Vec3 {
x: p.x,
y: p.y,
z: p.z,
},
);
let b_elapsed = b_start.elapsed();
world_lock
.set_entity_pos(
p.id,
Vec3 {
x: p.x,
y: p.y,
z: p.z,
},
)
.map_err(|e| HandleError::Other(e.into()))?;
if a_start.elapsed() > Duration::from_millis(1) {
warn!(
"Set entity pos took too long: {:?} {:?}",
a_elapsed, b_elapsed
);
}
}
ClientboundGamePacket::UpdateAdvancements(p) => {
debug!("Got update advancements packet {:?}", p);
@ -861,18 +871,14 @@ impl Client {
// debug!("Got rotate head packet {:?}", p);
}
ClientboundGamePacket::MoveEntityPos(p) => {
let mut world_lock = client.world.write();
let mut world_lock = client.world.lock();
world_lock
.move_entity_with_delta(p.entity_id, &p.delta)
.map_err(|e| HandleError::Other(e.into()))?;
let _ = world_lock.move_entity_with_delta(p.entity_id, &p.delta);
}
ClientboundGamePacket::MoveEntityPosRot(p) => {
let mut world_lock = client.world.write();
let mut world_lock = client.world.lock();
world_lock
.move_entity_with_delta(p.entity_id, &p.delta)
.map_err(|e| HandleError::Other(e.into()))?;
let _ = world_lock.move_entity_with_delta(p.entity_id, &p.delta);
}
ClientboundGamePacket::MoveEntityRot(_p) => {
// debug!("Got move entity rot packet {:?}", p);
@ -887,7 +893,7 @@ impl Client {
debug!("Got remove entities packet {:?}", p);
}
ClientboundGamePacket::PlayerChat(p) => {
// debug!("Got player chat packet {:?}", p);
debug!("Got player chat packet {:?}", p);
tx.send(Event::Chat(ChatPacket::Player(Box::new(p.clone()))))
.unwrap();
}
@ -896,14 +902,14 @@ impl Client {
tx.send(Event::Chat(ChatPacket::System(p.clone()))).unwrap();
}
ClientboundGamePacket::Sound(p) => {
debug!("Got sound packet {:?}", p);
// debug!("Got sound packet {:?}", p);
}
ClientboundGamePacket::LevelEvent(p) => {
debug!("Got level event packet {:?}", p);
}
ClientboundGamePacket::BlockUpdate(p) => {
debug!("Got block update packet {:?}", p);
let mut world = client.world.write();
let mut world = client.world.lock();
world.set_block_state(&p.pos, p.block_state);
}
ClientboundGamePacket::Animate(p) => {
@ -911,7 +917,7 @@ impl Client {
}
ClientboundGamePacket::SectionBlocksUpdate(p) => {
debug!("Got section blocks update packet {:?}", p);
let mut world = client.world.write();
let mut world = client.world.lock();
for state in &p.states {
world.set_block_state(&(p.section_pos + state.pos.clone()), state.state);
}
@ -1015,36 +1021,75 @@ impl Client {
game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst);
loop {
game_tick_interval.tick().await;
let start = Instant::now();
Self::game_tick(&mut client, &tx).await;
let elapsed = start.elapsed();
if elapsed > time::Duration::from_millis(50) {
warn!("Game tick took too long: {:?}", elapsed);
}
}
}
/// Runs every 50 milliseconds.
async fn game_tick(client: &mut Client, tx: &UnboundedSender<Event>) {
// return if there's no chunk at the player's position
let game_tick_start = Instant::now();
{
let world_lock = client.world.write();
let a_start = Instant::now();
let world_lock = client.world.lock();
let a_elapsed = a_start.elapsed();
let b_start = Instant::now();
let player_entity_id = *client.entity_id.read();
let b_elapsed = b_start.elapsed();
let c_start = Instant::now();
let player_entity = world_lock.entity(player_entity_id);
let player_entity = if let Some(player_entity) = player_entity {
player_entity
} else {
let c_elapsed = c_start.elapsed();
let d_start = Instant::now();
let Some(player_entity) = player_entity else {
return;
};
let player_chunk_pos: ChunkPos = player_entity.pos().into();
if world_lock.get_chunk(&player_chunk_pos).is_none() {
return;
}
let d_elapsed = d_start.elapsed();
if a_start.elapsed() > time::Duration::from_millis(20) {
warn!("a_elapsed: {:?}", a_elapsed);
warn!("b_elapsed: {:?}", b_elapsed);
warn!("c_elapsed: {:?}", c_elapsed);
warn!("d_elapsed: {:?}", d_elapsed);
}
}
let check_chunk_elapsed = game_tick_start.elapsed();
tx.send(Event::Tick).unwrap();
// TODO: if we're a passenger, send the required packets
let send_position_start = Instant::now();
if let Err(e) = client.send_position().await {
warn!("Error sending position: {:?}", e);
}
let send_position_elapsed = send_position_start.elapsed();
let ai_step_start = Instant::now();
client.ai_step();
let ai_step_elapsed = ai_step_start.elapsed();
let game_tick_elapsed = game_tick_start.elapsed();
if game_tick_elapsed > time::Duration::from_millis(50) {
warn!(
"(internal) game tick took too long: {:?}",
game_tick_elapsed
);
warn!("check_chunk_elapsed: {:?}", check_chunk_elapsed);
warn!("send_position_elapsed: {:?}", send_position_elapsed);
warn!("ai_step_elapsed: {:?}", ai_step_elapsed);
}
// TODO: minecraft does ambient sounds here
}
@ -1069,10 +1114,10 @@ impl Client {
}
/// Returns the entity associated to the player.
pub fn entity_mut(&self) -> Entity<RwLockWriteGuard<World>> {
pub fn entity_mut(&self) -> Entity<MutexGuard<World>> {
let entity_id = *self.entity_id.read();
let world = self.world.write();
let world = self.world.lock();
let entity_data = world
.entity_storage
@ -1082,10 +1127,9 @@ impl Client {
Entity::new(world, entity_id, entity_ptr)
}
/// Returns the entity associated to the player.
pub fn entity(&self) -> Entity<RwLockReadGuard<World>> {
pub fn entity(&self) -> Entity<MutexGuard<World>> {
let entity_id = *self.entity_id.read();
let world = self.world.read();
let world = self.world.lock();
let entity_data = world
.entity_storage

View file

@ -7,6 +7,8 @@
#![allow(incomplete_features)]
#![feature(trait_upcasting)]
#![feature(error_generic_member_access)]
#![feature(provide_any)]
mod account;
mod chat;

View file

@ -1,3 +1,5 @@
use std::backtrace::Backtrace;
use crate::Client;
use azalea_core::Vec3;
use azalea_physics::collision::{MovableEntity, MoverType};
@ -15,7 +17,7 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum MovePlayerError {
#[error("Player is not in world")]
PlayerNotInWorld,
PlayerNotInWorld(Backtrace),
#[error("{0}")]
Io(#[from] std::io::Error),
}
@ -23,7 +25,9 @@ pub enum MovePlayerError {
impl From<MoveEntityError> for MovePlayerError {
fn from(err: MoveEntityError) -> Self {
match err {
MoveEntityError::EntityDoesNotExist => MovePlayerError::PlayerNotInWorld,
MoveEntityError::EntityDoesNotExist(backtrace) => {
MovePlayerError::PlayerNotInWorld(backtrace)
}
}
}
}
@ -152,9 +156,9 @@ impl Client {
}
// Set our current position to the provided Vec3, potentially clipping through blocks.
pub async fn set_pos(&mut self, new_pos: Vec3) -> Result<(), MovePlayerError> {
pub async fn set_position(&mut self, new_pos: Vec3) -> Result<(), MovePlayerError> {
let player_entity_id = *self.entity_id.read();
let mut world_lock = self.world.write();
let mut world_lock = self.world.lock();
world_lock.set_entity_pos(player_entity_id, new_pos)?;
@ -162,12 +166,12 @@ impl Client {
}
pub async fn move_entity(&mut self, movement: &Vec3) -> Result<(), MovePlayerError> {
let mut world_lock = self.world.write();
let mut world_lock = self.world.lock();
let player_entity_id = *self.entity_id.read();
let mut entity = world_lock
.entity_mut(player_entity_id)
.ok_or(MovePlayerError::PlayerNotInWorld)?;
.ok_or(MovePlayerError::PlayerNotInWorld(Backtrace::capture()))?;
log::trace!(
"move entity bounding box: {} {:?}",
entity.id,

View file

@ -22,7 +22,7 @@ const SECTION_HEIGHT: u32 = 16;
/// distance. This has support for using a shared [`WeakChunkStorage`]. If you
/// have an infinite render distance (like a server), you should use
/// [`ChunkStorage`] instead.
pub struct LimitedChunkStorage {
pub struct PartialChunkStorage {
/// Chunk storage that can be shared by clients.
shared: Arc<RwLock<WeakChunkStorage>>,
@ -84,10 +84,10 @@ impl Default for Chunk {
}
}
impl LimitedChunkStorage {
impl PartialChunkStorage {
pub fn new(chunk_radius: u32, shared: Arc<RwLock<WeakChunkStorage>>) -> Self {
let view_range = chunk_radius * 2 + 1;
LimitedChunkStorage {
PartialChunkStorage {
shared,
view_center: ChunkPos::new(0, 0),
chunk_radius,
@ -157,7 +157,7 @@ impl LimitedChunkStorage {
}
/// Get a [`Chunk`] within render distance, or `None` if it's not loaded.
/// Use [`LimitedChunkStorage::get`] to get a chunk from the shared storage.
/// Use [`PartialChunkStorage::get`] to get a chunk from the shared storage.
///
/// # Panics
/// If the chunk is not in the render distance.
@ -166,7 +166,7 @@ impl LimitedChunkStorage {
&self.chunks[index]
}
/// Get a mutable reference to a [`Chunk`] within render distance, or
/// `None` if it's not loaded. Use [`LimitedChunkStorage::get`] to get
/// `None` if it's not loaded. Use [`PartialChunkStorage::get`] to get
/// a chunk from the shared storage.
///
/// # Panics
@ -293,7 +293,7 @@ impl McBufWritable for Chunk {
}
}
impl Debug for LimitedChunkStorage {
impl Debug for PartialChunkStorage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChunkStorage")
.field("view_center", &self.view_center)
@ -371,7 +371,7 @@ impl Section {
}
}
impl Default for LimitedChunkStorage {
impl Default for PartialChunkStorage {
fn default() -> Self {
Self::new(8, Arc::new(RwLock::new(WeakChunkStorage::default())))
}
@ -401,7 +401,7 @@ mod tests {
#[test]
fn test_out_of_bounds_y() {
let mut chunk_storage = LimitedChunkStorage::default();
let mut chunk_storage = PartialChunkStorage::default();
chunk_storage.set(
&ChunkPos { x: 0, z: 0 },
Some(Arc::new(Mutex::new(Chunk::default()))),

View file

@ -1,4 +1,4 @@
use crate::{WeakWorld, World};
use crate::WeakWorld;
use azalea_core::ResourceLocation;
use log::error;
use std::{

View file

@ -6,63 +6,101 @@ use parking_lot::RwLock;
use std::{
collections::HashMap,
sync::{Arc, Weak},
time::{Duration, Instant},
};
use uuid::Uuid;
/// Store a map of entities by ID. To get an iterator over all entities, use `storage.shared.read().entities`
/// [`WeakEntityStorage::entities`]
// How entity updates are processed (to avoid issues with shared worlds)
// - each bot contains a map of { entity id: updates received }
// - the shared world also contains a canonical "true" updates received for each entity
// - when a client loads an entity, its "updates received" is set to the same as the global "updates received"
// - when the shared world sees an entity for the first time, the "updates received" is set to 1.
// - clients can force the shared "updates received" to 0 to make it so certain entities (i.e. other bots in our swarm) don't get confused and updated by other bots
// - when a client gets an update to an entity, we check if our "updates received" is the same as the shared world's "updates received":
// if it is, then process the update and increment the client's and shared world's "updates received"
// if not, then we simply increment our local "updates received" and do nothing else
/// Store a map of entities by ID. To get an iterator over all entities, use
/// `storage.shared.read().entities` [`WeakEntityStorage::entities`].
///
/// This is meant to be used with shared worlds.
#[derive(Debug, Default)]
pub struct EntityStorage {
pub struct PartialEntityStorage {
pub shared: Arc<RwLock<WeakEntityStorage>>,
/// The entity id of the player that owns this struct.
pub owner_entity_id: u32,
pub updates_received: IntMap<u32, u32>,
/// Strong references to the entities we have loaded.
_data_by_id: IntMap<u32, Arc<EntityData>>,
data_by_id: IntMap<u32, Arc<EntityData>>,
}
/// Weakly store entities in a world. If the entities aren't being referenced
/// by anything else (like an [`EntityStorage`]), they'll be forgotten.
/// by anything else (like an [`PartialEntityStorage`]), they'll be forgotten.
#[derive(Debug, Default)]
pub struct WeakEntityStorage {
data_by_id: IntMap<u32, Weak<EntityData>>,
/// An index of all the entity ids we know are in a chunk
ids_by_chunk: HashMap<ChunkPos, IntSet<u32>>,
/// An index of entity ids by their UUIDs
id_by_uuid: HashMap<Uuid, u32>,
pub updates_received: IntMap<u32, u32>,
}
impl EntityStorage {
pub fn new(shared: Arc<RwLock<WeakEntityStorage>>) -> Self {
impl PartialEntityStorage {
pub fn new(shared: Arc<RwLock<WeakEntityStorage>>, owner_entity_id: u32) -> Self {
shared.write().updates_received.insert(owner_entity_id, 0);
Self {
shared,
_data_by_id: IntMap::default(),
owner_entity_id,
updates_received: IntMap::default(),
data_by_id: IntMap::default(),
}
}
/// Add an entity to the storage.
#[inline]
pub fn insert(&mut self, id: u32, entity: EntityData) {
self.shared
.write()
// if the entity is already in the shared world, we don't need to do anything
if self.shared.read().data_by_id.contains_key(&id) {
return;
}
// add the entity to the "indexes"
let mut shared = self.shared.write();
shared
.ids_by_chunk
.entry(ChunkPos::from(entity.pos()))
.or_default()
.insert(id);
self.shared.write().id_by_uuid.insert(entity.uuid, id);
shared.id_by_uuid.insert(entity.uuid, id);
// now store the actual entity data
let entity = Arc::new(entity);
self.shared
.write()
.data_by_id
.insert(id, Arc::downgrade(&entity));
self._data_by_id.insert(id, entity);
shared.data_by_id.insert(id, Arc::downgrade(&entity));
self.data_by_id.insert(id, entity);
// set our updates_received to the shared updates_received, unless it's
// not there in which case set both to 1
if let Some(&shared_updates_received) = shared.updates_received.get(&id) {
// 0 means we're never tracking updates for this entity
if shared_updates_received != 0 || id == self.owner_entity_id {
self.updates_received.insert(id, 1);
}
} else {
shared.updates_received.insert(id, 1);
self.updates_received.insert(id, 1);
}
}
/// Remove an entity from this storage by its id. It will only be removed
/// from the shared storage if there are no other references to it.
#[inline]
pub fn remove_by_id(&mut self, id: u32) {
if let Some(entity) = self._data_by_id.remove(&id) {
if let Some(entity) = self.data_by_id.remove(&id) {
let chunk = ChunkPos::from(entity.pos());
let uuid = entity.uuid;
self.updates_received.remove(&id);
drop(entity);
// maybe remove it from the storage
self.shared.write().remove_entity_if_unused(id, uuid, chunk);
@ -76,7 +114,7 @@ impl EntityStorage {
/// [`WeakEntityStorage::contains_id`].
#[inline]
pub fn limited_contains_id(&self, id: &u32) -> bool {
self._data_by_id.contains_key(id)
self.data_by_id.contains_key(id)
}
/// Whether the entity with the given id is in the shared storage (i.e.
@ -91,14 +129,36 @@ impl EntityStorage {
/// Get a reference to an entity by its id, if it's being loaded by this storage.
#[inline]
pub fn limited_get_by_id(&self, id: u32) -> Option<&Arc<EntityData>> {
self._data_by_id.get(&id)
self.data_by_id.get(&id)
}
/// Get a mutable reference to an entity by its id, if it's being loaded by
/// this storage.
#[inline]
pub fn limited_get_mut_by_id(&mut self, id: u32) -> Option<&mut Arc<EntityData>> {
self._data_by_id.get_mut(&id)
self.data_by_id.get_mut(&id)
}
/// Returns whether we're allowed to update this entity (to prevent two clients in
/// a shared world updating it twice), and acknowleges that we WILL update
/// it if it's true. Don't call this unless you actually got an entity
/// update that all other clients within render distance will get too.
pub fn maybe_update(&mut self, id: u32) -> bool {
let this_client_updates_received = self.updates_received.get(&id).copied();
let shared_updates_received = self.shared.read().updates_received.get(&id).copied();
let can_update = this_client_updates_received == shared_updates_received;
if can_update {
let new_updates_received = this_client_updates_received.unwrap_or(0) + 1;
self.updates_received.insert(id, new_updates_received);
self.shared
.write()
.updates_received
.insert(id, new_updates_received);
true
} else {
false
}
}
/// Get an entity in the shared storage by its id, if it exists.
@ -119,7 +179,7 @@ impl EntityStorage {
.read()
.id_by_uuid
.get(uuid)
.and_then(|id| self._data_by_id.get(id))
.and_then(|id| self.data_by_id.get(id))
}
/// Get a mutable reference to an entity by its UUID, if it's being loaded
@ -130,7 +190,7 @@ impl EntityStorage {
.read()
.id_by_uuid
.get(uuid)
.and_then(|id| self._data_by_id.get_mut(id))
.and_then(|id| self.data_by_id.get_mut(id))
}
/// Get an entity in the shared storage by its UUID, if it exists.
@ -150,7 +210,7 @@ impl EntityStorage {
pub fn clear_chunk(&mut self, chunk: &ChunkPos) {
if let Some(entities) = self.shared.read().ids_by_chunk.get(chunk) {
for id in entities.iter() {
if let Some(entity) = self._data_by_id.remove(id) {
if let Some(entity) = self.data_by_id.remove(id) {
let uuid = entity.uuid;
drop(entity);
// maybe remove it from the storage
@ -222,6 +282,7 @@ impl WeakEntityStorage {
data_by_id: IntMap::default(),
ids_by_chunk: HashMap::default(),
id_by_uuid: HashMap::default(),
updates_received: IntMap::default(),
}
}
@ -241,6 +302,12 @@ impl WeakEntityStorage {
"Tried to remove entity with id {id} from uuid {uuid:?} but it was not found."
);
}
if self.updates_received.remove(&id).is_none() {
// if this happens it means we weren't tracking the updates_received for the client (bad)
warn!(
"Tried to remove entity with id {id} from updates_received but it was not found."
);
}
true
}
}
@ -290,7 +357,7 @@ mod tests {
#[test]
fn test_store_entity() {
let mut storage = EntityStorage::default();
let mut storage = PartialEntityStorage::default();
assert!(storage.get_by_id(0).is_none());
let uuid = Uuid::from_u128(100);

View file

@ -1,4 +1,6 @@
#![feature(int_roundings)]
#![feature(error_generic_member_access)]
#![feature(provide_any)]
mod bit_storage;
mod chunk_storage;
@ -8,15 +10,17 @@ mod entity_storage;
mod palette;
mod world;
use std::backtrace::Backtrace;
pub use bit_storage::BitStorage;
pub use chunk_storage::{Chunk, ChunkStorage, LimitedChunkStorage, WeakChunkStorage};
pub use chunk_storage::{Chunk, ChunkStorage, PartialChunkStorage, WeakChunkStorage};
pub use container::*;
pub use entity_storage::{EntityStorage, WeakEntityStorage};
pub use entity_storage::{PartialEntityStorage, WeakEntityStorage};
use thiserror::Error;
pub use world::*;
#[derive(Error, Debug)]
pub enum MoveEntityError {
#[error("Entity doesn't exist")]
EntityDoesNotExist,
EntityDoesNotExist(Backtrace),
}

View file

@ -1,13 +1,18 @@
use crate::{
entity::{Entity, EntityData},
Chunk, EntityStorage, LimitedChunkStorage, MoveEntityError, WeakChunkStorage,
Chunk, MoveEntityError, PartialChunkStorage, PartialEntityStorage, WeakChunkStorage,
WeakEntityStorage,
};
use azalea_block::BlockState;
use azalea_buf::BufReadError;
use azalea_core::{BlockPos, ChunkPos, PositionDelta8, Vec3};
use log::warn;
use parking_lot::{Mutex, RwLock};
use std::fmt::Debug;
use std::{
backtrace::Backtrace,
fmt::Debug,
time::{Duration, Instant},
};
use std::{fmt::Formatter, io::Cursor, sync::Arc};
use uuid::Uuid;
@ -18,8 +23,8 @@ pub struct World {
// dropped, we don't need to do anything with it
_shared: Arc<WeakWorld>,
pub chunk_storage: LimitedChunkStorage,
pub entity_storage: EntityStorage,
pub chunk_storage: PartialChunkStorage,
pub entity_storage: PartialEntityStorage,
}
/// A world where the chunks are stored as weak pointers. This is used for shared worlds.
@ -30,11 +35,14 @@ pub struct WeakWorld {
}
impl World {
pub fn new(chunk_radius: u32, shared: Arc<WeakWorld>) -> Self {
pub fn new(chunk_radius: u32, shared: Arc<WeakWorld>, owner_entity_id: u32) -> Self {
World {
_shared: shared.clone(),
chunk_storage: LimitedChunkStorage::new(chunk_radius, shared.chunk_storage.clone()),
entity_storage: EntityStorage::new(shared.entity_storage.clone()),
chunk_storage: PartialChunkStorage::new(chunk_radius, shared.chunk_storage.clone()),
entity_storage: PartialEntityStorage::new(
shared.entity_storage.clone(),
owner_entity_id,
),
}
}
@ -69,18 +77,30 @@ impl World {
}
pub fn set_entity_pos(&mut self, entity_id: u32, new_pos: Vec3) -> Result<(), MoveEntityError> {
let mut entity = self
.entity_mut(entity_id)
.ok_or(MoveEntityError::EntityDoesNotExist)?;
let a_start = Instant::now();
let mut entity = self.entity_mut(entity_id).ok_or_else(|| {
warn!("!!! a_elapsed: {:?}", a_start.elapsed());
MoveEntityError::EntityDoesNotExist(Backtrace::capture())
})?;
let a_elapsed = a_start.elapsed();
let b_start = Instant::now();
let old_chunk = ChunkPos::from(entity.pos());
let new_chunk = ChunkPos::from(&new_pos);
// this is fine because we update the chunk below
unsafe { entity.move_unchecked(new_pos) };
let b_elapsed = b_start.elapsed();
let c_start = Instant::now();
if old_chunk != new_chunk {
self.entity_storage
.update_entity_chunk(entity_id, &old_chunk, &new_chunk);
}
let c_elapsed = c_start.elapsed();
warn!(
"!!! a_elapsed: {:?}, b_elapsed: {:?}, c_elapsed: {:?}",
a_elapsed, b_elapsed, c_elapsed
);
Ok(())
}
@ -89,9 +109,15 @@ impl World {
entity_id: u32,
delta: &PositionDelta8,
) -> Result<(), MoveEntityError> {
let owner_entity_id = self.entity_storage.owner_entity_id;
let mut entity = self
.entity_mut(entity_id)
.ok_or(MoveEntityError::EntityDoesNotExist)?;
.ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
if entity_id == owner_entity_id {
println!("moving entity {} (self)", entity_id);
} else {
println!("moving entity {}", entity_id);
}
let new_pos = entity.pos().with_delta(delta);
let old_chunk = ChunkPos::from(entity.pos());
@ -128,7 +154,13 @@ impl World {
Some(Entity::new(self, id, entity_ptr))
}
/// Returns a mutable reference to the entity with the given ID.
pub fn entity_mut(&mut self, id: u32) -> Option<Entity<'_, &mut World>> {
// no entity for you (we're processing this entity somewhere else)
if id != self.entity_storage.owner_entity_id && !self.entity_storage.maybe_update(id) {
return None;
}
let entity_data = self.entity_storage.get_by_id(id)?;
let entity_ptr = unsafe { entity_data.as_ptr() };
Some(Entity::new(self, id, entity_ptr))

View file

@ -80,7 +80,7 @@ impl Trait for azalea_client::Client {
let successors = |node: &Node| {
let mut edges = Vec::new();
let world = self.world.read();
let world = self.world.lock();
for possible_move in possible_moves.iter() {
edges.push(Edge {
target: possible_move.next_node(node),
@ -131,7 +131,7 @@ fn tick_execute_path(bot: &mut Client, path: &mut VecDeque<Node>) {
}
if target.is_reached(&bot.entity()) {
println!("ok target {target:?} reached");
// println!("ok target {target:?} reached");
path.pop_front();
if path.is_empty() {
bot.walk(WalkDirection::None);
@ -169,13 +169,13 @@ impl Node {
/// Returns whether the entity is at the node and should start going to the
/// next node.
pub fn is_reached(&self, entity: &EntityData) -> bool {
println!(
"entity.delta.y: {} {:?}=={:?}, self.vertical_vel={:?}",
entity.delta.y,
BlockPos::from(entity.pos()),
self.pos,
self.vertical_vel
);
// println!(
// "entity.delta.y: {} {:?}=={:?}, self.vertical_vel={:?}",
// entity.delta.y,
// BlockPos::from(entity.pos()),
// self.pos,
// self.vertical_vel
// );
BlockPos::from(entity.pos()) == self.pos
&& match self.vertical_vel {
VerticalVel::NoneMidair => entity.delta.y > -0.1 && entity.delta.y < 0.1,

View file

@ -1,5 +1,5 @@
use azalea::pathfinder::BlockPosGoal;
use azalea::{prelude::*, BlockPos, Swarm, SwarmEvent};
use azalea::{prelude::*, BlockPos, Swarm, SwarmEvent, WalkDirection};
use azalea::{Account, Client, Event};
use azalea_protocol::packets::game::serverbound_client_command_packet::ServerboundClientCommandPacket;
use std::time::Duration;
@ -69,7 +69,7 @@ async fn main() -> anyhow::Result<()> {
}
}
async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()> {
async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<()> {
match event {
Event::Login => {
bot.chat("Hello world").await?;
@ -80,17 +80,28 @@ async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()>
tokio::time::sleep(Duration::from_millis(50)).await;
bot.disconnect().await?;
}
if m.message().to_string() == "<py5> goto" {
let entity = bot
.world
.read()
.entity_by_uuid(&uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd"));
println!("entity: {:?}", entity);
if let Some(entity) = entity {
let entity = bot
.world
.lock()
.entity_by_uuid(&uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd"));
if let Some(entity) = entity {
if m.content() == "goto" {
let target_pos_vec3 = entity.pos();
let target_pos: BlockPos = target_pos_vec3.into();
println!("target_pos: {:?}", target_pos);
bot.goto(BlockPosGoal::from(target_pos));
} else if m.content() == "look" {
let target_pos_vec3 = entity.pos();
let target_pos: BlockPos = target_pos_vec3.into();
println!("target_pos: {:?}", target_pos);
bot.look_at(&target_pos.center());
} else if m.content() == "jump" {
bot.set_jumping(true);
} else if m.content() == "walk" {
bot.walk(WalkDirection::Forward);
} else if m.content() == "stop" {
bot.set_jumping(false);
bot.walk(WalkDirection::None);
}
}
}
@ -122,7 +133,6 @@ async fn swarm_handle(
SwarmEvent::Chat(m) => {
println!("swarm chat message: {}", m.message().to_ansi(None));
if m.message().to_string() == "<py5> world" {
let worlds = swarm.worlds.read();
for (name, world) in &swarm.worlds.read().worlds {
println!("world name: {}", name);
if let Some(w) = world.upgrade() {