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

merge main

This commit is contained in:
mat 2023-03-11 17:10:38 -06:00
commit e1f04989fb
27 changed files with 742 additions and 294 deletions

182
Cargo.lock generated
View file

@ -67,6 +67,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e"
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.69" version = "1.0.69"
@ -317,6 +323,7 @@ dependencies = [
"azalea-registry", "azalea-registry",
"bevy_ecs", "bevy_ecs",
"num-traits", "num-traits",
"serde",
"uuid", "uuid",
] ]
@ -714,9 +721,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.3" version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [ dependencies = [
"generic-array", "generic-array",
] ]
@ -783,10 +790,37 @@ dependencies = [
] ]
[[package]] [[package]]
name = "cipher" name = "ciborium"
version = "0.4.3" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" 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.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [ dependencies = [
"crypto-common", "crypto-common",
"inout", "inout",
@ -794,13 +828,23 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.34.0" version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"clap_lex",
"indexmap",
"textwrap", "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]] [[package]]
@ -848,15 +892,16 @@ dependencies = [
[[package]] [[package]]
name = "criterion" name = "criterion"
version = "0.3.6" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [ dependencies = [
"anes",
"atty", "atty",
"cast", "cast",
"ciborium",
"clap", "clap",
"criterion-plot", "criterion-plot",
"csv",
"itertools", "itertools",
"lazy_static", "lazy_static",
"num-traits", "num-traits",
@ -865,7 +910,6 @@ dependencies = [
"rayon", "rayon",
"regex", "regex",
"serde", "serde",
"serde_cbor",
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"tinytemplate", "tinytemplate",
@ -874,9 +918,9 @@ dependencies = [
[[package]] [[package]]
name = "criterion-plot" name = "criterion-plot"
version = "0.4.5" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [ dependencies = [
"cast", "cast",
"itertools", "itertools",
@ -935,27 +979,6 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "csv"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad"
dependencies = [
"csv-core",
"itoa",
"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]] [[package]]
name = "data-encoding" name = "data-encoding"
version = "2.3.3" version = "2.3.3"
@ -1088,9 +1111,9 @@ dependencies = [
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -1103,9 +1126,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
@ -1113,15 +1136,15 @@ dependencies = [
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd"
[[package]] [[package]]
name = "futures-executor" name = "futures-executor"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-task", "futures-task",
@ -1130,9 +1153,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91"
[[package]] [[package]]
name = "futures-lite" name = "futures-lite"
@ -1151,9 +1174,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1162,21 +1185,21 @@ dependencies = [
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.26" version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -1330,9 +1353,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.24" version = "0.14.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -1455,9 +1478,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.139" version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
@ -1684,6 +1707,12 @@ version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -2045,28 +2074,18 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.153" version = "1.0.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a382c72b4ba118526e187430bb4963cd6d55051ebf13d9b25574d379cc98d20" checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8"
dependencies = [ dependencies = [
"serde_derive", "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]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.153" version = "1.0.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ef476a5790f0f6decbc66726b6e5d63680ed518283e64c7df415989d880954f" checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2193,12 +2212,9 @@ dependencies = [
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
@ -2330,9 +2346,9 @@ checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.19.4" version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" checksum = "7082a95d48029677a28f181e5f6422d0c8339ad8396a39d3f33d62a90c1f6c30"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"toml_datetime", "toml_datetime",
@ -2481,9 +2497,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.10" version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@ -2500,12 +2516,6 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.7.1" version = "0.7.1"

View file

@ -18,8 +18,8 @@ reqwest = { version = "0.11.12", default-features = false, features = [
"json", "json",
"rustls-tls", "rustls-tls",
] } ] }
serde = { version = "1.0.145", features = ["derive"] } serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.86" serde_json = "1.0.93"
thiserror = "1.0.37" thiserror = "1.0.37"
tokio = { version = "1.24.2", features = ["fs"] } tokio = { version = "1.24.2", features = ["fs"] }
uuid = { version = "^1.1.2", features = ["serde"] } uuid = { version = "^1.1.2", features = ["serde"] }

View file

@ -18,5 +18,5 @@ azalea-buf = { path = "../azalea-buf", features = [
azalea-language = { path = "../azalea-language", version = "^0.6.0" } azalea-language = { path = "../azalea-language", version = "^0.6.0" }
log = "0.4.17" log = "0.4.17"
once_cell = "1.16.0" once_cell = "1.16.0"
serde = { version = "^1.0.148", features = ["derive"] } serde = { version = "^1.0.152", features = ["derive"] }
serde_json = "^1.0.72" serde_json = "^1.0.93"

View file

@ -40,7 +40,7 @@ use azalea_protocol::{
}; };
use azalea_world::{ use azalea_world::{
entity::{EntityPlugin, EntityUpdateSet, Local, WorldName}, entity::{EntityPlugin, EntityUpdateSet, Local, WorldName},
Instance, PartialWorld, WorldContainer, Instance, InstanceContainer, PartialInstance,
}; };
use bevy_app::{App, CoreSchedule, Plugin, PluginGroup, PluginGroupBuilder}; use bevy_app::{App, CoreSchedule, Plugin, PluginGroup, PluginGroupBuilder};
use bevy_ecs::{ use bevy_ecs::{
@ -77,13 +77,13 @@ pub struct Client {
/// and skin data. /// and skin data.
/// ///
/// This is immutable; the server cannot change it. To get the username and /// This is immutable; the server cannot change it. To get the username and
/// skin the server chose for you, get your player from /// skin the server chose for you, get your player from the [`TabList`]
/// [`Self::players`]. /// component.
pub profile: GameProfile, pub profile: GameProfile,
/// The entity for this client in the ECS. /// The entity for this client in the ECS.
pub entity: Entity, pub entity: Entity,
/// The world that this client is in. /// The world that this client is in.
pub world: Arc<RwLock<PartialWorld>>, pub world: Arc<RwLock<PartialInstance>>,
/// The entity component system. You probably don't need to access this /// The entity component system. You probably don't need to access this
/// directly. Note that if you're using a shared world (i.e. a swarm), this /// directly. Note that if you're using a shared world (i.e. a swarm), this
@ -96,8 +96,7 @@ pub struct Client {
/// A component that contains some of the "settings" for this client that are /// A component that contains some of the "settings" for this client that are
/// sent to the server, such as render distance. /// sent to the server, such as render distance.
#[derive(Component, Clone, Debug, Deref, DerefMut, Default, Eq, PartialEq)] pub type ClientInformation = ServerboundClientInformationPacket;
pub struct ClientInformation(ServerboundClientInformationPacket);
/// A component that contains a map of player UUIDs to their information in the /// A component that contains a map of player UUIDs to their information in the
/// tab list. /// tab list.
@ -148,7 +147,7 @@ impl Client {
profile, profile,
// default our id to 0, it'll be set later // default our id to 0, it'll be set later
entity, entity,
world: Arc::new(RwLock::new(PartialWorld::default())), world: Arc::new(RwLock::new(PartialInstance::default())),
ecs, ecs,
@ -438,14 +437,14 @@ impl Client {
/// Get a reference to our (potentially shared) world. /// Get a reference to our (potentially shared) world.
/// ///
/// This gets the [`World`] from our world container. If it's a normal /// This gets the [`Instance`] from our world container. If it's a normal
/// client, then it'll be the same as the world the client has loaded. /// client, then it'll be the same as the world the client has loaded.
/// If the client using a shared world, then the shared world will be a /// If the client using a shared world, then the shared world will be a
/// superset of the client's world. /// superset of the client's world.
pub fn world(&self) -> Arc<RwLock<Instance>> { pub fn world(&self) -> Arc<RwLock<Instance>> {
let world_name = self.component::<WorldName>(); let world_name = self.component::<WorldName>();
let ecs = self.ecs.lock(); let ecs = self.ecs.lock();
let world_container = ecs.resource::<WorldContainer>(); let world_container = ecs.resource::<InstanceContainer>();
world_container.get(&world_name).unwrap() world_container.get(&world_name).unwrap()
} }
@ -478,7 +477,7 @@ impl Client {
{ {
let mut ecs = self.ecs.lock(); let mut ecs = self.ecs.lock();
let mut client_information_mut = self.query::<&mut ClientInformation>(&mut ecs); let mut client_information_mut = self.query::<&mut ClientInformation>(&mut ecs);
**client_information_mut = client_information.clone(); *client_information_mut = client_information.clone();
} }
if self.logged_in() { if self.logged_in() {
@ -531,7 +530,7 @@ impl Plugin for AzaleaPlugin {
app.add_event::<SendPacketEvent>() app.add_event::<SendPacketEvent>()
.add_system(handle_send_packet_event); .add_system(handle_send_packet_event);
app.init_resource::<WorldContainer>(); app.init_resource::<InstanceContainer>();
} }
} }

View file

@ -28,7 +28,9 @@ mod player;
pub mod task_pool; pub mod task_pool;
pub use account::Account; pub use account::Account;
pub use client::{init_ecs_app, start_ecs, Client, ClientInformation, JoinError}; pub use client::{
init_ecs_app, start_ecs, Client, ClientInformation, JoinError, JoinedClientBundle, TabList,
};
pub use events::Event; pub use events::Event;
pub use local_player::{GameProfileComponent, LocalPlayer}; pub use local_player::{GameProfileComponent, LocalPlayer};
pub use movement::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection}; pub use movement::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection};

View file

@ -5,7 +5,7 @@ use azalea_core::{ChunkPos, GameMode};
use azalea_protocol::packets::game::ServerboundGamePacket; use azalea_protocol::packets::game::ServerboundGamePacket;
use azalea_world::{ use azalea_world::{
entity::{self, Dead}, entity::{self, Dead},
Instance, PartialWorld, Instance, PartialInstance,
}; };
use bevy_ecs::{ use bevy_ecs::{
component::Component, entity::Entity, event::EventReader, query::Added, system::Query, component::Component, entity::Entity, event::EventReader, query::Added, system::Query,
@ -33,11 +33,12 @@ use crate::{
pub struct LocalPlayer { pub struct LocalPlayer {
packet_writer: mpsc::UnboundedSender<ServerboundGamePacket>, packet_writer: mpsc::UnboundedSender<ServerboundGamePacket>,
/// The partial world is the world this client currently has loaded. It has /// The partial instance is the world this client currently has loaded. It
/// a limited render distance. /// has a limited render distance.
pub partial_world: Arc<RwLock<PartialWorld>>, pub partial_instance: Arc<RwLock<PartialInstance>>,
/// The world is the combined [`PartialWorld`]s of all clients in the same /// The world is the combined [`PartialInstance`]s of all clients in the
/// world. (Only relevant if you're using a shared world, i.e. a swarm) /// same world. (Only relevant if you're using a shared world, i.e. a
/// swarm)
pub world: Arc<RwLock<Instance>>, pub world: Arc<RwLock<Instance>>,
/// A task that reads packets from the server. The client is disconnected /// A task that reads packets from the server. The client is disconnected
@ -99,7 +100,7 @@ impl LocalPlayer {
packet_writer, packet_writer,
world, world,
partial_world: Arc::new(RwLock::new(PartialWorld::new( partial_instance: Arc::new(RwLock::new(PartialInstance::new(
client_information.view_distance.into(), client_information.view_distance.into(),
Some(entity), Some(entity),
))), ))),

View file

@ -20,7 +20,7 @@ use azalea_world::{
MinecraftEntityId, Physics, PlayerBundle, Position, WorldName, MinecraftEntityId, Physics, PlayerBundle, Position, WorldName,
}, },
entity::{LoadedBy, RelativeEntityUpdate}, entity::{LoadedBy, RelativeEntityUpdate},
PartialWorld, WorldContainer, InstanceContainer, PartialInstance,
}; };
use bevy_app::{App, CoreSet, Plugin}; use bevy_app::{App, CoreSet, Plugin};
use bevy_ecs::{ use bevy_ecs::{
@ -193,65 +193,24 @@ fn process_packet_events(ecs: &mut World) {
&GameProfileComponent, &GameProfileComponent,
&ClientInformation, &ClientInformation,
)>, )>,
ResMut<WorldContainer>, ResMut<InstanceContainer>,
)> = SystemState::new(ecs); )> = SystemState::new(ecs);
let (mut commands, mut query, mut world_container) = system_state.get_mut(ecs); let (mut commands, mut query, mut world_container) = system_state.get_mut(ecs);
let (mut local_player, world_name, game_profile, client_information) = let (mut local_player, world_name, game_profile, client_information) =
query.get_mut(player_entity).unwrap(); query.get_mut(player_entity).unwrap();
{ {
// TODO: have registry_holder be a struct because this sucks rn let dimension = &p
// best way would be to add serde support to azalea-nbt
let registry_holder = p
.registry_holder .registry_holder
.as_compound() .root
.expect("Registry holder is not a compound") .dimension_type
.get("") .value
.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
.iter() .iter()
.find(|t| { .find(|t| t.name == p.dimension_type)
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()
})
.unwrap_or_else(|| { .unwrap_or_else(|| {
panic!("No dimension_type with name {}", p.dimension_type) panic!("No dimension_type with name {}", p.dimension_type)
}) })
.as_compound() .element;
.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");
let new_world_name = p.dimension.clone(); let new_world_name = p.dimension.clone();
@ -264,12 +223,16 @@ fn process_packet_events(ecs: &mut World) {
} }
// add this world to the world_container (or don't if it's already // add this world to the world_container (or don't if it's already
// there) // 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 // set the partial_world to an empty world
// (when we add chunks or entities those will be in the // (when we add chunks or entities those will be in the
// world_container) // world_container)
*local_player.partial_world.write() = PartialWorld::new( *local_player.partial_instance.write() = PartialInstance::new(
client_information.view_distance.into(), client_information.view_distance.into(),
// this argument makes it so other clients don't update this // this argument makes it so other clients don't update this
// player entity // player entity
@ -298,8 +261,7 @@ fn process_packet_events(ecs: &mut World) {
"Sending client information because login: {:?}", "Sending client information because login: {:?}",
client_information client_information
); );
let client_information: ClientInformation = client_information.clone(); local_player.write_packet(client_information.clone().get());
local_player.write_packet((*client_information).clone().get());
// brand // brand
local_player.write_packet( local_player.write_packet(
@ -517,7 +479,7 @@ fn process_packet_events(ecs: &mut World) {
let mut system_state: SystemState<Query<&mut LocalPlayer>> = SystemState::new(ecs); let mut system_state: SystemState<Query<&mut LocalPlayer>> = SystemState::new(ecs);
let mut query = system_state.get_mut(ecs); let mut query = system_state.get_mut(ecs);
let local_player = query.get_mut(player_entity).unwrap(); let local_player = query.get_mut(player_entity).unwrap();
let mut partial_world = local_player.partial_world.write(); let mut partial_world = local_player.partial_instance.write();
partial_world.chunks.view_center = ChunkPos::new(p.x, p.z); partial_world.chunks.view_center = ChunkPos::new(p.x, p.z);
} }
@ -536,14 +498,14 @@ fn process_packet_events(ecs: &mut World) {
// by this client. // by this client.
let shared_chunk = local_player.world.read().chunks.get(&pos); let shared_chunk = local_player.world.read().chunks.get(&pos);
let this_client_has_chunk = local_player let this_client_has_chunk = local_player
.partial_world .partial_instance
.read() .read()
.chunks .chunks
.limited_get(&pos) .limited_get(&pos)
.is_some(); .is_some();
let mut world = local_player.world.write(); let mut world = local_player.world.write();
let mut partial_world = local_player.partial_world.write(); let mut partial_world = local_player.partial_instance.write();
if !this_client_has_chunk { if !this_client_has_chunk {
if let Some(shared_chunk) = shared_chunk { if let Some(shared_chunk) = shared_chunk {
@ -709,7 +671,7 @@ fn process_packet_events(ecs: &mut World) {
if let Some(entity) = entity { if let Some(entity) = entity {
let new_position = p.position; let new_position = p.position;
commands.entity(entity).add(RelativeEntityUpdate { commands.entity(entity).add(RelativeEntityUpdate {
partial_world: local_player.partial_world.clone(), partial_world: local_player.partial_instance.clone(),
update: Box::new(move |entity| { update: Box::new(move |entity| {
let mut position = entity.get_mut::<Position>().unwrap(); let mut position = entity.get_mut::<Position>().unwrap();
**position = new_position; **position = new_position;
@ -740,7 +702,7 @@ fn process_packet_events(ecs: &mut World) {
if let Some(entity) = entity { if let Some(entity) = entity {
let delta = p.delta.clone(); let delta = p.delta.clone();
commands.entity(entity).add(RelativeEntityUpdate { commands.entity(entity).add(RelativeEntityUpdate {
partial_world: local_player.partial_world.clone(), partial_world: local_player.partial_instance.clone(),
update: Box::new(move |entity_mut| { update: Box::new(move |entity_mut| {
let mut position = entity_mut.get_mut::<Position>().unwrap(); let mut position = entity_mut.get_mut::<Position>().unwrap();
**position = position.with_delta(&delta); **position = position.with_delta(&delta);
@ -768,7 +730,7 @@ fn process_packet_events(ecs: &mut World) {
if let Some(entity) = entity { if let Some(entity) = entity {
let delta = p.delta.clone(); let delta = p.delta.clone();
commands.entity(entity).add(RelativeEntityUpdate { commands.entity(entity).add(RelativeEntityUpdate {
partial_world: local_player.partial_world.clone(), partial_world: local_player.partial_instance.clone(),
update: Box::new(move |entity_mut| { update: Box::new(move |entity_mut| {
let mut position = entity_mut.get_mut::<Position>().unwrap(); let mut position = entity_mut.get_mut::<Position>().unwrap();
**position = position.with_delta(&delta); **position = position.with_delta(&delta);

View file

@ -15,7 +15,9 @@ azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0" }
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" } azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
bevy_ecs = { version = "0.10.0", default-features = false, optional = true } bevy_ecs = { version = "0.10.0", default-features = false, optional = true }
num-traits = "0.2.15" num-traits = "0.2.15"
serde = { version = "^1.0.152", optional = true }
uuid = "^1.1.2" uuid = "^1.1.2"
[features] [features]
bevy_ecs = ["dep:bevy_ecs"] bevy_ecs = ["dep:bevy_ecs"]
serde = ["dep:serde"]

View file

@ -3,7 +3,10 @@
use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
use std::io::{Cursor, Write}; 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. // it's correct at compile-time.
#[derive(Hash, Clone, PartialEq, Eq)] #[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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -19,7 +19,7 @@ sha-1 = "^0.10.0"
uuid = "^1.1.2" uuid = "^1.1.2"
[dev-dependencies] [dev-dependencies]
criterion = {version = "^0.3.5", features = ["html_reports"]} criterion = {version = "^0.4.0", features = ["html_reports"]}
[[bench]] [[bench]]
harness = false harness = false

View file

@ -10,6 +10,6 @@ version = "0.6.0"
[dependencies] [dependencies]
once_cell = "1.16.0" once_cell = "1.16.0"
serde = "1.0.137" serde = "^1.0.152"
serde_json = "1.0.81" serde_json = "^1.0.93"
# tokio = {version = "^1.21.2", features = ["fs"]} # tokio = {version = "^1.21.2", features = ["fs"]}

View file

@ -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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ahash = { version = "^0.8.0", features = ["serde"]} ahash = { version = "^0.8.3" }
azalea-buf = {path = "../azalea-buf", version = "^0.6.0" } azalea-buf = { path = "../azalea-buf", version = "^0.6.0" }
byteorder = "^1.4.3" byteorder = "^1.4.3"
flate2 = "^1.0.23" flate2 = "^1.0.25"
log = "0.4.17" log = "0.4.17"
num-derive = "^0.3.3" num-derive = "^0.3.3"
num-traits = "^0.2.14" num-traits = "^0.2.15"
serde = {version = "^1.0.148", features = ["derive"]} serde = { version = "1.0.152", features = ["derive"], optional = true }
[dev-dependencies] [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] [profile.release]
lto = true lto = true

View file

@ -7,11 +7,9 @@ A fast NBT serializer and deserializer.
``` ```
use ahash::AHashMap; use ahash::AHashMap;
use azalea_nbt::Tag; use azalea_nbt::Tag;
use std::{io::{Cursor, Read}, fs::File}; use std::io::Cursor;
let mut file = File::open("tests/hello_world.nbt").unwrap(); let buf = include_bytes!("../tests/hello_world.nbt");
let mut buf = vec![];
file.read_to_end(&mut buf).unwrap();
let tag = Tag::read(&mut Cursor::new(&buf[..])).unwrap(); let tag = Tag::read(&mut Cursor::new(&buf[..])).unwrap();
assert_eq!( assert_eq!(
tag, tag,

View file

@ -1,45 +1,38 @@
use ahash::AHashMap; use ahash::AHashMap;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// An NBT value. /// An NBT value.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)] #[derive(Clone, Debug, PartialEq, Default)]
#[serde(untagged)] #[repr(u8)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
pub enum Tag { pub enum Tag {
#[default] #[default]
End, // 0 End = 0,
Byte(i8), // 1 Byte(i8) = 1,
Short(i16), // 2 Short(i16) = 2,
Int(i32), // 3 Int(i32) = 3,
Long(i64), // 4 Long(i64) = 4,
Float(f32), // 5 Float(f32) = 5,
Double(f64), // 6 Double(f64) = 6,
ByteArray(Vec<u8>), // 7 ByteArray(Vec<u8>) = 7,
String(String), // 8 String(String) = 8,
List(Vec<Tag>), // 9 List(Vec<Tag>) = 9,
Compound(AHashMap<String, Tag>), // 10 Compound(AHashMap<String, Tag>) = 10,
IntArray(Vec<i32>), // 11 IntArray(Vec<i32>) = 11,
LongArray(Vec<i64>), // 12 LongArray(Vec<i64>) = 12,
} }
impl Tag { impl Tag {
/// Get the numerical ID of the tag type. /// Get the numerical ID of the tag type.
#[inline] #[inline]
pub fn id(&self) -> u8 { pub fn id(&self) -> u8 {
match self { // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)`
Tag::End => 0, // `union` between `repr(C)` structs, each of which has the `u8`
Tag::Byte(_) => 1, // discriminant as its first field, so we can read the discriminant
Tag::Short(_) => 2, // without offsetting the pointer.
Tag::Int(_) => 3, unsafe { *<*const _>::from(self).cast::<u8>() }
Tag::Long(_) => 4,
Tag::Float(_) => 5,
Tag::Double(_) => 6,
Tag::ByteArray(_) => 7,
Tag::String(_) => 8,
Tag::List(_) => 9,
Tag::Compound(_) => 10,
Tag::IntArray(_) => 11,
Tag::LongArray(_) => 12,
}
} }
/// If the type is a byte, return the [`i8`]. /// If the type is a byte, return the [`i8`].

View file

@ -1,16 +1,11 @@
use ahash::AHashMap; use ahash::AHashMap;
use azalea_nbt::Tag; use azalea_nbt::Tag;
use std::{ use std::io::Cursor;
fs::File,
io::{Cursor, Read},
};
#[test] #[test]
fn test_decode_hello_world() { fn test_decode_hello_world() {
// read hello_world.nbt // read hello_world.nbt
let mut file = File::open("tests/hello_world.nbt").unwrap(); let buf = include_bytes!("hello_world.nbt").to_vec();
let mut buf = vec![];
file.read_to_end(&mut buf).unwrap();
let tag = Tag::read(&mut Cursor::new(&buf[..])).unwrap(); let tag = Tag::read(&mut Cursor::new(&buf[..])).unwrap();
assert_eq!( assert_eq!(
tag, tag,
@ -26,9 +21,7 @@ fn test_decode_hello_world() {
#[test] #[test]
fn test_roundtrip_hello_world() { fn test_roundtrip_hello_world() {
let mut file = File::open("tests/hello_world.nbt").unwrap(); let original = include_bytes!("hello_world.nbt").to_vec();
let mut original = Vec::new();
file.read_to_end(&mut original).unwrap();
let mut original_stream = Cursor::new(&original[..]); let mut original_stream = Cursor::new(&original[..]);
let tag = Tag::read(&mut original_stream).unwrap(); let tag = Tag::read(&mut original_stream).unwrap();
@ -43,9 +36,7 @@ fn test_roundtrip_hello_world() {
#[test] #[test]
fn test_bigtest() { fn test_bigtest() {
// read bigtest.nbt // read bigtest.nbt
let mut file = File::open("tests/bigtest.nbt").unwrap(); let original = include_bytes!("bigtest.nbt").to_vec();
let mut original = Vec::new();
file.read_to_end(&mut original).unwrap();
let mut original_stream = Cursor::new(original); let mut original_stream = Cursor::new(original);
let original_tag = Tag::read_gzip(&mut original_stream).unwrap(); let original_tag = Tag::read_gzip(&mut original_stream).unwrap();
@ -81,9 +72,7 @@ fn test_stringtest() {
Tag::String("😁".to_string()), Tag::String("😁".to_string()),
]) ])
)])); )]));
let mut file = std::fs::File::open("tests/stringtest.nbt").unwrap(); let original = include_bytes!("stringtest.nbt").to_vec();
let mut original = Vec::new();
file.read_to_end(&mut original).unwrap();
let mut original_stream = Cursor::new(original); let mut original_stream = Cursor::new(original);
let original_tag = Tag::read_gzip(&mut original_stream).unwrap(); let original_tag = Tag::read_gzip(&mut original_stream).unwrap();
@ -93,9 +82,7 @@ fn test_stringtest() {
#[test] #[test]
fn test_complex_player() { fn test_complex_player() {
let mut file = File::open("tests/complex_player.dat").unwrap(); let original = include_bytes!("complex_player.dat").to_vec();
let mut original = Vec::new();
file.read_to_end(&mut original).unwrap();
let mut original_stream = Cursor::new(original); let mut original_stream = Cursor::new(original);
let original_tag = Tag::read_gzip(&mut original_stream).unwrap(); let original_tag = Tag::read_gzip(&mut original_stream).unwrap();
@ -110,9 +97,7 @@ fn test_complex_player() {
#[test] #[test]
fn test_simple_player() { fn test_simple_player() {
let mut file = File::open("tests/simple_player.dat").unwrap(); let original = include_bytes!("simple_player.dat").to_vec();
let mut original = Vec::new();
file.read_to_end(&mut original).unwrap();
let mut original_stream = Cursor::new(original); let mut original_stream = Cursor::new(original);
let original_tag = Tag::read_gzip(&mut original_stream).unwrap(); let original_tag = Tag::read_gzip(&mut original_stream).unwrap();

View file

@ -11,7 +11,7 @@ use azalea_world::{
metadata::Sprinting, move_relative, Attributes, Jumping, Local, LookDirection, Physics, metadata::Sprinting, move_relative, Attributes, Jumping, Local, LookDirection, Physics,
Position, WorldName, Position, WorldName,
}, },
Instance, WorldContainer, Instance, InstanceContainer,
}; };
use bevy_app::{App, CoreSchedule, IntoSystemAppConfigs, Plugin}; use bevy_app::{App, CoreSchedule, IntoSystemAppConfigs, Plugin};
use bevy_ecs::{ use bevy_ecs::{
@ -54,7 +54,7 @@ fn travel(
), ),
With<Local>, With<Local>,
>, >,
world_container: Res<WorldContainer>, world_container: Res<InstanceContainer>,
) { ) {
for (mut physics, direction, mut position, attributes, world_name) in &mut query { for (mut physics, direction, mut position, attributes, world_name) in &mut query {
let world_lock = world_container let world_lock = world_container
@ -176,7 +176,7 @@ pub fn force_jump_listener(
&Sprinting, &Sprinting,
&WorldName, &WorldName,
)>, )>,
world_container: Res<WorldContainer>, world_container: Res<InstanceContainer>,
mut events: EventReader<ForceJumpEvent>, mut events: EventReader<ForceJumpEvent>,
) { ) {
for event in events.iter() { for event in events.iter() {
@ -327,7 +327,7 @@ mod tests {
use azalea_core::{ChunkPos, ResourceLocation}; use azalea_core::{ChunkPos, ResourceLocation};
use azalea_world::{ use azalea_world::{
entity::{EntityBundle, EntityPlugin, MinecraftEntityId}, entity::{EntityBundle, EntityPlugin, MinecraftEntityId},
Chunk, PartialWorld, Chunk, PartialInstance,
}; };
use bevy_app::App; use bevy_app::App;
use bevy_time::fixed_timestep::FixedTime; use bevy_time::fixed_timestep::FixedTime;
@ -339,14 +339,14 @@ mod tests {
app.add_plugin(PhysicsPlugin) app.add_plugin(PhysicsPlugin)
.add_plugin(EntityPlugin) .add_plugin(EntityPlugin)
.insert_resource(FixedTime::new(Duration::from_millis(50))) .insert_resource(FixedTime::new(Duration::from_millis(50)))
.init_resource::<WorldContainer>(); .init_resource::<InstanceContainer>();
app app
} }
#[test] #[test]
fn test_gravity() { fn test_gravity() {
let mut app = make_test_app(); let mut app = make_test_app();
let _world_lock = app.world.resource_mut::<WorldContainer>().insert( let _world_lock = app.world.resource_mut::<InstanceContainer>().insert(
ResourceLocation::new("minecraft:overworld").unwrap(), ResourceLocation::new("minecraft:overworld").unwrap(),
384, 384,
-64, -64,
@ -398,12 +398,12 @@ mod tests {
#[test] #[test]
fn test_collision() { fn test_collision() {
let mut app = make_test_app(); let mut app = make_test_app();
let world_lock = app.world.resource_mut::<WorldContainer>().insert( let world_lock = app.world.resource_mut::<InstanceContainer>().insert(
ResourceLocation::new("minecraft:overworld").unwrap(), ResourceLocation::new("minecraft:overworld").unwrap(),
384, 384,
-64, -64,
); );
let mut partial_world = PartialWorld::default(); let mut partial_world = PartialInstance::default();
partial_world.chunks.set( partial_world.chunks.set(
&ChunkPos { x: 0, z: 0 }, &ChunkPos { x: 0, z: 0 },
@ -457,12 +457,12 @@ mod tests {
#[test] #[test]
fn test_slab_collision() { fn test_slab_collision() {
let mut app = make_test_app(); let mut app = make_test_app();
let world_lock = app.world.resource_mut::<WorldContainer>().insert( let world_lock = app.world.resource_mut::<InstanceContainer>().insert(
ResourceLocation::new("minecraft:overworld").unwrap(), ResourceLocation::new("minecraft:overworld").unwrap(),
384, 384,
-64, -64,
); );
let mut partial_world = PartialWorld::default(); let mut partial_world = PartialInstance::default();
partial_world.chunks.set( partial_world.chunks.set(
&ChunkPos { x: 0, z: 0 }, &ChunkPos { x: 0, z: 0 },
@ -511,12 +511,12 @@ mod tests {
#[test] #[test]
fn test_top_slab_collision() { fn test_top_slab_collision() {
let mut app = make_test_app(); let mut app = make_test_app();
let world_lock = app.world.resource_mut::<WorldContainer>().insert( let world_lock = app.world.resource_mut::<InstanceContainer>().insert(
ResourceLocation::new("minecraft:overworld").unwrap(), ResourceLocation::new("minecraft:overworld").unwrap(),
384, 384,
-64, -64,
); );
let mut partial_world = PartialWorld::default(); let mut partial_world = PartialInstance::default();
partial_world.chunks.set( partial_world.chunks.set(
&ChunkPos { x: 0, z: 0 }, &ChunkPos { x: 0, z: 0 },
@ -564,12 +564,12 @@ mod tests {
#[test] #[test]
fn test_weird_wall_collision() { fn test_weird_wall_collision() {
let mut app = make_test_app(); let mut app = make_test_app();
let world_lock = app.world.resource_mut::<WorldContainer>().insert( let world_lock = app.world.resource_mut::<InstanceContainer>().insert(
ResourceLocation::new("minecraft:overworld").unwrap(), ResourceLocation::new("minecraft:overworld").unwrap(),
384, 384,
-64, -64,
); );
let mut partial_world = PartialWorld::default(); let mut partial_world = PartialInstance::default();
partial_world.chunks.set( partial_world.chunks.set(
&ChunkPos { x: 0, z: 0 }, &ChunkPos { x: 0, z: 0 },
@ -622,12 +622,12 @@ mod tests {
#[test] #[test]
fn test_negative_coordinates_weird_wall_collision() { fn test_negative_coordinates_weird_wall_collision() {
let mut app = make_test_app(); let mut app = make_test_app();
let world_lock = app.world.resource_mut::<WorldContainer>().insert( let world_lock = app.world.resource_mut::<InstanceContainer>().insert(
ResourceLocation::new("minecraft:overworld").unwrap(), ResourceLocation::new("minecraft:overworld").unwrap(),
384, 384,
-64, -64,
); );
let mut partial_world = PartialWorld::default(); let mut partial_world = PartialInstance::default();
partial_world.chunks.set( partial_world.chunks.set(
&ChunkPos { x: -1, z: -1 }, &ChunkPos { x: -1, z: -1 },

View file

@ -21,21 +21,21 @@ azalea-brigadier = { path = "../azalea-brigadier", version = "^0.6.0", features
] } ] }
azalea-buf = { path = "../azalea-buf", version = "^0.6.0" } azalea-buf = { path = "../azalea-buf", version = "^0.6.0" }
azalea-chat = { path = "../azalea-chat", 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-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-protocol-macros = { path = "./azalea-protocol-macros", version = "^0.6.0" }
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" } azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
azalea-world = { path = "../azalea-world", version = "^0.6.0" } azalea-world = { path = "../azalea-world", version = "^0.6.0" }
bevy_ecs = { version = "0.10.0", default-features = false } bevy_ecs = { version = "0.10.0", default-features = false }
byteorder = "^1.4.3" byteorder = "^1.4.3"
bytes = "^1.1.0" bytes = "^1.1.0"
flate2 = "1.0.23" flate2 = "1.0.25"
futures = "0.3.24" futures = "0.3.24"
futures-util = "0.3.24" futures-util = "0.3.24"
log = "0.4.17" log = "0.4.17"
serde = { version = "1.0.130", features = ["serde_derive"] } serde = { version = "1.0.152", features = ["serde_derive"] }
serde_json = "^1.0.72" serde_json = "^1.0.93"
thiserror = "1.0.37" thiserror = "1.0.37"
tokio = { version = "^1.24.2", features = ["io-util", "net", "macros"] } tokio = { version = "^1.24.2", features = ["io-util", "net", "macros"] }
tokio-util = { version = "0.7.4", features = ["codec"] } tokio-util = { version = "0.7.4", features = ["codec"] }
@ -48,6 +48,7 @@ uuid = "1.1.2"
connecting = [] connecting = []
default = ["packets"] default = ["packets"]
packets = ["connecting", "dep:async-compression", "dep:azalea-core"] packets = ["connecting", "dep:async-compression", "dep:azalea-core"]
strict_registry = ["packets"]
[dev-dependencies] [dev-dependencies]
anyhow = "^1.0.65" anyhow = "^1.0.65"

View file

@ -1,7 +1,12 @@
use self::registry::RegistryHolder;
use azalea_buf::McBuf; use azalea_buf::McBuf;
use azalea_core::{GameMode, GlobalPos, OptionalGameType, ResourceLocation}; use azalea_core::{GameMode, GlobalPos, OptionalGameType, ResourceLocation};
use azalea_protocol_macros::ClientboundGamePacket; 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)] #[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundLoginPacket { pub struct ClientboundLoginPacket {
pub player_id: u32, pub player_id: u32,
@ -9,7 +14,7 @@ pub struct ClientboundLoginPacket {
pub game_type: GameMode, pub game_type: GameMode,
pub previous_game_type: OptionalGameType, pub previous_game_type: OptionalGameType,
pub levels: Vec<ResourceLocation>, pub levels: Vec<ResourceLocation>,
pub registry_holder: azalea_nbt::Tag, pub registry_holder: RegistryHolder,
pub dimension_type: ResourceLocation, pub dimension_type: ResourceLocation,
pub dimension: ResourceLocation, pub dimension: ResourceLocation,
pub seed: i64, pub seed: i64,
@ -25,3 +30,445 @@ pub struct ClientboundLoginPacket {
pub is_flat: bool, pub is_flat: bool,
pub last_death_location: Option<GlobalPos>, 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");
}
}

View file

@ -1,8 +1,9 @@
use azalea_buf::{McBuf, McBufReadable, McBufWritable}; use azalea_buf::{McBuf, McBufReadable, McBufWritable};
use azalea_core::FixedBitSet; use azalea_core::FixedBitSet;
use azalea_protocol_macros::ServerboundGamePacket; use azalea_protocol_macros::ServerboundGamePacket;
use bevy_ecs::component::Component;
#[derive(Clone, Debug, McBuf, ServerboundGamePacket, PartialEq, Eq)] #[derive(Clone, Debug, McBuf, ServerboundGamePacket, PartialEq, Eq, Component)]
pub struct ServerboundClientInformationPacket { pub struct ServerboundClientInformationPacket {
/// The locale of the client. /// The locale of the client.
pub language: String, pub language: String,

View file

@ -50,6 +50,15 @@ pub async fn resolve_address(address: &ServerAddress) -> Result<SocketAddr, Reso
port: redirect_srv.port(), port: redirect_srv.port(),
}; };
if redirect_address.host == address.host {
let lookup_ip_result = resolver.lookup_ip(redirect_address.host).await;
let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?;
return Ok(SocketAddr::new(
lookup_ip.iter().next().unwrap(),
redirect_address.port,
))
}
// debug!("redirecting to {:?}", redirect_address); // debug!("redirecting to {:?}", redirect_address);
return resolve_address(&redirect_address).await; return resolve_address(&redirect_address).await;

View file

@ -10,10 +10,10 @@ use std::{
use crate::{ChunkStorage, Instance}; use crate::{ChunkStorage, Instance};
/// A container of [`World`]s. Worlds are stored as a Weak pointer here, so /// A container of [`Instance`]s (aka worlds). Instances are stored as a Weak
/// if no clients are using a world it will be forgotten. /// pointer here, so if no clients are using an instance it will be forgotten.
#[derive(Default, Resource)] #[derive(Default, Resource)]
pub struct WorldContainer { pub struct InstanceContainer {
// We just refer to the chunks here and don't include entities because there's not that many // We just refer to the chunks here and don't include entities because there's not that many
// cases where we'd want to get every entity in the world (just getting the entities in chunks // cases where we'd want to get every entity in the world (just getting the entities in chunks
// should work fine). // should work fine).
@ -29,9 +29,9 @@ pub struct WorldContainer {
pub worlds: HashMap<ResourceLocation, Weak<RwLock<Instance>>>, pub worlds: HashMap<ResourceLocation, Weak<RwLock<Instance>>>,
} }
impl WorldContainer { impl InstanceContainer {
pub fn new() -> Self { pub fn new() -> Self {
WorldContainer { InstanceContainer {
worlds: HashMap::new(), worlds: HashMap::new(),
} }
} }

View file

@ -6,7 +6,7 @@ use crate::{
entity::{ entity::{
self, add_dead, update_bounding_box, EntityUuid, MinecraftEntityId, Position, WorldName, self, add_dead, update_bounding_box, EntityUuid, MinecraftEntityId, Position, WorldName,
}, },
update_entity_by_id_index, update_uuid_index, PartialWorld, WorldContainer, update_entity_by_id_index, update_uuid_index, InstanceContainer, PartialInstance,
}; };
use azalea_core::ChunkPos; use azalea_core::ChunkPos;
use bevy_app::{App, CoreSet, Plugin}; use bevy_app::{App, CoreSet, Plugin};
@ -135,9 +135,9 @@ impl PartialEntityInfos {
} }
} }
/// A [`Command`] that applies a "relative update" to an entity, which means /// An [`EntityCommand`] that applies a "relative update" to an entity, which
/// this update won't be run multiple times by different clients in the same /// means this update won't be run multiple times by different clients in the
/// world. /// same world.
/// ///
/// This is used to avoid a bug where when there's multiple clients in the same /// This is used to avoid a bug where when there's multiple clients in the same
/// world and an entity sends a relative move packet to all clients, its /// world and an entity sends a relative move packet to all clients, its
@ -147,7 +147,7 @@ impl PartialEntityInfos {
/// other clients within render distance will get too. You usually don't need /// other clients within render distance will get too. You usually don't need
/// this when the change isn't relative either. /// this when the change isn't relative either.
pub struct RelativeEntityUpdate { pub struct RelativeEntityUpdate {
pub partial_world: Arc<RwLock<PartialWorld>>, pub partial_world: Arc<RwLock<PartialInstance>>,
// a function that takes the entity and updates it // a function that takes the entity and updates it
pub update: Box<dyn FnOnce(&mut EntityMut) + Send + Sync>, pub update: Box<dyn FnOnce(&mut EntityMut) + Send + Sync>,
} }
@ -219,7 +219,7 @@ fn update_entity_chunk_positions(
), ),
Changed<entity::Position>, Changed<entity::Position>,
>, >,
world_container: Res<WorldContainer>, world_container: Res<InstanceContainer>,
) { ) {
for (entity, pos, last_pos, world_name) in query.iter_mut() { for (entity, pos, last_pos, world_name) in query.iter_mut() {
let world_lock = world_container.get(world_name).unwrap(); let world_lock = world_container.get(world_name).unwrap();
@ -286,7 +286,7 @@ fn debug_detect_updates_received_on_local_entities(
fn remove_despawned_entities_from_indexes( fn remove_despawned_entities_from_indexes(
mut commands: Commands, mut commands: Commands,
mut entity_infos: ResMut<EntityInfos>, mut entity_infos: ResMut<EntityInfos>,
world_container: Res<WorldContainer>, world_container: Res<InstanceContainer>,
query: Query<(Entity, &EntityUuid, &Position, &WorldName, &LoadedBy), Changed<LoadedBy>>, query: Query<(Entity, &EntityUuid, &Position, &WorldName, &LoadedBy), Changed<LoadedBy>>,
) { ) {
for (entity, uuid, position, world_name, loaded_by) in &query { for (entity, uuid, position, world_name, loaded_by) in &query {

View file

@ -264,7 +264,7 @@ pub struct EyeHeight(f32);
/// Most of the time, you should be using `azalea_registry::EntityKind` /// Most of the time, you should be using `azalea_registry::EntityKind`
/// directly instead. /// directly instead.
#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)] #[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, /// A bundle of components that every entity has. This doesn't contain metadata,
/// that has to be added separately. /// that has to be added separately.

View file

@ -4,7 +4,7 @@ use crate::{
}, },
iterators::ChunkIterator, iterators::ChunkIterator,
palette::Palette, palette::Palette,
ChunkStorage, PartialChunkStorage, WorldContainer, ChunkStorage, InstanceContainer, PartialChunkStorage,
}; };
use azalea_block::{BlockState, BlockStates}; use azalea_block::{BlockState, BlockStates};
use azalea_core::{BlockPos, ChunkPos}; use azalea_core::{BlockPos, ChunkPos};
@ -21,24 +21,24 @@ use std::{
fmt::Debug, fmt::Debug,
}; };
/// PartialWorlds are usually owned by clients, and hold strong references to /// PartialInstances are usually owned by clients, and hold strong references to
/// chunks and entities in [`World`]s. /// chunks and entities in [`Instance`]s.
/// ///
/// Basically, they hold the chunks and entities that are within render /// Basically, they hold the chunks and entities that are within render
/// distance but can still access chunks and entities owned by other /// distance but can still access chunks and entities owned by other
/// `PartialWorld`s that have the same `World`. /// `PartialInstance`s that have the same `Instance`.
/// ///
/// This is primarily useful for having multiple clients in the same world. /// This is primarily useful for having multiple clients in the same Instance.
pub struct PartialWorld { pub struct PartialInstance {
pub chunks: PartialChunkStorage, pub chunks: PartialChunkStorage,
/// Some metadata about entities, like what entities are in certain chunks. /// Some metadata about entities, like what entities are in certain chunks.
/// This does not contain the entity data itself, that's in the ECS. /// This does not contain the entity data itself, that's in the ECS.
pub entity_infos: PartialEntityInfos, pub entity_infos: PartialEntityInfos,
} }
impl PartialWorld { impl PartialInstance {
pub fn new(chunk_radius: u32, owner_entity: Option<Entity>) -> Self { pub fn new(chunk_radius: u32, owner_entity: Option<Entity>) -> Self {
PartialWorld { PartialInstance {
chunks: PartialChunkStorage::new(chunk_radius), chunks: PartialChunkStorage::new(chunk_radius),
entity_infos: PartialEntityInfos::new(owner_entity), entity_infos: PartialEntityInfos::new(owner_entity),
} }
@ -59,7 +59,7 @@ pub fn deduplicate_entities(
(Changed<MinecraftEntityId>, Without<Local>), (Changed<MinecraftEntityId>, Without<Local>),
>, >,
mut loaded_by_query: Query<&mut LoadedBy>, mut loaded_by_query: Query<&mut LoadedBy>,
world_container: Res<WorldContainer>, world_container: Res<InstanceContainer>,
) { ) {
// if this entity already exists, remove it // if this entity already exists, remove it
for (new_entity, id, world_name) in query.iter_mut() { for (new_entity, id, world_name) in query.iter_mut() {
@ -104,7 +104,7 @@ pub fn deduplicate_local_entities(
(Entity, &MinecraftEntityId, &WorldName), (Entity, &MinecraftEntityId, &WorldName),
(Changed<MinecraftEntityId>, With<Local>), (Changed<MinecraftEntityId>, With<Local>),
>, >,
world_container: Res<WorldContainer>, world_container: Res<InstanceContainer>,
) { ) {
// if this entity already exists, remove the old one // if this entity already exists, remove the old one
for (new_entity, id, world_name) in query.iter_mut() { for (new_entity, id, world_name) in query.iter_mut() {
@ -262,7 +262,7 @@ impl Instance {
} }
} }
impl Debug for PartialWorld { impl Debug for PartialInstance {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("World") f.debug_struct("World")
.field("chunk_storage", &self.chunks) .field("chunk_storage", &self.chunks)
@ -271,8 +271,8 @@ impl Debug for PartialWorld {
} }
} }
impl Default for PartialWorld { impl Default for PartialInstance {
/// Creates a completely self-contained `PartialWorld`. This is only for /// Creates a completely self-contained `PartialInstance`. This is only for
/// testing and shouldn't be used in actual code! /// testing and shouldn't be used in actual code!
fn default() -> Self { fn default() -> Self {
let chunk_storage = PartialChunkStorage::default(); let chunk_storage = PartialChunkStorage::default();
@ -290,7 +290,7 @@ pub fn update_entity_by_id_index(
(Entity, &MinecraftEntityId, &WorldName, Option<&Local>), (Entity, &MinecraftEntityId, &WorldName, Option<&Local>),
Changed<MinecraftEntityId>, Changed<MinecraftEntityId>,
>, >,
world_container: Res<WorldContainer>, world_container: Res<InstanceContainer>,
) { ) {
for (entity, id, world_name, local) in query.iter_mut() { for (entity, id, world_name, local) in query.iter_mut() {
let world_lock = world_container.get(world_name).unwrap(); let world_lock = world_container.get(world_name).unwrap();

View file

@ -20,7 +20,7 @@ use azalea_world::entity::metadata::Player;
use azalea_world::entity::Local; use azalea_world::entity::Local;
use azalea_world::{ use azalea_world::{
entity::{Physics, Position, WorldName}, entity::{Physics, Position, WorldName},
WorldContainer, InstanceContainer,
}; };
use bevy_tasks::{AsyncComputeTaskPool, Task}; use bevy_tasks::{AsyncComputeTaskPool, Task};
use futures_lite::future; use futures_lite::future;
@ -93,7 +93,7 @@ fn goto_listener(
mut commands: Commands, mut commands: Commands,
mut events: EventReader<GotoEvent>, mut events: EventReader<GotoEvent>,
mut query: Query<(&Position, &WorldName)>, mut query: Query<(&Position, &WorldName)>,
world_container: Res<WorldContainer>, world_container: Res<InstanceContainer>,
) { ) {
let thread_pool = AsyncComputeTaskPool::get(); let thread_pool = AsyncComputeTaskPool::get();

View file

@ -151,11 +151,11 @@ mod tests {
use super::*; use super::*;
use azalea_block::BlockState; use azalea_block::BlockState;
use azalea_core::ChunkPos; use azalea_core::ChunkPos;
use azalea_world::{Chunk, ChunkStorage, PartialWorld}; use azalea_world::{Chunk, ChunkStorage, PartialInstance};
#[test] #[test]
fn test_is_passable() { fn test_is_passable() {
let mut partial_world = PartialWorld::default(); let mut partial_world = PartialInstance::default();
let mut chunk_storage = ChunkStorage::default(); let mut chunk_storage = ChunkStorage::default();
partial_world.chunks.set( partial_world.chunks.set(
@ -181,7 +181,7 @@ mod tests {
#[test] #[test]
fn test_is_solid() { fn test_is_solid() {
let mut partial_world = PartialWorld::default(); let mut partial_world = PartialInstance::default();
let mut chunk_storage = ChunkStorage::default(); let mut chunk_storage = ChunkStorage::default();
partial_world.chunks.set( partial_world.chunks.set(
&ChunkPos { x: 0, z: 0 }, &ChunkPos { x: 0, z: 0 },
@ -206,7 +206,7 @@ mod tests {
#[test] #[test]
fn test_is_standable() { fn test_is_standable() {
let mut partial_world = PartialWorld::default(); let mut partial_world = PartialInstance::default();
let mut chunk_storage = ChunkStorage::default(); let mut chunk_storage = ChunkStorage::default();
partial_world.chunks.set( partial_world.chunks.set(
&ChunkPos { x: 0, z: 0 }, &ChunkPos { x: 0, z: 0 },

View file

@ -11,7 +11,7 @@ use azalea_protocol::{
resolver::{self, ResolverError}, resolver::{self, ResolverError},
ServerAddress, ServerAddress,
}; };
use azalea_world::WorldContainer; use azalea_world::InstanceContainer;
use bevy_app::{App, Plugin, PluginGroup, PluginGroupBuilder}; use bevy_app::{App, Plugin, PluginGroup, PluginGroupBuilder};
use bevy_ecs::{component::Component, entity::Entity, system::Resource, world::World}; use bevy_ecs::{component::Component, entity::Entity, system::Resource, world::World};
use futures::future::join_all; use futures::future::join_all;
@ -24,7 +24,7 @@ use tokio::sync::mpsc;
/// A swarm is a way to conveniently control many bots at once, while also /// A swarm is a way to conveniently control many bots at once, while also
/// being able to control bots at an individual level when desired. /// being able to control bots at an individual level when desired.
/// ///
/// Swarms are created from [`azalea::swarm::SwarmBuilder`]. /// Swarms are created from [`SwarmBuilder`].
/// ///
/// The `S` type parameter is the type of the state for individual bots. /// The `S` type parameter is the type of the state for individual bots.
/// It's used to make the [`Swarm::add`] function work. /// It's used to make the [`Swarm::add`] function work.
@ -37,7 +37,7 @@ pub struct Swarm {
// bot_datas: Arc<Mutex<Vec<(Client, S)>>>, // bot_datas: Arc<Mutex<Vec<(Client, S)>>>,
resolved_address: SocketAddr, resolved_address: SocketAddr,
address: ServerAddress, address: ServerAddress,
pub world_container: Arc<RwLock<WorldContainer>>, pub world_container: Arc<RwLock<InstanceContainer>>,
bots_tx: mpsc::UnboundedSender<(Option<Event>, Client)>, bots_tx: mpsc::UnboundedSender<(Option<Event>, Client)>,
swarm_tx: mpsc::UnboundedSender<SwarmEvent>, swarm_tx: mpsc::UnboundedSender<SwarmEvent>,
@ -248,7 +248,7 @@ where
// resolve the address // resolve the address
let resolved_address = resolver::resolve_address(&address).await?; let resolved_address = resolver::resolve_address(&address).await?;
let world_container = Arc::new(RwLock::new(WorldContainer::default())); let world_container = Arc::new(RwLock::new(InstanceContainer::default()));
// we can't modify the swarm plugins after this // we can't modify the swarm plugins after this
let (bots_tx, mut bots_rx) = mpsc::unbounded_channel(); let (bots_tx, mut bots_rx) = mpsc::unbounded_channel();