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

start adding moving

This commit is contained in:
mat 2022-06-24 23:10:59 -05:00
parent 5643cc4a94
commit b030b0ea33
16 changed files with 437 additions and 128 deletions

2
Cargo.lock generated
View file

@ -122,6 +122,7 @@ dependencies = [
"azalea-world", "azalea-world",
"owning_ref", "owning_ref",
"tokio", "tokio",
"uuid",
] ]
[[package]] [[package]]
@ -219,6 +220,7 @@ dependencies = [
"azalea-nbt", "azalea-nbt",
"log", "log",
"nohash-hasher", "nohash-hasher",
"uuid",
] ]
[[package]] [[package]]

View file

@ -6,4 +6,4 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
uuid = "^1.1.2" uuid = "1.1.2"

View file

@ -14,3 +14,4 @@ azalea-protocol = {path = "../azalea-protocol"}
azalea-world = {path = "../azalea-world"} azalea-world = {path = "../azalea-world"}
owning_ref = "0.4.1" owning_ref = "0.4.1"
tokio = {version = "1.19.2", features = ["sync"]} tokio = {version = "1.19.2", features = ["sync"]}
uuid = "1.1.2"

View file

@ -1,8 +1,8 @@
//! Connect to Minecraft servers.
use crate::Client; use crate::Client;
use azalea_protocol::ServerAddress; use azalea_protocol::ServerAddress;
///! Connect to Minecraft servers.
/// Something that can join Minecraft servers. /// Something that can join Minecraft servers.
pub struct Account { pub struct Account {
pub username: String, pub username: String,

View file

@ -1,5 +1,6 @@
use crate::{Account, Player}; use crate::{Account, Player};
use azalea_core::{ChunkPos, EntityPos, ResourceLocation}; use azalea_auth::game_profile::GameProfile;
use azalea_core::{ChunkPos, EntityPos, PositionDelta, PositionDeltaTrait, ResourceLocation};
use azalea_entity::Entity; use azalea_entity::Entity;
use azalea_protocol::{ use azalea_protocol::{
connect::{GameConnection, HandshakeConnection}, connect::{GameConnection, HandshakeConnection},
@ -63,6 +64,7 @@ pub enum ChatPacket {
/// A player that you can control that is currently in a Minecraft server. /// A player that you can control that is currently in a Minecraft server.
pub struct Client { pub struct Client {
event_receiver: UnboundedReceiver<Event>, event_receiver: UnboundedReceiver<Event>,
game_profile: GameProfile,
pub conn: Arc<tokio::sync::Mutex<GameConnection>>, pub conn: Arc<tokio::sync::Mutex<GameConnection>>,
pub state: Arc<Mutex<ClientState>>, pub state: Arc<Mutex<ClientState>>,
// game_loop // game_loop
@ -104,7 +106,7 @@ impl Client {
) )
.await; .await;
let conn = loop { let (conn, game_profile) = loop {
let packet_result = conn.read().await; let packet_result = conn.read().await;
match packet_result { match packet_result {
Ok(packet) => match packet { Ok(packet) => match packet {
@ -132,7 +134,7 @@ impl Client {
} }
LoginPacket::ClientboundGameProfilePacket(p) => { LoginPacket::ClientboundGameProfilePacket(p) => {
println!("Got profile {:?}", p.game_profile); println!("Got profile {:?}", p.game_profile);
break conn.game(); break (conn.game(), p.game_profile);
} }
LoginPacket::ClientboundLoginDisconnectPacket(p) => { LoginPacket::ClientboundLoginDisconnectPacket(p) => {
println!("Got disconnect {:?}", p); println!("Got disconnect {:?}", p);
@ -154,6 +156,7 @@ impl Client {
// we got the GameConnection, so the server is now connected :) // we got the GameConnection, so the server is now connected :)
let client = Client { let client = Client {
game_profile: game_profile.clone(),
event_receiver: rx, event_receiver: rx,
conn: conn.clone(), conn: conn.clone(),
state: Arc::new(Mutex::new(ClientState::default())), state: Arc::new(Mutex::new(ClientState::default())),
@ -167,6 +170,7 @@ impl Client {
conn.clone(), conn.clone(),
tx.clone(), tx.clone(),
game_loop_state.clone(), game_loop_state.clone(),
game_profile.clone(),
)); ));
tokio::spawn(Self::game_tick_loop(conn, tx, game_loop_state)); tokio::spawn(Self::game_tick_loop(conn, tx, game_loop_state));
@ -177,21 +181,24 @@ impl Client {
conn: Arc<tokio::sync::Mutex<GameConnection>>, conn: Arc<tokio::sync::Mutex<GameConnection>>,
tx: UnboundedSender<Event>, tx: UnboundedSender<Event>,
state: Arc<Mutex<ClientState>>, state: Arc<Mutex<ClientState>>,
game_profile: GameProfile,
) { ) {
loop { loop {
let r = conn.lock().await.read().await; let r = conn.lock().await.read().await;
match r { match r {
Ok(packet) => match Self::handle(&packet, &tx, &state, &conn).await { Ok(packet) => {
Ok(_) => {} match Self::handle(&packet, &tx, &state, &conn, &game_profile).await {
Err(e) => { Ok(_) => {}
println!("Error handling packet: {:?}", e); Err(e) => {
if IGNORE_ERRORS { println!("Error handling packet: {:?}", e);
continue; if IGNORE_ERRORS {
} else { continue;
panic!("Error handling packet: {:?}", e); } else {
panic!("Error handling packet: {:?}", e);
}
} }
} }
}, }
Err(e) => { Err(e) => {
if IGNORE_ERRORS { if IGNORE_ERRORS {
println!("Error: {:?}", e); println!("Error: {:?}", e);
@ -211,13 +218,14 @@ impl Client {
tx: &UnboundedSender<Event>, tx: &UnboundedSender<Event>,
state: &Arc<Mutex<ClientState>>, state: &Arc<Mutex<ClientState>>,
conn: &Arc<tokio::sync::Mutex<GameConnection>>, conn: &Arc<tokio::sync::Mutex<GameConnection>>,
game_profile: &GameProfile,
) -> Result<(), HandleError> { ) -> Result<(), HandleError> {
match packet { match packet {
GamePacket::ClientboundLoginPacket(p) => { GamePacket::ClientboundLoginPacket(p) => {
println!("Got login packet {:?}", p); println!("Got login packet {:?}", p);
{ {
let mut state = state.lock()?; let mut state_lock = state.lock()?;
// // write p into login.txt // // write p into login.txt
// std::io::Write::write_all( // std::io::Write::write_all(
@ -226,8 +234,6 @@ impl Client {
// ) // )
// .unwrap(); // .unwrap();
state.player.entity.id = p.player_id;
// TODO: have registry_holder be a struct because this sucks rn // TODO: have registry_holder be a struct because this sucks rn
// best way would be to add serde support to azalea-nbt // best way would be to add serde support to azalea-nbt
@ -281,7 +287,18 @@ impl Client {
.as_int() .as_int()
.expect("min_y tag is not an int"); .expect("min_y tag is not an int");
state.world = Some(World::new(16, height, min_y)); // the 16 here is our render distance
// i'll make this an actual setting later
state_lock.world = Some(World::new(16, height, min_y));
let entity = Entity::new(p.player_id, game_profile.uuid, EntityPos::default());
state_lock
.world
.as_mut()
.expect("World doesn't exist! We should've gotten a login packet by now.")
.add_entity(entity);
state_lock.player.set_entity_id(p.player_id);
} }
conn.lock() conn.lock()
@ -334,6 +351,99 @@ impl Client {
GamePacket::ClientboundPlayerPositionPacket(p) => { GamePacket::ClientboundPlayerPositionPacket(p) => {
// TODO: reply with teleport confirm // TODO: reply with teleport confirm
println!("Got player position packet {:?}", p); println!("Got player position packet {:?}", p);
let mut state_lock = state.lock()?;
let player_entity_id = state_lock.player.entity_id;
let world = state_lock.world.as_mut().unwrap();
let player_entity = world
.mut_entity_by_id(player_entity_id)
.expect("Player entity doesn't exist");
let delta_movement = &player_entity.delta;
let is_x_relative = p.relative_arguments.x;
let is_y_relative = p.relative_arguments.y;
let is_z_relative = p.relative_arguments.z;
let (delta_x, new_pos_x) = if is_x_relative {
player_entity.old_pos.x += p.x;
(delta_movement.x(), player_entity.pos().x + p.x)
} else {
player_entity.old_pos.x = p.x;
(0.0, p.x)
};
let (delta_y, new_pos_y) = if is_y_relative {
player_entity.old_pos.y += p.y;
(delta_movement.y(), player_entity.pos().y + p.y)
} else {
player_entity.old_pos.y = p.y;
(0.0, p.y)
};
let (delta_z, new_pos_z) = if is_z_relative {
player_entity.old_pos.z += p.z;
(delta_movement.z(), player_entity.pos().z + p.z)
} else {
player_entity.old_pos.z = p.z;
(0.0, p.z)
};
let mut y_rot = p.y_rot;
let mut x_rot = p.x_rot;
if p.relative_arguments.x_rot {
y_rot += player_entity.x_rot;
}
if p.relative_arguments.y_rot {
x_rot += player_entity.y_rot;
}
player_entity.delta = PositionDelta {
xa: delta_x,
ya: delta_y,
za: delta_z,
};
player_entity.set_rotation(x_rot, y_rot);
// TODO: minecraft sets "xo", "yo", and "zo" here but idk what that means
// so investigate that ig
world
.move_entity(
player_entity_id,
EntityPos {
x: new_pos_x,
y: new_pos_y,
z: new_pos_z,
},
)
.expect("The player entity should always exist");
let mut state_lock = state.lock()?;
let player = &state_lock.player;
let player_entity_id = player.entity_id;
let world = state_lock.world.as_mut().unwrap();
world.move_entity(
player_entity_id,
EntityPos {
x: p.x,
y: p.y,
z: p.z,
},
)?;
conn.lock()
.await
.write(ServerboundAcceptTeleportationPacket {}.get())
.await;
conn.lock()
.await
.write(
ServerboundMovePlayerPacketPosRot {
identifier: ResourceLocation::new("brand").unwrap(),
// they don't have to know :)
data: "vanilla".into(),
}
.get(),
)
.await;
} }
GamePacket::ClientboundPlayerInfoPacket(p) => { GamePacket::ClientboundPlayerInfoPacket(p) => {
println!("Got player info packet {:?}", p); println!("Got player info packet {:?}", p);
@ -534,13 +644,22 @@ impl Client {
/// Gets the `World` the client is in. /// Gets the `World` the client is in.
/// ///
/// This is basically a shortcut for `let world = client.state.lock().unwrap().world.as_ref().unwrap()`. /// This is basically a shortcut for `client.state.lock().unwrap().world.as_ref().unwrap()`.
/// If the client hasn't received a login packet yet, this will panic. /// If the client hasn't received a login packet yet, this will panic.
pub fn world(&self) -> OwningRef<std::sync::MutexGuard<ClientState>, World> { pub fn world(&self) -> OwningRef<std::sync::MutexGuard<ClientState>, World> {
let state_lock: std::sync::MutexGuard<ClientState> = self.state.lock().unwrap(); let state_lock: std::sync::MutexGuard<ClientState> = self.state.lock().unwrap();
let state_lock_ref = OwningRef::new(state_lock); let state_lock_ref = OwningRef::new(state_lock);
state_lock_ref.map(|state| state.world.as_ref().expect("World doesn't exist!")) state_lock_ref.map(|state| state.world.as_ref().expect("World doesn't exist!"))
} }
/// Gets the `Player` struct for our player.
///
/// This is basically a shortcut for `client.state.lock().unwrap().player`.
pub fn player(&self) -> OwningRef<std::sync::MutexGuard<ClientState>, Player> {
let state_lock: std::sync::MutexGuard<ClientState> = self.state.lock().unwrap();
let state_lock_ref = OwningRef::new(state_lock);
state_lock_ref.map(|state| &state.player)
}
} }
impl<T> From<std::sync::PoisonError<T>> for HandleError { impl<T> From<std::sync::PoisonError<T>> for HandleError {

View file

@ -4,15 +4,29 @@ use azalea_protocol::packets::game::serverbound_move_player_packet_pos_rot::Serv
impl Client { impl Client {
/// Set the client's position to the given coordinates. /// Set the client's position to the given coordinates.
pub async fn move_to(&mut self, pos: &EntityPos) { pub async fn move_to(&mut self, new_pos: EntityPos) -> Result<(), String> {
let mut state_lock = self.state.lock().unwrap();
let world = state_lock.world.as_ref().unwrap();
let player = &state_lock.player;
let player_id = if let Some(player) = player.entity(world) {
player.id
} else {
return Err("Player entity not found".to_string());
};
let world = state_lock.world.as_mut().unwrap();
world.move_entity(player_id, new_pos)?;
self.conn self.conn
.lock() .lock()
.await .await
.write( .write(
ServerboundMovePlayerPacketPosRot { ServerboundMovePlayerPacketPosRot {
x: pos.x, x: new_pos.x,
y: pos.y, y: new_pos.y,
z: pos.z, z: new_pos.z,
x_rot: 0.0, x_rot: 0.0,
y_rot: 0.0, y_rot: 0.0,
on_ground: false, on_ground: false,
@ -20,5 +34,7 @@ impl Client {
.get(), .get(),
) )
.await; .await;
Ok(())
} }
} }

View file

@ -1,7 +1,27 @@
use azalea_entity::Entity; use azalea_entity::Entity;
use azalea_world::World;
use uuid::Uuid;
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct Player { pub struct Player {
/// The entity attached to the player. There's some useful fields here. /// The player's uuid.
pub entity: Entity, pub uuid: Uuid,
/// The player's entity id.
pub entity_id: u32,
}
impl Player {
/// Get the entity of the player in the world.
pub fn entity<'a>(&self, world: &'a World) -> Option<&'a Entity> {
// world.entity_by_uuid(&self.uuid)
world.entity_by_id(self.entity_id)
}
pub fn set_uuid(&mut self, uuid: Uuid) {
self.uuid = uuid;
}
pub fn set_entity_id(&mut self, entity_id: u32) {
self.entity_id = entity_id;
}
} }

View file

@ -1,15 +1,41 @@
use crate::EntityPos; use crate::EntityPos;
pub use azalea_buf::McBuf; pub use azalea_buf::McBuf;
/// Only works for up to 8 blocks pub trait PositionDeltaTrait {
#[derive(Clone, Debug, McBuf)] fn x(&self) -> f64;
pub struct PositionDelta { fn y(&self) -> f64;
xa: i16, fn z(&self) -> f64;
ya: i16,
za: i16,
} }
impl PositionDelta { #[derive(Clone, Debug, McBuf, Default)]
pub struct PositionDelta {
pub xa: f64,
pub ya: f64,
pub za: f64,
}
/// Only works for up to 8 blocks
#[derive(Clone, Debug, McBuf, Default)]
pub struct PositionDelta8 {
pub xa: i16,
pub ya: i16,
pub za: i16,
}
impl PositionDeltaTrait for PositionDelta {
fn x(&self) -> f64 {
self.xa
}
fn y(&self) -> f64 {
self.ya
}
fn z(&self) -> f64 {
self.za
}
}
impl PositionDelta8 {
#[deprecated]
pub fn float(&self) -> (f64, f64, f64) { pub fn float(&self) -> (f64, f64, f64) {
( (
(self.xa as f64) / 4096.0, (self.xa as f64) / 4096.0,
@ -19,13 +45,24 @@ impl PositionDelta {
} }
} }
impl PositionDeltaTrait for PositionDelta8 {
fn x(&self) -> f64 {
(self.xa as f64) / 4096.0
}
fn y(&self) -> f64 {
(self.ya as f64) / 4096.0
}
fn z(&self) -> f64 {
(self.za as f64) / 4096.0
}
}
impl EntityPos { impl EntityPos {
pub fn with_delta(&self, delta: &PositionDelta) -> EntityPos { pub fn with_delta(&self, delta: &dyn PositionDeltaTrait) -> EntityPos {
let (x, y, z) = delta.float();
EntityPos { EntityPos {
x: self.x + x, x: self.x + delta.x(),
y: self.y + y, y: self.y + delta.y(),
z: self.z + z, z: self.z + delta.z(),
} }
} }
} }

View file

@ -5,6 +5,12 @@ use std::{
ops::Rem, ops::Rem,
}; };
pub trait PositionXYZ<T> {
fn add_x(&self, n: T) -> Self;
fn add_y(&self, n: T) -> Self;
fn add_z(&self, n: T) -> Self;
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct BlockPos { pub struct BlockPos {
pub x: i32, pub x: i32,
@ -30,6 +36,30 @@ impl Rem<i32> for BlockPos {
} }
} }
impl PositionXYZ<i32> for BlockPos {
fn add_x(&self, n: i32) -> Self {
BlockPos {
x: self.x + n,
y: self.y,
z: self.z,
}
}
fn add_y(&self, n: i32) -> Self {
BlockPos {
x: self.x,
y: self.y + n,
z: self.z,
}
}
fn add_z(&self, n: i32) -> Self {
BlockPos {
x: self.x,
y: self.y,
z: self.z + n,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct ChunkPos { pub struct ChunkPos {
pub x: i32, pub x: i32,
@ -42,15 +72,6 @@ impl ChunkPos {
} }
} }
impl From<&BlockPos> for ChunkPos {
fn from(pos: &BlockPos) -> Self {
ChunkPos {
x: pos.x.div_floor(16),
z: pos.z.div_floor(16),
}
}
}
/// The coordinates of a chunk section in the world. /// The coordinates of a chunk section in the world.
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct ChunkSectionPos { pub struct ChunkSectionPos {
@ -64,6 +85,83 @@ impl ChunkSectionPos {
ChunkSectionPos { x, y, z } ChunkSectionPos { x, y, z }
} }
} }
/// The coordinates of a block inside a chunk.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ChunkBlockPos {
pub x: u8,
pub y: i32,
pub z: u8,
}
impl ChunkBlockPos {
pub fn new(x: u8, y: i32, z: u8) -> Self {
ChunkBlockPos { x, y, z }
}
}
/// The coordinates of a block inside a chunk section.
#[derive(Clone, Copy, Debug, Default)]
pub struct ChunkSectionBlockPos {
/// A number between 0 and 16.
pub x: u8,
/// A number between 0 and 16.
pub y: u8,
/// A number between 0 and 16.
pub z: u8,
}
impl ChunkSectionBlockPos {
pub fn new(x: u8, y: u8, z: u8) -> Self {
ChunkSectionBlockPos { x, y, z }
}
}
/// A block pos with an attached dimension
#[derive(Debug, Clone)]
pub struct GlobalPos {
pub pos: BlockPos,
// this is actually a ResourceKey in Minecraft, but i don't think it matters?
pub dimension: ResourceLocation,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct EntityPos {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl PositionXYZ<f64> for EntityPos {
fn add_x(&self, n: f64) -> Self {
EntityPos {
x: self.x + n,
y: self.y,
z: self.z,
}
}
fn add_y(&self, n: f64) -> Self {
EntityPos {
x: self.x,
y: self.y + n,
z: self.z,
}
}
fn add_z(&self, n: f64) -> Self {
EntityPos {
x: self.x,
y: self.y,
z: self.z + n,
}
}
}
impl From<&BlockPos> for ChunkPos {
fn from(pos: &BlockPos) -> Self {
ChunkPos {
x: pos.x.div_floor(16),
z: pos.z.div_floor(16),
}
}
}
impl From<BlockPos> for ChunkSectionPos { impl From<BlockPos> for ChunkSectionPos {
fn from(pos: BlockPos) -> Self { fn from(pos: BlockPos) -> Self {
@ -81,20 +179,6 @@ impl From<ChunkSectionPos> for ChunkPos {
} }
} }
/// The coordinates of a block inside a chunk.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ChunkBlockPos {
pub x: u8,
pub y: i32,
pub z: u8,
}
impl ChunkBlockPos {
pub fn new(x: u8, y: i32, z: u8) -> Self {
ChunkBlockPos { x, y, z }
}
}
impl From<&BlockPos> for ChunkBlockPos { impl From<&BlockPos> for ChunkBlockPos {
fn from(pos: &BlockPos) -> Self { fn from(pos: &BlockPos) -> Self {
ChunkBlockPos { ChunkBlockPos {
@ -105,23 +189,6 @@ impl From<&BlockPos> for ChunkBlockPos {
} }
} }
/// The coordinates of a block inside a chunk section.
#[derive(Clone, Copy, Debug, Default)]
pub struct ChunkSectionBlockPos {
/// A number between 0 and 16.
pub x: u8,
/// A number between 0 and 16.
pub y: u8,
/// A number between 0 and 16.
pub z: u8,
}
impl ChunkSectionBlockPos {
pub fn new(x: u8, y: u8, z: u8) -> Self {
ChunkSectionBlockPos { x, y, z }
}
}
impl From<&BlockPos> for ChunkSectionBlockPos { impl From<&BlockPos> for ChunkSectionBlockPos {
fn from(pos: &BlockPos) -> Self { fn from(pos: &BlockPos) -> Self {
ChunkSectionBlockPos { ChunkSectionBlockPos {
@ -141,22 +208,6 @@ impl From<&ChunkBlockPos> for ChunkSectionBlockPos {
} }
} }
} }
/// A block pos with an attached dimension
#[derive(Debug, Clone)]
pub struct GlobalPos {
pub pos: BlockPos,
// this is actually a ResourceKey in Minecraft, but i don't think it matters?
pub dimension: ResourceLocation,
}
#[derive(Debug, Clone, Default)]
pub struct EntityPos {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl From<&EntityPos> for BlockPos { impl From<&EntityPos> for BlockPos {
fn from(pos: &EntityPos) -> Self { fn from(pos: &EntityPos) -> Self {
BlockPos { BlockPos {

View file

@ -1,11 +1,6 @@
mod data; mod data;
use azalea_core::EntityPos; use azalea_core::{EntityPos, PositionDelta};
#[cfg(feature = "protocol")]
use azalea_protocol::packets::game::{
clientbound_add_entity_packet::ClientboundAddEntityPacket,
clientbound_add_player_packet::ClientboundAddPlayerPacket,
};
pub use data::*; pub use data::*;
use uuid::Uuid; use uuid::Uuid;
@ -14,12 +9,27 @@ pub struct Entity {
/// The incrementing numerical id of the entity. /// The incrementing numerical id of the entity.
pub id: u32, pub id: u32,
pub uuid: Uuid, pub uuid: Uuid,
/// The position of the entity right now.
pos: EntityPos, pos: EntityPos,
/// The position of the entity last tick.
pub old_pos: EntityPos,
pub delta: PositionDelta,
pub x_rot: f32,
pub y_rot: f32,
} }
impl Entity { impl Entity {
pub fn new(id: u32, uuid: Uuid, pos: EntityPos) -> Self { pub fn new(id: u32, uuid: Uuid, pos: EntityPos) -> Self {
Self { id, uuid, pos } Self {
id,
uuid,
pos,
old_pos: pos,
delta: PositionDelta::default(),
x_rot: 0.0,
y_rot: 0.0,
}
} }
pub fn pos(&self) -> &EntityPos { pub fn pos(&self) -> &EntityPos {
@ -31,6 +41,12 @@ impl Entity {
pub fn unsafe_move(&mut self, new_pos: EntityPos) { pub fn unsafe_move(&mut self, new_pos: EntityPos) {
self.pos = new_pos; self.pos = new_pos;
} }
pub fn set_rotation(&mut self, x_rot: f32, y_rot: f32) {
self.x_rot = x_rot % 360.0;
self.y_rot = y_rot.clamp(-90.0, 90.0) % 360.0;
// TODO: minecraft also sets yRotO and xRotO to xRot and yRot ... but idk what they're used for so
}
} }
// #[cfg(test)] // #[cfg(test)]

View file

@ -1,11 +1,11 @@
use azalea_buf::McBuf; use azalea_buf::McBuf;
use azalea_core::PositionDelta; use azalea_core::PositionDelta8;
use packet_macros::GamePacket; use packet_macros::GamePacket;
#[derive(Clone, Debug, McBuf, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundMoveEntityPosPacket { pub struct ClientboundMoveEntityPosPacket {
#[var] #[var]
pub entity_id: u32, pub entity_id: u32,
pub delta: PositionDelta, pub delta: PositionDelta8,
pub on_ground: bool, pub on_ground: bool,
} }

View file

@ -1,5 +1,5 @@
use azalea_buf::McBuf; use azalea_buf::McBuf;
use azalea_core::PositionDelta; use azalea_core::PositionDelta8;
use packet_macros::GamePacket; use packet_macros::GamePacket;
/// This packet is sent by the server when an entity moves less then 8 blocks. /// This packet is sent by the server when an entity moves less then 8 blocks.
@ -7,7 +7,7 @@ use packet_macros::GamePacket;
pub struct ClientboundMoveEntityPosRotPacket { pub struct ClientboundMoveEntityPosRotPacket {
#[var] #[var]
pub entity_id: u32, pub entity_id: u32,
pub delta: PositionDelta, pub delta: PositionDelta8,
pub y_rot: i8, pub y_rot: i8,
pub x_rot: i8, pub x_rot: i8,
pub on_ground: bool, pub on_ground: bool,

View file

@ -13,6 +13,7 @@ azalea-entity = {path = "../azalea-entity"}
azalea-nbt = {path = "../azalea-nbt"} azalea-nbt = {path = "../azalea-nbt"}
log = "0.4.17" log = "0.4.17"
nohash-hasher = "0.2.0" nohash-hasher = "0.2.0"
uuid = "1.1.2"
[profile.release] [profile.release]
lto = true lto = true

View file

@ -3,12 +3,13 @@ use azalea_entity::Entity;
use log::warn; use log::warn;
use nohash_hasher::{IntMap, IntSet}; use nohash_hasher::{IntMap, IntSet};
use std::collections::HashMap; use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug)] #[derive(Debug)]
pub struct EntityStorage { pub struct EntityStorage {
by_id: IntMap<u32, Entity>, by_id: IntMap<u32, Entity>,
// TODO: this doesn't work yet (should be updated in the set_pos method in azalea-entity)
by_chunk: HashMap<ChunkPos, IntSet<u32>>, by_chunk: HashMap<ChunkPos, IntSet<u32>>,
by_uuid: HashMap<Uuid, u32>,
} }
impl EntityStorage { impl EntityStorage {
@ -16,6 +17,7 @@ impl EntityStorage {
Self { Self {
by_id: IntMap::default(), by_id: IntMap::default(),
by_chunk: HashMap::default(), by_chunk: HashMap::default(),
by_uuid: HashMap::default(),
} }
} }
@ -26,6 +28,7 @@ impl EntityStorage {
.entry(ChunkPos::from(entity.pos())) .entry(ChunkPos::from(entity.pos()))
.or_default() .or_default()
.insert(entity.id); .insert(entity.id);
self.by_uuid.insert(entity.uuid, entity.id);
self.by_id.insert(entity.id, entity); self.by_id.insert(entity.id, entity);
} }
@ -34,9 +37,13 @@ impl EntityStorage {
pub fn remove_by_id(&mut self, id: u32) { pub fn remove_by_id(&mut self, id: u32) {
if let Some(entity) = self.by_id.remove(&id) { if let Some(entity) = self.by_id.remove(&id) {
let entity_chunk = ChunkPos::from(entity.pos()); let entity_chunk = ChunkPos::from(entity.pos());
let entity_uuid = entity.uuid;
if self.by_chunk.remove(&entity_chunk).is_none() { if self.by_chunk.remove(&entity_chunk).is_none() {
warn!("Tried to remove entity with id {id} from chunk {entity_chunk:?} but it was not found."); warn!("Tried to remove entity with id {id} from chunk {entity_chunk:?} but it was not found.");
} }
if self.by_uuid.remove(&entity_uuid).is_none() {
warn!("Tried to remove entity with id {id} from uuid {entity_uuid:?} but it was not found.");
}
} else { } else {
warn!("Tried to remove entity with id {id} but it was not found.") warn!("Tried to remove entity with id {id} but it was not found.")
} }
@ -54,11 +61,27 @@ impl EntityStorage {
self.by_id.get_mut(&id) self.by_id.get_mut(&id)
} }
/// Get a reference to an entity by its uuid.
#[inline]
pub fn get_by_uuid(&self, uuid: &Uuid) -> Option<&Entity> {
self.by_uuid.get(uuid).and_then(|id| self.by_id.get(id))
}
/// Get a mutable reference to an entity by its uuid.
#[inline]
pub fn get_mut_by_uuid(&mut self, uuid: &Uuid) -> Option<&mut Entity> {
self.by_uuid.get(uuid).and_then(|id| self.by_id.get_mut(id))
}
/// Clear all entities in a chunk. /// Clear all entities in a chunk.
pub fn clear_chunk(&mut self, chunk: &ChunkPos) { pub fn clear_chunk(&mut self, chunk: &ChunkPos) {
if let Some(entities) = self.by_chunk.remove(chunk) { if let Some(entities) = self.by_chunk.remove(chunk) {
for entity_id in entities { for entity_id in entities {
self.by_id.remove(&entity_id); if let Some(entity) = self.by_id.remove(&entity_id) {
self.by_uuid.remove(&entity.uuid);
} else {
warn!("While clearing chunk {chunk:?}, found an entity that isn't in by_id {entity_id}.");
}
} }
} }
} }

View file

@ -6,7 +6,7 @@ mod entity;
mod palette; mod palette;
use azalea_block::BlockState; use azalea_block::BlockState;
use azalea_core::{BlockPos, ChunkPos, EntityPos, PositionDelta}; use azalea_core::{BlockPos, ChunkPos, EntityPos, PositionDelta8};
use azalea_entity::Entity; use azalea_entity::Entity;
pub use bit_storage::BitStorage; pub use bit_storage::BitStorage;
pub use chunk::{Chunk, ChunkStorage}; pub use chunk::{Chunk, ChunkStorage};
@ -16,6 +16,7 @@ use std::{
ops::{Index, IndexMut}, ops::{Index, IndexMut},
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use uuid::Uuid;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -76,7 +77,7 @@ impl World {
pub fn move_entity_with_delta( pub fn move_entity_with_delta(
&mut self, &mut self,
entity_id: u32, entity_id: u32,
delta: &PositionDelta, delta: &PositionDelta8,
) -> Result<(), String> { ) -> Result<(), String> {
let entity = self let entity = self
.entity_storage .entity_storage
@ -112,6 +113,14 @@ impl World {
self.entity_storage.get_by_id(id) self.entity_storage.get_by_id(id)
} }
pub fn mut_entity_by_id(&mut self, id: u32) -> Option<&mut Entity> {
self.entity_storage.get_mut_by_id(id)
}
pub fn entity_by_uuid(&self, uuid: &Uuid) -> Option<&Entity> {
self.entity_storage.get_by_uuid(uuid)
}
/// Get an iterator over all entities. /// Get an iterator over all entities.
#[inline] #[inline]
pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Entity> { pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Entity> {

View file

@ -1,12 +1,12 @@
use azalea_client::{Account, Event}; use azalea_client::{Account, Event};
use azalea_core::BlockPos; use azalea_core::PositionXYZ;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Hello, world!"); println!("Hello, world!");
// let address = "95.111.249.143:10000"; // let address = "95.111.249.143:10000";
let address = "localhost:49982"; let address = "localhost:65399";
// let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // let response = azalea_client::ping::ping_server(&address.try_into().unwrap())
// .await // .await
// .unwrap(); // .unwrap();
@ -20,23 +20,37 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
match e { match e {
// TODO: have a "loaded" or "ready" event that fires when all chunks are loaded // TODO: have a "loaded" or "ready" event that fires when all chunks are loaded
Event::Login => {} Event::Login => {}
Event::GameTick => { // Event::GameTick => {
let world = client.world(); // let world = client.world();
if let Some(b) = world.find_one_entity(|e| { // if let Some(b) = world.find_one_entity(|e| {
e.uuid == uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd") // e.uuid == uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd")
}) { // }) {
// let world = state.world.as_ref().unwrap(); // // let world = state.world.as_ref().unwrap();
// world. // // world.
println!("{:?}", b); // println!("{:?}", b);
} // }
// world.get_block_state(state.player.entity.pos); // // world.get_block_state(state.player.entity.pos);
// println!("{}", p.message.to_ansi(None)); // // println!("{}", p.message.to_ansi(None));
// if p.message.to_ansi(None) == "<py5> ok" { // // if p.message.to_ansi(None) == "<py5> ok" {
// let state = client.state.lock(); // // let state = client.state.lock();
// let world = state.world.as_ref().unwrap(); // // let world = state.world.as_ref().unwrap();
// let c = world.get_block_state(&BlockPos::new(5, 78, -2)).unwrap(); // // let c = world.get_block_state(&BlockPos::new(5, 78, -2)).unwrap();
// println!("block state: {:?}", c); // // println!("block state: {:?}", c);
// } // // }
// }
Event::Chat(msg) => {
let new_pos = {
let state_lock = client.state.lock().unwrap();
let world = state_lock.world.as_ref().unwrap();
let player = &state_lock.player;
let entity = player
.entity(&world)
.expect("Player entity is not in world");
entity.pos().add_y(0.5)
};
println!("{:?}", new_pos);
client.move_to(new_pos).await.unwrap();
} }
_ => {} _ => {}
} }