mirror of
https://github.com/mat-1/azalea.git
synced 2024-09-19 14:42:32 +00:00
Add RegistryHolder struct and serde features (#81)
* Make RegistryHolder struct * Update deps * Move RegistryHolder to azalea-protocol * Convert bytes to bools and back * Rename and shuffle logic * Move logic into trait, rename methods * Final touchups * Ah, merge mistakes * Add serde support for ResourceLocation * Reuse structs * Error when serde skips values in debug mode Add missing attributes * Strict_registry feature, require packet feature * Add test * Move into packets * Docs and touchups * Reword docs * Move into module inside ClientboundLoginPacket * Add azalea-nbt serde feature * remove duplicate comment and type_ -> kind --------- Co-authored-by: mat <github@matdoes.dev>
This commit is contained in:
parent
f28efd5637
commit
c57c68ddf8
13 changed files with 591 additions and 147 deletions
143
Cargo.lock
generated
143
Cargo.lock
generated
|
@ -67,6 +67,12 @@ version = "0.2.0"
|
|||
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"
|
||||
|
@ -315,6 +321,7 @@ dependencies = [
|
|||
"azalea-chat",
|
||||
"azalea-nbt",
|
||||
"bevy_ecs",
|
||||
"serde",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
@ -702,18 +709,6 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -775,6 +770,33 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -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",
|
||||
|
@ -928,28 +960,6 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -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",
|
||||
|
@ -1426,12 +1436,6 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -1684,6 +1688,12 @@ version = "11.1.3"
|
|||
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"
|
||||
|
@ -2052,16 +2062,6 @@ dependencies = [
|
|||
"serde_derive",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -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"
|
||||
|
@ -2499,12 +2496,6 @@ dependencies = [
|
|||
"tinyvec",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"]
|
|
@ -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::*;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue