diff options
| -rw-r--r-- | Cargo.lock | 143 | ||||
| -rw-r--r-- | azalea-auth/Cargo.toml | 4 | ||||
| -rw-r--r-- | azalea-chat/Cargo.toml | 4 | ||||
| -rw-r--r-- | azalea-client/src/packet_handling.rs | 59 | ||||
| -rw-r--r-- | azalea-core/Cargo.toml | 2 | ||||
| -rwxr-xr-x | azalea-core/src/resource_location.rs | 36 | ||||
| -rw-r--r-- | azalea-crypto/Cargo.toml | 2 | ||||
| -rw-r--r-- | azalea-language/Cargo.toml | 4 | ||||
| -rw-r--r-- | azalea-nbt/Cargo.toml | 16 | ||||
| -rwxr-xr-x | azalea-nbt/src/tag.rs | 6 | ||||
| -rw-r--r-- | azalea-protocol/Cargo.toml | 11 | ||||
| -rwxr-xr-x | azalea-protocol/src/packets/game/clientbound_login_packet.rs | 449 | ||||
| -rw-r--r-- | azalea-world/src/entity/mod.rs | 2 |
13 files changed, 591 insertions, 147 deletions
@@ -68,6 +68,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" [[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] name = "anyhow" version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -315,6 +321,7 @@ dependencies = [ "azalea-chat", "azalea-nbt", "bevy_ecs", + "serde", "uuid", ] @@ -703,18 +710,6 @@ dependencies = [ ] [[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - -[[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -776,6 +771,33 @@ dependencies = [ ] [[package]] +name = "ciborium" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" + +[[package]] +name = "ciborium-ll" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] name = "cipher" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -787,13 +809,23 @@ dependencies = [ [[package]] name = "clap" -version = "2.34.0" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "bitflags", + "clap_lex", + "indexmap", "textwrap", - "unicode-width", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", ] [[package]] @@ -841,15 +873,16 @@ dependencies = [ [[package]] name = "criterion" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" dependencies = [ + "anes", "atty", "cast", + "ciborium", "clap", "criterion-plot", - "csv", "itertools", "lazy_static", "num-traits", @@ -858,7 +891,6 @@ dependencies = [ "rayon", "regex", "serde", - "serde_cbor", "serde_derive", "serde_json", "tinytemplate", @@ -867,9 +899,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", @@ -929,28 +961,6 @@ dependencies = [ ] [[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr", - "csv-core", - "itoa 0.4.8", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - -[[package]] name = "data-encoding" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1290,7 +1300,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.5", + "itoa", ] [[package]] @@ -1337,7 +1347,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.5", + "itoa", "pin-project-lite", "socket2", "tokio", @@ -1428,12 +1438,6 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" @@ -1685,6 +1689,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2053,16 +2063,6 @@ dependencies = [ ] [[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - -[[package]] name = "serde_derive" version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2075,11 +2075,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ - "itoa 1.0.5", + "itoa", "ryu", "serde", ] @@ -2091,7 +2091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.5", + "itoa", "ryu", "serde", ] @@ -2193,12 +2193,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.11.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" @@ -2500,12 +2497,6 @@ dependencies = [ ] [[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/azalea-auth/Cargo.toml b/azalea-auth/Cargo.toml index 65e6b9e6..0f2c7125 100644 --- a/azalea-auth/Cargo.toml +++ b/azalea-auth/Cargo.toml @@ -18,8 +18,8 @@ reqwest = { version = "0.11.12", default-features = false, features = [ "json", "rustls-tls", ] } -serde = { version = "1.0.145", features = ["derive"] } -serde_json = "1.0.86" +serde = { version = "1.0.152", features = ["derive"] } +serde_json = "1.0.93" thiserror = "1.0.37" tokio = { version = "1.24.2", features = ["fs"] } uuid = { version = "^1.1.2", features = ["serde"] } diff --git a/azalea-chat/Cargo.toml b/azalea-chat/Cargo.toml index 067062bc..33a3aafe 100644 --- a/azalea-chat/Cargo.toml +++ b/azalea-chat/Cargo.toml @@ -18,5 +18,5 @@ azalea-buf = { path = "../azalea-buf", features = [ 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"] } -serde_json = "^1.0.72" +serde = { version = "^1.0.152", features = ["derive"] } +serde_json = "^1.0.93" diff --git a/azalea-client/src/packet_handling.rs b/azalea-client/src/packet_handling.rs index 2f4f060a..ae38ec5a 100644 --- a/azalea-client/src/packet_handling.rs +++ b/azalea-client/src/packet_handling.rs @@ -199,58 +199,17 @@ fn process_packet_events(ecs: &mut World) { query.get_mut(player_entity).unwrap(); { - // TODO: have registry_holder be a struct because this sucks rn - // best way would be to add serde support to azalea-nbt - - let registry_holder = p + let dimension = &p .registry_holder - .as_compound() - .expect("Registry holder is not a compound") - .get("") - .expect("No \"\" tag") - .as_compound() - .expect("\"\" tag is not a compound"); - let dimension_types = registry_holder - .get("minecraft:dimension_type") - .expect("No dimension_type tag") - .as_compound() - .expect("dimension_type is not a compound") - .get("value") - .expect("No dimension_type value") - .as_list() - .expect("dimension_type value is not a list"); - let dimension_type = dimension_types + .root + .dimension_type + .value .iter() - .find(|t| { - t.as_compound() - .expect("dimension_type value is not a compound") - .get("name") - .expect("No name tag") - .as_string() - .expect("name is not a string") - == p.dimension_type.to_string() - }) + .find(|t| t.name == p.dimension_type) .unwrap_or_else(|| { panic!("No dimension_type with name {}", p.dimension_type) }) - .as_compound() - .unwrap() - .get("element") - .expect("No element tag") - .as_compound() - .expect("element is not a compound"); - let height = (*dimension_type - .get("height") - .expect("No height tag") - .as_int() - .expect("height tag is not an int")) - .try_into() - .expect("height is not a u32"); - let min_y = *dimension_type - .get("min_y") - .expect("No min_y tag") - .as_int() - .expect("min_y tag is not an int"); + .element; let new_world_name = p.dimension.clone(); @@ -263,7 +222,11 @@ fn process_packet_events(ecs: &mut World) { } // add this world to the world_container (or don't if it's already // there) - let weak_world = world_container.insert(new_world_name.clone(), height, min_y); + let weak_world = world_container.insert( + new_world_name.clone(), + dimension.height, + dimension.min_y, + ); // set the partial_world to an empty world // (when we add chunks or entities those will be in the // world_container) diff --git a/azalea-core/Cargo.toml b/azalea-core/Cargo.toml index e31351e8..8b92e845 100644 --- a/azalea-core/Cargo.toml +++ b/azalea-core/Cargo.toml @@ -13,7 +13,9 @@ 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" } bevy_ecs = { version = "0.10.0", default-features = false, optional = true } +serde = {version = "^1.0.152", optional = true} uuid = "^1.1.2" [features] bevy_ecs = ["dep:bevy_ecs"] +serde = ["dep:serde"]
\ No newline at end of file diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs index 4e25d00e..09a35efd 100755 --- a/azalea-core/src/resource_location.rs +++ b/azalea-core/src/resource_location.rs @@ -3,7 +3,10 @@ use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; use std::io::{Cursor, Write}; -// TODO: make a `resourcelocation!("minecraft:overwolrd")` macro that checks if +#[cfg(feature = "serde")] +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +// TODO: make a `resourcelocation!("minecraft:overworld")` macro that checks if // it's correct at compile-time. #[derive(Hash, Clone, PartialEq, Eq)] @@ -60,6 +63,37 @@ impl McBufWritable for ResourceLocation { } } +#[cfg(feature = "serde")] +impl Serialize for ResourceLocation { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for ResourceLocation { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + if s.contains(':') { + match ResourceLocation::new(&s) { + Ok(r) => Ok(r), + Err(e) => Err(de::Error::custom(e)), + } + } else { + Err(de::Error::invalid_value( + de::Unexpected::Str(&s), + &"a valid ResourceLocation", + )) + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-crypto/Cargo.toml b/azalea-crypto/Cargo.toml index e49e5e9d..41fcc262 100644 --- a/azalea-crypto/Cargo.toml +++ b/azalea-crypto/Cargo.toml @@ -19,7 +19,7 @@ sha-1 = "^0.10.0" uuid = "^1.1.2" [dev-dependencies] -criterion = {version = "^0.3.5", features = ["html_reports"]} +criterion = {version = "^0.4.0", features = ["html_reports"]} [[bench]] harness = false diff --git a/azalea-language/Cargo.toml b/azalea-language/Cargo.toml index d0b160c3..5d872451 100644 --- a/azalea-language/Cargo.toml +++ b/azalea-language/Cargo.toml @@ -10,6 +10,6 @@ version = "0.6.0" [dependencies] once_cell = "1.16.0" -serde = "1.0.137" -serde_json = "1.0.81" +serde = "^1.0.152" +serde_json = "^1.0.93" # tokio = {version = "^1.21.2", features = ["fs"]} diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml index b67c90c8..007da55a 100644 --- a/azalea-nbt/Cargo.toml +++ b/azalea-nbt/Cargo.toml @@ -9,17 +9,21 @@ 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.6.0" } +ahash = { version = "^0.8.3" } +azalea-buf = { path = "../azalea-buf", version = "^0.6.0" } byteorder = "^1.4.3" -flate2 = "^1.0.23" +flate2 = "^1.0.25" log = "0.4.17" num-derive = "^0.3.3" -num-traits = "^0.2.14" -serde = {version = "^1.0.148", features = ["derive"]} +num-traits = "^0.2.15" +serde = { version = "1.0.152", features = ["derive"], optional = true } [dev-dependencies] -criterion = {version = "^0.3.5", features = ["html_reports"]} +criterion = {version = "^0.4.0", features = ["html_reports"]} + +[features] +default = [] +serde = ["dep:serde", "ahash/serde"] [profile.release] lto = true diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs index a18a42b1..23bed1d4 100755 --- a/azalea-nbt/src/tag.rs +++ b/azalea-nbt/src/tag.rs @@ -1,9 +1,11 @@ use ahash::AHashMap; + +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// An NBT value. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)] -#[serde(untagged)] +#[derive(Clone, Debug, PartialEq, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))] pub enum Tag { #[default] End, // 0 diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index 1bbda7eb..e9834d58 100644 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -21,21 +21,21 @@ azalea-brigadier = { path = "../azalea-brigadier", version = "^0.6.0", features ] } 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-core = { path = "../azalea-core", optional = true, version = "^0.6.0", features = ["serde"]} azalea-crypto = { path = "../azalea-crypto", version = "^0.6.0" } -azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0" } +azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0", features = ["serde"] } 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.10.0", default-features = false } byteorder = "^1.4.3" bytes = "^1.1.0" -flate2 = "1.0.23" +flate2 = "1.0.25" futures = "0.3.24" futures-util = "0.3.24" log = "0.4.17" -serde = { version = "1.0.130", features = ["serde_derive"] } -serde_json = "^1.0.72" +serde = { version = "1.0.152", features = ["serde_derive"] } +serde_json = "^1.0.93" thiserror = "1.0.37" tokio = { version = "^1.24.2", features = ["io-util", "net", "macros"] } tokio-util = { version = "0.7.4", features = ["codec"] } @@ -48,6 +48,7 @@ uuid = "1.1.2" connecting = [] default = ["packets"] packets = ["connecting", "dep:async-compression", "dep:azalea-core"] +strict_registry = ["packets"] [dev-dependencies] anyhow = "^1.0.65" diff --git a/azalea-protocol/src/packets/game/clientbound_login_packet.rs b/azalea-protocol/src/packets/game/clientbound_login_packet.rs index 149f1e3f..de5444b8 100755 --- a/azalea-protocol/src/packets/game/clientbound_login_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_login_packet.rs @@ -1,7 +1,12 @@ +use self::registry::RegistryHolder; use azalea_buf::McBuf; use azalea_core::{GameType, GlobalPos, OptionalGameType, ResourceLocation}; use azalea_protocol_macros::ClientboundGamePacket; +/// The first packet sent by the server to the client after login. +/// +/// This packet contains information about the state of the player, the +/// world, and the registry. #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundLoginPacket { pub player_id: u32, @@ -9,7 +14,7 @@ pub struct ClientboundLoginPacket { pub game_type: GameType, pub previous_game_type: OptionalGameType, pub levels: Vec<ResourceLocation>, - pub registry_holder: azalea_nbt::Tag, + pub registry_holder: RegistryHolder, pub dimension_type: ResourceLocation, pub dimension: ResourceLocation, pub seed: i64, @@ -25,3 +30,445 @@ pub struct ClientboundLoginPacket { pub is_flat: bool, pub last_death_location: Option<GlobalPos>, } + +pub mod registry { + //! [ClientboundLoginPacket](super::ClientboundLoginPacket) Registry + //! Structures + //! + //! This module contains the structures used to represent the registry + //! sent to the client upon login. This contains a lot of information about + //! the game, including the types of chat messages, dimensions, and + //! biomes. + + use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; + use azalea_core::ResourceLocation; + use azalea_nbt::Tag; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + use std::{collections::HashMap, io::Cursor}; + + /// The base of the registry. + /// + /// This is the registry that is sent to the client upon login. + /// + /// As a tag, it is a compound tag that only contains a single compound tag. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct RegistryHolder { + #[serde(rename = "")] + pub root: RegistryRoot, + } + + impl TryFrom<Tag> for RegistryHolder { + type Error = serde_json::Error; + + fn try_from(value: Tag) -> Result<Self, Self::Error> { + serde_json::from_value(serde_json::to_value(value)?) + } + } + + impl TryInto<Tag> for RegistryHolder { + type Error = serde_json::Error; + + fn try_into(self) -> Result<Tag, Self::Error> { + serde_json::from_value(serde_json::to_value(self)?) + } + } + + impl McBufReadable for RegistryHolder { + fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> { + RegistryHolder::try_from(Tag::read_from(buf)?) + .map_err(|e| BufReadError::Deserialization { source: e }) + } + } + + impl McBufWritable for RegistryHolder { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + TryInto::<Tag>::try_into(self.clone())?.write_into(buf) + } + } + + /// The main part of the registry. + /// + /// The only field of [`RegistryHolder`]. + /// Contains information from the server about chat, dimensions, + /// and world generation. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct RegistryRoot { + #[serde(rename = "minecraft:chat_type")] + pub chat_type: RegistryType<ChatTypeElement>, + #[serde(rename = "minecraft:dimension_type")] + pub dimension_type: RegistryType<DimensionTypeElement>, + #[serde(rename = "minecraft:worldgen/biome")] + pub world_type: RegistryType<WorldTypeElement>, + } + + /// A collection of values for a certain type of registry data. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct RegistryType<T> { + #[serde(rename = "type")] + pub kind: ResourceLocation, + pub value: Vec<TypeValue<T>>, + } + + /// A value for a certain type of registry data. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct TypeValue<T> { + pub id: u32, + pub name: ResourceLocation, + pub element: T, + } + + /// Data about a kind of chat message + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct ChatTypeElement { + pub chat: ChatTypeData, + pub narration: ChatTypeData, + } + + /// Data about a chat message. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct ChatTypeData { + pub translation_key: String, + pub parameters: Vec<String>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub style: Option<ChatTypeStyle>, + } + + /// The style of a chat message. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct ChatTypeStyle { + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub color: Option<String>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub bold: Option<bool>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub italic: Option<bool>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub underlined: Option<bool>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub strikethrough: Option<bool>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub obfuscated: Option<bool>, + } + + /// Dimension attributes. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct DimensionTypeElement { + pub ambient_light: f32, + #[serde(with = "Convert")] + pub bed_works: bool, + pub coordinate_scale: f32, + pub effects: ResourceLocation, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub fixed_time: Option<u32>, + #[serde(with = "Convert")] + pub has_ceiling: bool, + #[serde(with = "Convert")] + pub has_raids: bool, + #[serde(with = "Convert")] + pub has_skylight: bool, + pub height: u32, + pub infiniburn: ResourceLocation, + pub logical_height: u32, + pub min_y: i32, + pub monster_spawn_block_light_limit: u32, + pub monster_spawn_light_level: MonsterSpawnLightLevel, + #[serde(with = "Convert")] + pub natural: bool, + #[serde(with = "Convert")] + pub piglin_safe: bool, + #[serde(with = "Convert")] + pub respawn_anchor_works: bool, + #[serde(with = "Convert")] + pub ultrawarm: bool, + } + + /// The light level at which monsters can spawn. + /// + /// This can be either a single minimum value, or a formula with a min and + /// max. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[serde(untagged)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub enum MonsterSpawnLightLevel { + /// A simple minimum value. + Simple(u32), + /// A complex value with a type, minimum, and maximum. + /// Vanilla minecraft only uses one type, "minecraft:uniform". + Complex { + #[serde(rename = "type")] + kind: ResourceLocation, + value: MonsterSpawnLightLevelValues, + }, + } + + /// The min and max light levels at which monsters can spawn. + /// + /// Values are inclusive. + #[derive(Debug, Copy, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct MonsterSpawnLightLevelValues { + #[serde(rename = "min_inclusive")] + pub min: u32, + #[serde(rename = "max_inclusive")] + pub max: u32, + } + + /// Biome attributes. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct WorldTypeElement { + pub temperature: f32, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub temperature_modifier: Option<String>, + pub downfall: f32, + pub precipitation: BiomePrecipitation, + pub effects: BiomeEffects, + } + + /// The precipitation of a biome. + #[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub enum BiomePrecipitation { + #[serde(rename = "none")] + None, + #[serde(rename = "rain")] + Rain, + #[serde(rename = "snow")] + Snow, + } + + /// The effects of a biome. + /// + /// This includes the sky, fog, water, and grass color, + /// as well as music and other sound effects. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct BiomeEffects { + pub sky_color: u32, + pub fog_color: u32, + pub water_color: u32, + pub water_fog_color: u32, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub foliage_color: Option<u32>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub grass_color: Option<u32>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub grass_color_modifier: Option<String>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub music: Option<BiomeMusic>, + pub mood_sound: BiomeMoodSound, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub additions_sound: Option<AdditionsSound>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub ambient_sound: Option<SoundId>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub particle: Option<BiomeParticle>, + } + + /// The music of the biome. + /// + /// Some biomes have unique music that only play when inside them. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct BiomeMusic { + #[serde(with = "Convert")] + pub replace_current_music: bool, + pub max_delay: u32, + pub min_delay: u32, + pub sound: SoundId, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct BiomeMoodSound { + pub tick_delay: u32, + pub block_search_extent: u32, + pub offset: f32, + pub sound: SoundId, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct AdditionsSound { + pub tick_chance: f32, + pub sound: SoundId, + } + + /// The ID of a sound. + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct SoundId { + pub sound_id: ResourceLocation, + } + + /// Biome particles. + /// + /// Some biomes have particles that spawn in the air. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct BiomeParticle { + pub probability: f32, + pub options: HashMap<String, String>, + } + + // Using a trait because you can't implement methods for + // types you don't own, in this case Option<bool> and bool. + trait Convert: Sized { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer; + + fn deserialize<'de, D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>; + } + + // Convert between bool and u8 + impl Convert for bool { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + serializer.serialize_u8(if *self { 1 } else { 0 }) + } + + fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error> + where + D: Deserializer<'de>, + { + convert::<D>(u8::deserialize(deserializer)?) + } + } + + // Convert between Option<bool> and u8 + impl Convert for Option<bool> { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + if let Some(value) = self { + Convert::serialize(value, serializer) + } else { + serializer.serialize_none() + } + } + + fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error> + where + D: Deserializer<'de>, + { + if let Some(value) = Option::<u8>::deserialize(deserializer)? { + Ok(Some(convert::<D>(value)?)) + } else { + Ok(None) + } + } + } + + // Deserializing logic here to deduplicate code + fn convert<'de, D>(value: u8) -> Result<bool, D::Error> + where + D: Deserializer<'de>, + { + match value { + 0 => Ok(false), + 1 => Ok(true), + other => Err(de::Error::invalid_value( + de::Unexpected::Unsigned(other as u64), + &"zero or one", + )), + } + } +} + +#[cfg(test)] +mod tests { + use super::registry::{ + ChatTypeElement, DimensionTypeElement, RegistryHolder, RegistryRoot, RegistryType, + WorldTypeElement, + }; + use azalea_core::ResourceLocation; + use azalea_nbt::Tag; + + #[test] + fn test_convert() { + let registry = RegistryHolder { + root: RegistryRoot { + chat_type: RegistryType::<ChatTypeElement> { + kind: ResourceLocation::new("minecraft:chat_type").unwrap(), + value: Vec::new(), + }, + dimension_type: RegistryType::<DimensionTypeElement> { + kind: ResourceLocation::new("minecraft:dimension_type").unwrap(), + value: Vec::new(), + }, + world_type: RegistryType::<WorldTypeElement> { + kind: ResourceLocation::new("minecraft:worldgen/biome").unwrap(), + value: Vec::new(), + }, + }, + }; + + let tag: Tag = registry.try_into().unwrap(); + let root = tag + .as_compound() + .unwrap() + .get("") + .unwrap() + .as_compound() + .unwrap(); + + let chat = root + .get("minecraft:chat_type") + .unwrap() + .as_compound() + .unwrap(); + let chat_type = chat.get("type").unwrap().as_string().unwrap(); + assert!(chat_type == "minecraft:chat_type"); + + let dimension = root + .get("minecraft:dimension_type") + .unwrap() + .as_compound() + .unwrap(); + let dimension_type = dimension.get("type").unwrap().as_string().unwrap(); + assert!(dimension_type == "minecraft:dimension_type"); + + let world = root + .get("minecraft:worldgen/biome") + .unwrap() + .as_compound() + .unwrap(); + let world_type = world.get("type").unwrap().as_string().unwrap(); + assert!(world_type == "minecraft:worldgen/biome"); + } +} diff --git a/azalea-world/src/entity/mod.rs b/azalea-world/src/entity/mod.rs index 6f77f1be..84c183ff 100644 --- a/azalea-world/src/entity/mod.rs +++ b/azalea-world/src/entity/mod.rs @@ -242,7 +242,7 @@ pub fn add_dead(mut commands: Commands, query: Query<(Entity, &Health), Changed< /// Most of the time, you should be using `azalea_registry::EntityKind` /// instead. #[derive(Component, Clone, Copy, Debug, PartialEq, Deref)] -pub struct EntityKind(azalea_registry::EntityKind); +pub struct EntityKind(pub azalea_registry::EntityKind); /// A bundle of components that every entity has. This doesn't contain metadata, /// that has to be added separately. |
