mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
fix panic on ungraceful disconnect
This commit is contained in:
commit
7340f634e5
44 changed files with 483 additions and 340 deletions
42
Cargo.lock
generated
42
Cargo.lock
generated
|
@ -222,7 +222,7 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|||
|
||||
[[package]]
|
||||
name = "azalea"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"azalea-auth",
|
||||
|
@ -262,7 +262,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-auth"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-crypto",
|
||||
|
@ -282,7 +282,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-block"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-block-macros",
|
||||
"azalea-buf",
|
||||
|
@ -291,7 +291,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-block-macros"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -300,7 +300,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-brigadier"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-chat",
|
||||
|
@ -311,7 +311,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-buf"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-buf-macros",
|
||||
"byteorder",
|
||||
|
@ -324,7 +324,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-buf-macros"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -333,7 +333,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-chat"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-language",
|
||||
|
@ -346,7 +346,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-client"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compat",
|
||||
|
@ -382,7 +382,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-core"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-chat",
|
||||
|
@ -398,7 +398,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-crypto"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"azalea-buf",
|
||||
|
@ -415,7 +415,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-entity"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-block",
|
||||
"azalea-buf",
|
||||
|
@ -438,7 +438,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-inventory"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-chat",
|
||||
|
@ -453,7 +453,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-inventory-macros"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -462,7 +462,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-language"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"compact_str",
|
||||
"serde",
|
||||
|
@ -471,7 +471,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-physics"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-block",
|
||||
"azalea-core",
|
||||
|
@ -489,7 +489,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-protocol"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
|
@ -525,7 +525,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-protocol-macros"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -534,7 +534,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-registry"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-registry-macros",
|
||||
|
@ -544,7 +544,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-registry-macros"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -552,7 +552,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "azalea-world"
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
dependencies = [
|
||||
"azalea-block",
|
||||
"azalea-buf",
|
||||
|
|
|
@ -22,7 +22,7 @@ resolver = "2"
|
|||
# --- Workspace Settings ---
|
||||
|
||||
[workspace.package]
|
||||
version = "0.11.0+mc1.21.5"
|
||||
version = "0.12.0+mc1.21.5"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/azalea-rs/azalea"
|
||||
|
@ -81,6 +81,7 @@ indexmap = "2.9.0"
|
|||
paste = "1.0.15"
|
||||
compact_str = "0.9.0"
|
||||
crc32fast = "1.4.2"
|
||||
async-compat = "0.2.4"
|
||||
|
||||
# --- Profile Settings ---
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ _Currently supported Minecraft version: `1.21.5`._
|
|||
- [Block interactions & building](https://azalea.matdoes.dev/azalea/struct.Client.html#method.block_interact) (this doesn't predict the block interactions/placement on the client yet but it's usually fine)
|
||||
- [Inventories](https://azalea.matdoes.dev/azalea/struct.Client.html#impl-ContainerClientExt-for-Client)
|
||||
- [Attacking entities](https://azalea.matdoes.dev/azalea/struct.Client.html#method.attack) (but you can't get the entity at the crosshair yet)
|
||||
- [Plugins](#plugins)
|
||||
|
||||
## Docs
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-crypto = { path = "../azalea-crypto", version = "0.11.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
azalea-crypto = { path = "../azalea-crypto", version = "0.12.0" }
|
||||
base64.workspace = true
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
md-5.workspace = true
|
||||
|
|
|
@ -7,6 +7,6 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
azalea-block-macros = { path = "./azalea-block-macros", version = "0.11.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
|
||||
azalea-block-macros = { path = "./azalea-block-macros", version = "0.12.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.12.0" }
|
||||
|
|
|
@ -552,7 +552,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
from_block_to_state_match_inner.extend(quote! {
|
||||
#block_struct_name {
|
||||
#from_block_to_state_combination_match_inner
|
||||
} => BlockState { id: #state_id },
|
||||
} => BlockState::new_const(#state_id),
|
||||
});
|
||||
|
||||
if is_default {
|
||||
|
@ -626,7 +626,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
azalea_registry::Block::#block_name_pascal_case => Box::new(#block_struct_name::default()),
|
||||
});
|
||||
from_registry_block_to_blockstate_match.extend(quote! {
|
||||
azalea_registry::Block::#block_name_pascal_case => BlockState { id: #default_state_id },
|
||||
azalea_registry::Block::#block_name_pascal_case => BlockState::new_const(#default_state_id),
|
||||
});
|
||||
from_registry_block_to_blockstates_match.extend(quote! {
|
||||
azalea_registry::Block::#block_name_pascal_case => BlockStates::from(#first_state_id..=#last_state_id),
|
||||
|
@ -646,7 +646,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
let block_id = block.name.to_string();
|
||||
|
||||
let from_block_to_state_match = if block.properties_and_defaults.is_empty() {
|
||||
quote! { BlockState { id: #first_state_id } }
|
||||
quote! { BlockState::new_const(#first_state_id) }
|
||||
} else {
|
||||
quote! {
|
||||
match self {
|
||||
|
@ -762,7 +762,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
type Value = #value;
|
||||
|
||||
fn try_from_block_state(block_state: BlockState) -> Option<Self::Value> {
|
||||
match block_state.id {
|
||||
match block_state.id() {
|
||||
#enum_inner_generated
|
||||
_ => None
|
||||
}
|
||||
|
@ -788,7 +788,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
|
||||
impl From<BlockState> for Box<dyn Block> {
|
||||
fn from(block_state: BlockState) -> Self {
|
||||
let b = block_state.id;
|
||||
let b = block_state.id();
|
||||
match b {
|
||||
#from_state_to_block_match
|
||||
_ => panic!("Invalid block state: {}", b),
|
||||
|
|
|
@ -27,7 +27,7 @@ pub type BlockStateIntegerRepr = u16;
|
|||
pub struct BlockState {
|
||||
/// The protocol ID for the block state. IDs may change every
|
||||
/// version, so you shouldn't hard-code them or store them in databases.
|
||||
pub id: BlockStateIntegerRepr,
|
||||
id: BlockStateIntegerRepr,
|
||||
}
|
||||
|
||||
impl BlockState {
|
||||
|
@ -35,12 +35,21 @@ impl BlockState {
|
|||
/// 0.
|
||||
pub const AIR: BlockState = BlockState { id: 0 };
|
||||
|
||||
/// Create a new BlockState and panic if the block is not a valid state.
|
||||
///
|
||||
/// You should probably use [`BlockState::try_from`] instead.
|
||||
#[inline]
|
||||
pub(crate) const fn new_const(id: BlockStateIntegerRepr) -> Self {
|
||||
assert!(Self::is_valid_state(id));
|
||||
Self { id }
|
||||
}
|
||||
|
||||
/// Whether the block state is possible to exist in vanilla Minecraft.
|
||||
///
|
||||
/// It's equivalent to checking that the state ID is not greater than
|
||||
/// [`Self::MAX_STATE`].
|
||||
#[inline]
|
||||
pub fn is_valid_state(state_id: BlockStateIntegerRepr) -> bool {
|
||||
pub const fn is_valid_state(state_id: BlockStateIntegerRepr) -> bool {
|
||||
state_id <= Self::MAX_STATE
|
||||
}
|
||||
|
||||
|
@ -50,6 +59,13 @@ impl BlockState {
|
|||
pub fn is_air(&self) -> bool {
|
||||
self == &Self::AIR
|
||||
}
|
||||
|
||||
/// Returns the protocol ID for the block state. IDs may change every
|
||||
/// version, so you shouldn't hard-code them or store them in databases.
|
||||
#[inline]
|
||||
pub const fn id(&self) -> BlockStateIntegerRepr {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for BlockState {
|
||||
|
|
|
@ -14,7 +14,7 @@ impl From<RangeInclusive<BlockStateIntegerRepr>> for BlockStates {
|
|||
fn from(range: RangeInclusive<BlockStateIntegerRepr>) -> Self {
|
||||
let mut set = HashSet::with_capacity((range.end() - range.start() + 1) as usize);
|
||||
for id in range {
|
||||
set.insert(BlockState { id });
|
||||
set.insert(BlockState::try_from(id).unwrap_or_default());
|
||||
}
|
||||
Self { set }
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ bevy_app.workspace = true
|
|||
bevy_ecs.workspace = true
|
||||
|
||||
[dependencies]
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0", optional = true }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.11.0", optional = true }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0", optional = true }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.12.0", optional = true }
|
||||
parking_lot.workspace = true
|
||||
|
||||
[features]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{HashMap, HashSet},
|
||||
mem,
|
||||
mem, ptr,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
@ -349,7 +349,7 @@ impl<S> CommandDispatcher<S> {
|
|||
}
|
||||
match &node.redirect {
|
||||
Some(redirect) => {
|
||||
let redirect = if std::ptr::eq(redirect.data_ptr(), self.root.data_ptr()) {
|
||||
let redirect = if ptr::eq(redirect.data_ptr(), self.root.data_ptr()) {
|
||||
"...".to_string()
|
||||
} else {
|
||||
format!("-> {}", redirect.read().usage_text())
|
||||
|
@ -427,7 +427,7 @@ impl<S> CommandDispatcher<S> {
|
|||
}
|
||||
|
||||
if let Some(redirect) = &node.redirect {
|
||||
let redirect = if std::ptr::eq(redirect.data_ptr(), self.root.data_ptr()) {
|
||||
let redirect = if ptr::eq(redirect.data_ptr(), self.root.data_ptr()) {
|
||||
"...".to_string()
|
||||
} else {
|
||||
format!("-> {}", redirect.read().usage_text())
|
||||
|
|
|
@ -7,7 +7,7 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
azalea-buf-macros = { path = "./azalea-buf-macros", version = "0.11.0" }
|
||||
azalea-buf-macros = { path = "./azalea-buf-macros", version = "0.12.0" }
|
||||
byteorder.workspace = true
|
||||
serde_json = { workspace = true, optional = true }
|
||||
simdnbt.workspace = true
|
||||
|
|
|
@ -13,11 +13,11 @@ azalea-buf = ["dep:azalea-buf", "simdnbt"]
|
|||
numbers = ["dep:azalea-registry", "dep:simdnbt"]
|
||||
|
||||
[dependencies]
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0", optional = true, features = [
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0", optional = true, features = [
|
||||
"serde_json",
|
||||
] }
|
||||
azalea-language = { path = "../azalea-language", version = "0.11.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.11.0", optional = true }
|
||||
azalea-language = { path = "../azalea-language", version = "0.12.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.12.0", optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
simdnbt = { workspace = true, optional = true }
|
||||
|
|
|
@ -7,19 +7,19 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-compat = "0.2.4"
|
||||
azalea-auth = { path = "../azalea-auth", version = "0.11.0" }
|
||||
azalea-block = { path = "../azalea-block", version = "0.11.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.11.0" }
|
||||
azalea-core = { path = "../azalea-core", version = "0.11.0" }
|
||||
azalea-crypto = { path = "../azalea-crypto", version = "0.11.0" }
|
||||
azalea-entity = { path = "../azalea-entity", version = "0.11.0" }
|
||||
azalea-inventory = { path = "../azalea-inventory", version = "0.11.0" }
|
||||
azalea-physics = { path = "../azalea-physics", version = "0.11.0" }
|
||||
azalea-protocol = { path = "../azalea-protocol", version = "0.11.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
|
||||
azalea-world = { path = "../azalea-world", version = "0.11.0" }
|
||||
async-compat.workspace = true
|
||||
azalea-auth = { path = "../azalea-auth", version = "0.12.0" }
|
||||
azalea-block = { path = "../azalea-block", version = "0.12.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.12.0" }
|
||||
azalea-core = { path = "../azalea-core", version = "0.12.0" }
|
||||
azalea-crypto = { path = "../azalea-crypto", version = "0.12.0" }
|
||||
azalea-entity = { path = "../azalea-entity", version = "0.12.0" }
|
||||
azalea-inventory = { path = "../azalea-inventory", version = "0.12.0" }
|
||||
azalea-physics = { path = "../azalea-physics", version = "0.12.0" }
|
||||
azalea-protocol = { path = "../azalea-protocol", version = "0.12.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.12.0" }
|
||||
azalea-world = { path = "../azalea-world", version = "0.12.0" }
|
||||
bevy_app.workspace = true
|
||||
bevy_ecs.workspace = true
|
||||
bevy_log = { workspace = true, optional = true }
|
||||
|
|
|
@ -38,15 +38,14 @@ use bevy_ecs::{
|
|||
component::Component,
|
||||
entity::Entity,
|
||||
schedule::{InternedScheduleLabel, IntoSystemConfigs, LogLevel, ScheduleBuildSettings},
|
||||
system::{Commands, ResMut, Resource},
|
||||
system::{Commands, Resource},
|
||||
world::World,
|
||||
};
|
||||
use derive_more::Deref;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use simdnbt::owned::NbtCompound;
|
||||
use thiserror::Error;
|
||||
use tokio::{
|
||||
sync::{broadcast, mpsc},
|
||||
sync::mpsc::{self},
|
||||
time,
|
||||
};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
@ -827,39 +826,6 @@ async fn run_schedule_loop(ecs: Arc<Mutex<World>>, outer_schedule_label: Interne
|
|||
}
|
||||
}
|
||||
|
||||
/// A resource that contains a [`broadcast::Sender`] that will be sent every
|
||||
/// Minecraft tick.
|
||||
///
|
||||
/// This is useful for running code every schedule from async user code.
|
||||
///
|
||||
/// ```
|
||||
/// use azalea_client::TickBroadcast;
|
||||
/// # async fn example(client: azalea_client::Client) {
|
||||
/// let mut receiver = {
|
||||
/// let ecs = client.ecs.lock();
|
||||
/// let tick_broadcast = ecs.resource::<TickBroadcast>();
|
||||
/// tick_broadcast.subscribe()
|
||||
/// };
|
||||
/// while receiver.recv().await.is_ok() {
|
||||
/// // do something
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Resource, Deref)]
|
||||
pub struct TickBroadcast(broadcast::Sender<()>);
|
||||
|
||||
pub fn send_tick_broadcast(tick_broadcast: ResMut<TickBroadcast>) {
|
||||
let _ = tick_broadcast.0.send(());
|
||||
}
|
||||
/// A plugin that makes the [`RanScheduleBroadcast`] resource available.
|
||||
pub struct TickBroadcastPlugin;
|
||||
impl Plugin for TickBroadcastPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(TickBroadcast(broadcast::channel(1).0))
|
||||
.add_systems(GameTick, send_tick_broadcast);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AmbiguityLoggerPlugin;
|
||||
impl Plugin for AmbiguityLoggerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
|
|
|
@ -23,7 +23,7 @@ pub use account::{Account, AccountOpts};
|
|||
pub use azalea_protocol::common::client_information::ClientInformation;
|
||||
pub use client::{
|
||||
Client, InConfigState, InGameState, JoinError, JoinedClientBundle, LocalPlayerBundle,
|
||||
StartClientOpts, TickBroadcast, start_ecs_runner,
|
||||
StartClientOpts, start_ecs_runner,
|
||||
};
|
||||
pub use events::Event;
|
||||
pub use local_player::{GameProfileComponent, Hunger, InstanceHolder, TabList};
|
||||
|
|
|
@ -19,7 +19,7 @@ use tokio::{
|
|||
net::tcp::OwnedWriteHalf,
|
||||
sync::mpsc::{self},
|
||||
};
|
||||
use tracing::{debug, error, trace};
|
||||
use tracing::{debug, error, info, trace};
|
||||
|
||||
use super::packet::{
|
||||
config::ReceiveConfigPacketEvent, game::ReceiveGamePacketEvent, login::ReceiveLoginPacketEvent,
|
||||
|
@ -99,6 +99,18 @@ pub fn read_packets(ecs: &mut World) {
|
|||
}
|
||||
Err(err) => {
|
||||
log_for_error(&err);
|
||||
|
||||
if matches!(
|
||||
&*err,
|
||||
ReadPacketError::IoError { .. } | ReadPacketError::ConnectionClosed
|
||||
) {
|
||||
info!("Server closed connection");
|
||||
// ungraceful disconnect :(
|
||||
conn.network = None;
|
||||
// setting this will make us send a DisconnectEvent
|
||||
conn.is_alive = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +320,8 @@ impl NetworkConnection {
|
|||
}
|
||||
|
||||
pub fn poll_writer(&mut self) {
|
||||
future::block_on(future::poll_once(&mut self.writer_task));
|
||||
let poll_once_res = future::poll_once(&mut self.writer_task);
|
||||
future::block_on(poll_once_res);
|
||||
}
|
||||
|
||||
pub fn set_compression_threshold(&mut self, threshold: Option<u32>) {
|
||||
|
|
|
@ -16,6 +16,7 @@ pub mod packet;
|
|||
pub mod pong;
|
||||
pub mod respawn;
|
||||
pub mod task_pool;
|
||||
pub mod tick_broadcast;
|
||||
pub mod tick_end;
|
||||
|
||||
/// This plugin group will add all the default plugins necessary for Azalea to
|
||||
|
@ -45,7 +46,7 @@ impl PluginGroup for DefaultPlugins {
|
|||
.add(chunks::ChunksPlugin)
|
||||
.add(tick_end::TickEndPlugin)
|
||||
.add(brand::BrandPlugin)
|
||||
.add(crate::client::TickBroadcastPlugin)
|
||||
.add(tick_broadcast::TickBroadcastPlugin)
|
||||
.add(pong::PongPlugin)
|
||||
.add(connection::ConnectionPlugin)
|
||||
.add(login::LoginPlugin);
|
||||
|
|
49
azalea-client/src/plugins/tick_broadcast.rs
Normal file
49
azalea-client/src/plugins/tick_broadcast.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use azalea_core::tick::GameTick;
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_ecs::prelude::*;
|
||||
use derive_more::Deref;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
/// A resource that contains a [`broadcast::Sender`] that will be sent every
|
||||
/// Minecraft tick.
|
||||
///
|
||||
/// This is useful for running code every schedule from async user code.
|
||||
///
|
||||
/// ```
|
||||
/// use azalea_client::tick_broadcast::TickBroadcast;
|
||||
/// # async fn example(client: azalea_client::Client) {
|
||||
/// let mut receiver = {
|
||||
/// let ecs = client.ecs.lock();
|
||||
/// let tick_broadcast = ecs.resource::<TickBroadcast>();
|
||||
/// tick_broadcast.subscribe()
|
||||
/// };
|
||||
/// while receiver.recv().await.is_ok() {
|
||||
/// // do something
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Resource, Deref)]
|
||||
pub struct TickBroadcast(broadcast::Sender<()>);
|
||||
/// A resource that contains a [`broadcast::Sender`] that will be sent every
|
||||
/// Azalea ECS Update.
|
||||
///
|
||||
/// Also see [`TickBroadcast`].
|
||||
#[derive(Resource, Deref)]
|
||||
pub struct UpdateBroadcast(broadcast::Sender<()>);
|
||||
|
||||
pub fn send_tick_broadcast(tick_broadcast: ResMut<TickBroadcast>) {
|
||||
let _ = tick_broadcast.0.send(());
|
||||
}
|
||||
pub fn send_update_broadcast(update_broadcast: ResMut<UpdateBroadcast>) {
|
||||
let _ = update_broadcast.0.send(());
|
||||
}
|
||||
/// A plugin that makes the [`RanScheduleBroadcast`] resource available.
|
||||
pub struct TickBroadcastPlugin;
|
||||
impl Plugin for TickBroadcastPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(TickBroadcast(broadcast::channel(1).0))
|
||||
.insert_resource(UpdateBroadcast(broadcast::channel(1).0))
|
||||
.add_systems(GameTick, send_tick_broadcast)
|
||||
.add_systems(Update, send_update_broadcast);
|
||||
}
|
||||
}
|
|
@ -7,15 +7,15 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.12.0" }
|
||||
bevy_ecs = { workspace = true, optional = true }
|
||||
nohash-hasher.workspace = true
|
||||
num-traits.workspace = true
|
||||
serde = { workspace = true, optional = true }
|
||||
simdnbt.workspace = true
|
||||
tracing.workspace = true
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.11.0" }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.12.0" }
|
||||
indexmap.workspace = true
|
||||
|
||||
[features]
|
||||
|
|
|
@ -11,7 +11,7 @@ criterion.workspace = true
|
|||
|
||||
[dependencies]
|
||||
aes.workspace = true
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
cfb8.workspace = true
|
||||
num-bigint.workspace = true
|
||||
rand = { workspace = true, features = ["getrandom"] }
|
||||
|
|
|
@ -7,15 +7,15 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
azalea-block = { path = "../azalea-block", version = "0.11.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.11.0", features = [
|
||||
azalea-block = { path = "../azalea-block", version = "0.12.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.12.0", features = [
|
||||
"azalea-buf",
|
||||
] }
|
||||
azalea-core = { path = "../azalea-core", version = "0.11.0" }
|
||||
azalea-inventory = { path = "../azalea-inventory", version = "0.11.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
|
||||
azalea-world = { path = "../azalea-world", version = "0.11.0" }
|
||||
azalea-core = { path = "../azalea-core", version = "0.12.0" }
|
||||
azalea-inventory = { path = "../azalea-inventory", version = "0.12.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.12.0" }
|
||||
azalea-world = { path = "../azalea-world", version = "0.12.0" }
|
||||
bevy_app.workspace = true
|
||||
bevy_ecs.workspace = true
|
||||
derive_more.workspace = true
|
||||
|
|
|
@ -7,13 +7,13 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.11.0", features = [
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.12.0", features = [
|
||||
"azalea-buf",
|
||||
] }
|
||||
azalea-core = { path = "../azalea-core", version = "0.11.0" }
|
||||
azalea-inventory-macros = { path = "./azalea-inventory-macros", version = "0.11.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
|
||||
azalea-core = { path = "../azalea-core", version = "0.12.0" }
|
||||
azalea-inventory-macros = { path = "./azalea-inventory-macros", version = "0.12.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.12.0" }
|
||||
indexmap.workspace = true
|
||||
|
||||
simdnbt.workspace = true
|
||||
|
|
|
@ -11,12 +11,12 @@ bevy_time.workspace = true
|
|||
uuid.workspace = true
|
||||
|
||||
[dependencies]
|
||||
azalea-block = { path = "../azalea-block", version = "0.11.0" }
|
||||
azalea-core = { path = "../azalea-core", version = "0.11.0" }
|
||||
azalea-entity = { version = "0.11.0", path = "../azalea-entity" }
|
||||
azalea-inventory = { version = "0.11.0", path = "../azalea-inventory" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
|
||||
azalea-world = { path = "../azalea-world", version = "0.11.0" }
|
||||
azalea-block = { path = "../azalea-block", version = "0.12.0" }
|
||||
azalea-core = { path = "../azalea-core", version = "0.12.0" }
|
||||
azalea-entity = { version = "0.12.0", path = "../azalea-entity" }
|
||||
azalea-inventory = { version = "0.12.0", path = "../azalea-inventory" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.12.0" }
|
||||
azalea-world = { path = "../azalea-world", version = "0.12.0" }
|
||||
bevy_app.workspace = true
|
||||
bevy_ecs.workspace = true
|
||||
tracing.workspace = true
|
||||
|
|
|
@ -4910,19 +4910,21 @@ static SHAPE760: LazyLock<VoxelShape> =
|
|||
impl BlockWithShape for BlockState {
|
||||
fn collision_shape(&self) -> &'static VoxelShape {
|
||||
COLLISION_SHAPES_MAP
|
||||
.get(self.id as usize)
|
||||
.get(self.id() as usize)
|
||||
.unwrap_or(&&SHAPE1)
|
||||
}
|
||||
fn outline_shape(&self) -> &'static VoxelShape {
|
||||
OUTLINE_SHAPES_MAP.get(self.id as usize).unwrap_or(&&SHAPE1)
|
||||
OUTLINE_SHAPES_MAP
|
||||
.get(self.id() as usize)
|
||||
.unwrap_or(&&SHAPE1)
|
||||
}
|
||||
|
||||
fn is_collision_shape_empty(&self) -> bool {
|
||||
matches!(self.id, 0|29..=42|45..=84|86..=117|1987..=2034|2047..=2050|2054..=2056|2109..=2136|2401..=2918|3042..=4337|4342..=4349|4366..=4589|4622..=4685|4758..=4777|4858..=4913|4922..=5385|5450..=5705|5802..=5827|5892..=5905|5908..=5911|5916..=5950|5978..=5993|6037..=6041|6043..=6044|7056..=7239|7368..=7369|7372..=7373|7376..=7377|7380..=7381|7384..=7385|7388..=7389|7392..=7393|7396..=7397|8169..=8172|8190|8305..=8448|8709|8712|9033|9036|9380..=9563|9588..=9635|9952..=9983|10129..=10152|11256..=11287|11636..=11967|12205..=12206|12209..=12210|12213..=12214|12217..=12218|12221..=12222|12225..=12226|12229..=12230|12233..=12234|12237..=12238|12241..=12242|12245..=12246|12249..=12250|12253..=12254|12257..=12258|12261..=12262|12265..=12266|12269..=12270|12273..=12274|12277..=12278|12281..=12282|12285..=12286|12289..=12290|12293..=12294|12297..=12298|12301..=12302|12305..=12306|12309..=12310|12313..=12314|12317..=12318|12321..=12322|12325..=12326|12329..=12330|12333..=12334|12337..=12338|12341..=12342|12345..=12346|12349..=12350|12353..=12354|12357..=12358|12361..=12362|12365..=12366|12369..=12370|12373..=12374|12377..=12378|12381..=12382|12385..=12386|12389..=12390|12393..=12394|12429..=12430|12433..=12434|12437..=12438|12441..=12442|12445..=12446|12449..=12450|12453..=12454|12457..=12458|12461..=12462|12465..=12466|12469..=12470|12473..=12474|12477..=12478|12481..=12482|12485..=12486|12489..=12490|13518..=13519|13522|13524|13526|13528|13530..=13535|13537|13572|13783..=13809|13836..=13955|13967|13981..=13984|15189|15192|15513|15516|15837|15840|16161|16164|16485|16488|16809|16812|17133|17136|17457|17460|17781|17784|18105|18108|18429|18432|18753|18756|19077|19080|19598..=19601|19615|19617..=19618|19632|19634..=19688|19703..=19706|19899..=19900|19903..=19904|19907..=19908|19911..=19912|19915..=19916|19919..=19920|19923..=19924|19927..=19928|19931..=19932|19935..=19936|19939..=19940|19943..=19944|19947..=19948|19951..=19952|19955..=19956|19959..=19960|20123..=20170|20299..=20378|20575|20578|20995|20998|21400..=21425|21432|21435|22202|22205|22613|22616|23025|23028|23346|23828..=23955|25797..=25851|25855..=25870|25910..=25911|25918..=25919|25926..=25927|25934..=25961|26060|26063|26471|26474|26882|26885|27293|27296|27632)
|
||||
matches!(self.id(), 0|29..=42|45..=84|86..=117|1987..=2034|2047..=2050|2054..=2056|2109..=2136|2401..=2918|3042..=4337|4342..=4349|4366..=4589|4622..=4685|4758..=4777|4858..=4913|4922..=5385|5450..=5705|5802..=5827|5892..=5905|5908..=5911|5916..=5950|5978..=5993|6037..=6041|6043..=6044|7056..=7239|7368..=7369|7372..=7373|7376..=7377|7380..=7381|7384..=7385|7388..=7389|7392..=7393|7396..=7397|8169..=8172|8190|8305..=8448|8709|8712|9033|9036|9380..=9563|9588..=9635|9952..=9983|10129..=10152|11256..=11287|11636..=11967|12205..=12206|12209..=12210|12213..=12214|12217..=12218|12221..=12222|12225..=12226|12229..=12230|12233..=12234|12237..=12238|12241..=12242|12245..=12246|12249..=12250|12253..=12254|12257..=12258|12261..=12262|12265..=12266|12269..=12270|12273..=12274|12277..=12278|12281..=12282|12285..=12286|12289..=12290|12293..=12294|12297..=12298|12301..=12302|12305..=12306|12309..=12310|12313..=12314|12317..=12318|12321..=12322|12325..=12326|12329..=12330|12333..=12334|12337..=12338|12341..=12342|12345..=12346|12349..=12350|12353..=12354|12357..=12358|12361..=12362|12365..=12366|12369..=12370|12373..=12374|12377..=12378|12381..=12382|12385..=12386|12389..=12390|12393..=12394|12429..=12430|12433..=12434|12437..=12438|12441..=12442|12445..=12446|12449..=12450|12453..=12454|12457..=12458|12461..=12462|12465..=12466|12469..=12470|12473..=12474|12477..=12478|12481..=12482|12485..=12486|12489..=12490|13518..=13519|13522|13524|13526|13528|13530..=13535|13537|13572|13783..=13809|13836..=13955|13967|13981..=13984|15189|15192|15513|15516|15837|15840|16161|16164|16485|16488|16809|16812|17133|17136|17457|17460|17781|17784|18105|18108|18429|18432|18753|18756|19077|19080|19598..=19601|19615|19617..=19618|19632|19634..=19688|19703..=19706|19899..=19900|19903..=19904|19907..=19908|19911..=19912|19915..=19916|19919..=19920|19923..=19924|19927..=19928|19931..=19932|19935..=19936|19939..=19940|19943..=19944|19947..=19948|19951..=19952|19955..=19956|19959..=19960|20123..=20170|20299..=20378|20575|20578|20995|20998|21400..=21425|21432|21435|22202|22205|22613|22616|23025|23028|23346|23828..=23955|25797..=25851|25855..=25870|25910..=25911|25918..=25919|25926..=25927|25934..=25961|26060|26063|26471|26474|26882|26885|27293|27296|27632)
|
||||
}
|
||||
|
||||
fn is_collision_shape_full(&self) -> bool {
|
||||
matches!(self.id, 1..=21|26..=28|85|118..=156|160..=188|192..=245|249..=447|476..=1730|2041..=2046|2063..=2068|2093..=2108|2137..=2400|2919|4338..=4341|4358..=4365|5912..=5915|5958..=5959|5977|5994..=5995|6028|6030..=6036|6042|6045..=6052|6124..=6139|6780..=6983|7054..=7055|7640..=7641|8056|8199|8201..=8202|8295..=8296|8449|8690..=8702|10032..=10033|10044..=10048|10153..=10180|11253..=11255|11352..=11354|11599..=11600|11605..=11606|11611..=11616|11633..=11635|11968..=11970|12055..=12056|12061..=12062|12067..=12068|12073..=12074|12079..=12080|12085..=12086|12091..=12092|12103..=12104|12109..=12110|12115..=12116|12121..=12122|12127..=12128|12133..=12134|12139..=12140|12145..=12146|12151..=12152|12157..=12158|12163..=12164|12169..=12170|12175..=12176|12181..=12182|12187..=12188|12193..=12194|12199..=12204|13427..=13436|13517|13538..=13571|13573..=13782|13810|13826..=13835|13964|15109..=15110|15115..=15116|15121..=15122|15127..=15128|15133..=15134|15139..=15140|15145..=15146|15151..=15152|15157..=15158|15163..=15164|15169..=15170|15175..=15176|15181..=15182|19427..=19460|19489|19602..=19614|19616|19619..=19631|19633|19689..=19690|19695..=19696|19701..=19702|20379..=20394|20409..=20472|20474..=20482|20487..=20488|20897..=20902|20907..=20908|21313|21398..=21399|21750..=21752|22059..=22060|22109|22114..=22115|22520|22525..=22526|22931..=22932|22937..=22938|23343..=23345|23827|23956..=23957|23966..=23983|24308..=24309|24314..=24315|24320..=24321|24326..=24335|24660..=24661|24666..=24667|24672..=24673|24678..=24679|25704..=25751|25796|25903|25962|25964..=25967|26052..=26053|26378|26463..=26464|26789|26874..=26875|27200|27285..=27286|27611..=27620|27623..=27631|27633|27650..=27703)
|
||||
matches!(self.id(), 1..=21|26..=28|85|118..=156|160..=188|192..=245|249..=447|476..=1730|2041..=2046|2063..=2068|2093..=2108|2137..=2400|2919|4338..=4341|4358..=4365|5912..=5915|5958..=5959|5977|5994..=5995|6028|6030..=6036|6042|6045..=6052|6124..=6139|6780..=6983|7054..=7055|7640..=7641|8056|8199|8201..=8202|8295..=8296|8449|8690..=8702|10032..=10033|10044..=10048|10153..=10180|11253..=11255|11352..=11354|11599..=11600|11605..=11606|11611..=11616|11633..=11635|11968..=11970|12055..=12056|12061..=12062|12067..=12068|12073..=12074|12079..=12080|12085..=12086|12091..=12092|12103..=12104|12109..=12110|12115..=12116|12121..=12122|12127..=12128|12133..=12134|12139..=12140|12145..=12146|12151..=12152|12157..=12158|12163..=12164|12169..=12170|12175..=12176|12181..=12182|12187..=12188|12193..=12194|12199..=12204|13427..=13436|13517|13538..=13571|13573..=13782|13810|13826..=13835|13964|15109..=15110|15115..=15116|15121..=15122|15127..=15128|15133..=15134|15139..=15140|15145..=15146|15151..=15152|15157..=15158|15163..=15164|15169..=15170|15175..=15176|15181..=15182|19427..=19460|19489|19602..=19614|19616|19619..=19631|19633|19689..=19690|19695..=19696|19701..=19702|20379..=20394|20409..=20472|20474..=20482|20487..=20488|20897..=20902|20907..=20908|21313|21398..=21399|21750..=21752|22059..=22060|22109|22114..=22115|22520|22525..=22526|22931..=22932|22937..=22938|23343..=23345|23827|23956..=23957|23966..=23983|24308..=24309|24314..=24315|24320..=24321|24326..=24335|24660..=24661|24666..=24667|24672..=24673|24678..=24679|25704..=25751|25796|25903|25962|25964..=25967|26052..=26053|26378|26463..=26464|26789|26874..=26875|27200|27285..=27286|27611..=27620|27623..=27631|27633|27650..=27703)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,25 +13,25 @@ tracing-subscriber.workspace = true
|
|||
|
||||
[dependencies]
|
||||
async-recursion.workspace = true
|
||||
azalea-auth = { path = "../azalea-auth", version = "0.11.0" }
|
||||
azalea-block = { path = "../azalea-block", version = "0.11.0", default-features = false }
|
||||
azalea-brigadier = { path = "../azalea-brigadier", version = "0.11.0", features = [
|
||||
azalea-auth = { path = "../azalea-auth", version = "0.12.0" }
|
||||
azalea-block = { path = "../azalea-block", version = "0.12.0", default-features = false }
|
||||
azalea-brigadier = { path = "../azalea-brigadier", version = "0.12.0", features = [
|
||||
"azalea-buf",
|
||||
] }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.11.0", features = [
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
azalea-chat = { path = "../azalea-chat", version = "0.12.0", features = [
|
||||
"numbers",
|
||||
"azalea-buf",
|
||||
] }
|
||||
azalea-core = { path = "../azalea-core", version = "0.11.0", optional = true, features = [
|
||||
azalea-core = { path = "../azalea-core", version = "0.12.0", optional = true, features = [
|
||||
"serde",
|
||||
] }
|
||||
azalea-crypto = { path = "../azalea-crypto", version = "0.11.0" }
|
||||
azalea-entity = { path = "../azalea-entity", version = "0.11.0" }
|
||||
azalea-inventory = { path = "../azalea-inventory", version = "0.11.0" }
|
||||
azalea-protocol-macros = { path = "./azalea-protocol-macros", version = "0.11.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
|
||||
azalea-world = { path = "../azalea-world", version = "0.11.0" }
|
||||
azalea-crypto = { path = "../azalea-crypto", version = "0.12.0" }
|
||||
azalea-entity = { path = "../azalea-entity", version = "0.12.0" }
|
||||
azalea-inventory = { path = "../azalea-inventory", version = "0.12.0" }
|
||||
azalea-protocol-macros = { path = "./azalea-protocol-macros", version = "0.12.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.12.0" }
|
||||
azalea-world = { path = "../azalea-world", version = "0.12.0" }
|
||||
bevy_ecs.workspace = true
|
||||
# byteorder.workspace = true
|
||||
flate2.workspace = true
|
||||
|
|
|
@ -35,7 +35,7 @@ impl AzaleaRead for BlockStateWithPosition {
|
|||
|
||||
impl AzaleaWrite for BlockStateWithPosition {
|
||||
fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
let data = ((self.state.id as u64) << 12)
|
||||
let data = ((self.state.id() as u64) << 12)
|
||||
| ((u64::from(self.pos.x) << 8) | (u64::from(self.pos.z) << 4) | u64::from(self.pos.y));
|
||||
u64::azalea_write_var(&data, buf)?;
|
||||
Ok(())
|
||||
|
|
|
@ -158,7 +158,7 @@ pub fn deserialize_packet<P: ProtocolPacket + Debug>(
|
|||
// this is always true in multiplayer, false in singleplayer
|
||||
static VALIDATE_DECOMPRESSED: bool = true;
|
||||
|
||||
pub static MAXIMUM_UNCOMPRESSED_LENGTH: u32 = 2_097_152;
|
||||
pub static MAXIMUM_UNCOMPRESSED_LENGTH: u32 = 8_388_608;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DecompressionError {
|
||||
|
|
|
@ -7,8 +7,8 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-registry-macros = { path = "./azalea-registry-macros", version = "0.11.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
azalea-registry-macros = { path = "./azalea-registry-macros", version = "0.12.0" }
|
||||
serde = { workspace = true, optional = true, features = ["derive"] }
|
||||
simdnbt.workspace = true
|
||||
|
||||
|
|
|
@ -11,10 +11,12 @@ azalea-client = { path = "../azalea-client" }
|
|||
criterion = "0.5.1"
|
||||
|
||||
[dependencies]
|
||||
azalea-block = { path = "../azalea-block", default-features = false, version = "0.11.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.11.0" }
|
||||
azalea-core = { path = "../azalea-core", version = "0.11.0", features = ["bevy_ecs"] }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
|
||||
azalea-block = { path = "../azalea-block", default-features = false, version = "0.12.0" }
|
||||
azalea-buf = { path = "../azalea-buf", version = "0.12.0" }
|
||||
azalea-core = { path = "../azalea-core", version = "0.12.0", features = [
|
||||
"bevy_ecs",
|
||||
] }
|
||||
azalea-registry = { path = "../azalea-registry", version = "0.12.0" }
|
||||
bevy_ecs.workspace = true
|
||||
derive_more = { workspace = true, features = ["deref", "deref_mut"] }
|
||||
nohash-hasher.workspace = true
|
||||
|
|
|
@ -476,25 +476,18 @@ impl AzaleaWrite for Section {
|
|||
|
||||
impl Section {
|
||||
pub fn get(&self, pos: ChunkSectionBlockPos) -> BlockState {
|
||||
// TODO: use the unsafe method and do the check earlier
|
||||
let state = self
|
||||
.states
|
||||
.get(pos.x as usize, pos.y as usize, pos.z as usize);
|
||||
// if there's an unknown block assume it's air
|
||||
BlockState::try_from(state).unwrap_or(BlockState::AIR)
|
||||
self.states
|
||||
.get(pos.x as usize, pos.y as usize, pos.z as usize)
|
||||
}
|
||||
|
||||
pub fn get_and_set(&mut self, pos: ChunkSectionBlockPos, state: BlockState) -> BlockState {
|
||||
let previous_state =
|
||||
self.states
|
||||
.get_and_set(pos.x as usize, pos.y as usize, pos.z as usize, state.id);
|
||||
// if there's an unknown block assume it's air
|
||||
BlockState::try_from(previous_state).unwrap_or(BlockState::AIR)
|
||||
self.states
|
||||
.get_and_set(pos.x as usize, pos.y as usize, pos.z as usize, state)
|
||||
}
|
||||
|
||||
pub fn set(&mut self, pos: ChunkSectionBlockPos, state: BlockState) {
|
||||
self.states
|
||||
.set(pos.x as usize, pos.y as usize, pos.z as usize, state.id);
|
||||
.set(pos.x as usize, pos.y as usize, pos.z as usize, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
use azalea_block::{BlockStates, block_state::BlockState};
|
||||
use azalea_block::BlockStates;
|
||||
use azalea_core::position::{BlockPos, ChunkPos};
|
||||
|
||||
use crate::{ChunkStorage, Instance, iterators::ChunkIterator, palette::Palette};
|
||||
|
||||
fn palette_maybe_has_block(palette: &Palette, block_states: &BlockStates) -> bool {
|
||||
match &palette {
|
||||
Palette::SingleValue(id) => block_states.contains(&BlockState { id: *id }),
|
||||
Palette::Linear(ids) => ids
|
||||
.iter()
|
||||
.any(|&id| block_states.contains(&BlockState { id })),
|
||||
Palette::Hashmap(ids) => ids
|
||||
.iter()
|
||||
.any(|&id| block_states.contains(&BlockState { id })),
|
||||
Palette::SingleValue(id) => block_states.contains(id),
|
||||
Palette::Linear(ids) => ids.iter().any(|id| block_states.contains(id)),
|
||||
Palette::Hashmap(ids) => ids.iter().any(|id| block_states.contains(id)),
|
||||
Palette::Global => true,
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +58,6 @@ impl Instance {
|
|||
|
||||
for i in 0..4096 {
|
||||
let block_state = section.states.get_at_index(i);
|
||||
let block_state = BlockState { id: block_state };
|
||||
|
||||
if block_states.contains(&block_state) {
|
||||
let (section_x, section_y, section_z) = section.states.coords_from_index(i);
|
||||
|
@ -116,7 +111,10 @@ impl Instance {
|
|||
/// Find all the coordinates of a block in the world.
|
||||
///
|
||||
/// This returns an iterator that yields the [`BlockPos`]s of blocks that
|
||||
/// are in the given block states. It's sorted by `x+y+z`.
|
||||
/// are in the given block states.
|
||||
///
|
||||
/// Note that this is sorted by `x+y+z` and not `x^2+y^2+z^2` for
|
||||
/// optimization purposes.
|
||||
pub fn find_blocks<'a>(
|
||||
&'a self,
|
||||
nearest_to: impl Into<BlockPos>,
|
||||
|
@ -187,7 +185,6 @@ impl Iterator for FindBlocks<'_> {
|
|||
|
||||
for i in 0..4096 {
|
||||
let block_state = section.states.get_at_index(i);
|
||||
let block_state = BlockState { id: block_state };
|
||||
|
||||
if self.block_states.contains(&block_state) {
|
||||
let (section_x, section_y, section_z) = section.states.coords_from_index(i);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::io::{Cursor, Write};
|
||||
|
||||
use azalea_block::block_state::BlockStateIntegerRepr;
|
||||
use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
|
||||
use azalea_block::BlockState;
|
||||
use azalea_buf::{AzaleaRead, AzaleaWrite, BufReadError};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::BitStorage;
|
||||
|
@ -28,7 +28,7 @@ pub struct PalettedContainer {
|
|||
|
||||
impl PalettedContainer {
|
||||
pub fn new(container_type: PalettedContainerKind) -> Self {
|
||||
let palette = Palette::SingleValue(0);
|
||||
let palette = Palette::SingleValue(BlockState::AIR);
|
||||
let size = container_type.size();
|
||||
let storage = BitStorage::new(0, size, Some(Box::new([]))).unwrap();
|
||||
|
||||
|
@ -105,7 +105,7 @@ impl PalettedContainer {
|
|||
/// This function panics if the index is greater than or equal to the number
|
||||
/// of things in the storage. (So for block states, it must be less than
|
||||
/// 4096).
|
||||
pub fn get_at_index(&self, index: usize) -> BlockStateIntegerRepr {
|
||||
pub fn get_at_index(&self, index: usize) -> BlockState {
|
||||
// first get the palette id
|
||||
let paletted_value = self.storage.get(index);
|
||||
// and then get the value from that id
|
||||
|
@ -113,35 +113,39 @@ impl PalettedContainer {
|
|||
}
|
||||
|
||||
/// Returns the value at the given coordinates.
|
||||
pub fn get(&self, x: usize, y: usize, z: usize) -> BlockStateIntegerRepr {
|
||||
pub fn get(&self, x: usize, y: usize, z: usize) -> BlockState {
|
||||
// let paletted_value = self.storage.get(self.get_index(x, y, z));
|
||||
// self.palette.value_for(paletted_value as usize)
|
||||
self.get_at_index(self.index_from_coords(x, y, z))
|
||||
}
|
||||
|
||||
/// Sets the id at the given coordinates and return the previous id
|
||||
pub fn get_and_set(
|
||||
&mut self,
|
||||
x: usize,
|
||||
y: usize,
|
||||
z: usize,
|
||||
value: BlockStateIntegerRepr,
|
||||
) -> BlockStateIntegerRepr {
|
||||
pub fn get_and_set(&mut self, x: usize, y: usize, z: usize, value: BlockState) -> BlockState {
|
||||
let paletted_value = self.id_for(value);
|
||||
self.storage
|
||||
.get_and_set(self.index_from_coords(x, y, z), paletted_value as u64)
|
||||
as BlockStateIntegerRepr
|
||||
let block_state_id = self
|
||||
.storage
|
||||
.get_and_set(self.index_from_coords(x, y, z), paletted_value as u64);
|
||||
// error in debug mode
|
||||
#[cfg(debug_assertions)]
|
||||
if block_state_id > BlockState::MAX_STATE.into() {
|
||||
warn!(
|
||||
"Old block state from get_and_set {block_state_id} was greater than max state {}",
|
||||
BlockState::MAX_STATE
|
||||
);
|
||||
}
|
||||
|
||||
BlockState::try_from(block_state_id as u32).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Sets the id at the given index and return the previous id. You probably
|
||||
/// want `.set` instead.
|
||||
pub fn set_at_index(&mut self, index: usize, value: BlockStateIntegerRepr) {
|
||||
pub fn set_at_index(&mut self, index: usize, value: BlockState) {
|
||||
let paletted_value = self.id_for(value);
|
||||
self.storage.set(index, paletted_value as u64);
|
||||
}
|
||||
|
||||
/// Sets the id at the given coordinates and return the previous id
|
||||
pub fn set(&mut self, x: usize, y: usize, z: usize, value: BlockStateIntegerRepr) {
|
||||
pub fn set(&mut self, x: usize, y: usize, z: usize, value: BlockState) {
|
||||
self.set_at_index(self.index_from_coords(x, y, z), value);
|
||||
}
|
||||
|
||||
|
@ -170,7 +174,7 @@ impl PalettedContainer {
|
|||
}
|
||||
}
|
||||
|
||||
fn on_resize(&mut self, bits_per_entry: u8, value: BlockStateIntegerRepr) -> usize {
|
||||
fn on_resize(&mut self, bits_per_entry: u8, value: BlockState) -> usize {
|
||||
// in vanilla this is always true, but it's sometimes false in purpur servers
|
||||
// assert!(bits_per_entry <= 5, "bits_per_entry must be <= 5");
|
||||
let mut new_data = self.create_or_reuse_data(bits_per_entry);
|
||||
|
@ -187,7 +191,7 @@ impl PalettedContainer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn id_for(&mut self, value: BlockStateIntegerRepr) -> usize {
|
||||
pub fn id_for(&mut self, value: BlockState) -> usize {
|
||||
match &mut self.palette {
|
||||
Palette::SingleValue(v) => {
|
||||
if *v != value {
|
||||
|
@ -209,7 +213,8 @@ impl PalettedContainer {
|
|||
}
|
||||
}
|
||||
Palette::Hashmap(palette) => {
|
||||
// TODO? vanilla keeps this in memory as a hashmap, but also i don't care
|
||||
// TODO? vanilla keeps this in memory as a hashmap, but it should be benchmarked
|
||||
// before changing it
|
||||
if let Some(index) = palette.iter().position(|v| *v == value) {
|
||||
return index;
|
||||
}
|
||||
|
@ -221,7 +226,7 @@ impl PalettedContainer {
|
|||
self.on_resize(self.bits_per_entry + 1, value)
|
||||
}
|
||||
}
|
||||
Palette::Global => value as usize,
|
||||
Palette::Global => value.id() as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -247,21 +252,21 @@ pub enum PaletteKind {
|
|||
#[derive(Clone, Debug)]
|
||||
pub enum Palette {
|
||||
/// ID of the corresponding entry in its global palette
|
||||
SingleValue(BlockStateIntegerRepr),
|
||||
SingleValue(BlockState),
|
||||
// in vanilla this keeps a `size` field that might be less than the length, but i'm not sure
|
||||
// it's actually needed?
|
||||
Linear(Vec<BlockStateIntegerRepr>),
|
||||
Hashmap(Vec<BlockStateIntegerRepr>),
|
||||
Linear(Vec<BlockState>),
|
||||
Hashmap(Vec<BlockState>),
|
||||
Global,
|
||||
}
|
||||
|
||||
impl Palette {
|
||||
pub fn value_for(&self, id: usize) -> BlockStateIntegerRepr {
|
||||
pub fn value_for(&self, id: usize) -> BlockState {
|
||||
match self {
|
||||
Palette::SingleValue(v) => *v,
|
||||
Palette::Linear(v) => v[id],
|
||||
Palette::Linear(v) => v.get(id).copied().unwrap_or_default(),
|
||||
Palette::Hashmap(v) => v.get(id).copied().unwrap_or_default(),
|
||||
Palette::Global => id as BlockStateIntegerRepr,
|
||||
Palette::Global => BlockState::try_from(id as u32).unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -270,13 +275,13 @@ impl AzaleaWrite for Palette {
|
|||
fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
match self {
|
||||
Palette::SingleValue(value) => {
|
||||
value.azalea_write_var(buf)?;
|
||||
value.azalea_write(buf)?;
|
||||
}
|
||||
Palette::Linear(values) => {
|
||||
values.azalea_write_var(buf)?;
|
||||
values.azalea_write(buf)?;
|
||||
}
|
||||
Palette::Hashmap(values) => {
|
||||
values.azalea_write_var(buf)?;
|
||||
values.azalea_write(buf)?;
|
||||
}
|
||||
Palette::Global => {}
|
||||
}
|
||||
|
@ -305,22 +310,16 @@ impl PaletteKind {
|
|||
Ok(match self {
|
||||
// since they're read as varints it's actually fine to just use BlockStateIntegerRepr
|
||||
// instead of the correct type (u32)
|
||||
PaletteKind::SingleValue => {
|
||||
Palette::SingleValue(BlockStateIntegerRepr::azalea_read_var(buf)?)
|
||||
}
|
||||
PaletteKind::Linear => {
|
||||
Palette::Linear(Vec::<BlockStateIntegerRepr>::azalea_read_var(buf)?)
|
||||
}
|
||||
PaletteKind::Hashmap => {
|
||||
Palette::Hashmap(Vec::<BlockStateIntegerRepr>::azalea_read_var(buf)?)
|
||||
}
|
||||
PaletteKind::SingleValue => Palette::SingleValue(BlockState::azalea_read(buf)?),
|
||||
PaletteKind::Linear => Palette::Linear(Vec::<BlockState>::azalea_read(buf)?),
|
||||
PaletteKind::Hashmap => Palette::Hashmap(Vec::<BlockState>::azalea_read(buf)?),
|
||||
PaletteKind::Global => Palette::Global,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_empty_palette(&self) -> Palette {
|
||||
match self {
|
||||
PaletteKind::SingleValue => Palette::SingleValue(0),
|
||||
PaletteKind::SingleValue => Palette::SingleValue(BlockState::AIR),
|
||||
PaletteKind::Linear => Palette::Linear(Vec::new()),
|
||||
PaletteKind::Hashmap => Palette::Hashmap(Vec::new()),
|
||||
PaletteKind::Global => Palette::Global,
|
||||
|
@ -361,13 +360,14 @@ mod tests {
|
|||
let mut palette_container = PalettedContainer::new(PalettedContainerKind::BlockStates);
|
||||
|
||||
assert_eq!(palette_container.bits_per_entry, 0);
|
||||
assert_eq!(palette_container.get_at_index(0), 0);
|
||||
assert_eq!(palette_container.get_at_index(0), BlockState::AIR);
|
||||
assert_eq!(
|
||||
PaletteKind::from(&palette_container.palette),
|
||||
PaletteKind::SingleValue
|
||||
);
|
||||
palette_container.set_at_index(0, 1);
|
||||
assert_eq!(palette_container.get_at_index(0), 1);
|
||||
let block_state_1 = BlockState::try_from(1_u32).unwrap();
|
||||
palette_container.set_at_index(0, block_state_1);
|
||||
assert_eq!(palette_container.get_at_index(0), block_state_1);
|
||||
assert_eq!(
|
||||
PaletteKind::from(&palette_container.palette),
|
||||
PaletteKind::Linear
|
||||
|
@ -378,34 +378,38 @@ mod tests {
|
|||
fn test_resize_0_bits_to_5() {
|
||||
let mut palette_container = PalettedContainer::new(PalettedContainerKind::BlockStates);
|
||||
|
||||
palette_container.set_at_index(0, 0); // 0 bits
|
||||
let set = |pc: &mut PalettedContainer, i, v: u32| {
|
||||
pc.set_at_index(i, BlockState::try_from(v).unwrap());
|
||||
};
|
||||
|
||||
set(&mut palette_container, 0, 0); // 0 bits
|
||||
assert_eq!(palette_container.bits_per_entry, 0);
|
||||
|
||||
palette_container.set_at_index(1, 1); // 1 bit
|
||||
set(&mut palette_container, 1, 1); // 1 bit
|
||||
assert_eq!(palette_container.bits_per_entry, 1);
|
||||
|
||||
palette_container.set_at_index(2, 2); // 2 bits
|
||||
set(&mut palette_container, 2, 2); // 2 bits
|
||||
assert_eq!(palette_container.bits_per_entry, 2);
|
||||
palette_container.set_at_index(3, 3);
|
||||
set(&mut palette_container, 3, 3);
|
||||
|
||||
palette_container.set_at_index(4, 4); // 3 bits
|
||||
set(&mut palette_container, 4, 4); // 3 bits
|
||||
assert_eq!(palette_container.bits_per_entry, 3);
|
||||
palette_container.set_at_index(5, 5);
|
||||
palette_container.set_at_index(6, 6);
|
||||
palette_container.set_at_index(7, 7);
|
||||
set(&mut palette_container, 5, 5);
|
||||
set(&mut palette_container, 6, 6);
|
||||
set(&mut palette_container, 7, 7);
|
||||
|
||||
palette_container.set_at_index(8, 8); // 4 bits
|
||||
set(&mut palette_container, 8, 8); // 4 bits
|
||||
assert_eq!(palette_container.bits_per_entry, 4);
|
||||
palette_container.set_at_index(9, 9);
|
||||
palette_container.set_at_index(10, 10);
|
||||
palette_container.set_at_index(11, 11);
|
||||
palette_container.set_at_index(12, 12);
|
||||
palette_container.set_at_index(13, 13);
|
||||
palette_container.set_at_index(14, 14);
|
||||
palette_container.set_at_index(15, 15);
|
||||
set(&mut palette_container, 9, 9);
|
||||
set(&mut palette_container, 10, 10);
|
||||
set(&mut palette_container, 11, 11);
|
||||
set(&mut palette_container, 12, 12);
|
||||
set(&mut palette_container, 13, 13);
|
||||
set(&mut palette_container, 14, 14);
|
||||
set(&mut palette_container, 15, 15);
|
||||
assert_eq!(palette_container.bits_per_entry, 4);
|
||||
|
||||
palette_container.set_at_index(16, 16); // 5 bits
|
||||
set(&mut palette_container, 16, 16); // 5 bits
|
||||
assert_eq!(palette_container.bits_per_entry, 5);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,19 +13,19 @@ pre-release-replacements = [
|
|||
|
||||
[dependencies]
|
||||
#async-trait.workspace = true
|
||||
azalea-auth = { version = "0.11.0", path = "../azalea-auth" }
|
||||
azalea-block = { version = "0.11.0", path = "../azalea-block" }
|
||||
azalea-brigadier = { version = "0.11.0", path = "../azalea-brigadier" }
|
||||
azalea-buf = { version = "0.11.0", path = "../azalea-buf" }
|
||||
azalea-chat = { version = "0.11.0", path = "../azalea-chat" }
|
||||
azalea-client = { version = "0.11.0", path = "../azalea-client", default-features = false }
|
||||
azalea-core = { version = "0.11.0", path = "../azalea-core" }
|
||||
azalea-entity = { version = "0.11.0", path = "../azalea-entity" }
|
||||
azalea-inventory = { version = "0.11.0", path = "../azalea-inventory" }
|
||||
azalea-physics = { version = "0.11.0", path = "../azalea-physics" }
|
||||
azalea-protocol = { version = "0.11.0", path = "../azalea-protocol" }
|
||||
azalea-registry = { version = "0.11.0", path = "../azalea-registry" }
|
||||
azalea-world = { version = "0.11.0", path = "../azalea-world" }
|
||||
azalea-auth = { version = "0.12.0", path = "../azalea-auth" }
|
||||
azalea-block = { version = "0.12.0", path = "../azalea-block" }
|
||||
azalea-brigadier = { version = "0.12.0", path = "../azalea-brigadier" }
|
||||
azalea-buf = { version = "0.12.0", path = "../azalea-buf" }
|
||||
azalea-chat = { version = "0.12.0", path = "../azalea-chat" }
|
||||
azalea-client = { version = "0.12.0", path = "../azalea-client", default-features = false }
|
||||
azalea-core = { version = "0.12.0", path = "../azalea-core" }
|
||||
azalea-entity = { version = "0.12.0", path = "../azalea-entity" }
|
||||
azalea-inventory = { version = "0.12.0", path = "../azalea-inventory" }
|
||||
azalea-physics = { version = "0.12.0", path = "../azalea-physics" }
|
||||
azalea-protocol = { version = "0.12.0", path = "../azalea-protocol" }
|
||||
azalea-registry = { version = "0.12.0", path = "../azalea-registry" }
|
||||
azalea-world = { version = "0.12.0", path = "../azalea-world" }
|
||||
bevy_app.workspace = true
|
||||
bevy_ecs.workspace = true
|
||||
bevy_log.workspace = true
|
||||
|
|
|
@ -9,13 +9,12 @@ First, install Rust nightly with `rustup install nightly` and `rustup default ni
|
|||
|
||||
Then, use one of the following commands to add Azalea to your project:
|
||||
|
||||
- Latest bleeding-edge version (recommended): `cargo add azalea --git=https://github.com/azalea-rs/azalea`\
|
||||
- Latest bleeding-edge version (recommended): `cargo add azalea --git=https://github.com/azalea-rs/azalea`
|
||||
- Latest "stable" release: `cargo add azalea`
|
||||
|
||||
## Optimization
|
||||
|
||||
For faster compile times, make a `.cargo/config.toml` file in your project
|
||||
and copy
|
||||
For faster compile times, create a `.cargo/config.toml` file in your project and copy
|
||||
[this file](https://github.com/azalea-rs/azalea/blob/main/.cargo/config_fast_builds)
|
||||
into it. You may have to install the LLD linker.
|
||||
|
||||
|
@ -42,9 +41,10 @@ You can just replace these with `azalea` in your code since everything from `aza
|
|||
```rust,no_run
|
||||
//! A bot that logs chat messages sent in the server to the console.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use azalea::prelude::*;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
@ -59,12 +59,15 @@ async fn main() {
|
|||
}
|
||||
|
||||
#[derive(Default, Clone, Component)]
|
||||
pub struct State {}
|
||||
pub struct State {
|
||||
pub messages_received: Arc<Mutex<usize>>
|
||||
}
|
||||
|
||||
async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
||||
match event {
|
||||
Event::Chat(m) => {
|
||||
println!("{}", m.message().to_ansi());
|
||||
*state.messages_received.lock() += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use azalea::pathfinder::goals::RadiusGoal;
|
||||
use azalea::{BlockPos, prelude::*};
|
||||
use azalea_inventory::ItemStack;
|
||||
use azalea_inventory::operations::QuickMoveClick;
|
||||
|
@ -21,6 +22,7 @@ async fn main() {
|
|||
|
||||
#[derive(Default, Clone, Component)]
|
||||
struct State {
|
||||
pub is_stealing: Arc<Mutex<bool>>,
|
||||
pub checked_chests: Arc<Mutex<Vec<BlockPos>>>,
|
||||
}
|
||||
|
||||
|
@ -32,43 +34,64 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
|||
if m.content() != "go" {
|
||||
return Ok(());
|
||||
}
|
||||
{
|
||||
state.checked_chests.lock().clear();
|
||||
}
|
||||
|
||||
let chest_block = bot
|
||||
.world()
|
||||
.read()
|
||||
.find_block(bot.position(), &azalea::registry::Block::Chest.into());
|
||||
// TODO: update this when find_blocks is implemented
|
||||
let Some(chest_block) = chest_block else {
|
||||
bot.chat("No chest found");
|
||||
return Ok(());
|
||||
};
|
||||
// bot.goto(BlockPosGoal(chest_block));
|
||||
let Some(chest) = bot.open_container_at(chest_block).await else {
|
||||
println!("Couldn't open chest");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
println!("Getting contents");
|
||||
for (index, slot) in chest
|
||||
.contents()
|
||||
.expect("we just opened the chest")
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
println!("Checking slot {index}: {slot:?}");
|
||||
if let ItemStack::Present(item) = slot {
|
||||
if item.kind == azalea::registry::Item::Diamond {
|
||||
println!("clicking slot ^");
|
||||
chest.click(QuickMoveClick::Left { slot: index as u16 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Done");
|
||||
steal(bot, state).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn steal(bot: Client, state: State) -> anyhow::Result<()> {
|
||||
{
|
||||
let mut is_stealing = state.is_stealing.lock();
|
||||
if *is_stealing {
|
||||
bot.chat("Already stealing");
|
||||
return Ok(());
|
||||
}
|
||||
*is_stealing = true;
|
||||
}
|
||||
|
||||
state.checked_chests.lock().clear();
|
||||
|
||||
loop {
|
||||
let chest_block = bot
|
||||
.world()
|
||||
.read()
|
||||
.find_blocks(bot.position(), &azalea::registry::Block::Chest.into())
|
||||
.filter(
|
||||
// filter for chests that haven't been checked
|
||||
|block_pos| !state.checked_chests.lock().contains(&block_pos),
|
||||
)
|
||||
.next();
|
||||
let Some(chest_block) = chest_block else {
|
||||
break;
|
||||
};
|
||||
|
||||
state.checked_chests.lock().push(chest_block);
|
||||
|
||||
bot.goto(RadiusGoal::new(chest_block.center(), 3.)).await;
|
||||
|
||||
let Some(chest) = bot.open_container_at(chest_block).await else {
|
||||
println!("Couldn't open chest at {chest_block:?}");
|
||||
continue;
|
||||
};
|
||||
|
||||
println!("Getting contents of chest at {chest_block:?}");
|
||||
for (index, slot) in chest.contents().unwrap_or_default().iter().enumerate() {
|
||||
println!("Checking slot {index}: {slot:?}");
|
||||
let ItemStack::Present(item) = slot else {
|
||||
continue;
|
||||
};
|
||||
if item.kind == azalea::registry::Item::Diamond {
|
||||
println!("clicking slot ^");
|
||||
chest.click(QuickMoveClick::Left { slot: index as u16 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bot.chat("Done");
|
||||
|
||||
*state.is_stealing.lock() = false;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -25,6 +25,12 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
|
|||
1
|
||||
}));
|
||||
|
||||
commands.register(literal("disconnect").executes(|ctx: &Ctx| {
|
||||
let source = ctx.source.lock();
|
||||
source.bot.disconnect();
|
||||
1
|
||||
}));
|
||||
|
||||
commands.register(literal("whereami").executes(|ctx: &Ctx| {
|
||||
let mut source = ctx.source.lock();
|
||||
let Some(entity) = source.entity() else {
|
||||
|
|
|
@ -28,7 +28,9 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
|
|||
return 0;
|
||||
};
|
||||
source.reply("ok");
|
||||
source.bot.goto(BlockPosGoal(BlockPos::from(position)));
|
||||
source
|
||||
.bot
|
||||
.start_goto(BlockPosGoal(BlockPos::from(position)));
|
||||
1
|
||||
})
|
||||
.then(literal("xz").then(argument("x", integer()).then(
|
||||
|
@ -38,7 +40,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
|
|||
let z = get_integer(ctx, "z").unwrap();
|
||||
println!("goto xz {x} {z}");
|
||||
source.reply("ok");
|
||||
source.bot.goto(XZGoal { x, z });
|
||||
source.bot.start_goto(XZGoal { x, z });
|
||||
1
|
||||
}),
|
||||
)))
|
||||
|
@ -52,7 +54,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
|
|||
let z = get_integer(ctx, "z").unwrap();
|
||||
println!("goto radius {radius}, position: {x} {y} {z}");
|
||||
source.reply("ok");
|
||||
source.bot.goto(RadiusGoal {
|
||||
source.bot.start_goto(RadiusGoal {
|
||||
pos: BlockPos::new(x, y, z).center(),
|
||||
radius,
|
||||
});
|
||||
|
@ -68,7 +70,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
|
|||
let z = get_integer(ctx, "z").unwrap();
|
||||
println!("goto xyz {x} {y} {z}");
|
||||
source.reply("ok");
|
||||
source.bot.goto(BlockPosGoal(BlockPos::new(x, y, z)));
|
||||
source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
|
||||
1
|
||||
}),
|
||||
))),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::f64::consts::PI;
|
||||
|
||||
use azalea_client::TickBroadcast;
|
||||
use azalea_client::interact::SwingArmEvent;
|
||||
use azalea_client::mining::Mining;
|
||||
use azalea_client::tick_broadcast::{TickBroadcast, UpdateBroadcast};
|
||||
use azalea_core::position::{BlockPos, Vec3};
|
||||
use azalea_core::tick::GameTick;
|
||||
use azalea_entity::{
|
||||
|
@ -86,6 +86,12 @@ pub trait BotClientExt {
|
|||
fn look_at(&self, pos: Vec3);
|
||||
/// Get a receiver that will receive a message every tick.
|
||||
fn get_tick_broadcaster(&self) -> tokio::sync::broadcast::Receiver<()>;
|
||||
/// Get a receiver that will receive a message every ECS Update.
|
||||
fn get_update_broadcaster(&self) -> tokio::sync::broadcast::Receiver<()>;
|
||||
/// Wait for one tick.
|
||||
fn wait_one_tick(&self) -> impl Future<Output = ()> + Send;
|
||||
/// Wait for one ECS Update.
|
||||
fn wait_one_update(&self) -> impl Future<Output = ()> + Send;
|
||||
/// Mine a block. This won't turn the bot's head towards the block, so if
|
||||
/// that's necessary you'll have to do that yourself with [`look_at`].
|
||||
///
|
||||
|
@ -133,6 +139,38 @@ impl BotClientExt for azalea_client::Client {
|
|||
tick_broadcast.subscribe()
|
||||
}
|
||||
|
||||
/// Returns a Receiver that receives a message every ECS Update.
|
||||
///
|
||||
/// ECS Updates happen at least at the frequency of game ticks, usually
|
||||
/// faster.
|
||||
///
|
||||
/// This is useful if you're sending an ECS event and want to make sure it's
|
||||
/// been handled before continuing.
|
||||
fn get_update_broadcaster(&self) -> tokio::sync::broadcast::Receiver<()> {
|
||||
let ecs = self.ecs.lock();
|
||||
let update_broadcast = ecs.resource::<UpdateBroadcast>();
|
||||
update_broadcast.subscribe()
|
||||
}
|
||||
|
||||
/// Wait for one tick using [`Self::get_tick_broadcaster`].
|
||||
///
|
||||
/// If you're going to run this in a loop, you may want to use that function
|
||||
/// instead and use the `Receiver` from it as it'll be more efficient.
|
||||
async fn wait_one_tick(&self) {
|
||||
let mut receiver = self.get_tick_broadcaster();
|
||||
// wait for the next tick
|
||||
let _ = receiver.recv().await;
|
||||
}
|
||||
/// Waits for one ECS Update using [`Self::get_update_broadcaster`].
|
||||
///
|
||||
/// If you're going to run this in a loop, you may want to use that function
|
||||
/// instead and use the `Receiver` from it as it'll be more efficient.
|
||||
async fn wait_one_update(&self) {
|
||||
let mut receiver = self.get_update_broadcaster();
|
||||
// wait for the next tick
|
||||
let _ = receiver.recv().await;
|
||||
}
|
||||
|
||||
async fn mine(&self, position: BlockPos) {
|
||||
self.start_mining(position);
|
||||
// vanilla sends an extra swing arm packet when we start mining
|
||||
|
|
|
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use super::costs::{COST_HEURISTIC, FALL_N_BLOCKS_COST, JUMP_ONE_BLOCK_COST};
|
||||
|
||||
pub trait Goal: Debug {
|
||||
pub trait Goal: Debug + Send + Sync {
|
||||
#[must_use]
|
||||
fn heuristic(&self, n: BlockPos) -> f32;
|
||||
#[must_use]
|
||||
|
@ -100,6 +100,11 @@ pub struct RadiusGoal {
|
|||
pub pos: Vec3,
|
||||
pub radius: f32,
|
||||
}
|
||||
impl RadiusGoal {
|
||||
pub fn new(pos: Vec3, radius: f32) -> Self {
|
||||
Self { pos, radius }
|
||||
}
|
||||
}
|
||||
impl Goal for RadiusGoal {
|
||||
fn heuristic(&self, n: BlockPos) -> f32 {
|
||||
let n = n.center();
|
||||
|
|
|
@ -27,16 +27,16 @@ impl MiningCache {
|
|||
let mut water_block_state_range_min = BlockStateIntegerRepr::MAX;
|
||||
let mut water_block_state_range_max = BlockStateIntegerRepr::MIN;
|
||||
for state in water_block_states {
|
||||
water_block_state_range_min = water_block_state_range_min.min(state.id);
|
||||
water_block_state_range_max = water_block_state_range_max.max(state.id);
|
||||
water_block_state_range_min = water_block_state_range_min.min(state.id());
|
||||
water_block_state_range_max = water_block_state_range_max.max(state.id());
|
||||
}
|
||||
let water_block_state_range = water_block_state_range_min..=water_block_state_range_max;
|
||||
|
||||
let mut lava_block_state_range_min = BlockStateIntegerRepr::MAX;
|
||||
let mut lava_block_state_range_max = BlockStateIntegerRepr::MIN;
|
||||
for state in lava_block_states {
|
||||
lava_block_state_range_min = lava_block_state_range_min.min(state.id);
|
||||
lava_block_state_range_max = lava_block_state_range_max.max(state.id);
|
||||
lava_block_state_range_min = lava_block_state_range_min.min(state.id());
|
||||
lava_block_state_range_max = lava_block_state_range_max.max(state.id());
|
||||
}
|
||||
let lava_block_state_range = lava_block_state_range_min..=lava_block_state_range_max;
|
||||
|
||||
|
@ -65,7 +65,7 @@ impl MiningCache {
|
|||
azalea_registry::Block::RedConcretePowder.into(),
|
||||
azalea_registry::Block::BlackConcretePowder.into(),
|
||||
];
|
||||
falling_blocks.sort_unstable_by_key(|block| block.id);
|
||||
falling_blocks.sort_unstable_by_key(|block| block.id());
|
||||
|
||||
Self {
|
||||
block_state_id_costs: UnsafeCell::new(IntMap::default()),
|
||||
|
@ -84,7 +84,7 @@ impl MiningCache {
|
|||
// SAFETY: mining is single-threaded, so this is safe
|
||||
let block_state_id_costs = unsafe { &mut *self.block_state_id_costs.get() };
|
||||
|
||||
if let Some(cost) = block_state_id_costs.get(&block.id) {
|
||||
if let Some(cost) = block_state_id_costs.get(&block.id()) {
|
||||
*cost
|
||||
} else {
|
||||
let best_tool_result = best_tool_in_hotbar_for_block(block, inventory_menu);
|
||||
|
@ -92,7 +92,7 @@ impl MiningCache {
|
|||
|
||||
cost += BLOCK_BREAK_ADDITIONAL_PENALTY;
|
||||
|
||||
block_state_id_costs.insert(block.id, cost);
|
||||
block_state_id_costs.insert(block.id(), cost);
|
||||
cost
|
||||
}
|
||||
}
|
||||
|
@ -101,14 +101,14 @@ impl MiningCache {
|
|||
// this already runs in about 1 nanosecond, so if you wanna try optimizing it at
|
||||
// least run the benchmarks (in benches/checks.rs)
|
||||
|
||||
self.water_block_state_range.contains(&block.id)
|
||||
|| self.lava_block_state_range.contains(&block.id)
|
||||
self.water_block_state_range.contains(&block.id())
|
||||
|| self.lava_block_state_range.contains(&block.id())
|
||||
|| is_waterlogged(block)
|
||||
}
|
||||
|
||||
pub fn is_falling_block(&self, block: BlockState) -> bool {
|
||||
self.falling_blocks
|
||||
.binary_search_by_key(&block.id, |block| block.id)
|
||||
.binary_search_by_key(&block.id(), |block| block.id())
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ use self::debug::debug_render_path_with_particles;
|
|||
use self::goals::Goal;
|
||||
use self::mining::MiningCache;
|
||||
use self::moves::{ExecuteCtx, IsReachedCtx, SuccessorsFn};
|
||||
use crate::WalkDirection;
|
||||
use crate::app::{App, Plugin};
|
||||
use crate::bot::{JumpEvent, LookAtEvent};
|
||||
use crate::ecs::{
|
||||
|
@ -58,6 +57,7 @@ use crate::ecs::{
|
|||
system::{Commands, Query, Res},
|
||||
};
|
||||
use crate::pathfinder::{astar::a_star, moves::PathfinderCtx, world::CachedWorld};
|
||||
use crate::{BotClientExt, WalkDirection};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PathfinderPlugin;
|
||||
|
@ -103,7 +103,7 @@ impl Plugin for PathfinderPlugin {
|
|||
/// A component that makes this client able to pathfind.
|
||||
#[derive(Component, Default, Clone)]
|
||||
pub struct Pathfinder {
|
||||
pub goal: Option<Arc<dyn Goal + Send + Sync>>,
|
||||
pub goal: Option<Arc<dyn Goal>>,
|
||||
pub successors_fn: Option<SuccessorsFn>,
|
||||
pub is_calculating: bool,
|
||||
pub allow_mining: bool,
|
||||
|
@ -134,7 +134,7 @@ pub struct ExecutingPath {
|
|||
pub struct GotoEvent {
|
||||
/// The local bot entity that will do the pathfinding and execute the path.
|
||||
pub entity: Entity,
|
||||
pub goal: Arc<dyn Goal + Send + Sync>,
|
||||
pub goal: Arc<dyn Goal>,
|
||||
/// The function that's used for checking what moves are possible. Usually
|
||||
/// `pathfinder::moves::default_move`
|
||||
pub successors_fn: SuccessorsFn,
|
||||
|
@ -180,22 +180,40 @@ pub fn add_default_pathfinder(
|
|||
}
|
||||
|
||||
pub trait PathfinderClientExt {
|
||||
fn goto(&self, goal: impl Goal + Send + Sync + 'static);
|
||||
fn goto_without_mining(&self, goal: impl Goal + Send + Sync + 'static);
|
||||
fn goto(&self, goal: impl Goal + 'static) -> impl Future<Output = ()>;
|
||||
fn start_goto(&self, goal: impl Goal + 'static);
|
||||
fn start_goto_without_mining(&self, goal: impl Goal + 'static);
|
||||
fn stop_pathfinding(&self);
|
||||
fn wait_until_goto_target_reached(&self) -> impl Future<Output = ()>;
|
||||
fn is_goto_target_reached(&self) -> bool;
|
||||
}
|
||||
|
||||
impl PathfinderClientExt for azalea_client::Client {
|
||||
/// Pathfind to the given goal and wait until either the target is reached
|
||||
/// or the pathfinding is canceled.
|
||||
///
|
||||
/// ```
|
||||
/// # use azalea::prelude::*;
|
||||
/// # use azalea::{BlockPos, pathfinder::goals::BlockPosGoal};
|
||||
/// # async fn example(bot: &Client) {
|
||||
/// bot.goto(BlockPosGoal(BlockPos::new(0, 70, 0))).await;
|
||||
/// # }
|
||||
/// ```
|
||||
async fn goto(&self, goal: impl Goal + 'static) {
|
||||
self.start_goto(goal);
|
||||
self.wait_until_goto_target_reached().await;
|
||||
}
|
||||
|
||||
/// Start pathfinding to a given goal.
|
||||
///
|
||||
/// ```
|
||||
/// # use azalea::prelude::*;
|
||||
/// # use azalea::{BlockPos, pathfinder::goals::BlockPosGoal};
|
||||
/// # fn example(bot: &Client) {
|
||||
/// bot.goto(BlockPosGoal(BlockPos::new(0, 70, 0)));
|
||||
/// bot.start_goto(BlockPosGoal(BlockPos::new(0, 70, 0)));
|
||||
/// # }
|
||||
/// ```
|
||||
fn goto(&self, goal: impl Goal + Send + Sync + 'static) {
|
||||
fn start_goto(&self, goal: impl Goal + 'static) {
|
||||
self.ecs.lock().send_event(GotoEvent {
|
||||
entity: self.entity,
|
||||
goal: Arc::new(goal),
|
||||
|
@ -206,9 +224,9 @@ impl PathfinderClientExt for azalea_client::Client {
|
|||
});
|
||||
}
|
||||
|
||||
/// Same as [`goto`](Self::goto). but the bot won't break any blocks while
|
||||
/// executing the path.
|
||||
fn goto_without_mining(&self, goal: impl Goal + Send + Sync + 'static) {
|
||||
/// Same as [`start_goto`](Self::start_goto). but the bot won't break any
|
||||
/// blocks while executing the path.
|
||||
fn start_goto_without_mining(&self, goal: impl Goal + 'static) {
|
||||
self.ecs.lock().send_event(GotoEvent {
|
||||
entity: self.entity,
|
||||
goal: Arc::new(goal),
|
||||
|
@ -225,6 +243,26 @@ impl PathfinderClientExt for azalea_client::Client {
|
|||
force: false,
|
||||
});
|
||||
}
|
||||
|
||||
/// Waits forever until the bot no longer has a pathfinder goal.
|
||||
async fn wait_until_goto_target_reached(&self) {
|
||||
// we do this to make sure the event got handled before we start checking
|
||||
// is_goto_target_reached
|
||||
self.wait_one_update().await;
|
||||
|
||||
let mut tick_broadcaster = self.get_tick_broadcaster();
|
||||
while !self.is_goto_target_reached() {
|
||||
// check every tick
|
||||
tick_broadcaster.recv().await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn is_goto_target_reached(&self) -> bool {
|
||||
self.map_get_component::<Pathfinder, _>(|p| {
|
||||
p.map(|p| p.goal.is_none() && !p.is_calculating)
|
||||
.unwrap_or(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
|
@ -331,7 +369,7 @@ pub fn goto_listener(
|
|||
pub struct CalculatePathOpts {
|
||||
pub entity: Entity,
|
||||
pub start: BlockPos,
|
||||
pub goal: Arc<dyn Goal + Send + Sync>,
|
||||
pub goal: Arc<dyn Goal>,
|
||||
pub successors_fn: SuccessorsFn,
|
||||
pub world_lock: Arc<RwLock<azalea_world::Instance>>,
|
||||
pub goto_id_atomic: Arc<AtomicUsize>,
|
||||
|
|
|
@ -194,8 +194,7 @@ impl CachedWorld {
|
|||
let mut passable_bitset = FixedBitSet::<{ 4096_usize.div_ceil(8) }>::new();
|
||||
let mut solid_bitset = FixedBitSet::<{ 4096_usize.div_ceil(8) }>::new();
|
||||
for i in 0..4096 {
|
||||
let block_state_id = section.get_at_index(i);
|
||||
let block_state = BlockState::try_from(block_state_id).unwrap_or(BlockState::AIR);
|
||||
let block_state = section.get_at_index(i);
|
||||
if is_block_state_passable(block_state) {
|
||||
passable_bitset.set(i);
|
||||
}
|
||||
|
@ -304,9 +303,7 @@ impl CachedWorld {
|
|||
let west_is_in_same_section = section_block_pos.x != 0;
|
||||
|
||||
let Some(mining_cost) = self.with_section(section_pos, |section| {
|
||||
let block_state =
|
||||
BlockState::try_from(section.get_at_index(u16::from(section_block_pos) as usize))
|
||||
.unwrap_or_default();
|
||||
let block_state = section.get_at_index(u16::from(section_block_pos) as usize);
|
||||
let mining_cost = mining_cache.cost_for(block_state);
|
||||
|
||||
if mining_cost == f32::INFINITY {
|
||||
|
@ -316,10 +313,7 @@ impl CachedWorld {
|
|||
|
||||
// if there's a falling block or liquid above this block, abort
|
||||
if up_is_in_same_section {
|
||||
let up_block = BlockState::try_from(
|
||||
section.get_at_index(u16::from(section_block_pos.up(1)) as usize),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let up_block = section.get_at_index(u16::from(section_block_pos.up(1)) as usize);
|
||||
if mining_cache.is_liquid(up_block) || mining_cache.is_falling_block(up_block) {
|
||||
return f32::INFINITY;
|
||||
}
|
||||
|
@ -327,10 +321,8 @@ impl CachedWorld {
|
|||
|
||||
// if there's a liquid to the north of this block, abort
|
||||
if north_is_in_same_section {
|
||||
let north_block = BlockState::try_from(
|
||||
section.get_at_index(u16::from(section_block_pos.north(1)) as usize),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let north_block =
|
||||
section.get_at_index(u16::from(section_block_pos.north(1)) as usize);
|
||||
if mining_cache.is_liquid(north_block) {
|
||||
return f32::INFINITY;
|
||||
}
|
||||
|
@ -338,10 +330,8 @@ impl CachedWorld {
|
|||
|
||||
// liquid to the east
|
||||
if east_is_in_same_section {
|
||||
let east_block = BlockState::try_from(
|
||||
section.get_at_index(u16::from(section_block_pos.east(1)) as usize),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let east_block =
|
||||
section.get_at_index(u16::from(section_block_pos.east(1)) as usize);
|
||||
if mining_cache.is_liquid(east_block) {
|
||||
return f32::INFINITY;
|
||||
}
|
||||
|
@ -349,10 +339,8 @@ impl CachedWorld {
|
|||
|
||||
// liquid to the south
|
||||
if south_is_in_same_section {
|
||||
let south_block = BlockState::try_from(
|
||||
section.get_at_index(u16::from(section_block_pos.south(1)) as usize),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let south_block =
|
||||
section.get_at_index(u16::from(section_block_pos.south(1)) as usize);
|
||||
if mining_cache.is_liquid(south_block) {
|
||||
return f32::INFINITY;
|
||||
}
|
||||
|
@ -360,10 +348,8 @@ impl CachedWorld {
|
|||
|
||||
// liquid to the west
|
||||
if west_is_in_same_section {
|
||||
let west_block = BlockState::try_from(
|
||||
section.get_at_index(u16::from(section_block_pos.west(1)) as usize),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let west_block =
|
||||
section.get_at_index(u16::from(section_block_pos.west(1)) as usize);
|
||||
if mining_cache.is_liquid(west_block) {
|
||||
return f32::INFINITY;
|
||||
}
|
||||
|
@ -391,10 +377,7 @@ impl CachedWorld {
|
|||
let check_should_avoid_this_block = |pos: BlockPos, check: &dyn Fn(BlockState) -> bool| {
|
||||
let block_state = self
|
||||
.with_section(ChunkSectionPos::from(pos), |section| {
|
||||
BlockState::try_from(
|
||||
section.get_at_index(u16::from(ChunkSectionBlockPos::from(pos)) as usize),
|
||||
)
|
||||
.unwrap_or_default()
|
||||
section.get_at_index(u16::from(ChunkSectionBlockPos::from(pos)) as usize)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
check(block_state)
|
||||
|
|
|
@ -151,18 +151,18 @@ pub trait BlockWithShape {{
|
|||
|
||||
impl BlockWithShape for BlockState {{
|
||||
fn collision_shape(&self) -> &'static VoxelShape {{
|
||||
COLLISION_SHAPES_MAP.get(self.id as usize).unwrap_or(&&SHAPE1)
|
||||
COLLISION_SHAPES_MAP.get(self.id() as usize).unwrap_or(&&SHAPE1)
|
||||
}}
|
||||
fn outline_shape(&self) -> &'static VoxelShape {{
|
||||
OUTLINE_SHAPES_MAP.get(self.id as usize).unwrap_or(&&SHAPE1)
|
||||
OUTLINE_SHAPES_MAP.get(self.id() as usize).unwrap_or(&&SHAPE1)
|
||||
}}
|
||||
|
||||
fn is_collision_shape_empty(&self) -> bool {{
|
||||
matches!(self.id, {empty_shape_match_code})
|
||||
matches!(self.id(), {empty_shape_match_code})
|
||||
}}
|
||||
|
||||
fn is_collision_shape_full(&self) -> bool {{
|
||||
matches!(self.id, {block_shape_match_code})
|
||||
matches!(self.id(), {block_shape_match_code})
|
||||
}}
|
||||
}}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ def to_snake_case(name: str):
|
|||
|
||||
|
||||
def to_camel_case(name: str):
|
||||
s = re.sub('[_ ](\w)', lambda m: m.group(1).upper(),
|
||||
s = re.sub(r'[_ ](\w)', lambda m: m.group(1).upper(),
|
||||
name.replace('.', '_').replace('/', '_'))
|
||||
s = upper_first_letter(s)
|
||||
# if the first character is a number, we need to add an underscore
|
||||
|
|
Loading…
Add table
Reference in a new issue