mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
Merge branch 'main' into 1.19.1
This commit is contained in:
commit
31e5629ce1
22 changed files with 168 additions and 203 deletions
25
.github/workflows/check.yml
vendored
Normal file
25
.github/workflows/check.yml
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
on: push
|
||||
name: Clippy check
|
||||
jobs:
|
||||
clippy_check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: clippy
|
||||
override: true
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -120,7 +120,6 @@ dependencies = [
|
|||
"azalea-entity",
|
||||
"azalea-protocol",
|
||||
"azalea-world",
|
||||
"owning_ref",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
@ -920,15 +919,6 @@ version = "11.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "owning_ref"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packet-macros"
|
||||
version = "0.1.0"
|
||||
|
@ -1277,12 +1267,6 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.96"
|
||||
|
|
|
@ -36,7 +36,7 @@ struct BlockDefinition {
|
|||
properties_and_defaults: Vec<PropertyAndDefault>,
|
||||
}
|
||||
impl PropertyAndDefault {
|
||||
fn into_property_with_name_and_default(&self, name: String) -> PropertyWithNameAndDefault {
|
||||
fn as_property_with_name_and_default(&self, name: String) -> PropertyWithNameAndDefault {
|
||||
PropertyWithNameAndDefault {
|
||||
name,
|
||||
struct_name: self.struct_name.clone(),
|
||||
|
@ -110,11 +110,7 @@ impl Parse for BlockDefinition {
|
|||
|
||||
let mut properties_and_defaults = Vec::new();
|
||||
|
||||
loop {
|
||||
let property = match content.parse() {
|
||||
Ok(property) => property,
|
||||
Err(_) => break,
|
||||
};
|
||||
while let Ok(property) = content.parse() {
|
||||
content.parse::<Token![=]>()?;
|
||||
let property_default = content.parse()?;
|
||||
properties_and_defaults.push(PropertyAndDefault {
|
||||
|
@ -248,7 +244,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
for property_name in block_property_names {
|
||||
let property_variants = properties_map
|
||||
.get(property_name)
|
||||
.expect(format!("Property '{}' not found", property_name).as_str())
|
||||
.unwrap_or_else(|| panic!("Property '{}' not found", property_name))
|
||||
.clone();
|
||||
block_properties_vec.push(property_variants);
|
||||
}
|
||||
|
@ -274,13 +270,13 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
};
|
||||
let mut property_name = property_struct_names_to_names
|
||||
.get(&property.struct_name.to_string())
|
||||
.expect(format!("Property '{}' is bad", property.struct_name).as_str())
|
||||
.unwrap_or_else(|| panic!("Property '{}' is bad", property.struct_name))
|
||||
.clone();
|
||||
if let Some(index) = index {
|
||||
property_name.push_str(&format!("_{}", &index.to_string()));
|
||||
}
|
||||
properties_with_name
|
||||
.push(property.into_property_with_name_and_default(property_name.clone()));
|
||||
.push(property.as_property_with_name_and_default(property_name.clone()));
|
||||
}
|
||||
|
||||
// pub face: properties::Face,
|
||||
|
@ -297,7 +293,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
{
|
||||
// let property_name_snake =
|
||||
// Ident::new(&property.to_string(), proc_macro2::Span::call_site());
|
||||
let name_ident = Ident::new(&name, proc_macro2::Span::call_site());
|
||||
let name_ident = Ident::new(name, proc_macro2::Span::call_site());
|
||||
block_struct_fields.extend(quote! {
|
||||
pub #name_ident: #struct_name,
|
||||
})
|
||||
|
@ -317,7 +313,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
let first_state_id = state_id;
|
||||
|
||||
// if there's no properties, then the block is just a single state
|
||||
if block_properties_vec.len() == 0 {
|
||||
if block_properties_vec.is_empty() {
|
||||
block_state_enum_variants.extend(quote! {
|
||||
#block_name_pascal_case,
|
||||
});
|
||||
|
@ -418,7 +414,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
let block_behavior = &block.behavior;
|
||||
let block_id = block.name.to_string();
|
||||
|
||||
let from_block_to_state_match = if block.properties_and_defaults.len() > 0 {
|
||||
let from_block_to_state_match = if !block.properties_and_defaults.is_empty() {
|
||||
quote! {
|
||||
match b {
|
||||
#from_block_to_state_match_inner
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
pub fn combinations_of<T: Clone>(items: &[Vec<T>]) -> Vec<Vec<T>> {
|
||||
let mut combinations = Vec::new();
|
||||
if items.len() == 0 {
|
||||
if items.is_empty() {
|
||||
return combinations;
|
||||
};
|
||||
if items.len() == 1 {
|
||||
|
@ -13,8 +13,7 @@ pub fn combinations_of<T: Clone>(items: &[Vec<T>]) -> Vec<Vec<T>> {
|
|||
for i in 0..items[0].len() {
|
||||
let item = &items[0][i];
|
||||
for other_combinations in combinations_of(&items[1..]) {
|
||||
let mut combination = Vec::new();
|
||||
combination.push(item.clone());
|
||||
let mut combination = vec![item.clone()];
|
||||
combination.extend(other_combinations);
|
||||
combinations.push(combination);
|
||||
}
|
||||
|
@ -29,13 +28,11 @@ pub fn to_pascal_case(s: &str) -> String {
|
|||
for c in s.chars() {
|
||||
if c == '_' {
|
||||
prev_was_underscore = true;
|
||||
} else if prev_was_underscore {
|
||||
result.push(c.to_ascii_uppercase());
|
||||
prev_was_underscore = false;
|
||||
} else {
|
||||
if prev_was_underscore {
|
||||
result.push(c.to_ascii_uppercase());
|
||||
prev_was_underscore = false;
|
||||
} else {
|
||||
result.push(c);
|
||||
}
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
result
|
||||
|
|
|
@ -7,8 +7,10 @@ pub use blocks::*;
|
|||
use std::mem;
|
||||
|
||||
impl BlockState {
|
||||
/// Transmutes a u32 to a block state. UB if the value is not a valid block
|
||||
/// state.
|
||||
/// Transmutes a u32 to a block state.
|
||||
///
|
||||
/// # Safety
|
||||
/// The `state_id` should be a valid block state.
|
||||
#[inline]
|
||||
pub unsafe fn from_u32_unsafe(state_id: u32) -> Self {
|
||||
mem::transmute::<u32, BlockState>(state_id)
|
||||
|
|
|
@ -231,7 +231,7 @@ impl McBufVarReadable for i64 {
|
|||
for i in 0..8 {
|
||||
buf.read_exact(&mut buffer)
|
||||
.map_err(|_| "Invalid VarLong".to_string())?;
|
||||
ans |= ((buffer[0] & 0b0111_1111) as i64) << 7 * i;
|
||||
ans |= ((buffer[0] & 0b0111_1111) as i64) << (7 * i);
|
||||
if buffer[0] & 0b1000_0000 == 0 {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ impl McBufVarWritable for i64 {
|
|||
}
|
||||
// this only writes a single byte, so write_all isn't necessary
|
||||
// the let _ = is so clippy doesn't complain
|
||||
let _ = buf.write(&mut buffer)?;
|
||||
let _ = buf.write(&buffer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ impl TranslatableComponent {
|
|||
}
|
||||
|
||||
pub fn read(&self) -> Result<String, fmt::Error> {
|
||||
let template = azalea_language::get(&self.key).unwrap_or_else(|| &self.key);
|
||||
let template = azalea_language::get(&self.key).unwrap_or(&self.key);
|
||||
// decode the % things
|
||||
|
||||
let mut result = String::new();
|
||||
|
|
|
@ -12,6 +12,5 @@ azalea-crypto = {path = "../azalea-crypto"}
|
|||
azalea-entity = {path = "../azalea-entity"}
|
||||
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,7 +1,8 @@
|
|||
//! Connect to Minecraft servers.
|
||||
|
||||
use crate::Client;
|
||||
use crate::{Client, Event};
|
||||
use azalea_protocol::ServerAddress;
|
||||
use tokio::sync::mpsc::UnboundedReceiver;
|
||||
|
||||
/// Something that can join Minecraft servers.
|
||||
pub struct Account {
|
||||
|
@ -14,7 +15,11 @@ impl Account {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn join(&self, address: &ServerAddress) -> Result<Client, String> {
|
||||
/// Joins the Minecraft server on the given address using this account.
|
||||
pub async fn join(
|
||||
&self,
|
||||
address: &ServerAddress,
|
||||
) -> Result<(Client, UnboundedReceiver<Event>), String> {
|
||||
Client::join(self, address).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,22 +24,15 @@ use azalea_protocol::{
|
|||
resolver, ServerAddress,
|
||||
};
|
||||
use azalea_world::Dimension;
|
||||
use owning_ref::OwningRef;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use tokio::{
|
||||
sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
|
||||
time::{self, MissedTickBehavior},
|
||||
time::{self},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ClientState {
|
||||
pub player: Player,
|
||||
pub world: Option<Dimension>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Event {
|
||||
Login,
|
||||
|
@ -64,11 +57,12 @@ pub enum ChatPacket {
|
|||
// }
|
||||
|
||||
/// A player that you can control that is currently in a Minecraft server.
|
||||
#[derive(Clone)]
|
||||
pub struct Client {
|
||||
event_receiver: UnboundedReceiver<Event>,
|
||||
game_profile: GameProfile,
|
||||
pub conn: Arc<tokio::sync::Mutex<GameConnection>>,
|
||||
pub state: Arc<Mutex<ClientState>>,
|
||||
pub player: Arc<Mutex<Player>>,
|
||||
pub dimension: Arc<Mutex<Option<Dimension>>>,
|
||||
// game_loop
|
||||
}
|
||||
|
||||
|
@ -80,7 +74,10 @@ struct HandleError(String);
|
|||
|
||||
impl Client {
|
||||
/// Connect to a Minecraft server with an account.
|
||||
pub async fn join(account: &Account, address: &ServerAddress) -> Result<Self, String> {
|
||||
pub async fn join(
|
||||
account: &Account,
|
||||
address: &ServerAddress,
|
||||
) -> Result<(Self, UnboundedReceiver<Event>), String> {
|
||||
let resolved_address = resolver::resolve_address(address).await?;
|
||||
|
||||
let mut conn = HandshakeConnection::new(&resolved_address).await?;
|
||||
|
@ -158,52 +155,38 @@ 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())),
|
||||
game_profile,
|
||||
conn,
|
||||
player: Arc::new(Mutex::new(Player::default())),
|
||||
dimension: Arc::new(Mutex::new(None)),
|
||||
};
|
||||
|
||||
// just start up the game loop and we're ready!
|
||||
|
||||
let game_loop_state = client.state.clone();
|
||||
|
||||
// if you get an error right here that means you're doing something with locks wrong
|
||||
// read the error to see where the issue is
|
||||
// you might be able to just drop the lock or put it in its own scope to fix
|
||||
tokio::spawn(Self::protocol_loop(
|
||||
conn.clone(),
|
||||
tx.clone(),
|
||||
game_loop_state.clone(),
|
||||
game_profile.clone(),
|
||||
));
|
||||
tokio::spawn(Self::game_tick_loop(conn, tx, game_loop_state));
|
||||
tokio::spawn(Self::protocol_loop(client.clone(), tx.clone()));
|
||||
tokio::spawn(Self::game_tick_loop(client.clone(), tx));
|
||||
|
||||
Ok(client)
|
||||
Ok((client, rx))
|
||||
}
|
||||
|
||||
async fn protocol_loop(
|
||||
conn: Arc<tokio::sync::Mutex<GameConnection>>,
|
||||
tx: UnboundedSender<Event>,
|
||||
state: Arc<Mutex<ClientState>>,
|
||||
game_profile: GameProfile,
|
||||
) {
|
||||
async fn protocol_loop(client: Client, tx: UnboundedSender<Event>) {
|
||||
loop {
|
||||
let r = conn.lock().await.read().await;
|
||||
let r = client.conn.lock().await.read().await;
|
||||
match r {
|
||||
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);
|
||||
}
|
||||
Ok(packet) => match Self::handle(&packet, &client, &tx).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);
|
||||
|
@ -220,18 +203,14 @@ impl Client {
|
|||
|
||||
async fn handle(
|
||||
packet: &GamePacket,
|
||||
client: &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_lock = state.lock()?;
|
||||
|
||||
// // write p into login.txt
|
||||
// std::io::Write::write_all(
|
||||
// &mut std::fs::File::create("login.txt").unwrap(),
|
||||
|
@ -292,23 +271,28 @@ impl Client {
|
|||
.as_int()
|
||||
.expect("min_y tag is not an int");
|
||||
|
||||
let mut dimension_lock = client.dimension.lock().unwrap();
|
||||
// the 16 here is our render distance
|
||||
// i'll make this an actual setting later
|
||||
state_lock.world = Some(Dimension::new(16, height, min_y));
|
||||
*dimension_lock = Some(Dimension::new(16, height, min_y));
|
||||
|
||||
let entity = Entity::new(p.player_id, game_profile.uuid, EntityPos::default());
|
||||
state_lock
|
||||
.world
|
||||
let entity =
|
||||
Entity::new(p.player_id, client.game_profile.uuid, EntityPos::default());
|
||||
dimension_lock
|
||||
.as_mut()
|
||||
.expect(
|
||||
"Dimension doesn't exist! We should've gotten a login packet by now.",
|
||||
)
|
||||
.add_entity(entity);
|
||||
|
||||
state_lock.player.set_entity_id(p.player_id);
|
||||
let mut player_lock = client.player.lock().unwrap();
|
||||
|
||||
player_lock.set_entity_id(p.player_id);
|
||||
}
|
||||
|
||||
conn.lock()
|
||||
client
|
||||
.conn
|
||||
.lock()
|
||||
.await
|
||||
.write(
|
||||
ServerboundCustomPayloadPacket {
|
||||
|
@ -360,12 +344,17 @@ impl Client {
|
|||
println!("Got player position packet {:?}", p);
|
||||
|
||||
let (new_pos, y_rot, x_rot) = {
|
||||
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
|
||||
let player_lock = client.player.lock().unwrap();
|
||||
let player_entity_id = player_lock.entity_id;
|
||||
drop(player_lock);
|
||||
|
||||
let mut dimension_lock = client.dimension.lock().unwrap();
|
||||
let dimension = dimension_lock.as_mut().unwrap();
|
||||
|
||||
let player_entity = dimension
|
||||
.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;
|
||||
|
@ -416,14 +405,14 @@ impl Client {
|
|||
y: new_pos_y,
|
||||
z: new_pos_z,
|
||||
};
|
||||
world
|
||||
dimension
|
||||
.move_entity(player_entity_id, new_pos)
|
||||
.expect("The player entity should always exist");
|
||||
|
||||
(new_pos, y_rot, x_rot)
|
||||
};
|
||||
|
||||
let mut conn_lock = conn.lock().await;
|
||||
let mut conn_lock = client.conn.lock().await;
|
||||
conn_lock
|
||||
.write(ServerboundAcceptTeleportationPacket { id: p.id }.get())
|
||||
.await;
|
||||
|
@ -447,9 +436,9 @@ impl Client {
|
|||
}
|
||||
GamePacket::ClientboundSetChunkCacheCenterPacket(p) => {
|
||||
println!("Got chunk cache center packet {:?}", p);
|
||||
state
|
||||
client
|
||||
.dimension
|
||||
.lock()?
|
||||
.world
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.update_view_center(&ChunkPos::new(p.x, p.z));
|
||||
|
@ -459,9 +448,9 @@ impl Client {
|
|||
let pos = ChunkPos::new(p.x, p.z);
|
||||
// let chunk = Chunk::read_with_world_height(&mut p.chunk_data);
|
||||
// println("chunk {:?}")
|
||||
state
|
||||
client
|
||||
.dimension
|
||||
.lock()?
|
||||
.world
|
||||
.as_mut()
|
||||
.expect("Dimension doesn't exist! We should've gotten a login packet by now.")
|
||||
.replace_with_packet_data(&pos, &mut p.chunk_data.data.as_slice())
|
||||
|
@ -473,9 +462,9 @@ impl Client {
|
|||
GamePacket::ClientboundAddEntityPacket(p) => {
|
||||
println!("Got add entity packet {:?}", p);
|
||||
let entity = Entity::from(p);
|
||||
state
|
||||
client
|
||||
.dimension
|
||||
.lock()?
|
||||
.world
|
||||
.as_mut()
|
||||
.expect("Dimension doesn't exist! We should've gotten a login packet by now.")
|
||||
.add_entity(entity);
|
||||
|
@ -495,9 +484,9 @@ impl Client {
|
|||
GamePacket::ClientboundAddPlayerPacket(p) => {
|
||||
println!("Got add player packet {:?}", p);
|
||||
let entity = Entity::from(p);
|
||||
state
|
||||
client
|
||||
.dimension
|
||||
.lock()?
|
||||
.world
|
||||
.as_mut()
|
||||
.expect("Dimension doesn't exist! We should've gotten a login packet by now.")
|
||||
.add_entity(entity);
|
||||
|
@ -521,10 +510,10 @@ impl Client {
|
|||
println!("Got set experience packet {:?}", p);
|
||||
}
|
||||
GamePacket::ClientboundTeleportEntityPacket(p) => {
|
||||
let mut state_lock = state.lock()?;
|
||||
let world = state_lock.world.as_mut().unwrap();
|
||||
let mut dimension_lock = client.dimension.lock()?;
|
||||
let dimension = dimension_lock.as_mut().unwrap();
|
||||
|
||||
world.move_entity(
|
||||
dimension.move_entity(
|
||||
p.id,
|
||||
EntityPos {
|
||||
x: p.x,
|
||||
|
@ -540,23 +529,25 @@ impl Client {
|
|||
// println!("Got rotate head packet {:?}", p);
|
||||
}
|
||||
GamePacket::ClientboundMoveEntityPosPacket(p) => {
|
||||
let mut state_lock = state.lock()?;
|
||||
let world = state_lock.world.as_mut().unwrap();
|
||||
let mut dimension_lock = client.dimension.lock()?;
|
||||
let dimension = dimension_lock.as_mut().unwrap();
|
||||
|
||||
world.move_entity_with_delta(p.entity_id, &p.delta)?;
|
||||
dimension.move_entity_with_delta(p.entity_id, &p.delta)?;
|
||||
}
|
||||
GamePacket::ClientboundMoveEntityPosrotPacket(p) => {
|
||||
let mut state_lock = state.lock()?;
|
||||
let world = state_lock.world.as_mut().unwrap();
|
||||
let mut dimension_lock = client.dimension.lock()?;
|
||||
let dimension = dimension_lock.as_mut().unwrap();
|
||||
|
||||
world.move_entity_with_delta(p.entity_id, &p.delta)?;
|
||||
dimension.move_entity_with_delta(p.entity_id, &p.delta)?;
|
||||
}
|
||||
GamePacket::ClientboundMoveEntityRotPacket(p) => {
|
||||
println!("Got move entity rot packet {:?}", p);
|
||||
}
|
||||
GamePacket::ClientboundKeepAlivePacket(p) => {
|
||||
println!("Got keep alive packet {:?}", p);
|
||||
conn.lock()
|
||||
client
|
||||
.conn
|
||||
.lock()
|
||||
.await
|
||||
.write(ServerboundKeepAlivePacket { id: p.id }.get())
|
||||
.await;
|
||||
|
@ -611,55 +602,24 @@ impl Client {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn next(&mut self) -> Option<Event> {
|
||||
self.event_receiver.recv().await
|
||||
}
|
||||
|
||||
/// Runs game_tick every 50 milliseconds.
|
||||
async fn game_tick_loop(
|
||||
conn: Arc<tokio::sync::Mutex<GameConnection>>,
|
||||
tx: UnboundedSender<Event>,
|
||||
state: Arc<Mutex<ClientState>>,
|
||||
) {
|
||||
async fn game_tick_loop(client: Client, tx: UnboundedSender<Event>) {
|
||||
let mut game_tick_interval = time::interval(time::Duration::from_millis(50));
|
||||
// TODO: Minecraft bursts up to 10 ticks and then skips, we should too
|
||||
game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst);
|
||||
loop {
|
||||
game_tick_interval.tick().await;
|
||||
Self::game_tick(&conn, &tx, &state).await;
|
||||
Self::game_tick(&client, &tx).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs every 50 milliseconds.
|
||||
async fn game_tick(
|
||||
conn: &Arc<tokio::sync::Mutex<GameConnection>>,
|
||||
tx: &UnboundedSender<Event>,
|
||||
state: &Arc<Mutex<ClientState>>,
|
||||
) {
|
||||
if state.lock().unwrap().world.is_none() {
|
||||
async fn game_tick(client: &Client, tx: &UnboundedSender<Event>) {
|
||||
if client.dimension.lock().unwrap().is_none() {
|
||||
return;
|
||||
}
|
||||
tx.send(Event::GameTick).unwrap();
|
||||
}
|
||||
|
||||
/// Gets the `Dimension` the client is in.
|
||||
///
|
||||
/// 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>, Dimension> {
|
||||
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("Dimension 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 {
|
||||
|
|
|
@ -5,24 +5,21 @@ 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, new_pos: EntityPos) -> Result<(), String> {
|
||||
println!("obtaining lock on state");
|
||||
let mut state_lock = self.state.lock().unwrap();
|
||||
println!("obtained lock on state");
|
||||
{
|
||||
let mut dimension_lock = self.dimension.lock().unwrap();
|
||||
let dimension = dimension_lock.as_mut().unwrap();
|
||||
|
||||
let world = state_lock.world.as_ref().unwrap();
|
||||
let player_lock = self.player.lock().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 player_id = if let Some(player_lock) = player_lock.entity(dimension) {
|
||||
player_lock.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)?;
|
||||
drop(state_lock);
|
||||
dimension.move_entity(player_id, new_pos)?;
|
||||
}
|
||||
|
||||
println!("obtaining lock on conn");
|
||||
self.conn
|
||||
.lock()
|
||||
.await
|
||||
|
@ -38,7 +35,6 @@ impl Client {
|
|||
.get(),
|
||||
)
|
||||
.await;
|
||||
println!("obtained lock on conn");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,6 +2,11 @@ use azalea_entity::Entity;
|
|||
use azalea_world::Dimension;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Something that has a dimension associated to it. Usually, this is a `Client`.
|
||||
pub trait DimensionHaver {
|
||||
fn dimension(&self) -> &Dimension;
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Player {
|
||||
/// The player's uuid.
|
||||
|
|
|
@ -256,7 +256,7 @@ impl McBufReadable for ParticleData {
|
|||
}
|
||||
|
||||
impl McBufWritable for ParticleData {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,9 +182,9 @@ impl From<ChunkSectionPos> for ChunkPos {
|
|||
impl From<&BlockPos> for ChunkBlockPos {
|
||||
fn from(pos: &BlockPos) -> Self {
|
||||
ChunkBlockPos {
|
||||
x: pos.x.rem_euclid(16).abs() as u8,
|
||||
x: pos.x.rem_euclid(16).unsigned_abs() as u8,
|
||||
y: pos.y,
|
||||
z: pos.z.rem_euclid(16).abs() as u8,
|
||||
z: pos.z.rem_euclid(16).unsigned_abs() as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,9 +192,9 @@ impl From<&BlockPos> for ChunkBlockPos {
|
|||
impl From<&BlockPos> for ChunkSectionBlockPos {
|
||||
fn from(pos: &BlockPos) -> Self {
|
||||
ChunkSectionBlockPos {
|
||||
x: pos.x.rem(16).abs() as u8,
|
||||
y: pos.y.rem(16).abs() as u8,
|
||||
z: pos.z.rem(16).abs() as u8,
|
||||
x: pos.x.rem(16).unsigned_abs() as u8,
|
||||
y: pos.y.rem(16).unsigned_abs() as u8,
|
||||
z: pos.z.rem(16).unsigned_abs() as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ impl From<&ChunkBlockPos> for ChunkSectionBlockPos {
|
|||
fn from(pos: &ChunkBlockPos) -> Self {
|
||||
ChunkSectionBlockPos {
|
||||
x: pos.x,
|
||||
y: pos.y.rem(16).abs() as u8,
|
||||
y: pos.y.rem(16).unsigned_abs() as u8,
|
||||
z: pos.z,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ impl McBufReadable for EntityDataValue {
|
|||
}
|
||||
|
||||
impl McBufWritable for EntityDataValue {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use proc_macro::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
self, braced,
|
||||
parse::{Parse, ParseStream, Result},
|
||||
parse_macro_input, Data, DeriveInput, FieldsNamed, Ident, LitInt, Token,
|
||||
parse_macro_input, DeriveInput, FieldsNamed, Ident, LitInt, Token,
|
||||
};
|
||||
|
||||
fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use super::GamePacket;
|
||||
use azalea_buf::McBuf;
|
||||
use azalea_buf::McBufVarReadable;
|
||||
use azalea_buf::{McBufReadable, McBufWritable, Readable, Writable};
|
||||
|
@ -236,7 +235,7 @@ impl McBufReadable for BrigadierNodeStub {
|
|||
}
|
||||
|
||||
impl McBufWritable for BrigadierNodeStub {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ impl McBufReadable for ClientboundLevelParticlesPacket {
|
|||
}
|
||||
|
||||
impl McBufWritable for ClientboundLevelParticlesPacket {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,15 +22,12 @@ impl McBufReadable for BlockStateWithPosition {
|
|||
let data = u64::var_read_from(buf)?;
|
||||
let position_part = data & 4095;
|
||||
let state = (data >> 12) as u32;
|
||||
let position = ChunkSectionBlockPos {
|
||||
let pos = ChunkSectionBlockPos {
|
||||
x: (position_part >> 8 & 15) as u8,
|
||||
y: (position_part >> 0 & 15) as u8,
|
||||
y: (position_part & 15) as u8,
|
||||
z: (position_part >> 4 & 15) as u8,
|
||||
};
|
||||
Ok(BlockStateWithPosition {
|
||||
pos: position,
|
||||
state: state,
|
||||
})
|
||||
Ok(BlockStateWithPosition { pos, state })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,9 +31,9 @@ impl ConnectionProtocol {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Packet {
|
||||
Game(game::GamePacket),
|
||||
Handshake(handshake::HandshakePacket),
|
||||
Login(login::LoginPacket),
|
||||
Game(Box<game::GamePacket>),
|
||||
Handshake(Box<handshake::HandshakePacket>),
|
||||
Login(Box<login::LoginPacket>),
|
||||
Status(Box<status::StatusPacket>),
|
||||
}
|
||||
|
||||
|
|
|
@ -6,17 +6,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
println!("Hello, world!");
|
||||
|
||||
// let address = "95.111.249.143:10000";
|
||||
let address = "localhost:65399";
|
||||
let address = "localhost:56150";
|
||||
// let response = azalea_client::ping::ping_server(&address.try_into().unwrap())
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// println!("{}", response.description.to_ansi(None));
|
||||
let account = Account::offline("bot");
|
||||
let mut client = account.join(&address.try_into().unwrap()).await.unwrap();
|
||||
let (mut client, mut rx) = account.join(&address.try_into().unwrap()).await.unwrap();
|
||||
println!("connected");
|
||||
|
||||
while let Some(e) = &client.next().await {
|
||||
while let Some(e) = &rx.recv().await {
|
||||
match e {
|
||||
// TODO: have a "loaded" or "ready" event that fires when all chunks are loaded
|
||||
Event::Login => {}
|
||||
|
@ -38,13 +38,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
// // println!("block state: {:?}", c);
|
||||
// // }
|
||||
// }
|
||||
Event::Chat(msg) => {
|
||||
Event::Chat(_m) => {
|
||||
let new_pos = {
|
||||
let state_lock = client.state.lock().unwrap();
|
||||
let world = state_lock.world.as_ref().unwrap();
|
||||
let player = &state_lock.player;
|
||||
let dimension_lock = client.dimension.lock().unwrap();
|
||||
let dimension = dimension_lock.as_ref().unwrap();
|
||||
let player = client.player.lock().unwrap();
|
||||
let entity = player
|
||||
.entity(&world)
|
||||
.entity(dimension)
|
||||
.expect("Player entity is not in world");
|
||||
entity.pos().add_y(0.5)
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue