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",
|
"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]]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue