1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

merge main

This commit is contained in:
Ubuntu 2023-02-10 19:22:07 +00:00
commit 2ae0fe044f
66 changed files with 7075 additions and 21152 deletions

60
Cargo.lock generated
View file

@ -162,7 +162,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "azalea"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"anyhow",
"async-trait",
@ -180,7 +180,6 @@ dependencies = [
"env_logger 0.10.0",
"futures",
"futures-lite",
"iyes_loopless",
"log",
"nohash-hasher",
"num-traits",
@ -193,7 +192,7 @@ dependencies = [
[[package]]
name = "azalea-auth"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-buf",
"azalea-crypto",
@ -211,15 +210,16 @@ dependencies = [
[[package]]
name = "azalea-block"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-block-macros",
"azalea-buf",
"azalea-registry",
]
[[package]]
name = "azalea-block-macros"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"proc-macro2",
"quote",
@ -228,7 +228,7 @@ dependencies = [
[[package]]
name = "azalea-brigadier"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-buf",
"azalea-chat",
@ -236,7 +236,7 @@ dependencies = [
[[package]]
name = "azalea-buf"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-buf-macros",
"byteorder",
@ -249,7 +249,7 @@ dependencies = [
[[package]]
name = "azalea-buf-macros"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"proc-macro2",
"quote",
@ -258,7 +258,7 @@ dependencies = [
[[package]]
name = "azalea-chat"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-buf",
"azalea-language",
@ -270,7 +270,7 @@ dependencies = [
[[package]]
name = "azalea-client"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"anyhow",
"async-trait",
@ -290,7 +290,6 @@ dependencies = [
"derive_more",
"env_logger 0.9.3",
"futures",
"iyes_loopless",
"log",
"nohash-hasher",
"once_cell",
@ -304,7 +303,7 @@ dependencies = [
[[package]]
name = "azalea-core"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-buf",
"azalea-chat",
@ -316,7 +315,7 @@ dependencies = [
[[package]]
name = "azalea-crypto"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"aes",
"azalea-buf",
@ -331,18 +330,17 @@ dependencies = [
[[package]]
name = "azalea-ecs"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-ecs-macros",
"bevy_app",
"bevy_ecs",
"iyes_loopless",
"tokio",
]
[[package]]
name = "azalea-ecs-macros"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"proc-macro2",
"quote",
@ -369,7 +367,7 @@ dependencies = [
[[package]]
name = "azalea-language"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"once_cell",
"serde",
@ -378,7 +376,7 @@ dependencies = [
[[package]]
name = "azalea-nbt"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"ahash 0.8.3",
"azalea-buf",
@ -393,14 +391,13 @@ dependencies = [
[[package]]
name = "azalea-physics"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-block",
"azalea-core",
"azalea-ecs",
"azalea-registry",
"azalea-world",
"iyes_loopless",
"once_cell",
"parking_lot",
"uuid",
@ -408,7 +405,7 @@ dependencies = [
[[package]]
name = "azalea-protocol"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"anyhow",
"async-compression",
@ -445,7 +442,7 @@ dependencies = [
[[package]]
name = "azalea-protocol-macros"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"proc-macro2",
"quote",
@ -454,7 +451,7 @@ dependencies = [
[[package]]
name = "azalea-registry"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-buf",
"azalea-registry-macros",
@ -463,7 +460,7 @@ dependencies = [
[[package]]
name = "azalea-registry-macros"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"proc-macro2",
"quote",
@ -472,7 +469,7 @@ dependencies = [
[[package]]
name = "azalea-world"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"azalea-block",
"azalea-buf",
@ -483,7 +480,6 @@ dependencies = [
"azalea-registry",
"derive_more",
"enum-as-inner",
"iyes_loopless",
"log",
"nohash-hasher",
"once_cell",
@ -1547,18 +1543,6 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "iyes_loopless"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c47fd2cbdb1d7f295c25e6bfccfd78a84b6eef3055bc9f01b34ae861721b01ee"
dependencies = [
"bevy_app",
"bevy_ecs",
"bevy_time",
"bevy_utils",
]
[[package]]
name = "js-sys"
version = "0.3.61"

View file

@ -4,13 +4,13 @@ edition = "2021"
license = "MIT"
name = "azalea-auth"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-auth"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-buf = {path = "../azalea-buf", version = "^0.5.0"}
azalea-crypto = {path = "../azalea-crypto", version = "^0.5.0"}
azalea-buf = {path = "../azalea-buf", version = "^0.6.0" }
azalea-crypto = {path = "../azalea-crypto", version = "^0.6.0" }
chrono = {version = "0.4.22", default-features = false}
log = "0.4.17"
num-bigint = "0.4.3"

7
azalea-block/Cargo.toml Executable file → Normal file
View file

@ -4,12 +4,13 @@ edition = "2021"
license = "MIT"
name = "azalea-block"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-block"
version = "0.5.0"
version = "0.6.0"
[lib]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-block-macros = {path = "./azalea-block-macros", version = "^0.5.0" }
azalea-buf = {path = "../azalea-buf", version = "^0.5.0" }
azalea-block-macros = { path = "./azalea-block-macros", version = "^0.6.0" }
azalea-buf = { path = "../azalea-buf", version = "^0.6.0" }
azalea-registry = { version = "0.6.0", path = "../azalea-registry" }

View file

@ -1,10 +1,48 @@
# Azalea Block
Representation of Minecraft block states.
There's two main things here, the `BlockState` enum and the `Block` trait.
`BlockState` is a simple enum with every possible block state as variant, and `Block` is a heavier trait which lets you access information about a block more easily.
There's three block types, used for different things. You can (mostly) convert between them with `.into()`.
Every block is a struct that implements `Block`. You can freely convert between `BlockState` and `Block` with .into().
## BlockState struct
[`BlockState`] is a struct containing the numerical protocol ID of a block state. This is how blocks are stored in the world.
```
# use azalea_block::BlockState;
let block_state: BlockState = azalea_block::CobblestoneWallBlock {
east: azalea_block::EastWall::Low,
north: azalea_block::NorthWall::Low,
south: azalea_block::SouthWall::Low,
west: azalea_block::WestWall::Low,
up: false,
waterlogged: false,
}
.into();
```
```
# use azalea_block::BlockState;
let block_state: BlockState = azalea_registry::Block::Jukebox.into();
```
## Block trait
The [`Block`] trait represents a type of a block. With the the [`Block`] trait, you can get some extra things like the string block ID and some information about the block's behavior. Also, the structs that implement the trait contain the block attributes as fields so it's more convenient to get them. Note that this is often used as `Box<dyn Block>`.
If for some reason you don't want the `Block` trait, set default-features to false.
```
# use azalea_block::{Block, BlockState};
# let block_state = BlockState::from(azalea_registry::Block::Jukebox);
let block = Box::<dyn Block>::from(block_state);
```
```
# use azalea_block::{Block, BlockState};
# let block_state: BlockState = azalea_registry::Block::Jukebox.into();
if let Some(jukebox) = Box::<dyn Block>::from(block_state).downcast_ref::<azalea_block::JukeboxBlock>() {
// ...
}
```
## azalea_registry::Block enum
This one technically isn't from the `azalea-block` crate, but it's still very relevant. It's an enum that contains every block type as a variant *without* containing any state data (unlike `BlockState` and the `Block` trait). Converting this into any other block type will use the default state for that block.
If you don't want the `Block` trait, set default-features to false.

2
azalea-block/azalea-block-macros/Cargo.toml Executable file → Normal file
View file

@ -4,7 +4,7 @@ edition = "2021"
license = "MIT"
name = "azalea-block-macros"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-block/azalea-block-macros"
version = "0.5.0"
version = "0.6.0"
[lib]
proc-macro = true

View file

@ -3,6 +3,7 @@
mod utils;
use proc_macro::TokenStream;
use proc_macro2::TokenTree;
use quote::quote;
use std::collections::HashMap;
use std::fmt::Write;
@ -234,7 +235,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let mut properties_map = HashMap::new();
let mut property_struct_names_to_names = HashMap::new();
let mut state_id: usize = 0;
let mut state_id: u32 = 0;
for property in &input.property_definitions.properties {
let property_type_name: Ident;
@ -282,8 +283,8 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
#property_enum_variants
}
impl From<usize> for #property_type_name {
fn from(value: usize) -> Self {
impl From<u32> for #property_type_name {
fn from(value: u32) -> Self {
match value {
#property_from_number_variants
_ => panic!("Invalid property value: {}", value),
@ -305,7 +306,11 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let mut block_state_enum_variants = quote! {};
let mut block_structs = quote! {};
let mut from_state_to_block_match = quote! {};
let mut from_registry_block_to_block_match = quote! {};
let mut from_registry_block_to_blockstate_match = quote! {};
for block in &input.block_definitions.blocks {
let block_property_names = &block
.properties_and_defaults
@ -403,30 +408,18 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let mut from_block_to_state_match_inner = quote! {};
let first_state_id = state_id;
let mut default_state_id = None;
// if there's no properties, then the block is just a single state
if block_properties_vec.is_empty() {
block_state_enum_variants.extend(quote! {
#block_name_pascal_case,
});
default_state_id = Some(state_id);
state_id += 1;
}
for combination in combinations_of(&block_properties_vec) {
state_id += 1;
let variant_name = Ident::new(
&format!(
"{}_{}",
block_name_pascal_case,
combination
.iter()
.map(|v| v[0..1].to_uppercase() + &v[1..])
.collect::<String>()
),
proc_macro2::Span::call_site(),
);
block_state_enum_variants.extend(quote! {
#variant_name,
});
let mut is_default = true;
// face: properties::Face::Floor,
// facing: properties::Facing::North,
@ -439,6 +432,18 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let variant =
Ident::new(&combination[i].to_string(), proc_macro2::Span::call_site());
// this terrible code just gets the property default as a string
let property_default_as_string = if let TokenTree::Ident(i) =
property.default.clone().into_iter().last().unwrap()
{
i.to_string()
} else {
panic!()
};
if property_default_as_string != combination[i] {
is_default = false;
}
let property_type = if property.is_enum {
quote! {#property_struct_name_ident::#variant}
} else {
@ -453,10 +458,21 @@ 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::#variant_name,
} => BlockState { id: #state_id },
});
if is_default {
default_state_id = Some(state_id);
}
state_id += 1;
}
let Some(default_state_id) = default_state_id else {
let defaults = properties_with_name.iter().map(|p| if let TokenTree::Ident(i) = p.default.clone().into_iter().last().unwrap() { i.to_string() } else { panic!() }).collect::<Vec<_>>();
panic!("Couldn't get default state id for {block_name_pascal_case}, combinations={block_properties_vec:?}, defaults={defaults:?}")
};
// 7035..=7058 => {
// let b = b - 7035;
// &AcaciaButtonBlock {
@ -466,7 +482,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
// }
// }
let mut from_state_to_block_inner = quote! {};
let mut division = 1usize;
let mut division = 1u32;
for i in (0..properties_with_name.len()).rev() {
let PropertyWithNameAndDefault {
property_type: property_struct_name_ident,
@ -475,11 +491,12 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
} = &properties_with_name[i];
let property_variants = &block_properties_vec[i];
let property_variants_count = property_variants.len();
let property_variants_count = property_variants.len() as u32;
let conversion_code = {
if &property_struct_name_ident.to_string() == "bool" {
assert_eq!(property_variants_count, 2);
quote! {(b / #division) % #property_variants_count != 0}
// this is not a mistake, it starts with true for some reason
quote! {(b / #division) % #property_variants_count == 0}
} else {
quote! {#property_struct_name_ident::from((b / #division) % #property_variants_count)}
}
@ -500,6 +517,12 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
})
},
});
from_registry_block_to_block_match.extend(quote! {
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 },
});
let mut block_default_fields = quote! {};
for PropertyWithNameAndDefault {
@ -515,10 +538,10 @@ 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::#block_name_pascal_case }
quote! { BlockState { id: #first_state_id } }
} else {
quote! {
match b {
match self {
#from_block_to_state_match_inner
}
}
@ -537,11 +560,14 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
fn id(&self) -> &'static str {
#block_id
}
fn as_blockstate(&self) -> BlockState {
#from_block_to_state_match
}
}
impl From<#block_struct_name> for BlockState {
fn from(b: #block_struct_name) -> Self {
#from_block_to_state_match
b.as_blockstate()
}
}
@ -557,18 +583,24 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
block_structs.extend(block_struct);
}
let last_state_id = (state_id - 1) as u32;
let last_state_id = state_id - 1;
let mut generated = quote! {
#property_enums
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum BlockState {
#block_state_enum_variants
/// A representation of a state a block can be in. (for example, a stone
/// block only has one state but each possible stair rotation is a
/// different state).
#[derive(Copy, Clone, PartialEq, Eq, Default)]
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: u32
}
impl BlockState {
/// Returns the highest possible state
pub const AIR: BlockState = BlockState { id: 0 };
/// Returns the highest possible state ID.
#[inline]
pub fn max_state() -> u32 {
#last_state_id
@ -577,8 +609,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
impl std::fmt::Debug for BlockState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// having a big match statement here would take up 700kb
f.write_str("BlockState")
write!(f, "BlockState(id: {}, {:?})", self.id, Box::<dyn Block>::from(*self))
}
}
};
@ -587,14 +618,30 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
#block_structs
impl From<BlockState> for Box<dyn Block> {
fn from(b: BlockState) -> Self {
let b = b as usize;
fn from(block_state: BlockState) -> Self {
let b = block_state.id;
match b {
#from_state_to_block_match
_ => panic!("Invalid block state: {}", b),
}
}
}
impl From<azalea_registry::Block> for Box<dyn Block> {
fn from(block: azalea_registry::Block) -> Self {
match block {
#from_registry_block_to_block_match
_ => unreachable!("There should always be a block struct for every azalea_registry::Block variant")
}
}
}
impl From<azalea_registry::Block> for BlockState {
fn from(block: azalea_registry::Block) -> Self {
match block {
#from_registry_block_to_blockstate_match
_ => unreachable!("There should always be a block state for every azalea_registry::Block variant")
}
}
}
});
generated.into()

View file

@ -1,9 +1,18 @@
use std::any::Any;
use crate::BlockBehavior;
use azalea_block_macros::make_block_states;
use std::fmt::Debug;
pub trait Block {
pub trait Block: Debug + Any {
fn behavior(&self) -> BlockBehavior;
fn id(&self) -> &'static str;
fn as_blockstate(&self) -> BlockState;
}
impl dyn Block {
pub fn downcast_ref<T: Block>(&self) -> Option<&T> {
(self as &dyn Any).downcast_ref::<T>()
}
}
make_block_states! {

View file

@ -1,4 +1,5 @@
#![doc = include_str!("../README.md")]
#![feature(trait_upcasting)]
mod behavior;
mod blocks;
@ -6,10 +7,7 @@ mod blocks;
use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable};
pub use behavior::BlockBehavior;
pub use blocks::*;
use std::{
io::{Cursor, Write},
mem,
};
use std::io::{Cursor, Write};
impl BlockState {
/// Transmutes a u32 to a block state.
@ -17,8 +15,8 @@ impl BlockState {
/// # 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)
pub unsafe fn from_u32_unchecked(state_id: u32) -> Self {
BlockState { id: state_id }
}
#[inline]
@ -33,7 +31,7 @@ impl TryFrom<u32> for BlockState {
/// Safely converts a state id to a block state.
fn try_from(state_id: u32) -> Result<Self, Self::Error> {
if Self::is_valid_state(state_id) {
Ok(unsafe { Self::from_u32_unsafe(state_id) })
Ok(unsafe { Self::from_u32_unchecked(state_id) })
} else {
Err(())
}
@ -50,7 +48,7 @@ impl McBufReadable for BlockState {
}
impl McBufWritable for BlockState {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
u32::var_write_into(&(*self as u32), buf)
u32::var_write_into(&self.id, buf)
}
}
@ -60,7 +58,7 @@ mod tests {
#[test]
fn test_from_u32() {
assert_eq!(BlockState::try_from(0).unwrap(), BlockState::Air);
assert_eq!(BlockState::try_from(0).unwrap(), BlockState::AIR);
assert!(BlockState::try_from(BlockState::max_state()).is_ok());
assert!(BlockState::try_from(BlockState::max_state() + 1).is_err());
@ -68,10 +66,34 @@ mod tests {
#[test]
fn test_from_blockstate() {
let block: Box<dyn Block> = Box::<dyn Block>::from(BlockState::Air);
let block: Box<dyn Block> = Box::<dyn Block>::from(BlockState::AIR);
assert_eq!(block.id(), "air");
let block: Box<dyn Block> = Box::<dyn Block>::from(BlockState::FloweringAzalea);
let block: Box<dyn Block> =
Box::<dyn Block>::from(BlockState::from(azalea_registry::Block::FloweringAzalea));
assert_eq!(block.id(), "flowering_azalea");
}
#[test]
fn test_debug_blockstate() {
let formatted = format!(
"{:?}",
BlockState::from(azalea_registry::Block::FloweringAzalea)
);
assert!(
formatted.ends_with(", FloweringAzaleaBlock)"),
"{}",
formatted
);
let formatted = format!(
"{:?}",
BlockState::from(azalea_registry::Block::BigDripleafStem)
);
assert!(
formatted.ends_with(", BigDripleafStemBlock { facing: North, waterlogged: false })"),
"{}",
formatted
);
}
}

6
azalea-brigadier/Cargo.toml Executable file → Normal file
View file

@ -4,13 +4,13 @@ edition = "2021"
license = "MIT"
name = "azalea-brigadier"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-brigadier"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-buf = {path = "../azalea-buf", version = "^0.5.0", optional = true}
azalea-chat = {path = "../azalea-chat", version = "^0.5.0", optional = true}
azalea-buf = {path = "../azalea-buf", version = "^0.6.0", optional = true}
azalea-chat = {path = "../azalea-chat", version = "^0.6.0", optional = true}
[features]
azalea-buf = ["dep:azalea-buf", "dep:azalea-chat"]

View file

@ -4,12 +4,12 @@ edition = "2021"
license = "MIT"
name = "azalea-buf"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-buf"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-buf-macros = {path = "./azalea-buf-macros", version = "^0.5.0" }
azalea-buf-macros = {path = "./azalea-buf-macros", version = "^0.6.0" }
byteorder = "^1.4.3"
log = "0.4.17"
serde_json = {version = "^1.0", optional = true}

2
azalea-buf/azalea-buf-macros/Cargo.toml Executable file → Normal file
View file

@ -4,7 +4,7 @@ edition = "2021"
license = "MIT"
name = "azalea-buf-macros"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-buf"
version = "0.5.0"
version = "0.6.0"
[lib]
proc-macro = true

6
azalea-chat/Cargo.toml Executable file → Normal file
View file

@ -4,7 +4,7 @@ edition = "2021"
license = "MIT"
name = "azalea-chat"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-chat"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -14,8 +14,8 @@ default = ["azalea-buf"]
[dependencies]
azalea-buf = { path = "../azalea-buf", features = [
"serde_json",
], version = "^0.5.0", optional = true }
azalea-language = { path = "../azalea-language", version = "^0.5.0" }
], version = "^0.6.0", optional = true }
azalea-language = { path = "../azalea-language", version = "^0.6.0" }
log = "0.4.17"
once_cell = "1.16.0"
serde = { version = "^1.0.148", features = ["derive"] }

View file

@ -4,29 +4,27 @@ edition = "2021"
license = "MIT"
name = "azalea-client"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-client"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.59"
async-trait = "0.1.58"
azalea-auth = {path = "../azalea-auth", version = "0.5.0"}
azalea-block = {path = "../azalea-block", version = "0.5.0"}
azalea-chat = {path = "../azalea-chat", version = "0.5.0"}
azalea-core = {path = "../azalea-core", version = "0.5.0"}
azalea-crypto = {path = "../azalea-crypto", version = "0.5.0"}
azalea-ecs = {path = "../azalea-ecs", version = "0.5.0"}
azalea-auth = { path = "../azalea-auth", version = "0.6.0" }
azalea-block = { path = "../azalea-block", version = "0.6.0" }
azalea-chat = { path = "../azalea-chat", version = "0.6.0" }
azalea-core = { path = "../azalea-core", version = "0.6.0" }
azalea-crypto = { path = "../azalea-crypto", version = "0.6.0" }
azalea-ecs = { path = "../azalea-ecs", version = "0.6.0" }
azalea-physics = { path = "../azalea-physics", version = "0.6.0" }
azalea-protocol = { path = "../azalea-protocol", version = "0.6.0" }
azalea-registry = { path = "../azalea-registry", version = "0.6.0" }
azalea-world = { path = "../azalea-world", version = "0.6.0" }
azalea-inventory = { path = "../azalea-inventory", version = "0.1.0" }
azalea-physics = {path = "../azalea-physics", version = "0.5.0"}
azalea-protocol = {path = "../azalea-protocol", version = "0.5.0"}
azalea-registry = {path = "../azalea-registry", version = "0.5.0"}
azalea-world = {path = "../azalea-world", version = "0.5.0"}
bevy_tasks = "0.9.1"
bevy_time = "0.9.1"
derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] }
futures = "0.3.25"
iyes_loopless = "0.9.1"
log = "0.4.17"
nohash-hasher = "0.2.0"
once_cell = "1.16.0"
@ -36,6 +34,7 @@ thiserror = "^1.0.34"
tokio = { version = "^1.24.2", features = ["sync"] }
typemap_rev = "0.3.0"
uuid = "^1.1.2"
bevy_tasks = "0.9.1"
[dev-dependencies]
env_logger = "0.9.1"

View file

@ -43,7 +43,10 @@ use azalea_protocol::{
},
resolver, ServerAddress,
};
use azalea_world::{entity::WorldName, EntityPlugin, Local, PartialWorld, World, WorldContainer};
use azalea_world::{
entity::{EntityPlugin, Local, WorldName},
PartialWorld, World, WorldContainer,
};
use log::{debug, error};
use parking_lot::{Mutex, RwLock};
use std::{collections::HashMap, fmt::Debug, io, net::SocketAddr, sync::Arc};
@ -377,7 +380,8 @@ impl Client {
/// # Examples
///
/// ```
/// # fn example(client: &azalea::Client) {
/// # use azalea_world::entity::WorldName;
/// # fn example(client: &azalea_client::Client) {
/// let world_name = client.component::<WorldName>();
/// # }
pub fn component<T: Component + Clone>(&self) -> T {

View file

@ -15,6 +15,7 @@ impl Client {
///
/// # Examples
/// ```
/// # use azalea_world::entity::WorldName;
/// # fn example(mut client: azalea_client::Client) {
/// let is_logged_in = client
/// .query::<Option<&WorldName>>(&mut client.ecs.lock())

View file

@ -20,8 +20,8 @@ use tokio::sync::mpsc;
use crate::{
packet_handling::{
AddPlayerEvent, ChatReceivedEvent, DeathEvent, PacketReceiver, RemovePlayerEvent,
UpdatePlayerEvent,
AddPlayerEvent, ChatReceivedEvent, DeathEvent, KeepAliveEvent, PacketReceiver,
RemovePlayerEvent, UpdatePlayerEvent,
},
ChatPacket, PlayerInfo,
};
@ -73,6 +73,8 @@ pub enum Event {
UpdatePlayer(PlayerInfo),
/// The client player died in-game.
Death(Option<Arc<ClientboundPlayerCombatKillPacket>>),
/// A `KeepAlive` packet was sent by the server.
KeepAlive(u64),
}
/// A component that contains an event sender for events that are only
@ -94,6 +96,7 @@ impl Plugin for EventPlugin {
.add_system(update_player_listener)
.add_system(remove_player_listener)
.add_system(death_listener)
.add_system(keepalive_listener)
.add_tick_system(tick_listener);
}
}
@ -157,7 +160,7 @@ fn update_player_listener(
for event in events.iter() {
let local_player_events = query
.get(event.entity)
.expect("Non-localplayer entities shouldn't be able to receive add player events");
.expect("Non-localplayer entities shouldn't be able to receive update player events");
local_player_events
.send(Event::UpdatePlayer(event.info.clone()))
.unwrap();
@ -171,7 +174,7 @@ fn remove_player_listener(
for event in events.iter() {
let local_player_events = query
.get(event.entity)
.expect("Non-localplayer entities shouldn't be able to receive add player events");
.expect("Non-localplayer entities shouldn't be able to receive remove player events");
local_player_events
.send(Event::RemovePlayer(event.info.clone()))
.unwrap();
@ -187,3 +190,14 @@ fn death_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<Deat
}
}
}
fn keepalive_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<KeepAliveEvent>) {
for event in events.iter() {
let local_player_events = query
.get(event.entity)
.expect("Non-localplayer entities shouldn't be able to receive keepalive events");
local_player_events
.send(Event::KeepAlive(event.id))
.unwrap();
}
}

View file

@ -28,7 +28,7 @@ use crate::{
/// You can also use the [`Local`] marker component for queries if you're only
/// checking for a local player and don't need the contents of this component.
///
/// [`Local`]: azalea_world::Local
/// [`Local`]: azalea_world::entity::Local
/// [`Client`]: crate::Client
#[derive(Component)]
pub struct LocalPlayer {

View file

@ -21,6 +21,7 @@ use azalea_protocol::{
serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket,
ClientboundGamePacket, ServerboundGamePacket,
},
read::ReadPacketError,
};
use azalea_world::{
entity::{
@ -28,7 +29,8 @@ use azalea_world::{
set_rotation, Dead, EntityBundle, EntityKind, LastSentPosition, MinecraftEntityId, Physics,
PlayerBundle, Position, WorldName,
},
LoadedBy, PartialWorld, RelativeEntityUpdate, WorldContainer,
entity::{LoadedBy, RelativeEntityUpdate},
PartialWorld, WorldContainer,
};
use log::{debug, error, trace, warn};
use parking_lot::Mutex;
@ -51,13 +53,14 @@ impl Plugin for PacketHandlerPlugin {
.add_event::<RemovePlayerEvent>()
.add_event::<UpdatePlayerEvent>()
.add_event::<ChatReceivedEvent>()
.add_event::<DeathEvent>();
.add_event::<DeathEvent>()
.add_event::<KeepAliveEvent>();
}
}
/// A player joined the game (or more specifically, was added to the tab
/// list of a local player).
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct AddPlayerEvent {
/// The local player entity that received this event.
pub entity: Entity,
@ -65,7 +68,7 @@ pub struct AddPlayerEvent {
}
/// A player left the game (or maybe is still in the game and was just
/// removed from the tab list of a local player).
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct RemovePlayerEvent {
/// The local player entity that received this event.
pub entity: Entity,
@ -73,7 +76,7 @@ pub struct RemovePlayerEvent {
}
/// A player was updated in the tab list of a local player (gamemode, display
/// name, or latency changed).
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct UpdatePlayerEvent {
/// The local player entity that received this event.
pub entity: Entity,
@ -81,7 +84,7 @@ pub struct UpdatePlayerEvent {
}
/// A client received a chat message packet.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ChatReceivedEvent {
pub entity: Entity,
pub packet: ChatPacket,
@ -90,11 +93,22 @@ pub struct ChatReceivedEvent {
/// Event for when an entity dies. dies. If it's a local player and there's a
/// reason in the death screen, the [`ClientboundPlayerCombatKillPacket`] will
/// be included.
#[derive(Debug, Clone)]
pub struct DeathEvent {
pub entity: Entity,
pub packet: Option<ClientboundPlayerCombatKillPacket>,
}
/// A KeepAlive packet is sent from the server to verify that the client is
/// still connected.
#[derive(Debug, Clone)]
pub struct KeepAliveEvent {
pub entity: Entity,
/// The ID of the keepalive. This is an arbitrary number, but vanilla
/// servers use the time to generate this.
pub id: u64,
}
/// Something that receives packets from the server.
#[derive(Component, Clone)]
pub struct PacketReceiver {
@ -826,11 +840,18 @@ fn handle_packets(ecs: &mut Ecs) {
ClientboundGamePacket::KeepAlive(p) => {
debug!("Got keep alive packet {p:?} for {player_entity:?}");
let mut system_state: SystemState<Query<&mut LocalPlayer>> =
SystemState::new(ecs);
let mut query = system_state.get_mut(ecs);
let mut local_player = query.get_mut(player_entity).unwrap();
let mut system_state: SystemState<(
Query<&mut LocalPlayer>,
EventWriter<KeepAliveEvent>,
)> = SystemState::new(ecs);
let (mut query, mut keepalive_events) = system_state.get_mut(ecs);
keepalive_events.send(KeepAliveEvent {
entity: player_entity,
id: p.id,
});
let mut local_player = query.get_mut(player_entity).unwrap();
local_player.write_packet(ServerboundKeepAlivePacket { id: p.id }.get());
debug!("Sent keep alive packet {p:?} for {player_entity:?}");
}
@ -1005,11 +1026,21 @@ impl PacketReceiver {
/// Loop that reads from the connection and adds the packets to the queue +
/// runs the schedule.
pub async fn read_task(self, mut read_conn: ReadConnection<ClientboundGamePacket>) {
while let Ok(packet) = read_conn.read().await {
loop {
match read_conn.read().await {
Ok(packet) => {
self.packets.lock().push(packet);
// tell the client to run all the systems
self.run_schedule_sender.send(()).await.unwrap();
}
Err(error) => {
if !matches!(*error, ReadPacketError::ConnectionClosed) {
error!("Error reading packet from Client: {error:?}");
}
return;
}
}
}
}
/// Consume the [`ServerboundGamePacket`] queue and actually write the

View file

@ -5,7 +5,7 @@ use azalea_ecs::{
event::EventReader,
system::{Commands, Res},
};
use azalea_world::EntityInfos;
use azalea_world::entity::EntityInfos;
use uuid::Uuid;
use crate::{packet_handling::AddPlayerEvent, GameProfileComponent};

View file

@ -4,15 +4,15 @@ edition = "2021"
license = "MIT"
name = "azalea-core"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-core"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-buf = {path = "../azalea-buf", version = "^0.5.0"}
azalea-chat = {path = "../azalea-chat", version = "^0.5.0"}
azalea-nbt = {path = "../azalea-nbt", version = "^0.5.0"}
azalea-registry = {path = "../azalea-registry", version = "^0.5.0"}
azalea-buf = { path = "../azalea-buf", version = "^0.6.0" }
azalea-chat = { path = "../azalea-chat", version = "^0.6.0" }
azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0" }
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
bevy_ecs = { version = "0.9.1", default-features = false, optional = true }
uuid = "^1.1.2"

4
azalea-crypto/Cargo.toml Executable file → Normal file
View file

@ -3,14 +3,14 @@ description = "Cryptography features used in Minecraft."
edition = "2021"
license = "MIT"
name = "azalea-crypto"
version = "0.5.0"
version = "0.6.0"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-crypto"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
aes = "0.8.1"
azalea-buf = {path = "../azalea-buf", version = "^0.5.0" }
azalea-buf = {path = "../azalea-buf", version = "^0.6.0" }
cfb8 = "0.8.1"
num-bigint = "^0.4.3"
rand = {version = "^0.8.4", features = ["getrandom"]}

View file

@ -1,13 +1,14 @@
[package]
description = "ECS stuff used in Azalea"
edition = "2021"
license = "MIT"
name = "azalea-ecs"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-ecs-macros = {path = "./azalea-ecs-macros", version = "^0.5.0"}
azalea-ecs-macros = {path = "./azalea-ecs-macros", version = "^0.6.0"}
bevy_app = "0.9.1"
bevy_ecs = {version = "0.9.1", default-features = false}
iyes_loopless = "0.9.1"
tokio = {version = "1.25.0", features = ["time"]}

View file

@ -3,7 +3,7 @@ description = "Azalea ECS Macros"
edition = "2021"
license = "MIT OR Apache-2.0"
name = "azalea-ecs-macros"
version = "0.5.0"
version = "0.6.0"
[lib]
proc-macro = true

View file

@ -6,5 +6,5 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-core = { version = "0.5.0", path = "../azalea-core" }
azalea-core = { version = "0.6.0", path = "../azalea-core" }
azalea-inventory-macros = { version = "0.1.0", path = "./azalea-inventory-macros" }

2
azalea-language/Cargo.toml Executable file → Normal file
View file

@ -4,7 +4,7 @@ edition = "2021"
license = "MIT"
name = "azalea-language"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-language"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

4
azalea-nbt/Cargo.toml Executable file → Normal file
View file

@ -3,14 +3,14 @@ description = "A fast NBT serializer and deserializer."
edition = "2021"
license = "MIT"
name = "azalea-nbt"
version = "0.5.0"
version = "0.6.0"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-nbt"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ahash = { version = "^0.8.0", features = ["serde"]}
azalea-buf = {path = "../azalea-buf", version = "^0.5.0" }
azalea-buf = {path = "../azalea-buf", version = "^0.6.0" }
byteorder = "^1.4.3"
flate2 = "^1.0.23"
log = "0.4.17"

View file

@ -4,19 +4,18 @@ edition = "2021"
license = "MIT"
name = "azalea-physics"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-physics"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-block = { path = "../azalea-block", version = "^0.5.0" }
azalea-core = { path = "../azalea-core", version = "^0.5.0" }
azalea-world = { path = "../azalea-world", version = "^0.5.0" }
azalea-registry = { path = "../azalea-registry", version = "^0.5.0" }
iyes_loopless = "0.9.1"
azalea-block = {path = "../azalea-block", version = "^0.6.0"}
azalea-core = {path = "../azalea-core", version = "^0.6.0"}
azalea-ecs = {version = "0.6.0", path = "../azalea-ecs"}
azalea-registry = {path = "../azalea-registry", version = "^0.6.0"}
azalea-world = {path = "../azalea-world", version = "^0.6.0"}
once_cell = "1.16.0"
parking_lot = "^0.12.1"
azalea-ecs = { version = "0.5.0", path = "../azalea-ecs" }
[dev-dependencies]
uuid = "^1.1.2"

File diff suppressed because it is too large Load diff

View file

@ -79,7 +79,7 @@ impl<'a> Iterator for BlockCollisions<'a> {
let block_state: BlockState = chunk
.read()
.get(&(&pos).into(), self.world.chunks.min_y)
.unwrap_or(BlockState::Air);
.unwrap_or(BlockState::AIR);
// TODO: continue if self.only_suffocating_blocks and the block is not
// suffocating

View file

@ -16,9 +16,10 @@ use azalea_ecs::{
};
use azalea_world::{
entity::{
metadata::Sprinting, move_relative, Attributes, Jumping, Physics, Position, WorldName,
metadata::Sprinting, move_relative, Attributes, Jumping, Local, Physics, Position,
WorldName,
},
Local, World, WorldContainer,
World, WorldContainer,
};
use collision::{move_colliding, MoverType};
@ -74,7 +75,7 @@ fn travel(
let block_state_below = world
.chunks
.get_block_state(&block_pos_below)
.unwrap_or(BlockState::Air);
.unwrap_or(BlockState::AIR);
let block_below: Box<dyn Block> = block_state_below.into();
let block_friction = block_below.behavior().friction;
@ -310,8 +311,8 @@ mod tests {
use azalea_core::{ChunkPos, ResourceLocation};
use azalea_ecs::{app::App, TickPlugin};
use azalea_world::{
entity::{EntityBundle, MinecraftEntityId},
Chunk, EntityPlugin, PartialWorld,
entity::{EntityBundle, EntityPlugin, MinecraftEntityId},
Chunk, PartialWorld,
};
use uuid::Uuid;
@ -411,7 +412,7 @@ mod tests {
.id();
let block_state = partial_world.chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::Stone,
azalea_registry::Block::Stone.into(),
&mut world_lock.write().chunks,
);
assert!(
@ -468,7 +469,11 @@ mod tests {
.id();
let block_state = partial_world.chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::StoneSlab_BottomFalse,
azalea_block::StoneSlabBlock {
kind: azalea_block::Type::Bottom,
waterlogged: false,
}
.into(),
&mut world_lock.write().chunks,
);
assert!(
@ -517,7 +522,11 @@ mod tests {
.id();
let block_state = world_lock.write().chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::StoneSlab_TopFalse,
azalea_block::StoneSlabBlock {
kind: azalea_block::Type::Top,
waterlogged: false,
}
.into(),
);
assert!(
block_state.is_some(),
@ -565,7 +574,15 @@ mod tests {
.id();
let block_state = world_lock.write().chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::CobblestoneWall_LowLowLowFalseFalseLow,
azalea_block::CobblestoneWallBlock {
east: azalea_block::EastWall::Low,
north: azalea_block::NorthWall::Low,
south: azalea_block::SouthWall::Low,
west: azalea_block::WestWall::Low,
up: false,
waterlogged: false,
}
.into(),
);
assert!(
block_state.is_some(),

View file

@ -4,24 +4,24 @@ edition = "2021"
license = "MIT"
name = "azalea-protocol"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-protocol"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-compression = {version = "^0.3.8", features = ["tokio", "zlib"], optional = true}
async-recursion = "1.0.0"
azalea-auth = {path = "../azalea-auth", version = "^0.5.0" }
azalea-block = {path = "../azalea-block", default-features = false, version = "^0.5.0" }
azalea-brigadier = {path = "../azalea-brigadier", version = "^0.5.0", features = ["azalea-buf"]}
azalea-buf = {path = "../azalea-buf", version = "^0.5.0" }
azalea-chat = {path = "../azalea-chat", version = "^0.5.0" }
azalea-core = {path = "../azalea-core", optional = true, version = "^0.5.0" }
azalea-crypto = {path = "../azalea-crypto", version = "^0.5.0" }
azalea-nbt = {path = "../azalea-nbt", version = "^0.5.0" }
azalea-protocol-macros = {path = "./azalea-protocol-macros", version = "^0.5.0" }
azalea-registry = {path = "../azalea-registry", version = "^0.5.0" }
azalea-world = {path = "../azalea-world", version = "^0.5.0" }
azalea-auth = {path = "../azalea-auth", version = "^0.6.0" }
azalea-block = {path = "../azalea-block", default-features = false, version = "^0.6.0" }
azalea-brigadier = {path = "../azalea-brigadier", version = "^0.6.0", features = ["azalea-buf"]}
azalea-buf = {path = "../azalea-buf", version = "^0.6.0" }
azalea-chat = {path = "../azalea-chat", version = "^0.6.0" }
azalea-core = {path = "../azalea-core", optional = true, version = "^0.6.0" }
azalea-crypto = {path = "../azalea-crypto", version = "^0.6.0" }
azalea-nbt = {path = "../azalea-nbt", version = "^0.6.0" }
azalea-protocol-macros = {path = "./azalea-protocol-macros", version = "^0.6.0" }
azalea-registry = {path = "../azalea-registry", version = "^0.6.0" }
azalea-world = {path = "../azalea-world", version = "^0.6.0" }
bevy_ecs = { version = "0.9.1", default-features = false }
byteorder = "^1.4.3"
bytes = "^1.1.0"

2
azalea-protocol/azalea-protocol-macros/Cargo.toml Executable file → Normal file
View file

@ -3,7 +3,7 @@ description = "Macros internally used in azalea-protocol."
edition = "2021"
license = "MIT"
name = "azalea-protocol-macros"
version = "0.5.0"
version = "0.6.0"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-protocol/azalea-protocol-macros"
[lib]

View file

@ -37,7 +37,7 @@ impl McBufReadable for BlockStateWithPosition {
impl McBufWritable for BlockStateWithPosition {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let data = (self.state 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::var_write_into(&data, buf)?;
Ok(())

6
azalea-registry/Cargo.toml Executable file → Normal file
View file

@ -4,11 +4,11 @@ edition = "2021"
license = "MIT"
name = "azalea-registry"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-registry"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-buf = {path = "../azalea-buf", version = "^0.5.0"}
azalea-registry-macros = {path = "./azalea-registry-macros", version = "^0.5.0"}
azalea-buf = {path = "../azalea-buf", version = "^0.6.0" }
azalea-registry-macros = {path = "./azalea-registry-macros", version = "^0.6.0" }
enum-as-inner = "0.5.1"

2
azalea-registry/azalea-registry-macros/Cargo.toml Executable file → Normal file
View file

@ -4,7 +4,7 @@ edition = "2021"
license = "MIT"
name = "azalea-registry-macros"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-registry/azalea-registry-macros"
version = "0.5.0"
version = "0.6.0"
[lib]
proc-macro = true

View file

@ -5,7 +5,7 @@ use syn::{
parse::{Parse, ParseStream, Result},
parse_macro_input,
punctuated::Punctuated,
Ident, LitStr, Token,
Attribute, Ident, LitStr, Token,
};
struct RegistryItem {
@ -16,6 +16,7 @@ struct RegistryItem {
struct Registry {
name: Ident,
items: Vec<RegistryItem>,
attributes: Vec<Attribute>,
}
impl Parse for RegistryItem {
@ -30,12 +31,16 @@ impl Parse for RegistryItem {
impl Parse for Registry {
fn parse(input: ParseStream) -> Result<Self> {
// Block, {
// enum Block {
// Air => "minecraft:air",
// Stone => "minecraft:stone"
// }
// this also includes docs
let attributes = input.call(Attribute::parse_outer).unwrap_or_default();
input.parse::<Token![enum]>()?;
let name = input.parse()?;
let _ = input.parse::<Token![,]>()?;
let content;
braced!(content in input);
let items: Punctuated<RegistryItem, Token![,]> =
@ -44,6 +49,7 @@ impl Parse for Registry {
Ok(Registry {
name,
items: items.into_iter().collect(),
attributes,
})
}
}
@ -66,7 +72,9 @@ pub fn registry(input: TokenStream) -> TokenStream {
#name = #protocol_id,
});
}
let attributes = input.attributes;
generated.extend(quote! {
#(#attributes)*
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, azalea_buf::McBuf)]
#[repr(u32)]
pub enum #name {

View file

@ -1,6 +1,9 @@
#![doc = include_str!("../README.md")]
// This file is automatically generated in codegen/lib/code/registry.py
// The contents of the macros below are generated in
// codegen/lib/code/registry.py, though the rest of the file isn't
// auto-generated (so you can add doc comments to the registry enums if you
// want)
use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable};
use azalea_registry_macros::registry;
@ -39,7 +42,9 @@ impl<T: Registry> McBufWritable for OptionalRegistry<T> {
}
}
registry!(Activity, {
registry! {
/// The AI code that's currently being executed for the entity.
enum Activity {
Core => "minecraft:core",
Idle => "minecraft:idle",
Work => "minecraft:work",
@ -66,9 +71,11 @@ registry!(Activity, {
Roar => "minecraft:roar",
Emerge => "minecraft:emerge",
Dig => "minecraft:dig",
});
}
}
registry!(Attribute, {
registry! {
enum Attribute {
GenericMaxHealth => "minecraft:generic.max_health",
GenericFollowRange => "minecraft:generic.follow_range",
GenericKnockbackResistance => "minecraft:generic.knockback_resistance",
@ -82,9 +89,11 @@ registry!(Attribute, {
GenericLuck => "minecraft:generic.luck",
ZombieSpawnReinforcements => "minecraft:zombie.spawn_reinforcements",
HorseJumpStrength => "minecraft:horse.jump_strength",
});
}
}
registry!(BannerPattern, {
registry! {
enum BannerPattern {
Base => "minecraft:base",
SquareBottomLeft => "minecraft:square_bottom_left",
SquareBottomRight => "minecraft:square_bottom_right",
@ -126,9 +135,16 @@ registry!(BannerPattern, {
Flower => "minecraft:flower",
Mojang => "minecraft:mojang",
Piglin => "minecraft:piglin",
});
}
}
registry!(Block, {
registry! {
/// An enum of every type of block in the game. To represent a block *state*,
/// use [`azalea_block::BlockState`] or the [`azalea_block::Block`] trait.
///
/// [`azalea_block::BlockState`]: https://docs.rs/azalea-block/latest/azalea_block/struct.BlockState.html
/// [`azalea_block::Block`]: https://docs.rs/azalea-block/latest/azalea_block/trait.Block.html
enum Block {
Air => "minecraft:air",
Stone => "minecraft:stone",
Granite => "minecraft:granite",
@ -1101,9 +1117,14 @@ registry!(Block, {
PearlescentFroglight => "minecraft:pearlescent_froglight",
Frogspawn => "minecraft:frogspawn",
ReinforcedDeepslate => "minecraft:reinforced_deepslate",
});
}
}
registry!(BlockEntityKind, {
registry! {
/// An enum that contains every type of block entity. A block entity is a block
/// that contains data that can't be represented as just a block state, like
/// how chests store items.
enum BlockEntityKind {
Furnace => "minecraft:furnace",
Chest => "minecraft:chest",
TrappedChest => "minecraft:trapped_chest",
@ -1142,9 +1163,11 @@ registry!(BlockEntityKind, {
SculkCatalyst => "minecraft:sculk_catalyst",
SculkShrieker => "minecraft:sculk_shrieker",
ChiseledBookshelf => "minecraft:chiseled_bookshelf",
});
}
}
registry!(BlockPredicateKind, {
registry! {
enum BlockPredicateKind {
MatchingBlocks => "minecraft:matching_blocks",
MatchingBlockTag => "minecraft:matching_block_tag",
MatchingFluids => "minecraft:matching_fluids",
@ -1157,9 +1180,11 @@ registry!(BlockPredicateKind, {
AllOf => "minecraft:all_of",
Not => "minecraft:not",
True => "minecraft:true",
});
}
}
registry!(CatVariant, {
registry! {
enum CatVariant {
Tabby => "minecraft:tabby",
Black => "minecraft:black",
Red => "minecraft:red",
@ -1171,9 +1196,11 @@ registry!(CatVariant, {
White => "minecraft:white",
Jellie => "minecraft:jellie",
AllBlack => "minecraft:all_black",
});
}
}
registry!(ChunkStatus, {
registry! {
enum ChunkStatus {
Empty => "minecraft:empty",
StructureStarts => "minecraft:structure_starts",
StructureReferences => "minecraft:structure_references",
@ -1187,9 +1214,11 @@ registry!(ChunkStatus, {
Spawn => "minecraft:spawn",
Heightmaps => "minecraft:heightmaps",
Full => "minecraft:full",
});
}
}
registry!(CommandArgumentKind, {
registry! {
enum CommandArgumentKind {
Bool => "brigadier:bool",
Float => "brigadier:float",
Double => "brigadier:double",
@ -1238,9 +1267,11 @@ registry!(CommandArgumentKind, {
TemplateMirror => "minecraft:template_mirror",
TemplateRotation => "minecraft:template_rotation",
Uuid => "minecraft:uuid",
});
}
}
registry!(CustomStat, {
registry! {
enum CustomStat {
LeaveGame => "minecraft:leave_game",
PlayTime => "minecraft:play_time",
TotalWorldTime => "minecraft:total_world_time",
@ -1316,9 +1347,11 @@ registry!(CustomStat, {
InteractWithGrindstone => "minecraft:interact_with_grindstone",
TargetHit => "minecraft:target_hit",
InteractWithSmithingTable => "minecraft:interact_with_smithing_table",
});
}
}
registry!(Enchantment, {
registry! {
enum Enchantment {
Protection => "minecraft:protection",
FireProtection => "minecraft:fire_protection",
FeatherFalling => "minecraft:feather_falling",
@ -1358,9 +1391,12 @@ registry!(Enchantment, {
Piercing => "minecraft:piercing",
Mending => "minecraft:mending",
VanishingCurse => "minecraft:vanishing_curse",
});
}
}
registry!(EntityKind, {
registry! {
/// An enum that contains every type of entity.
enum EntityKind {
Allay => "minecraft:allay",
AreaEffectCloud => "minecraft:area_effect_cloud",
ArmorStand => "minecraft:armor_stand",
@ -1480,30 +1516,38 @@ registry!(EntityKind, {
ZombifiedPiglin => "minecraft:zombified_piglin",
Player => "minecraft:player",
FishingBobber => "minecraft:fishing_bobber",
});
}
}
registry!(FloatProviderKind, {
registry! {
enum FloatProviderKind {
Constant => "minecraft:constant",
Uniform => "minecraft:uniform",
ClampedNormal => "minecraft:clamped_normal",
Trapezoid => "minecraft:trapezoid",
});
}
}
registry!(Fluid, {
registry! {
enum Fluid {
Empty => "minecraft:empty",
FlowingWater => "minecraft:flowing_water",
Water => "minecraft:water",
FlowingLava => "minecraft:flowing_lava",
Lava => "minecraft:lava",
});
}
}
registry!(FrogVariant, {
registry! {
enum FrogVariant {
Temperate => "minecraft:temperate",
Warm => "minecraft:warm",
Cold => "minecraft:cold",
});
}
}
registry!(GameEvent, {
registry! {
enum GameEvent {
BlockActivate => "minecraft:block_activate",
BlockAttach => "minecraft:block_attach",
BlockChange => "minecraft:block_change",
@ -1550,18 +1594,22 @@ registry!(GameEvent, {
Step => "minecraft:step",
Swim => "minecraft:swim",
Teleport => "minecraft:teleport",
});
}
}
registry!(HeightProviderKind, {
registry! {
enum HeightProviderKind {
Constant => "minecraft:constant",
Uniform => "minecraft:uniform",
BiasedToBottom => "minecraft:biased_to_bottom",
VeryBiasedToBottom => "minecraft:very_biased_to_bottom",
Trapezoid => "minecraft:trapezoid",
WeightedList => "minecraft:weighted_list",
});
}
}
registry!(Instrument, {
registry! {
enum Instrument {
PonderGoatHorn => "minecraft:ponder_goat_horn",
SingGoatHorn => "minecraft:sing_goat_horn",
SeekGoatHorn => "minecraft:seek_goat_horn",
@ -1570,18 +1618,22 @@ registry!(Instrument, {
CallGoatHorn => "minecraft:call_goat_horn",
YearnGoatHorn => "minecraft:yearn_goat_horn",
DreamGoatHorn => "minecraft:dream_goat_horn",
});
}
}
registry!(IntProviderKind, {
registry! {
enum IntProviderKind {
Constant => "minecraft:constant",
Uniform => "minecraft:uniform",
BiasedToBottom => "minecraft:biased_to_bottom",
Clamped => "minecraft:clamped",
WeightedList => "minecraft:weighted_list",
ClampedNormal => "minecraft:clamped_normal",
});
}
}
registry!(Item, {
registry! {
enum Item {
Air => "minecraft:air",
Stone => "minecraft:stone",
Granite => "minecraft:granite",
@ -2768,9 +2820,11 @@ registry!(Item, {
PearlescentFroglight => "minecraft:pearlescent_froglight",
Frogspawn => "minecraft:frogspawn",
EchoShard => "minecraft:echo_shard",
});
}
}
registry!(LootConditionKind, {
registry! {
enum LootConditionKind {
Inverted => "minecraft:inverted",
Alternative => "minecraft:alternative",
RandomChance => "minecraft:random_chance",
@ -2788,9 +2842,11 @@ registry!(LootConditionKind, {
Reference => "minecraft:reference",
TimeCheck => "minecraft:time_check",
ValueCheck => "minecraft:value_check",
});
}
}
registry!(LootFunctionKind, {
registry! {
enum LootFunctionKind {
SetCount => "minecraft:set_count",
EnchantWithLevels => "minecraft:enchant_with_levels",
EnchantRandomly => "minecraft:enchant_randomly",
@ -2816,21 +2872,27 @@ registry!(LootFunctionKind, {
SetBannerPattern => "minecraft:set_banner_pattern",
SetPotion => "minecraft:set_potion",
SetInstrument => "minecraft:set_instrument",
});
}
}
registry!(LootNbtProviderKind, {
registry! {
enum LootNbtProviderKind {
Storage => "minecraft:storage",
Context => "minecraft:context",
});
}
}
registry!(LootNumberProviderKind, {
registry! {
enum LootNumberProviderKind {
Constant => "minecraft:constant",
Uniform => "minecraft:uniform",
Binomial => "minecraft:binomial",
Score => "minecraft:score",
});
}
}
registry!(LootPoolEntryKind, {
registry! {
enum LootPoolEntryKind {
Empty => "minecraft:empty",
Item => "minecraft:item",
LootTable => "minecraft:loot_table",
@ -2839,14 +2901,18 @@ registry!(LootPoolEntryKind, {
Alternatives => "minecraft:alternatives",
Sequence => "minecraft:sequence",
Group => "minecraft:group",
});
}
}
registry!(LootScoreProviderKind, {
registry! {
enum LootScoreProviderKind {
Fixed => "minecraft:fixed",
Context => "minecraft:context",
});
}
}
registry!(MemoryModuleKind, {
registry! {
enum MemoryModuleKind {
Dummy => "minecraft:dummy",
Home => "minecraft:home",
JobSite => "minecraft:job_site",
@ -2940,9 +3006,11 @@ registry!(MemoryModuleKind, {
LikedNoteblock => "minecraft:liked_noteblock",
LikedNoteblockCooldownTicks => "minecraft:liked_noteblock_cooldown_ticks",
ItemPickupCooldownTicks => "minecraft:item_pickup_cooldown_ticks",
});
}
}
registry!(Menu, {
registry! {
enum Menu {
Generic9x1 => "minecraft:generic_9x1",
Generic9x2 => "minecraft:generic_9x2",
Generic9x3 => "minecraft:generic_9x3",
@ -2967,9 +3035,11 @@ registry!(Menu, {
Smoker => "minecraft:smoker",
CartographyTable => "minecraft:cartography_table",
Stonecutter => "minecraft:stonecutter",
});
}
}
registry!(MobEffect, {
registry! {
enum MobEffect {
Speed => "minecraft:speed",
Slowness => "minecraft:slowness",
Haste => "minecraft:haste",
@ -3003,9 +3073,11 @@ registry!(MobEffect, {
BadOmen => "minecraft:bad_omen",
HeroOfTheVillage => "minecraft:hero_of_the_village",
Darkness => "minecraft:darkness",
});
}
}
registry!(PaintingVariant, {
registry! {
enum PaintingVariant {
Kebab => "minecraft:kebab",
Aztec => "minecraft:aztec",
Alban => "minecraft:alban",
@ -3036,9 +3108,11 @@ registry!(PaintingVariant, {
Water => "minecraft:water",
Fire => "minecraft:fire",
DonkeyKong => "minecraft:donkey_kong",
});
}
}
registry!(ParticleKind, {
registry! {
enum ParticleKind {
AmbientEntityEffect => "minecraft:ambient_entity_effect",
AngryVillager => "minecraft:angry_villager",
Block => "minecraft:block",
@ -3132,9 +3206,11 @@ registry!(ParticleKind, {
ElectricSpark => "minecraft:electric_spark",
Scrape => "minecraft:scrape",
Shriek => "minecraft:shriek",
});
}
}
registry!(PointOfInterestKind, {
registry! {
enum PointOfInterestKind {
Armorer => "minecraft:armorer",
Butcher => "minecraft:butcher",
Cartographer => "minecraft:cartographer",
@ -3155,20 +3231,26 @@ registry!(PointOfInterestKind, {
NetherPortal => "minecraft:nether_portal",
Lodestone => "minecraft:lodestone",
LightningRod => "minecraft:lightning_rod",
});
}
}
registry!(PosRuleTest, {
registry! {
enum PosRuleTest {
AlwaysTrue => "minecraft:always_true",
LinearPos => "minecraft:linear_pos",
AxisAlignedLinearPos => "minecraft:axis_aligned_linear_pos",
});
}
}
registry!(PositionSourceKind, {
registry! {
enum PositionSourceKind {
Block => "minecraft:block",
Entity => "minecraft:entity",
});
}
}
registry!(Potion, {
registry! {
enum Potion {
Empty => "minecraft:empty",
Water => "minecraft:water",
Mundane => "minecraft:mundane",
@ -3212,9 +3294,11 @@ registry!(Potion, {
Luck => "minecraft:luck",
SlowFalling => "minecraft:slow_falling",
LongSlowFalling => "minecraft:long_slow_falling",
});
}
}
registry!(RecipeSerializer, {
registry! {
enum RecipeSerializer {
CraftingShaped => "minecraft:crafting_shaped",
CraftingShapeless => "minecraft:crafting_shapeless",
CraftingSpecialArmordye => "minecraft:crafting_special_armordye",
@ -3236,9 +3320,11 @@ registry!(RecipeSerializer, {
CampfireCooking => "minecraft:campfire_cooking",
Stonecutting => "minecraft:stonecutting",
Smithing => "minecraft:smithing",
});
}
}
registry!(RecipeKind, {
registry! {
enum RecipeKind {
Crafting => "minecraft:crafting",
Smelting => "minecraft:smelting",
Blasting => "minecraft:blasting",
@ -3246,25 +3332,31 @@ registry!(RecipeKind, {
CampfireCooking => "minecraft:campfire_cooking",
Stonecutting => "minecraft:stonecutting",
Smithing => "minecraft:smithing",
});
}
}
registry!(RuleTest, {
registry! {
enum RuleTest {
AlwaysTrue => "minecraft:always_true",
BlockMatch => "minecraft:block_match",
BlockstateMatch => "minecraft:blockstate_match",
TagMatch => "minecraft:tag_match",
RandomBlockMatch => "minecraft:random_block_match",
RandomBlockstateMatch => "minecraft:random_blockstate_match",
});
}
}
registry!(Schedule, {
registry! {
enum Schedule {
Empty => "minecraft:empty",
Simple => "minecraft:simple",
VillagerBaby => "minecraft:villager_baby",
VillagerDefault => "minecraft:villager_default",
});
}
}
registry!(SensorKind, {
registry! {
enum SensorKind {
Dummy => "minecraft:dummy",
NearestItems => "minecraft:nearest_items",
NearestLivingEntities => "minecraft:nearest_living_entities",
@ -3287,9 +3379,11 @@ registry!(SensorKind, {
FrogAttackables => "minecraft:frog_attackables",
IsInWater => "minecraft:is_in_water",
WardenEntitySensor => "minecraft:warden_entity_sensor",
});
}
}
registry!(SoundEvent, {
registry! {
enum SoundEvent {
EntityAllayAmbientWithItem => "minecraft:entity.allay.ambient_with_item",
EntityAllayAmbientWithoutItem => "minecraft:entity.allay.ambient_without_item",
EntityAllayDeath => "minecraft:entity.allay.death",
@ -4682,9 +4776,11 @@ registry!(SoundEvent, {
EntityZombieVillagerDeath => "minecraft:entity.zombie_villager.death",
EntityZombieVillagerHurt => "minecraft:entity.zombie_villager.hurt",
EntityZombieVillagerStep => "minecraft:entity.zombie_villager.step",
});
}
}
registry!(StatKind, {
registry! {
enum StatKind {
Mined => "minecraft:mined",
Crafted => "minecraft:crafted",
Used => "minecraft:used",
@ -4694,9 +4790,11 @@ registry!(StatKind, {
Killed => "minecraft:killed",
KilledBy => "minecraft:killed_by",
Custom => "minecraft:custom",
});
}
}
registry!(VillagerProfession, {
registry! {
enum VillagerProfession {
None => "minecraft:none",
Armorer => "minecraft:armorer",
Butcher => "minecraft:butcher",
@ -4712,9 +4810,11 @@ registry!(VillagerProfession, {
Shepherd => "minecraft:shepherd",
Toolsmith => "minecraft:toolsmith",
Weaponsmith => "minecraft:weaponsmith",
});
}
}
registry!(VillagerKind, {
registry! {
enum VillagerKind {
Desert => "minecraft:desert",
Jungle => "minecraft:jungle",
Plains => "minecraft:plains",
@ -4722,16 +4822,20 @@ registry!(VillagerKind, {
Snow => "minecraft:snow",
Swamp => "minecraft:swamp",
Taiga => "minecraft:taiga",
});
}
}
registry!(WorldgenBiomeSource, {
registry! {
enum WorldgenBiomeSource {
Fixed => "minecraft:fixed",
MultiNoise => "minecraft:multi_noise",
Checkerboard => "minecraft:checkerboard",
TheEnd => "minecraft:the_end",
});
}
}
registry!(WorldgenBlockStateProviderKind, {
registry! {
enum WorldgenBlockStateProviderKind {
SimpleStateProvider => "minecraft:simple_state_provider",
WeightedStateProvider => "minecraft:weighted_state_provider",
NoiseThresholdProvider => "minecraft:noise_threshold_provider",
@ -4739,21 +4843,27 @@ registry!(WorldgenBlockStateProviderKind, {
DualNoiseProvider => "minecraft:dual_noise_provider",
RotatedBlockProvider => "minecraft:rotated_block_provider",
RandomizedIntStateProvider => "minecraft:randomized_int_state_provider",
});
}
}
registry!(WorldgenCarver, {
registry! {
enum WorldgenCarver {
Cave => "minecraft:cave",
NetherCave => "minecraft:nether_cave",
Canyon => "minecraft:canyon",
});
}
}
registry!(WorldgenChunkGenerator, {
registry! {
enum WorldgenChunkGenerator {
Noise => "minecraft:noise",
Flat => "minecraft:flat",
Debug => "minecraft:debug",
});
}
}
registry!(WorldgenDensityFunctionKind, {
registry! {
enum WorldgenDensityFunctionKind {
BlendAlpha => "minecraft:blend_alpha",
BlendOffset => "minecraft:blend_offset",
Beardifier => "minecraft:beardifier",
@ -4786,9 +4896,11 @@ registry!(WorldgenDensityFunctionKind, {
Spline => "minecraft:spline",
Constant => "minecraft:constant",
YClampedGradient => "minecraft:y_clamped_gradient",
});
}
}
registry!(WorldgenFeature, {
registry! {
enum WorldgenFeature {
NoOp => "minecraft:no_op",
Tree => "minecraft:tree",
Flower => "minecraft:flower",
@ -4850,14 +4962,18 @@ registry!(WorldgenFeature, {
LargeDripstone => "minecraft:large_dripstone",
PointedDripstone => "minecraft:pointed_dripstone",
SculkPatch => "minecraft:sculk_patch",
});
}
}
registry!(WorldgenFeatureSizeKind, {
registry! {
enum WorldgenFeatureSizeKind {
TwoLayersFeatureSize => "minecraft:two_layers_feature_size",
ThreeLayersFeatureSize => "minecraft:three_layers_feature_size",
});
}
}
registry!(WorldgenFoliagePlacerKind, {
registry! {
enum WorldgenFoliagePlacerKind {
BlobFoliagePlacer => "minecraft:blob_foliage_placer",
SpruceFoliagePlacer => "minecraft:spruce_foliage_placer",
PineFoliagePlacer => "minecraft:pine_foliage_placer",
@ -4868,9 +4984,11 @@ registry!(WorldgenFoliagePlacerKind, {
MegaPineFoliagePlacer => "minecraft:mega_pine_foliage_placer",
DarkOakFoliagePlacer => "minecraft:dark_oak_foliage_placer",
RandomSpreadFoliagePlacer => "minecraft:random_spread_foliage_placer",
});
}
}
registry!(WorldgenMaterialCondition, {
registry! {
enum WorldgenMaterialCondition {
Biome => "minecraft:biome",
NoiseThreshold => "minecraft:noise_threshold",
VerticalGradient => "minecraft:vertical_gradient",
@ -4882,16 +5000,20 @@ registry!(WorldgenMaterialCondition, {
Hole => "minecraft:hole",
AbovePreliminarySurface => "minecraft:above_preliminary_surface",
StoneDepth => "minecraft:stone_depth",
});
}
}
registry!(WorldgenMaterialRule, {
registry! {
enum WorldgenMaterialRule {
Bandlands => "minecraft:bandlands",
Block => "minecraft:block",
Sequence => "minecraft:sequence",
Condition => "minecraft:condition",
});
}
}
registry!(WorldgenPlacementModifierKind, {
registry! {
enum WorldgenPlacementModifierKind {
BlockPredicateFilter => "minecraft:block_predicate_filter",
RarityFilter => "minecraft:rarity_filter",
SurfaceRelativeThresholdFilter => "minecraft:surface_relative_threshold_filter",
@ -4907,13 +5029,17 @@ registry!(WorldgenPlacementModifierKind, {
InSquare => "minecraft:in_square",
RandomOffset => "minecraft:random_offset",
CarvingMask => "minecraft:carving_mask",
});
}
}
registry!(WorldgenRootPlacerKind, {
registry! {
enum WorldgenRootPlacerKind {
MangroveRootPlacer => "minecraft:mangrove_root_placer",
});
}
}
registry!(WorldgenStructurePiece, {
registry! {
enum WorldgenStructurePiece {
Mscorridor => "minecraft:mscorridor",
Mscrossing => "minecraft:mscrossing",
Msroom => "minecraft:msroom",
@ -4970,22 +5096,28 @@ registry!(WorldgenStructurePiece, {
Shipwreck => "minecraft:shipwreck",
Nefos => "minecraft:nefos",
Jigsaw => "minecraft:jigsaw",
});
}
}
registry!(WorldgenStructurePlacement, {
registry! {
enum WorldgenStructurePlacement {
RandomSpread => "minecraft:random_spread",
ConcentricRings => "minecraft:concentric_rings",
});
}
}
registry!(WorldgenStructurePoolElement, {
registry! {
enum WorldgenStructurePoolElement {
SinglePoolElement => "minecraft:single_pool_element",
ListPoolElement => "minecraft:list_pool_element",
FeaturePoolElement => "minecraft:feature_pool_element",
EmptyPoolElement => "minecraft:empty_pool_element",
LegacySinglePoolElement => "minecraft:legacy_single_pool_element",
});
}
}
registry!(WorldgenStructureProcessor, {
registry! {
enum WorldgenStructureProcessor {
BlockIgnore => "minecraft:block_ignore",
BlockRot => "minecraft:block_rot",
Gravity => "minecraft:gravity",
@ -4996,9 +5128,11 @@ registry!(WorldgenStructureProcessor, {
BlackstoneReplace => "minecraft:blackstone_replace",
LavaSubmergedBlock => "minecraft:lava_submerged_block",
ProtectedBlocks => "minecraft:protected_blocks",
});
}
}
registry!(WorldgenStructureKind, {
registry! {
enum WorldgenStructureKind {
BuriedTreasure => "minecraft:buried_treasure",
DesertPyramid => "minecraft:desert_pyramid",
EndCity => "minecraft:end_city",
@ -5015,18 +5149,22 @@ registry!(WorldgenStructureKind, {
Stronghold => "minecraft:stronghold",
SwampHut => "minecraft:swamp_hut",
WoodlandMansion => "minecraft:woodland_mansion",
});
}
}
registry!(WorldgenTreeDecoratorKind, {
registry! {
enum WorldgenTreeDecoratorKind {
TrunkVine => "minecraft:trunk_vine",
LeaveVine => "minecraft:leave_vine",
Cocoa => "minecraft:cocoa",
Beehive => "minecraft:beehive",
AlterGround => "minecraft:alter_ground",
AttachedToLeaves => "minecraft:attached_to_leaves",
});
}
}
registry!(WorldgenTrunkPlacerKind, {
registry! {
enum WorldgenTrunkPlacerKind {
StraightTrunkPlacer => "minecraft:straight_trunk_placer",
ForkingTrunkPlacer => "minecraft:forking_trunk_placer",
GiantTrunkPlacer => "minecraft:giant_trunk_placer",
@ -5035,4 +5173,5 @@ registry!(WorldgenTrunkPlacerKind, {
FancyTrunkPlacer => "minecraft:fancy_trunk_placer",
BendingTrunkPlacer => "minecraft:bending_trunk_placer",
UpwardsBranchingTrunkPlacer => "minecraft:upwards_branching_trunk_placer",
});
}
}

View file

@ -4,21 +4,20 @@ edition = "2021"
license = "MIT"
name = "azalea-world"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-world"
version = "0.5.0"
version = "0.6.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-block = {path = "../azalea-block", default-features = false, version = "^0.5.0"}
azalea-buf = {path = "../azalea-buf", version = "^0.5.0"}
azalea-chat = {path = "../azalea-chat", version = "^0.5.0"}
azalea-core = {path = "../azalea-core", version = "^0.5.0", features = ["bevy_ecs"]}
azalea-ecs = { version = "0.5.0", path = "../azalea-ecs" }
azalea-nbt = {path = "../azalea-nbt", version = "^0.5.0"}
azalea-registry = {path = "../azalea-registry", version = "^0.5.0"}
azalea-block = {path = "../azalea-block", default-features = false, version = "^0.6.0"}
azalea-buf = {path = "../azalea-buf", version = "^0.6.0"}
azalea-chat = {path = "../azalea-chat", version = "^0.6.0"}
azalea-core = {path = "../azalea-core", version = "^0.6.0", features = ["bevy_ecs"]}
azalea-ecs = {version = "0.6.0", path = "../azalea-ecs"}
azalea-nbt = {path = "../azalea-nbt", version = "^0.6.0"}
azalea-registry = {path = "../azalea-registry", version = "^0.6.0"}
derive_more = {version = "0.99.17", features = ["deref", "deref_mut"]}
enum-as-inner = "0.5.1"
iyes_loopless = "0.9.1"
log = "0.4.17"
nohash-hasher = "0.2.0"
once_cell = "1.16.0"

View file

@ -343,20 +343,20 @@ impl Section {
.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)
BlockState::try_from(state).unwrap_or(BlockState::AIR)
}
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 as u32);
.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)
BlockState::try_from(previous_state).unwrap_or(BlockState::AIR)
}
fn set(&mut self, pos: ChunkSectionBlockPos, state: BlockState) {
self.states
.set(pos.x as usize, pos.y as usize, pos.z as usize, state as u32);
.set(pos.x as usize, pos.y as usize, pos.z as usize, state.id);
}
}

View file

@ -1,3 +1,6 @@
//! Implement things relating to entity datas, like an index of uuids to
//! entities.
use crate::{
deduplicate_entities, deduplicate_local_entities,
entity::{
@ -27,6 +30,8 @@ use std::{
};
use uuid::Uuid;
use super::Local;
/// Plugin handling some basic entity functionality.
pub struct EntityPlugin;
impl Plugin for EntityPlugin {
@ -263,11 +268,6 @@ pub fn add_updates_received(
}
}
/// A marker component that signifies that this entity is "local" and shouldn't
/// be updated by other clients.
#[derive(Component)]
pub struct Local;
/// The [`UpdatesReceived`] component should never be on [`Local`] entities.
/// This warns if an entity has both components.
fn debug_detect_updates_received_on_local_entities(

View file

@ -2368,7 +2368,7 @@ impl Default for EndermanMetadataBundle {
},
},
},
carry_state: CarryState(BlockState::Air),
carry_state: CarryState(BlockState::AIR),
creepy: Creepy(false),
stared_at: StaredAt(false),
}

View file

@ -3,6 +3,7 @@
pub mod attributes;
mod data;
mod dimensions;
mod info;
pub mod metadata;
use crate::ChunkStorage;
@ -21,6 +22,7 @@ use azalea_ecs::{
pub use data::*;
use derive_more::{Deref, DerefMut};
pub use dimensions::{update_bounding_box, EntityDimensions};
pub use info::{EntityInfos, EntityPlugin, LoadedBy, PartialEntityInfos, RelativeEntityUpdate};
use std::fmt::Debug;
use uuid::Uuid;
@ -92,7 +94,7 @@ pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: &Position) -> Bloc
// TODO: check if block below is a fence, wall, or fence gate
let block_pos = pos.down(1);
let block_state = chunk_storage.get_block_state(&block_pos);
if block_state == Some(BlockState::Air) {
if block_state == Some(BlockState::AIR) {
let block_pos_below = block_pos.down(1);
let block_state_below = chunk_storage.get_block_state(&block_pos_below);
if let Some(_block_state_below) = block_state_below {
@ -314,6 +316,11 @@ pub struct PlayerBundle {
pub metadata: metadata::PlayerMetadataBundle,
}
/// A marker component that signifies that this entity is "local" and shouldn't
/// be updated by other clients.
#[derive(Component)]
pub struct Local;
// #[cfg(test)]
// mod tests {
// use super::*;

View file

@ -7,7 +7,6 @@ mod bit_storage;
mod chunk_storage;
mod container;
pub mod entity;
mod entity_info;
mod palette;
mod world;
@ -16,9 +15,6 @@ use std::backtrace::Backtrace;
pub use bit_storage::BitStorage;
pub use chunk_storage::{Chunk, ChunkStorage, PartialChunkStorage};
pub use container::*;
pub use entity_info::{
EntityInfos, EntityPlugin, LoadedBy, Local, PartialEntityInfos, RelativeEntityUpdate,
};
use thiserror::Error;
pub use world::*;

View file

@ -1,7 +1,8 @@
use crate::{
entity::{EntityUuid, MinecraftEntityId, WorldName},
entity_info::LoadedBy,
ChunkStorage, EntityInfos, Local, PartialChunkStorage, PartialEntityInfos, WorldContainer,
entity::{
EntityInfos, EntityUuid, LoadedBy, Local, MinecraftEntityId, PartialEntityInfos, WorldName,
},
ChunkStorage, PartialChunkStorage, WorldContainer,
};
use azalea_core::ChunkPos;
use azalea_ecs::{

View file

@ -4,30 +4,29 @@ edition = "2021"
license = "MIT"
name = "azalea"
repository = "https://github.com/mat-1/azalea/tree/main/azalea"
version = "0.5.0"
version = "0.6.0"
[package.metadata.release]
pre-release-replacements = [
{file = "src/lib.rs", search = "//! `azalea = \"[a-z0-9\\.-]+\"`", replace = "//! `azalea = \"{{version}}\"`"},
{file = "README.md", search = "`azalea = \"[a-z0-9\\.-]+\"`", replace = "`azalea = \"{{version}}\"`"},
]
[dependencies]
anyhow = "^1.0.65"
async-trait = "0.1.58"
azalea-block = {version = "0.5.0", path = "../azalea-block"}
azalea-chat = {version = "0.5.0", path = "../azalea-chat"}
azalea-client = {version = "0.5.0", path = "../azalea-client"}
azalea-core = {version = "0.5.0", path = "../azalea-core"}
azalea-ecs = { version = "0.5.0", path = "../azalea-ecs" }
azalea-physics = {version = "0.5.0", path = "../azalea-physics"}
azalea-protocol = {version = "0.5.0", path = "../azalea-protocol"}
azalea-registry = {version = "0.5.0", path = "../azalea-registry"}
azalea-world = {version = "0.5.0", path = "../azalea-world"}
azalea-block = {version = "0.6.0", path = "../azalea-block"}
azalea-chat = {version = "0.6.0", path = "../azalea-chat"}
azalea-client = {version = "0.6.0", path = "../azalea-client"}
azalea-core = {version = "0.6.0", path = "../azalea-core"}
azalea-ecs = {version = "0.6.0", path = "../azalea-ecs"}
azalea-physics = {version = "0.6.0", path = "../azalea-physics"}
azalea-protocol = {version = "0.6.0", path = "../azalea-protocol"}
azalea-registry = {version = "0.6.0", path = "../azalea-registry"}
azalea-world = {version = "0.6.0", path = "../azalea-world"}
bevy_tasks = "0.9.1"
derive_more = {version = "0.99.17", features = ["deref", "deref_mut"]}
futures = "0.3.25"
futures-lite = "1.12.0"
iyes_loopless = "0.9.1"
log = "0.4.17"
nohash-hasher = "0.2.0"
num-traits = "0.2.15"

View file

@ -15,7 +15,7 @@ Then, add one of the following lines to your Cargo.toml:
Latest bleeding-edge version:
`azalea = { git="https://github.com/mat-1/azalea" }`\
Latest "stable" release:
`azalea = "0.5.0"`
`azalea = "0.6.0"`
## Optimization
@ -33,7 +33,6 @@ opt-level = 1
opt-level = 3
```
# Examples
```rust,no_run
@ -49,9 +48,9 @@ async fn main() {
// or Account::microsoft("example@example.com").await.unwrap();
loop {
let e = azalea::ClientBuilder::new()
let e = ClientBuilder::new()
.set_handler(handle)
.start(account, "localhost")
.start(account.clone(), "localhost")
.await;
eprintln!("{:?}", e);
}
@ -72,6 +71,10 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
}
```
# Swarms
Azalea lets you create "swarms", which are a group of bots in the same world that can perform actions together. See [testbot](https://github.com/mat-1/azalea/blob/main/azalea/examples/testbot.rs) for an example. Also, if you're using swarms, you should also have both `azalea::prelude::*` and `azalea::swarm::prelude::*`.
# Plugins
Azalea uses [Bevy ECS](https://docs.rs/bevy_ecs) internally to store information about the world and clients. Bevy plugins are more powerful than async handler functions, but more difficult to use. See [pathfinder](azalea/src/pathfinder/mod.rs) as an example of how to make a plugin. You can then enable a plugin by adding `.add_plugin(ExamplePlugin)` in your client/swarm builder.

View file

@ -3,7 +3,7 @@ use azalea::prelude::*;
use parking_lot::Mutex;
use std::sync::Arc;
#[derive(Default, Clone)]
#[derive(Default, Clone, Component)]
struct State {
pub started: Arc<Mutex<bool>>,
}
@ -13,13 +13,9 @@ async fn main() {
let account = Account::offline("bot");
// or let bot = Account::microsoft("email").await;
azalea::start(azalea::Options {
account,
address: "localhost",
state: State::default(),
plugins: plugins![],
handle,
})
azalea::ClientBuilder::new()
.set_handler(handle)
.start(account, "localhost")
.await
.unwrap();
}

View file

@ -7,13 +7,9 @@ async fn main() {
let account = Account::offline("bot");
// or let account = Account::microsoft("email").await;
azalea::start(azalea::Options {
account,
address: "localhost",
state: State::default(),
plugins: plugins![],
handle,
})
ClientBuilder::new()
.set_handler(handle)
.start(account, "localhost")
.await
.unwrap();
}
@ -28,7 +24,7 @@ async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()>
if sender == bot.profile.name {
return Ok(()); // ignore our own messages
}
bot.chat(&content).await?;
bot.chat(&content);
};
}
_ => {}

View file

@ -1,5 +1,4 @@
use azalea::{prelude::*, SwarmEvent};
use azalea::{Account, Client, Event, Swarm};
use azalea::{prelude::*, swarm::prelude::*};
#[tokio::main]
async fn main() {
@ -7,44 +6,29 @@ async fn main() {
let mut states = Vec::new();
for i in 0..10 {
accounts.push(Account::offline(&format!("bot{o}")));
accounts.push(Account::offline(&format!("bot{i}")));
states.push(State::default());
}
azalea::start_swarm(azalea::SwarmOptions {
accounts,
address: "localhost",
swarm_state: SwarmState::default(),
states,
swarm_plugins: plugins![],
plugins: plugins![],
handle,
swarm_handle,
join_delay: None,
})
.await
.unwrap();
let e = SwarmBuilder::new()
.add_accounts(accounts.clone())
.set_handler(handle)
.set_swarm_handler(swarm_handle)
.start("localhost")
.await;
}
#[derive(Default, Clone)]
#[derive(Default, Clone, Component)]
struct State {}
#[derive(Default, Clone)]
#[derive(Default, Clone, Resource)]
struct SwarmState {}
async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
Ok(())
}
async fn swarm_handle(
swarm: Swarm<State>,
event: SwarmEvent,
state: SwarmState,
) -> anyhow::Result<()> {
async fn swarm_handle(swarm: Swarm, event: SwarmEvent, state: SwarmState) -> anyhow::Result<()> {
match &event {
SwarmEvent::Login => {
swarm.goto(azalea::BlockPos::new(0, 70, 0)).await;

View file

@ -1,24 +0,0 @@
A relatively complex bot for farming potatoes.
Note: At the moment, all of the code here is only hypothetical. I decided to write this to help me decide how I want some the APIs to look.
## Attempted
- Sync: a sync function is called with the state and bot every time we get an event, and the function can queue events to execute at the end of the tick
Pros: No .lock().unwrap() necessary, and theoretically pausable by saving the state.
Cons: Async functions like opening containers and pathfinding are annoying because you have to keep state for them, and the code generally ends up being more confusing.
- Async non-blocking: an async function is called in a new task with the state mutex and bot every time we get an event
Pros: Easier to do async stuff like interacting with containers, code is somewhat easier to understand
Cons: Lock spam everywhere is annoying, and you have to make sure stuff doesn't accidentally run in parallel.
## Considered:
(I didn't actually try this because the problems were apparent)
- Async blocking: an async function is called with the state and bot every time we get an event, and only handles the next event when this one finishes running
Pros: No lock spam
Cons: Sometimes you want to handle multiple events at once like eating if you get hungry while pathfinding, this makes it harder without increasing complexity

View file

@ -1,31 +0,0 @@
//! Automatically eat when we get hungry.
use async_trait::async_trait;
use azalea::prelude::*;
use azalea::{Client, Event};
use parking_lot::Mutex;
use std::sync::Arc;
#[derive(Default, Clone)]
pub struct Plugin {
pub state: State,
}
#[derive(Default, Clone, Component)]
pub struct State {}
#[async_trait]
impl azalea::PluginState for Plugin {
async fn handle(self: Box<Self>, event: Event, bot: Client) {
match event {
Event::UpdateHunger => {
if !bot.using_held_item() && bot.food_level() <= 17 {
if bot.hold(azalea::ItemGroup::Food).await {
bot.use_held_item().await;
}
}
}
_ => {}
}
}
}

View file

@ -1,69 +0,0 @@
mod autoeat;
use azalea::prelude::*;
use azalea::{pathfinder, BlockPos, ItemKind, Vec3};
#[derive(Default, Clone)]
struct State {}
#[tokio::main]
async fn main() {
env_logger::init();
let account = Account::offline("bot");
azalea::start(azalea::Options {
account,
address: "localhost",
state: State::default(),
plugins: plugins![autoeat::Plugin, pathfinder::Plugin],
handle,
})
.await
.unwrap();
}
async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
match event {
Event::Login => {
goto_farm(bot, state).await?;
// after we get to the farm, start farming
farm(bot, state).await?;
}
_ => {}
}
Ok(())
}
// go to the place where we start farming
async fn goto_farm(bot: Client, state: State) -> anyhow::Result<()> {
bot.goto(pathfinder::Goals::Near(5, BlockPos::new(0, 70, 0)))
.await?;
Ok(())
}
// go to the chest and deposit everything in our inventory.
async fn deposit(bot: &mut Client, state: State) -> anyhow::Result<()> {
// first throw away any garbage we might have
bot.toss(|item| item.kind != ItemKind::Potato && item.kind != ItemKind::DiamondHoe);
bot.goto(Vec3::new(0, 70, 0)).await?;
let chest = bot
.open_container(&bot.world.block_at(BlockPos::new(0, 70, 0)))
.await
.unwrap();
let inventory_potato_count: usize = bot
.inventory()
.count_total(|item| item.kind == ItemKind::Potato);
if inventory_potato_count > 64 {
chest
.deposit_total_count(
|item| item.kind == azalea::ItemKind::Potato,
inventory_potato_count - 64,
)
.await;
}
chest.close().await;
Ok(())
}

View file

@ -1,5 +1,9 @@
use azalea::{pathfinder, Account, Client, Event, SwarmEvent};
use azalea::{prelude::*, Swarm};
use std::time::Duration;
use azalea::entity::metadata::Player;
use azalea::{pathfinder, Account, Client, Event, GameProfileComponent};
use azalea::{prelude::*, swarm::prelude::*};
use azalea_ecs::query::With;
#[tokio::main]
async fn main() {
@ -11,43 +15,32 @@ async fn main() {
states.push(State::default());
}
azalea::start_swarm(azalea::SwarmOptions {
accounts,
address: "localhost",
swarm_state: SwarmState::default(),
states,
swarm_plugins: swarm_plugins![pathfinder::Plugin],
plugins: plugins![],
handle,
swarm_handle,
join_delay: None,
})
SwarmBuilder::new()
.add_accounts(accounts.clone())
.set_handler(handle)
.set_swarm_handler(swarm_handle)
.join_delay(Duration::from_millis(1000))
.start("localhost")
.await
.unwrap();
}
#[derive(Default, Clone)]
#[derive(Component, Default, Clone)]
struct State {}
#[derive(Default, Clone)]
#[derive(Resource, Default, Clone)]
struct SwarmState {}
async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
Ok(())
}
async fn swarm_handle(
swarm: Swarm<State>,
event: SwarmEvent,
state: SwarmState,
) -> anyhow::Result<()> {
async fn swarm_handle(swarm: Swarm, event: SwarmEvent, state: SwarmState) -> anyhow::Result<()> {
match event {
SwarmEvent::Tick => {
if let Some(target_entity) =
swarm.entity_by::<Player>(|name: &Name| name == "Herobrine")
swarm.entity_by::<With<Player>>(|profile: &&GameProfileComponent| {
profile.name == "Herobrine"
})
{
let target_bounding_box =
swarm.map_entity(target_entity, |bb: &BoundingBox| bb.clone());

View file

@ -1,4 +1,4 @@
//! mat's bot for testing new azalea features
//! a bot for testing new azalea features
#![feature(type_alias_impl_trait)]
@ -7,7 +7,7 @@ use azalea::entity::metadata::Player;
use azalea::entity::Position;
use azalea::inventory::InventoryComponent;
use azalea::pathfinder::BlockPosGoal;
use azalea::{prelude::*, BlockPos, GameProfileComponent, Swarm, SwarmEvent, WalkDirection};
use azalea::{prelude::*, swarm::prelude::*, BlockPos, GameProfileComponent, WalkDirection};
use azalea::{Account, Client, Event};
use azalea_protocol::packets::game::serverbound_client_command_packet::ServerboundClientCommandPacket;
use std::time::Duration;
@ -15,7 +15,7 @@ use std::time::Duration;
#[derive(Default, Clone, Component)]
struct State {}
#[derive(Default, Clone, Component)]
#[derive(Default, Clone, Resource)]
struct SwarmState {}
#[tokio::main]
@ -55,7 +55,7 @@ async fn main() -> anyhow::Result<()> {
}
loop {
let e = azalea::SwarmBuilder::new()
let e = SwarmBuilder::new()
.add_accounts(accounts.clone())
.set_handler(handle)
.set_swarm_handler(swarm_handle)

View file

@ -9,10 +9,7 @@ use azalea_ecs::{
system::{Commands, Query},
AppTickExt,
};
use azalea_world::{
entity::{metadata::Player, set_rotation, Jumping, Physics, Position},
Local,
};
use azalea_world::entity::{metadata::Player, set_rotation, Jumping, Local, Physics, Position};
use std::f64::consts::PI;
use crate::pathfinder::PathfinderPlugin;

View file

@ -4,7 +4,7 @@
mod bot;
pub mod pathfinder;
pub mod prelude;
mod swarm;
pub mod swarm;
pub use azalea_block as blocks;
pub use azalea_client::*;
@ -23,7 +23,6 @@ use protocol::{
resolver::{self, ResolverError},
ServerAddress,
};
pub use swarm::*;
use thiserror::Error;
use tokio::sync::mpsc;
@ -43,10 +42,19 @@ pub enum StartError {
/// making Azalea bots.
///
/// ```no_run
/// azalea::ClientBuilder::new()
/// # use azalea::prelude::*;
/// # #[tokio::main]
/// # async fn main() {
/// ClientBuilder::new()
/// .set_handler(handle)
/// .start(Account::offline("bot"), "localhost")
/// .await;
/// # }
/// # #[derive(Component, Clone, Default)]
/// # pub struct State;
/// # async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> {
/// # Ok(())
/// # }
/// ```
pub struct ClientBuilder<S, Fut>
where
@ -79,9 +87,20 @@ where
/// Set the function that's called every time a bot receives an [`Event`].
/// This is the way to handle normal per-bot events.
///
/// You can only have one client handler, calling this again will replace
/// the old client handler function (you can have a client handler and swarm
/// handler separately though).
/// You must have exactly one client handler, calling this again will
/// replace the old client handler function.
///
/// ```
/// # use azalea::prelude::*;
/// # let client_builder = azalea::ClientBuilder::new();
/// client_builder.set_handler(handle);
///
/// # #[derive(Component, Clone, Default)]
/// # pub struct State;
/// async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> {
/// Ok(())
/// }
/// ```
#[must_use]
pub fn set_handler(mut self, handler: HandleFn<Fut, S>) -> Self {
self.handler = Some(handler);

View file

@ -17,7 +17,7 @@ use azalea_ecs::{
AppTickExt,
};
use azalea_world::entity::metadata::Player;
use azalea_world::Local;
use azalea_world::entity::Local;
use azalea_world::{
entity::{Physics, Position, WorldName},
WorldContainer,

View file

@ -165,12 +165,12 @@ mod tests {
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
BlockState::Stone,
azalea_registry::Block::Stone.into(),
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 1, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);
@ -190,12 +190,12 @@ mod tests {
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
BlockState::Stone,
azalea_registry::Block::Stone.into(),
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 1, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);
@ -215,22 +215,22 @@ mod tests {
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
BlockState::Stone,
azalea_registry::Block::Stone.into(),
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 1, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 2, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 3, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);

View file

@ -1,7 +1,6 @@
//! The Azalea prelude. Things that are necessary for a bare-bones bot are
//! re-exported here.
pub use crate::bot::BotClientExt;
pub use crate::pathfinder::PathfinderClientExt;
pub use crate::{bot::BotClientExt, pathfinder::PathfinderClientExt, ClientBuilder};
pub use azalea_client::{Account, Client, Event};
pub use azalea_ecs::component::Component;
pub use azalea_ecs::{component::Component, system::Resource};

View file

@ -23,7 +23,7 @@ use azalea_ecs::{
};
use std::collections::VecDeque;
use crate::{Swarm, SwarmEvent};
use super::{Swarm, SwarmEvent};
#[derive(Clone)]
pub struct SwarmChatPlugin;

View file

@ -2,6 +2,7 @@
mod chat;
mod events;
pub mod prelude;
use crate::{bot::DefaultBotPlugins, HandleFn};
use azalea_client::{init_ecs_app, start_ecs, Account, ChatPacket, Client, Event, JoinError};
@ -28,12 +29,10 @@ use tokio::sync::mpsc;
/// A swarm is a way to conveniently control many bots at once, while also
/// being able to control bots at an individual level when desired.
///
/// Swarms are created from the [`azalea::start_swarm`] function.
/// Swarms are created from [`azalea::swarm::SwarmBuilder`].
///
/// The `S` type parameter is the type of the state for individual bots.
/// It's used to make the [`Swarm::add`] function work.
///
/// [`azalea::start_swarm`]: fn.start_swarm.html
#[derive(Clone, Resource)]
pub struct Swarm {
pub ecs_lock: Arc<Mutex<Ecs>>,
@ -84,7 +83,7 @@ where
Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
S: Default + Send + Sync + Clone + Component + 'static,
SS: Default + Send + Sync + Clone + Component + 'static,
SS: Default + Send + Sync + Clone + Resource + 'static,
{
/// Start creating the swarm.
#[must_use]
@ -138,9 +137,30 @@ where
/// Set the function that's called every time a bot receives an [`Event`].
/// This is the way to handle normal per-bot events.
///
/// You can only have one client handler, calling this again will replace
/// the old client handler function (you can have a client handler and swarm
/// handler separately though).
/// You must have exactly one client handler and one swarm handler, calling
/// this again will replace the old client handler function.
///
/// ```
/// # use azalea::{prelude::*, swarm::prelude::*};
/// # let swarm_builder = SwarmBuilder::new().set_swarm_handler(swarm_handle);
/// swarm_builder.set_handler(handle);
///
/// #[derive(Component, Default, Clone)]
/// struct State {}
/// async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> {
/// Ok(())
/// }
///
/// # #[derive(Resource, Default, Clone)]
/// # struct SwarmState {}
/// # async fn swarm_handle(
/// # mut swarm: Swarm,
/// # event: SwarmEvent,
/// # state: SwarmState,
/// # ) -> anyhow::Result<()> {
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn set_handler(mut self, handler: HandleFn<Fut, S>) -> Self {
self.handler = Some(handler);
@ -149,9 +169,31 @@ where
/// Set the function that's called every time the swarm receives a
/// [`SwarmEvent`]. This is the way to handle global swarm events.
///
/// You can only have one swarm handler, calling this again will replace
/// the old swarm handler function (you can have a client handler and swarm
/// handler separately though).
/// You must have exactly one client handler and one swarm handler, calling
/// this again will replace the old swarm handler function.
///
/// ```
/// # use azalea::{prelude::*, swarm::prelude::*};
/// # let swarm_builder = SwarmBuilder::new().set_handler(handle);
/// swarm_builder.set_swarm_handler(swarm_handle);
///
/// # #[derive(Component, Default, Clone)]
/// # struct State {}
///
/// # async fn handle(mut bot: Client, event: Event, state: State) -> anyhow::Result<()> {
/// # Ok(())
/// # }
///
/// #[derive(Resource, Default, Clone)]
/// struct SwarmState {}
/// async fn swarm_handle(
/// mut swarm: Swarm,
/// event: SwarmEvent,
/// state: SwarmState,
/// ) -> anyhow::Result<()> {
/// Ok(())
/// }
/// ```
#[must_use]
pub fn set_swarm_handler(mut self, handler: SwarmHandleFn<SwarmFut, SS>) -> Self {
self.swarm_handler = Some(handler);
@ -297,7 +339,7 @@ where
Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
S: Default + Send + Sync + Clone + Component + 'static,
SS: Default + Send + Sync + Clone + Component + 'static,
SS: Default + Send + Sync + Clone + Resource + 'static,
{
fn default() -> Self {
Self::new()
@ -339,14 +381,13 @@ pub enum SwarmStartError {
///
/// # Examples
/// ```rust,no_run
/// use azalea::{prelude::*, Swarm, SwarmEvent};
/// use azalea::{Account, Client, Event};
/// use azalea::{prelude::*, swarm::prelude::*};
/// use std::time::Duration;
///
/// #[derive(Default, Clone)]
/// #[derive(Default, Clone, Component)]
/// struct State {}
///
/// #[derive(Default, Clone)]
/// #[derive(Default, Clone, Resource)]
/// struct SwarmState {}
///
/// #[tokio::main]
@ -360,21 +401,12 @@ pub enum SwarmStartError {
/// }
///
/// loop {
/// let e = azalea::start_swarm(azalea::SwarmOptions {
/// accounts: accounts.clone(),
/// address: "localhost",
///
/// states: states.clone(),
/// swarm_state: SwarmState::default(),
///
/// plugins: plugins![],
/// swarm_plugins: swarm_plugins![],
///
/// handle,
/// swarm_handle,
///
/// join_delay: Some(Duration::from_millis(1000)),
/// })
/// let e = SwarmBuilder::new()
/// .add_accounts(accounts.clone())
/// .set_handler(handle)
/// .set_swarm_handler(swarm_handle)
/// .join_delay(Duration::from_millis(1000))
/// .start("localhost")
/// .await;
/// println!("{e:?}");
/// }
@ -388,7 +420,7 @@ pub enum SwarmStartError {
/// }
///
/// async fn swarm_handle(
/// mut swarm: Swarm<State>,
/// mut swarm: Swarm,
/// event: SwarmEvent,
/// _state: SwarmState,
/// ) -> anyhow::Result<()> {
@ -405,16 +437,6 @@ pub enum SwarmStartError {
/// }
/// Ok(())
/// }
// pub async fn start_swarm<
// S: Send + Sync + Clone + 'static,
// SS: Send + Sync + Clone + 'static,
// A: Send + TryInto<ServerAddress>,
// Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
// SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
// >(
// options: SwarmOptions<S, SS, A, Fut, SwarmFut>,
// ) -> Result<(), SwarmStartError> {
// }
impl Swarm {
/// Add a new account to the swarm. You can remove it later by calling
@ -457,7 +479,7 @@ impl Swarm {
tokio::spawn(async move {
while let Some(event) = rx.recv().await {
// we can't handle events here (since we can't copy the handler),
// they're handled above in start_swarm
// they're handled above in SwarmBuilder::start
if let Err(e) = cloned_bots_tx.send((Some(event), cloned_bot.clone())) {
error!("Error sending event to swarm: {e}");
}
@ -505,10 +527,15 @@ impl IntoIterator for Swarm {
/// Iterate over the bots in this swarm.
///
/// ```rust,no_run
/// # use azalea::{prelude::*, swarm::prelude::*};
/// #[derive(Component, Clone)]
/// # pub struct State;
/// # fn example(swarm: Swarm) {
/// for bot in swarm {
/// let state = bot.component::<State>();
/// // ...
/// }
/// # }
/// ```
fn into_iter(self) -> Self::IntoIter {
self.bots

View file

@ -0,0 +1,3 @@
//! A prelude that re-exports common swarm types in Azalea.
pub use crate::swarm::{Swarm, SwarmBuilder, SwarmEvent};

View file

@ -367,7 +367,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
elif type_name == 'ItemStack':
default = f'Slot::Present({default})' if default != 'Empty' else 'Slot::Empty'
elif type_name == 'BlockState':
default = f'{default}' if default != 'Empty' else 'BlockState::Air'
default = f'{default}' if default != 'Empty' else 'BlockState::AIR'
elif type_name == 'OptionalFormattedText':
default = f'Some({default})' if default != 'Empty' else 'None'
elif type_name == 'CompoundTag':

View file

@ -7,49 +7,8 @@ REGISTRIES_DIR = get_dir_location('../azalea-registry/src/lib.rs')
def generate_registries(registries: dict):
code = []
code.append('''#![doc = include_str!("../README.md")]
// This file is automatically generated in codegen/lib/code/registry.py
use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable};
use azalea_registry_macros::registry;
use std::io::{Cursor, Write};
pub trait Registry
where
Self: Sized,
{
fn from_u32(value: u32) -> Option<Self>;
fn to_u32(&self) -> u32;
}
/// A registry that might not be present. This is transmitted as a single
/// varint in the protocol.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct OptionalRegistry<T: Registry>(Option<T>);
impl<T: Registry> McBufReadable for OptionalRegistry<T> {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
Ok(OptionalRegistry(match u32::var_read_from(buf)? {
0 => None,
value => Some(
T::from_u32(value - 1)
.ok_or(BufReadError::UnexpectedEnumVariant { id: value as i32 })?,
),
}))
}
}
impl<T: Registry> McBufWritable for OptionalRegistry<T> {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
match &self.0 {
None => 0u32.var_write_into(buf),
Some(value) => (value.to_u32() + 1).var_write_into(buf),
}
}
}
''')
with open(REGISTRIES_DIR, 'r') as f:
code = f.read().split('\n')
for registry_name, registry in registries.items():
# registry!(Block, {
@ -64,14 +23,34 @@ impl<T: Registry> McBufWritable for OptionalRegistry<T> {
registry_struct_name = to_camel_case(registry_name.split(':')[1])
code.append(f'registry!({registry_struct_name}, {{')
registry_code = []
registry_code.append(f'enum {registry_struct_name} {{')
registry_entries = sorted(
registry['entries'].items(), key=lambda x: x[1]['protocol_id'])
for variant_name, _variant in registry_entries:
variant_struct_name = to_camel_case(
variant_name.split(':')[1])
code.append(f'\t{variant_struct_name} => "{variant_name}",')
code.append('});')
registry_code.append(f'\t{variant_struct_name} => "{variant_name}",')
registry_code.append('}')
# when we find a "registry! {" line, find the next line that starts
# with "enum <name>" and replace that until we find a line that's "}"
found = False
in_registry_macro = False
for i, line in enumerate(list(code)):
if not in_registry_macro and line == "registry! {":
in_registry_macro = True
elif in_registry_macro and line == registry_code[0]:
# found it, now delete until we get to "}"
while code[i] != '}':
code.pop(i)
code[i] = '\n'.join(registry_code)
found = True
break
if not found:
code.append('registry! {')
code.append('\n'.join(registry_code))
code.append('}')
code.append('')
with open(REGISTRIES_DIR, 'w') as f:

View file

@ -7,8 +7,8 @@ COLLISION_BLOCKS_RS_DIR = get_dir_location(
'../azalea-physics/src/collision/blocks.rs')
def generate_block_shapes(blocks: dict, shapes: dict, aabbs: dict, block_states_report, block_datas_burger, mappings: Mappings):
blocks, shapes = simplify_shapes(blocks, shapes, aabbs)
def generate_block_shapes(blocks_pixlyzer: dict, shapes: dict, aabbs: dict, block_states_report, block_datas_burger, mappings: Mappings):
blocks, shapes = simplify_shapes(blocks_pixlyzer, shapes, aabbs)
code = generate_block_shapes_code(
blocks, shapes, block_states_report, block_datas_burger, mappings)
@ -28,8 +28,7 @@ def simplify_shapes(blocks: dict, shapes: dict, aabbs: dict):
used_shape_ids = set()
# determine the used shape ids
for block_id, block_data in blocks.items():
block_id = block_id.split(':')[-1]
for _block_id, block_data in blocks.items():
block_shapes = [state.get('collision_shape')
for state in block_data['states'].values()]
for s in block_shapes:
@ -73,9 +72,9 @@ def generate_block_shapes_code(blocks: dict, shapes: dict, block_states_report,
for (shape_id, shape) in sorted(shapes.items(), key=lambda shape: int(shape[0])):
generated_shape_code += generate_code_for_shape(shape_id, shape)
# BlockState::PurpurStairs_NorthTopStraightTrue => &SHAPE24,
# 1..100 | 200..300 => &SHAPE1,
generated_match_inner_code = ''
shape_ids_to_variants = {}
shape_ids_to_block_state_ids = {}
for block_id, shape_ids in blocks.items():
if isinstance(shape_ids, int):
shape_ids = [shape_ids]
@ -83,23 +82,34 @@ def generate_block_shapes_code(blocks: dict, shapes: dict, block_states_report,
block_data_burger = block_datas_burger[block_id]
for possible_state, shape_id in zip(block_report_data['states'], shape_ids):
variant_values = []
for value in tuple(possible_state.get('properties', {}).values()):
variant_values.append(to_camel_case(value))
block_state_id = possible_state['id']
if variant_values == []:
variant_name = to_camel_case(block_id)
else:
variant_name = f'{to_camel_case(block_id)}_{"".join(variant_values)}'
if shape_id not in shape_ids_to_variants:
shape_ids_to_variants[shape_id] = []
shape_ids_to_variants[shape_id].append(
f'BlockState::{variant_name}')
if shape_id not in shape_ids_to_block_state_ids:
shape_ids_to_block_state_ids[shape_id] = []
shape_ids_to_block_state_ids[shape_id].append(block_state_id)
# shape 1 is the most common so we have a _ => &SHAPE1 at the end
del shape_ids_to_variants[1]
for shape_id, variants in shape_ids_to_variants.items():
generated_match_inner_code += f'{"|".join(variants)} => &SHAPE{shape_id},\n'
del shape_ids_to_block_state_ids[1]
for shape_id, block_state_ids in shape_ids_to_block_state_ids.items():
# convert them into ranges (so like 1|2|3 is 1..=3 instead)
block_state_ids_ranges = []
range_start_block_state_id = None
last_block_state_id = None
for block_state_id in sorted(block_state_ids):
if range_start_block_state_id is None:
range_start_block_state_id = block_state_id
if last_block_state_id is not None:
# check if the range is done
if block_state_id - 1 != last_block_state_id:
block_state_ids_ranges.append(f'{range_start_block_state_id}..={last_block_state_id}' if range_start_block_state_id != last_block_state_id else str(range_start_block_state_id))
range_start_block_state_id = block_state_id
last_block_state_id = block_state_id
block_state_ids_ranges.append(f'{range_start_block_state_id}..={last_block_state_id}' if range_start_block_state_id != last_block_state_id else str(range_start_block_state_id))
generated_match_inner_code += f'{"|".join(block_state_ids_ranges)} => &SHAPE{shape_id},\n'
generated_match_inner_code += '_ => &SHAPE1'
return f'''
//! Autogenerated block collisions for every block
@ -123,8 +133,8 @@ pub trait BlockWithShape {{
impl BlockWithShape for BlockState {{
fn shape(&self) -> &'static VoxelShape {{
match self {{
{generated_match_inner_code}_ => &SHAPE1
match self.id {{
{generated_match_inner_code}
}}
}}
}}

View file

@ -4,6 +4,7 @@ from lib.download import get_server_jar, get_burger, get_client_jar, get_pixlyze
from lib.utils import get_dir_location
from zipfile import ZipFile
import subprocess
import requests
import json
import sys
import re
@ -114,7 +115,20 @@ def get_pixlyzer_data(version_id: str, category: str):
target_dir = get_dir_location(f'downloads/pixlyzer-{version_id}')
if not os.path.exists(get_dir_location(target_dir)):
# TODO: right now this False is hard-coded, it should retry with this
# enabled if # initially getting the data fails
if False or (os.path.exists(target_dir) and not os.path.exists(f'{target_dir}/{category}.min.json')):
print('Downloading', category, 'from pixlyzer-data.')
data = requests.get(f'https://gitlab.com/Bixilon/pixlyzer-data/-/raw/master/version/{version_id}/{category}.min.json?inline=false').text
try:
os.mkdir(target_dir)
except:
pass
with open(f'{target_dir}/{category}.min.json', 'w') as f:
f.write(data)
return json.loads(data)
if not os.path.exists(target_dir):
pixlyzer_dir = get_pixlyzer()
# for some reason pixlyzer doesn't work right unless the mvn clean
@ -231,7 +245,6 @@ def get_pixlyzer_data(version_id: str, category: str):
with open(f'{target_dir}/{category}.min.json', 'r') as f:
return json.load(f)
def get_file_from_jar(version_id: str, file_dir: str):
get_client_jar(version_id)
with ZipFile(get_dir_location(f'downloads/client-{version_id}.jar')) as z: