diff --git a/core/Cargo.toml b/core/Cargo.toml index 68f991a..aaf6952 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,10 +1,8 @@ [package] -name = "rimecraft_core" +name = "rimecraft-core" version = "1.20.1" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] rimecraft-primitives = { path = "../primitives", features = [ "serde", diff --git a/core/src/net/mod.rs b/core/src/net/mod.rs index e44026d..7137ae9 100644 --- a/core/src/net/mod.rs +++ b/core/src/net/mod.rs @@ -1,23 +1,23 @@ pub mod listener; pub mod packet; -#[repr(i32)] #[derive(Clone, Copy, PartialEq, Eq)] pub enum State { - Handshaking = -1, - Play = 0, - Status = 1, - Login = 2, + Handshaking, + Play, + Status, + Login, + Configuration, } impl State { - pub fn from_id(id: i32) -> Option { - match id { - -1 => Some(Self::Handshaking), - 0 => Some(Self::Play), - 1 => Some(Self::Status), - 2 => Some(Self::Login), - _ => None, + pub fn id(&self) -> &str { + match self { + State::Handshaking => "handshake", + State::Play => "play", + State::Status => "status", + State::Login => "login", + State::Configuration => "configuration", } } } diff --git a/core/src/net/packet.rs b/core/src/net/packet.rs index 4ded68f..ee898be 100644 --- a/core/src/net/packet.rs +++ b/core/src/net/packet.rs @@ -30,6 +30,18 @@ where fn is_writing_err_skippable(&self) -> bool { false } + + /// Returns a new network state to transition to, or `None` + /// to indicate no state change. + /// + /// The state transition is done on both the sender and receiver + /// sides, but it is only in one direction (out of C2S and S2C). + /// Another packet must be processed in the reverse direction to + /// ensure the state in both directions are updated. + #[inline] + fn new_net_state(&self) -> Option { + None + } } /// Provides an abstraction to [`Packet::apply`], without complex diff --git a/core/src/net/packet/c2s.rs b/core/src/net/packet/c2s.rs index 0addcf2..ed21150 100644 --- a/core/src/net/packet/c2s.rs +++ b/core/src/net/packet/c2s.rs @@ -10,12 +10,12 @@ pub struct Handshake { proto_ver: i32, addr: String, port: u16, - intended_state: crate::net::State, + intended_state: ConnectionIntent, } impl Handshake { #[inline] - pub fn new(addr: String, port: u16, intended_state: crate::net::State) -> Self { + pub fn new(addr: String, port: u16, intended_state: ConnectionIntent) -> Self { ///TODO: Need to implement net.minecraft.SharedConstants const PROTO_VER: i32 = 114514; @@ -26,6 +26,26 @@ impl Handshake { intended_state, } } + + #[inline] + pub fn proto_version(&self) -> i32 { + self.proto_ver + } + + #[inline] + pub fn address(&self) -> &str { + &self.addr + } + + #[inline] + pub fn port(&self) -> u16 { + self.port + } + + #[inline] + pub fn intended_state(&self) -> ConnectionIntent { + self.intended_state + } } impl Encode for Handshake { @@ -44,7 +64,6 @@ impl Encode for Handshake { impl<'de> Decode<'de> for Handshake { type Output = Self; - #[inline] fn decode(buf: &'de mut B) -> anyhow::Result where B: bytes::Buf, @@ -58,16 +77,42 @@ impl<'de> Decode<'de> for Handshake { proto_ver, addr, port, - intended_state: crate::net::State::from_id(state).unwrap(), + intended_state: ConnectionIntent::n(state) + .ok_or_else(|| anyhow::anyhow!("unknown connection intent: {state}"))?, }) } } -impl super::Packet for Handshake where L: listener::Accept {} +impl super::Packet for Handshake +where + L: listener::Accept, +{ + #[inline] + fn new_net_state(&self) -> Option { + Some(self.intended_state.state()) + } +} + +#[repr(i32)] +#[derive(Clone, Copy, PartialEq, Eq, enumn::N)] +pub enum ConnectionIntent { + Status, + Login, +} + +impl ConnectionIntent { + #[inline] + pub fn state(self) -> crate::net::State { + match self { + ConnectionIntent::Status => crate::net::State::Status, + ConnectionIntent::Login => crate::net::State::Login, + } + } +} pub struct LoginHello { name: String, - uuid: Option, + profile_id: uuid::Uuid, } impl Encode for LoginHello { @@ -77,7 +122,7 @@ impl Encode for LoginHello { B: bytes::BufMut, { self.name.encode(buf)?; - self.uuid.encode(buf) + self.profile_id.encode(buf) } } @@ -90,9 +135,12 @@ impl<'de> Decode<'de> for LoginHello { B: bytes::Buf, { let name = String::decode(buf)?; - let uuid = Option::::decode(buf)?; + let uuid = uuid::Uuid::decode(buf)?; - Ok(Self { name, uuid }) + Ok(Self { + name, + profile_id: uuid, + }) } } @@ -108,11 +156,6 @@ impl LoginQueryRes { pub fn query_id(&self) -> i32 { self.query_id } - - #[inline] - pub fn response(&self) -> Option<&[u8]> { - self.res.as_ref().map(|value| &value[..]) - } } impl Encode for LoginQueryRes { @@ -151,7 +194,9 @@ impl<'de> Decode<'de> for LoginQueryRes { { let remaining = buf.remaining(); if remaining <= super::QUERY_MAX_PAYLOAD_LEN { - Ok(buf.copy_to_bytes(remaining)) + // this was changed in 1.20.2 so the bytes are empty + buf.advance(remaining); + Ok(Bytes::new()) } else { Err(anyhow::anyhow!( "payload may not be larger than {} bytes", diff --git a/core/src/net/packet/s2c.rs b/core/src/net/packet/s2c.rs index 9b4ce53..4532f22 100644 --- a/core/src/net/packet/s2c.rs +++ b/core/src/net/packet/s2c.rs @@ -184,14 +184,9 @@ impl LoginQueryReq { } #[inline] - pub fn channel(&self) -> &Id { + pub fn channel_id(&self) -> &Id { &self.channel } - - #[inline] - pub fn payload(&self) -> &[u8] { - &self.payload - } } impl Encode for LoginQueryReq { @@ -219,7 +214,9 @@ impl<'de> Decode<'de> for LoginQueryReq { let remaining = buf.remaining(); if remaining <= super::QUERY_MAX_PAYLOAD_LEN { - let payload = buf.copy_to_bytes(remaining); + // this was changed in 1.20.2 + buf.advance(remaining); + let payload = Bytes::new(); Ok(Self { query_id, channel, @@ -238,11 +235,11 @@ impl super::Packet for LoginQueryReq where L: listener::Accept {} //TODO: LoginSuccessS2CPacket and authlib's GameProfile implementation -pub struct QueryPong { +pub struct PingResult { start_time: u64, } -impl QueryPong { +impl PingResult { #[inline] pub fn new(start_time: u64) -> Self { Self { start_time } @@ -254,7 +251,7 @@ impl QueryPong { } } -impl Encode for QueryPong { +impl Encode for PingResult { #[inline] fn encode(&self, buf: &mut B) -> anyhow::Result<()> where @@ -265,7 +262,7 @@ impl Encode for QueryPong { } } -impl<'de> Decode<'de> for QueryPong { +impl<'de> Decode<'de> for PingResult { type Output = Self; #[inline] @@ -279,6 +276,6 @@ impl<'de> Decode<'de> for QueryPong { } } -impl super::Packet for QueryPong where L: listener::Accept {} +impl super::Packet for PingResult where L: listener::Accept {} //TODO: QueryResponseS2CPacket and ServerMetadata diff --git a/core/src/registry/tag.rs b/core/src/registry/tag.rs index 17e1d59..ac7e270 100644 --- a/core/src/registry/tag.rs +++ b/core/src/registry/tag.rs @@ -1,7 +1,7 @@ use rimecraft_primitives::Id; -static KEYS_CACHE: once_cell::sync::Lazy> = - once_cell::sync::Lazy::new(rimecraft_caches::ArcCaches::new); +static KEYS_CACHE: once_cell::sync::Lazy> = + once_cell::sync::Lazy::new(rimecraft_caches::arc::Caches::new); /// Represents a tag key. pub struct Key { diff --git a/core/src/util/math.rs b/core/src/util/math.rs index 14ba930..95b75cb 100644 --- a/core/src/util/math.rs +++ b/core/src/util/math.rs @@ -3,7 +3,7 @@ use std::ops::Deref; /// A box with double-valued coords. /// The box is axis-aligned and the coords are minimum inclusive and maximum exclusive. #[derive(Clone, Copy, PartialEq)] -pub struct Box { +pub struct BoundingBox { pub min_x: f64, pub min_y: f64, pub min_z: f64, @@ -12,8 +12,9 @@ pub struct Box { pub max_z: f64, } -impl Box { +impl BoundingBox { /// Creates a box of the given positions (in (x, y, z)) as corners. + #[inline] pub fn new>(pos1: T, pos2: T) -> Self { let p1 = pos1.into(); let p2 = pos2.into(); @@ -72,6 +73,7 @@ impl Box { self } + #[inline] pub fn expand(mut self, x: f64, y: f64, z: f64) -> Self { self.min_x -= x; self.min_y -= y; @@ -79,9 +81,11 @@ impl Box { self.max_x += x; self.max_y += y; self.max_z += z; + self } + #[inline] pub fn expand_all(self, value: f64) -> Self { self.expand(value, value, value) } @@ -146,6 +150,7 @@ impl Box { /// Creates a box that is translated by the given offset /// on each axis from this box. + #[inline] pub fn offset(mut self, x: f64, y: f64, z: f64) -> Self { self.min_x += x; self.min_y += y; @@ -156,6 +161,7 @@ impl Box { self } + #[inline] pub fn is_nan(self) -> bool { self.min_x.is_nan() || self.min_y.is_nan() @@ -166,7 +172,8 @@ impl Box { } } -impl std::hash::Hash for Box { +impl std::hash::Hash for BoundingBox { + #[inline] fn hash(&self, state: &mut H) { state.write_u64(self.min_x.to_bits()); state.write_u64(self.min_y.to_bits()); @@ -177,9 +184,10 @@ impl std::hash::Hash for Box { } } -impl Eq for Box {} +impl Eq for BoundingBox {} -impl From for Box { +impl From for BoundingBox { + #[inline] fn from(value: glam::DVec3) -> Self { Self { min_x: value.x, @@ -192,7 +200,7 @@ impl From for Box { } } -impl std::fmt::Display for Box { +impl std::fmt::Display for BoundingBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("Box[")?; diff --git a/core/src/world/palette.rs b/core/src/world/palette.rs index 5b4d1a2..c42f752 100644 --- a/core/src/world/palette.rs +++ b/core/src/world/palette.rs @@ -104,7 +104,7 @@ where }) .sum::() } - Inner::Indexed(_) => VarI32(0).len(), + Inner::Indexed(_) => 0, Inner::Singular(option) => { if let Some(entry) = option { VarI32(self.ids.index_of(&entry).map(|e| e as i32).unwrap_or(-1)).len() diff --git a/primitives/readme.md b/primitives/readme.md index 9ce4eb7..3e777b3 100644 --- a/primitives/readme.md +++ b/primitives/readme.md @@ -4,7 +4,7 @@ Primitive types and traits for building Rimecraft applications. ## Status -Targeting MCJE version: `1.20.1` +Targeting MCJE version: `1.20.2` ## Features diff --git a/primitives/src/identifier.rs b/primitives/src/identifier.rs index 208694a..26734ef 100644 --- a/primitives/src/identifier.rs +++ b/primitives/src/identifier.rs @@ -87,12 +87,14 @@ impl Identifier { /// Parse a string identifier (ex. `minecraft:air`). #[inline] pub fn try_parse(id: &str) -> Result { - Self::split_on(id, ':') + Self::split(id, ':') } - /// Split a string identifier based on a custom - /// delimiter. - fn split_on(id: &str, delimiter: char) -> Result { + /// Splits the `id` into an array of two strings at the first occurrence + /// of `delimiter`, excluding the delimiter character, or uses `:` for + /// the first string in the resulting array when the deliminator does + /// not exist or is the first character. + fn split(id: &str, delimiter: char) -> Result { if let Some(arr) = id.split_once(delimiter) { Self::try_new(arr.0, arr.1.to_owned()) } else { @@ -100,8 +102,8 @@ impl Identifier { } } - #[inline] - fn is_namespace_valid(namespace: &str) -> bool { + /// Whether `namespace` can be used as an identifier's namespace + pub fn is_namespace_valid(namespace: &str) -> bool { for c in namespace.chars() { if !(c == '_' || c == '-' || c >= 'a' || c <= 'z' || c >= '0' || c <= '9' || c == '.') { return false; @@ -110,8 +112,8 @@ impl Identifier { true } - #[inline] - fn is_path_valid(path: &str) -> bool { + /// Whether `path` can be used as an identifier's path + pub fn is_path_valid(path: &str) -> bool { for c in path.chars() { if !(c == '_' || c == '-'