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:
parent
5643cc4a94
commit
b030b0ea33
16 changed files with 437 additions and 128 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -122,6 +122,7 @@ dependencies = [
|
|||
"azalea-world",
|
||||
"owning_ref",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -219,6 +220,7 @@ dependencies = [
|
|||
"azalea-nbt",
|
||||
"log",
|
||||
"nohash-hasher",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,11 +181,13 @@ 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(packet) => {
|
||||
match Self::handle(&packet, &tx, &state, &conn, &game_profile).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("Error handling packet: {:?}", e);
|
||||
|
@ -191,7 +197,8 @@ impl Client {
|
|||
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 {
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,41 @@
|
|||
use crate::EntityPos;
|
||||
pub use azalea_buf::McBuf;
|
||||
|
||||
/// Only works for up to 8 blocks
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct PositionDelta {
|
||||
xa: i16,
|
||||
ya: i16,
|
||||
za: i16,
|
||||
pub trait PositionDeltaTrait {
|
||||
fn x(&self) -> f64;
|
||||
fn y(&self) -> f64;
|
||||
fn z(&self) -> f64;
|
||||
}
|
||||
|
||||
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) {
|
||||
(
|
||||
(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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,6 +85,83 @@ impl ChunkSectionPos {
|
|||
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 {
|
||||
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 {
|
||||
fn from(pos: &BlockPos) -> Self {
|
||||
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 {
|
||||
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 {
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue