aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xCargo.lock2
-rwxr-xr-xazalea-auth/Cargo.toml2
-rwxr-xr-xazalea-client/Cargo.toml1
-rw-r--r--azalea-client/src/account.rs4
-rw-r--r--azalea-client/src/client.rs153
-rw-r--r--azalea-client/src/movement.rs24
-rw-r--r--azalea-client/src/player.rs24
-rw-r--r--azalea-core/src/delta.rs59
-rw-r--r--azalea-core/src/position.rs157
-rw-r--r--azalea-entity/src/lib.rs30
-rw-r--r--azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs4
-rw-r--r--azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs4
-rw-r--r--azalea-world/Cargo.toml1
-rw-r--r--azalea-world/src/entity.rs27
-rw-r--r--azalea-world/src/lib.rs13
-rw-r--r--bot/src/main.rs52
16 files changed, 433 insertions, 124 deletions
diff --git a/Cargo.lock b/Cargo.lock
index aeea0fb1..b9a60580 100755
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -122,6 +122,7 @@ dependencies = [
"azalea-world",
"owning_ref",
"tokio",
+ "uuid",
]
[[package]]
@@ -219,6 +220,7 @@ dependencies = [
"azalea-nbt",
"log",
"nohash-hasher",
+ "uuid",
]
[[package]]
diff --git a/azalea-auth/Cargo.toml b/azalea-auth/Cargo.toml
index a7a11b53..fe306186 100755
--- a/azalea-auth/Cargo.toml
+++ b/azalea-auth/Cargo.toml
@@ -6,4 +6,4 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-uuid = "^1.1.2"
+uuid = "1.1.2"
diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml
index 46ea8039..b39d6a49 100755
--- a/azalea-client/Cargo.toml
+++ b/azalea-client/Cargo.toml
@@ -14,3 +14,4 @@ azalea-protocol = {path = "../azalea-protocol"}
azalea-world = {path = "../azalea-world"}
owning_ref = "0.4.1"
tokio = {version = "1.19.2", features = ["sync"]}
+uuid = "1.1.2"
diff --git a/azalea-client/src/account.rs b/azalea-client/src/account.rs
index 0bcad630..56f4918a 100644
--- a/azalea-client/src/account.rs
+++ b/azalea-client/src/account.rs
@@ -1,8 +1,8 @@
+//! Connect to Minecraft servers.
+
use crate::Client;
use azalea_protocol::ServerAddress;
-///! Connect to Minecraft servers.
-
/// Something that can join Minecraft servers.
pub struct Account {
pub username: String,
diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs
index 6efe521b..943e0f9f 100644
--- a/azalea-client/src/client.rs
+++ b/azalea-client/src/client.rs
@@ -1,5 +1,6 @@
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_protocol::{
connect::{GameConnection, HandshakeConnection},
@@ -63,6 +64,7 @@ pub enum ChatPacket {
/// A player that you can control that is currently in a Minecraft server.
pub struct Client {
event_receiver: UnboundedReceiver<Event>,
+ game_profile: GameProfile,
pub conn: Arc<tokio::sync::Mutex<GameConnection>>,
pub state: Arc<Mutex<ClientState>>,
// game_loop
@@ -104,7 +106,7 @@ impl Client {
)
.await;
- let conn = loop {
+ let (conn, game_profile) = loop {
let packet_result = conn.read().await;
match packet_result {
Ok(packet) => match packet {
@@ -132,7 +134,7 @@ impl Client {
}
LoginPacket::ClientboundGameProfilePacket(p) => {
println!("Got profile {:?}", p.game_profile);
- break conn.game();
+ break (conn.game(), p.game_profile);
}
LoginPacket::ClientboundLoginDisconnectPacket(p) => {
println!("Got disconnect {:?}", p);
@@ -154,6 +156,7 @@ impl Client {
// we got the GameConnection, so the server is now connected :)
let client = Client {
+ game_profile: game_profile.clone(),
event_receiver: rx,
conn: conn.clone(),
state: Arc::new(Mutex::new(ClientState::default())),
@@ -167,6 +170,7 @@ impl Client {
conn.clone(),
tx.clone(),
game_loop_state.clone(),
+ game_profile.clone(),
));
tokio::spawn(Self::game_tick_loop(conn, tx, game_loop_state));
@@ -177,21 +181,24 @@ impl Client {
conn: Arc<tokio::sync::Mutex<GameConnection>>,
tx: UnboundedSender<Event>,
state: Arc<Mutex<ClientState>>,
+ game_profile: GameProfile,
) {
loop {
let r = conn.lock().await.read().await;
match r {
- Ok(packet) => match Self::handle(&packet, &tx, &state, &conn).await {
- Ok(_) => {}
- Err(e) => {
- println!("Error handling packet: {:?}", e);
- if IGNORE_ERRORS {
- continue;
- } else {
- panic!("Error handling packet: {:?}", e);
+ Ok(packet) => {
+ match Self::handle(&packet, &tx, &state, &conn, &game_profile).await {
+ Ok(_) => {}
+ Err(e) => {
+ println!("Error handling packet: {:?}", e);
+ if IGNORE_ERRORS {
+ continue;
+ } else {
+ panic!("Error handling packet: {:?}", e);
+ }
}
}
- },
+ }
Err(e) => {
if IGNORE_ERRORS {
println!("Error: {:?}", e);
@@ -211,13 +218,14 @@ impl Client {
tx: &UnboundedSender<Event>,
state: &Arc<Mutex<ClientState>>,
conn: &Arc<tokio::sync::Mutex<GameConnection>>,
+ game_profile: &GameProfile,
) -> Result<(), HandleError> {
match packet {
GamePacket::ClientboundLoginPacket(p) => {
println!("Got login packet {:?}", p);
{
- let mut state = state.lock()?;
+ let mut state_lock = state.lock()?;
// // write p into login.txt
// std::io::Write::write_all(
@@ -226,8 +234,6 @@ impl Client {
// )
// .unwrap();
- state.player.entity.id = p.player_id;
-
// TODO: have registry_holder be a struct because this sucks rn
// best way would be to add serde support to azalea-nbt
@@ -281,7 +287,18 @@ impl Client {
.as_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()
@@ -334,6 +351,99 @@ impl Client {
GamePacket::ClientboundPlayerPositionPacket(p) => {
// TODO: reply with teleport confirm
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) => {
println!("Got player info packet {:?}", p);
@@ -534,13 +644,22 @@ impl Client {
/// 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.
pub fn world(&self) -> OwningRef<std::sync::MutexGuard<ClientState>, World> {
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.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 {
diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs
index c9cd62e9..0402b15b 100644
--- a/azalea-client/src/movement.rs
+++ b/azalea-client/src/movement.rs
@@ -4,15 +4,29 @@ use azalea_protocol::packets::game::serverbound_move_player_packet_pos_rot::Serv
impl Client {
/// 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
.lock()
.await
.write(
ServerboundMovePlayerPacketPosRot {
- x: pos.x,
- y: pos.y,
- z: pos.z,
+ x: new_pos.x,
+ y: new_pos.y,
+ z: new_pos.z,
x_rot: 0.0,
y_rot: 0.0,
on_ground: false,
@@ -20,5 +34,7 @@ impl Client {
.get(),
)
.await;
+
+ Ok(())
}
}
diff --git a/azalea-client/src/player.rs b/azalea-client/src/player.rs
index 9a5ba8ab..ee0b9718 100644
--- a/azalea-client/src/player.rs
+++ b/azalea-client/src/player.rs
@@ -1,7 +1,27 @@
use azalea_entity::Entity;
+use azalea_world::World;
+use uuid::Uuid;
#[derive(Default, Debug)]
pub struct Player {
- /// The entity attached to the player. There's some useful fields here.
- pub entity: Entity,
+ /// The player's uuid.
+ 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;
+ }
}
diff --git a/azalea-core/src/delta.rs b/azalea-core/src/delta.rs
index 41923ffb..c0056411 100644
--- a/azalea-core/src/delta.rs
+++ b/azalea-core/src/delta.rs
@@ -1,15 +1,41 @@
use crate::EntityPos;
pub use azalea_buf::McBuf;
-/// Only works for up to 8 blocks
-#[derive(Clone, Debug, McBuf)]
+pub trait PositionDeltaTrait {
+ fn x(&self) -> f64;
+ fn y(&self) -> f64;
+ fn z(&self) -> f64;
+}
+
+#[derive(Clone, Debug, McBuf, Default)]
pub struct PositionDelta {
- xa: i16,
- ya: i16,
- za: i16,
+ 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 PositionDelta {
+impl PositionDelta8 {
+ #[deprecated]
pub fn float(&self) -> (f64, f64, f64) {
(
(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 {
- pub fn with_delta(&self, delta: &PositionDelta) -> EntityPos {
- let (x, y, z) = delta.float();
+ pub fn with_delta(&self, delta: &dyn PositionDeltaTrait) -> EntityPos {
EntityPos {
- x: self.x + x,
- y: self.y + y,
- z: self.z + z,
+ x: self.x + delta.x(),
+ y: self.y + delta.y(),
+ z: self.z + delta.z(),
}
}
}
diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs
index 8caa5799..de8e2516 100644
--- a/azalea-core/src/position.rs
+++ b/azalea-core/src/position.rs
@@ -5,6 +5,12 @@ use std::{
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)]
pub struct BlockPos {
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)]
pub struct ChunkPos {
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.
#[derive(Clone, Copy, Debug, Default)]
pub struct ChunkSectionPos {
@@ -64,23 +85,6 @@ impl ChunkSectionPos {
ChunkSectionPos { x, y, z }
}
}
-
-impl From<BlockPos> for ChunkSectionPos {
- fn from(pos: BlockPos) -> Self {
- ChunkSectionPos {
- x: pos.x.div_floor(16),
- y: pos.y.div_floor(16),
- z: pos.z.div_floor(16),
- }
- }
-}
-
-impl From<ChunkSectionPos> for ChunkPos {
- fn from(pos: ChunkSectionPos) -> Self {
- ChunkPos { x: pos.x, z: pos.z }
- }
-}
-
/// The coordinates of a block inside a chunk.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ChunkBlockPos {
@@ -94,17 +98,6 @@ impl ChunkBlockPos {
ChunkBlockPos { x, y, z }
}
}
-
-impl From<&BlockPos> for ChunkBlockPos {
- fn from(pos: &BlockPos) -> Self {
- ChunkBlockPos {
- x: pos.x.rem_euclid(16).abs() as u8,
- y: pos.y,
- z: pos.z.rem_euclid(16).abs() as u8,
- }
- }
-}
-
/// The coordinates of a block inside a chunk section.
#[derive(Clone, Copy, Debug, Default)]
pub struct ChunkSectionBlockPos {
@@ -122,6 +115,80 @@ impl 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, 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 {
+ fn from(pos: BlockPos) -> Self {
+ ChunkSectionPos {
+ x: pos.x.div_floor(16),
+ y: pos.y.div_floor(16),
+ z: pos.z.div_floor(16),
+ }
+ }
+}
+
+impl From<ChunkSectionPos> for ChunkPos {
+ fn from(pos: ChunkSectionPos) -> Self {
+ ChunkPos { x: pos.x, z: pos.z }
+ }
+}
+
+impl From<&BlockPos> for ChunkBlockPos {
+ fn from(pos: &BlockPos) -> Self {
+ ChunkBlockPos {
+ x: pos.x.rem_euclid(16).abs() as u8,
+ y: pos.y,
+ z: pos.z.rem_euclid(16).abs() as u8,
+ }
+ }
+}
+
impl From<&BlockPos> for ChunkSectionBlockPos {
fn from(pos: &BlockPos) -> Self {
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 {
fn from(pos: &EntityPos) -> Self {
BlockPos {
diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs
index 065413a5..63c717d3 100644
--- a/azalea-entity/src/lib.rs
+++ b/azalea-entity/src/lib.rs
@@ -1,11 +1,6 @@
mod data;
-use azalea_core::EntityPos;
-#[cfg(feature = "protocol")]
-use azalea_protocol::packets::game::{
- clientbound_add_entity_packet::ClientboundAddEntityPacket,
- clientbound_add_player_packet::ClientboundAddPlayerPacket,
-};
+use azalea_core::{EntityPos, PositionDelta};
pub use data::*;
use uuid::Uuid;
@@ -14,12 +9,27 @@ pub struct Entity {
/// The incrementing numerical id of the entity.
pub id: u32,
pub uuid: Uuid,
+ /// The position of the entity right now.
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 {
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 {
@@ -31,6 +41,12 @@ impl Entity {
pub fn unsafe_move(&mut self, new_pos: EntityPos) {
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)]
diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs
index 63428dd8..cd3e3148 100644
--- a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs
@@ -1,11 +1,11 @@
use azalea_buf::McBuf;
-use azalea_core::PositionDelta;
+use azalea_core::PositionDelta8;
use packet_macros::GamePacket;
#[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundMoveEntityPosPacket {
#[var]
pub entity_id: u32,
- pub delta: PositionDelta,
+ pub delta: PositionDelta8,
pub on_ground: bool,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs
index dd1d96e1..e3422ac0 100644
--- a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs
@@ -1,5 +1,5 @@
use azalea_buf::McBuf;
-use azalea_core::PositionDelta;
+use azalea_core::PositionDelta8;
use packet_macros::GamePacket;
/// 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 {
#[var]
pub entity_id: u32,
- pub delta: PositionDelta,
+ pub delta: PositionDelta8,
pub y_rot: i8,
pub x_rot: i8,
pub on_ground: bool,
diff --git a/azalea-world/Cargo.toml b/azalea-world/Cargo.toml
index 02f05b46..ca890d82 100644
--- a/azalea-world/Cargo.toml
+++ b/azalea-world/Cargo.toml
@@ -13,6 +13,7 @@ azalea-entity = {path = "../azalea-entity"}
azalea-nbt = {path = "../azalea-nbt"}
log = "0.4.17"
nohash-hasher = "0.2.0"
+uuid = "1.1.2"
[profile.release]
lto = true
diff --git a/azalea-world/src/entity.rs b/azalea-world/src/entity.rs
index e4e9864f..5219e410 100644
--- a/azalea-world/src/entity.rs
+++ b/azalea-world/src/entity.rs
@@ -3,12 +3,13 @@ use azalea_entity::Entity;
use log::warn;
use nohash_hasher::{IntMap, IntSet};
use std::collections::HashMap;
+use uuid::Uuid;
#[derive(Debug)]
pub struct EntityStorage {
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_uuid: HashMap<Uuid, u32>,
}
impl EntityStorage {
@@ -16,6 +17,7 @@ impl EntityStorage {
Self {
by_id: IntMap::default(),
by_chunk: HashMap::default(),
+ by_uuid: HashMap::default(),
}
}
@@ -26,6 +28,7 @@ impl EntityStorage {
.entry(ChunkPos::from(entity.pos()))
.or_default()
.insert(entity.id);
+ self.by_uuid.insert(entity.uuid, entity.id);
self.by_id.insert(entity.id, entity);
}
@@ -34,9 +37,13 @@ impl EntityStorage {
pub fn remove_by_id(&mut self, id: u32) {
if let Some(entity) = self.by_id.remove(&id) {
let entity_chunk = ChunkPos::from(entity.pos());
+ let entity_uuid = entity.uuid;
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.");
}
+ 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 {
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)
}
+ /// 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.
pub fn clear_chunk(&mut self, chunk: &ChunkPos) {
if let Some(entities) = self.by_chunk.remove(chunk) {
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}.");
+ }
}
}
}
diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs
index bc73c13d..3afa4fee 100644
--- a/azalea-world/src/lib.rs
+++ b/azalea-world/src/lib.rs
@@ -6,7 +6,7 @@ mod entity;
mod palette;
use azalea_block::BlockState;
-use azalea_core::{BlockPos, ChunkPos, EntityPos, PositionDelta};
+use azalea_core::{BlockPos, ChunkPos, EntityPos, PositionDelta8};
use azalea_entity::Entity;
pub use bit_storage::BitStorage;
pub use chunk::{Chunk, ChunkStorage};
@@ -16,6 +16,7 @@ use std::{
ops::{Index, IndexMut},
sync::{Arc, Mutex},
};
+use uuid::Uuid;
#[cfg(test)]
mod tests {
@@ -76,7 +77,7 @@ impl World {
pub fn move_entity_with_delta(
&mut self,
entity_id: u32,
- delta: &PositionDelta,
+ delta: &PositionDelta8,
) -> Result<(), String> {
let entity = self
.entity_storage
@@ -112,6 +113,14 @@ impl World {
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.
#[inline]
pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Entity> {
diff --git a/bot/src/main.rs b/bot/src/main.rs
index 02f802a5..2976920b 100644
--- a/bot/src/main.rs
+++ b/bot/src/main.rs
@@ -1,12 +1,12 @@
use azalea_client::{Account, Event};
-use azalea_core::BlockPos;
+use azalea_core::PositionXYZ;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Hello, world!");
// 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())
// .await
// .unwrap();
@@ -20,23 +20,37 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
match e {
// TODO: have a "loaded" or "ready" event that fires when all chunks are loaded
Event::Login => {}
- Event::GameTick => {
- let world = client.world();
- if let Some(b) = world.find_one_entity(|e| {
- e.uuid == uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd")
- }) {
- // let world = state.world.as_ref().unwrap();
- // world.
- println!("{:?}", b);
- }
- // world.get_block_state(state.player.entity.pos);
- // println!("{}", p.message.to_ansi(None));
- // if p.message.to_ansi(None) == "<py5> ok" {
- // let state = client.state.lock();
- // let world = state.world.as_ref().unwrap();
- // let c = world.get_block_state(&BlockPos::new(5, 78, -2)).unwrap();
- // println!("block state: {:?}", c);
- // }
+ // Event::GameTick => {
+ // let world = client.world();
+ // if let Some(b) = world.find_one_entity(|e| {
+ // e.uuid == uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd")
+ // }) {
+ // // let world = state.world.as_ref().unwrap();
+ // // world.
+ // println!("{:?}", b);
+ // }
+ // // world.get_block_state(state.player.entity.pos);
+ // // println!("{}", p.message.to_ansi(None));
+ // // if p.message.to_ansi(None) == "<py5> ok" {
+ // // let state = client.state.lock();
+ // // let world = state.world.as_ref().unwrap();
+ // // let c = world.get_block_state(&BlockPos::new(5, 78, -2)).unwrap();
+ // // 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();
}
_ => {}
}