aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock143
-rw-r--r--azalea-auth/Cargo.toml4
-rw-r--r--azalea-chat/Cargo.toml4
-rw-r--r--azalea-client/src/packet_handling.rs59
-rw-r--r--azalea-core/Cargo.toml2
-rwxr-xr-xazalea-core/src/resource_location.rs36
-rw-r--r--azalea-crypto/Cargo.toml2
-rw-r--r--azalea-language/Cargo.toml4
-rw-r--r--azalea-nbt/Cargo.toml16
-rwxr-xr-xazalea-nbt/src/tag.rs6
-rw-r--r--azalea-protocol/Cargo.toml11
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_login_packet.rs449
-rw-r--r--azalea-world/src/entity/mod.rs2
13 files changed, 591 insertions, 147 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6058bc2a..f1bfe1e5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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.