mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 14:31:30 +11:00
Make Ident
consistent with vanilla. (#309)
## Description Makes `Ident` consistent with vanilla by prepending the default namespace if none is provided in the constructor. Previously, the constructor did not normalize `foo` to `minecraft:foo`. This could lead to subtle bugs when the ident is eventually unwrapped with `Ident::as_str`. (comparing `foo` with `minecraft:foo` while inside the `Ident` was still handled correctly). ## Test Plan Steps: 1. `cargo test`
This commit is contained in:
parent
ba625b3217
commit
53573642ec
|
@ -221,7 +221,7 @@ impl Client {
|
|||
|
||||
pub fn send_custom_payload(&mut self, channel: Ident<&str>, data: &[u8]) {
|
||||
self.write_packet(&CustomPayloadS2c {
|
||||
channel,
|
||||
channel: channel.into(),
|
||||
data: data.into(),
|
||||
});
|
||||
}
|
||||
|
@ -682,13 +682,13 @@ fn initial_join(
|
|||
|
||||
let dimension_names = server
|
||||
.dimensions()
|
||||
.map(|(_, dim)| dim.name.as_str_ident())
|
||||
.map(|(_, dim)| dim.name.as_str_ident().into())
|
||||
.collect();
|
||||
|
||||
let dimension_name = server.dimension(instance.dimension()).name.as_str_ident();
|
||||
|
||||
let last_death_location = q.death_loc.0.map(|(id, pos)| GlobalPos {
|
||||
dimension_name: server.dimension(id).name.as_str_ident(),
|
||||
dimension_name: server.dimension(id).name.as_str_ident().into(),
|
||||
position: pos,
|
||||
});
|
||||
|
||||
|
@ -701,8 +701,8 @@ fn initial_join(
|
|||
previous_game_mode: q.prev_game_mode.0.map(|g| g as i8).unwrap_or(-1),
|
||||
dimension_names,
|
||||
registry_codec: Cow::Borrowed(server.registry_codec()),
|
||||
dimension_type_name: dimension_name,
|
||||
dimension_name,
|
||||
dimension_type_name: dimension_name.into(),
|
||||
dimension_name: dimension_name.into(),
|
||||
hashed_seed: q.hashed_seed.0 as i64,
|
||||
max_players: VarInt(0), // Ignored by clients.
|
||||
view_distance: VarInt(q.view_distance.0 as i32),
|
||||
|
@ -756,13 +756,13 @@ fn respawn(
|
|||
let dimension_name = server.dimension(instance.dimension()).name.as_str_ident();
|
||||
|
||||
let last_death_location = death_loc.0.map(|(id, pos)| GlobalPos {
|
||||
dimension_name: server.dimension(id).name.as_str_ident(),
|
||||
dimension_name: server.dimension(id).name.as_str_ident().into(),
|
||||
position: pos,
|
||||
});
|
||||
|
||||
client.write_packet(&PlayerRespawnS2c {
|
||||
dimension_type_name: dimension_name,
|
||||
dimension_name,
|
||||
dimension_type_name: dimension_name.into(),
|
||||
dimension_name: dimension_name.into(),
|
||||
hashed_seed: hashed_seed.0,
|
||||
game_mode: (*game_mode).into(),
|
||||
previous_game_mode: prev_game_mode.0.map(|g| g as i8).unwrap_or(-1),
|
||||
|
|
|
@ -138,7 +138,7 @@ pub struct CloseHandledScreen {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct CustomPayload {
|
||||
pub client: Entity,
|
||||
pub channel: Ident<Box<str>>,
|
||||
pub channel: Ident<String>,
|
||||
pub data: Box<[u8]>,
|
||||
}
|
||||
|
||||
|
@ -263,7 +263,7 @@ pub struct PickFromInventory {
|
|||
pub struct CraftRequest {
|
||||
pub client: Entity,
|
||||
pub window_id: i8,
|
||||
pub recipe: Ident<Box<str>>,
|
||||
pub recipe: Ident<String>,
|
||||
pub make_all: bool,
|
||||
}
|
||||
|
||||
|
@ -354,7 +354,7 @@ pub struct RecipeCategoryOptions {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct RecipeBookData {
|
||||
pub client: Entity,
|
||||
pub recipe_id: Ident<Box<str>>,
|
||||
pub recipe_id: Ident<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -395,7 +395,7 @@ pub struct ResourcePackStatusChange {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct OpenAdvancementTab {
|
||||
pub client: Entity,
|
||||
pub tab_id: Ident<Box<str>>,
|
||||
pub tab_id: Ident<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -452,9 +452,9 @@ pub struct CreativeInventoryAction {
|
|||
pub struct UpdateJigsaw {
|
||||
pub client: Entity,
|
||||
pub position: BlockPos,
|
||||
pub name: Ident<Box<str>>,
|
||||
pub target: Ident<Box<str>>,
|
||||
pub pool: Ident<Box<str>>,
|
||||
pub name: Ident<String>,
|
||||
pub target: Ident<String>,
|
||||
pub pool: Ident<String>,
|
||||
pub final_state: Box<str>,
|
||||
pub joint_type: Box<str>,
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ use tokio::sync::OwnedSemaphorePermit;
|
|||
use tracing::{error, info, instrument, trace, warn};
|
||||
use uuid::Uuid;
|
||||
use valence_protocol::codec::{PacketDecoder, PacketEncoder};
|
||||
use valence_protocol::ident::Ident;
|
||||
use valence_protocol::packet::c2s::handshake::handshake::NextState;
|
||||
use valence_protocol::packet::c2s::handshake::HandshakeC2s;
|
||||
use valence_protocol::packet::c2s::login::{LoginHelloC2s, LoginKeyC2s, LoginQueryResponseC2s};
|
||||
|
@ -35,7 +34,7 @@ use valence_protocol::raw::RawBytes;
|
|||
use valence_protocol::text::Text;
|
||||
use valence_protocol::types::Property;
|
||||
use valence_protocol::var_int::VarInt;
|
||||
use valence_protocol::{translation_key, Decode, MINECRAFT_VERSION, PROTOCOL_VERSION};
|
||||
use valence_protocol::{ident_str, translation_key, Decode, MINECRAFT_VERSION, PROTOCOL_VERSION};
|
||||
|
||||
use crate::config::{AsyncCallbacks, ConnectionMode, ServerListPing};
|
||||
use crate::server::connection::InitialConnection;
|
||||
|
@ -440,7 +439,7 @@ pub(super) async fn login_velocity(
|
|||
// Send Player Info Request into the Plugin Channel
|
||||
conn.send_packet(&LoginQueryRequestS2c {
|
||||
message_id: VarInt(message_id),
|
||||
channel: Ident::new("velocity:player_info").unwrap(),
|
||||
channel: ident_str!("velocity:player_info").into(),
|
||||
data: RawBytes(&[VELOCITY_MIN_SUPPORTED_VERSION]),
|
||||
})
|
||||
.await?;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use num_integer::{div_ceil, Integer};
|
||||
use thiserror::Error;
|
||||
use valence::biome::BiomeId;
|
||||
|
@ -210,11 +212,11 @@ where
|
|||
converted_biome_palette.clear();
|
||||
|
||||
for biome_name in palette {
|
||||
let Ok(ident) = Ident::new(biome_name.as_str()) else {
|
||||
let Ok(ident) = Ident::<Cow<str>>::new(biome_name) else {
|
||||
return Err(ToValenceError::BadBiomeName)
|
||||
};
|
||||
|
||||
converted_biome_palette.push(map_biome(ident));
|
||||
converted_biome_palette.push(map_biome(ident.as_str_ident()));
|
||||
}
|
||||
|
||||
if converted_biome_palette.len() == 1 {
|
||||
|
@ -274,7 +276,7 @@ where
|
|||
let Ok(ident) = Ident::new(&ident[..]) else {
|
||||
return Err(ToValenceError::UnknownBlockEntityIdent(ident.clone()));
|
||||
};
|
||||
let Some(kind) = BlockEntityKind::from_ident(ident) else {
|
||||
let Some(kind) = BlockEntityKind::from_ident(ident.as_str_ident()) else {
|
||||
return Err(ToValenceError::UnknownBlockEntityIdent(ident.as_str().to_string()));
|
||||
};
|
||||
let block_entity = BlockEntity {
|
||||
|
|
|
@ -58,7 +58,7 @@ pub fn build() -> anyhow::Result<TokenStream> {
|
|||
let str_name = &sound.name;
|
||||
let name = ident(str_name.to_pascal_case());
|
||||
quote! {
|
||||
Self::#name => #str_name,
|
||||
Self::#name => ident_str!(#str_name),
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
@ -105,8 +105,8 @@ pub fn build() -> anyhow::Result<TokenStream> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Gets the snake_case name of this sound.
|
||||
pub const fn to_str(self) -> &'static str {
|
||||
/// Gets the identifier of this sound.
|
||||
pub const fn to_ident(self) -> Ident<&'static str> {
|
||||
match self {
|
||||
#sound_to_str_arms
|
||||
}
|
||||
|
|
|
@ -499,6 +499,8 @@ impl PacketDecoder {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::*;
|
||||
use crate::block_pos::BlockPos;
|
||||
use crate::ident::Ident;
|
||||
|
@ -521,7 +523,7 @@ mod tests {
|
|||
e: f64,
|
||||
f: BlockPos,
|
||||
g: Hand,
|
||||
h: Ident<&'a str>,
|
||||
h: Ident<Cow<'a, str>>,
|
||||
i: Option<ItemStack>,
|
||||
j: Text,
|
||||
k: VarInt,
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
//! Resource identifiers.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::cmp::Ordering;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Write;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use serde::de::Error as _;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{nbt, Decode, Encode};
|
||||
|
||||
|
@ -21,114 +19,230 @@ use crate::{nbt, Decode, Encode};
|
|||
/// A resource identifier is a string divided into a "namespace" part and a
|
||||
/// "path" part. For instance `minecraft:apple` and `valence:frobnicator` are
|
||||
/// both valid identifiers. A string must match the regex
|
||||
/// `^([a-z0-9_.-]+:)?[a-z0-9_.-\/]+$` to be considered valid.
|
||||
/// `^([a-z0-9_.-]+:)?[a-z0-9_.-\/]+$` to be successfully parsed.
|
||||
///
|
||||
/// If the namespace part is left off (the part before and including the colon)
|
||||
/// the namespace is considered to be "minecraft" for the purposes of equality,
|
||||
/// ordering, and hashing.
|
||||
///
|
||||
/// # Contract
|
||||
///
|
||||
/// The type `S` must meet the following criteria:
|
||||
/// - All calls to [`AsRef::as_ref`] and [`Borrow::borrow`][borrow] while the
|
||||
/// string is wrapped in `Ident` must return the same value.
|
||||
///
|
||||
/// [borrow]: std::borrow::Borrow::borrow
|
||||
#[derive(Copy, Clone)]
|
||||
/// While parsing, if the namespace part is left off (the part before and
|
||||
/// including the colon) then "minecraft:" is inserted at the beginning of the
|
||||
/// string.
|
||||
#[derive(Copy, Clone, Eq, Ord, Hash)]
|
||||
pub struct Ident<S> {
|
||||
string: S,
|
||||
path_start: usize,
|
||||
}
|
||||
|
||||
impl<S> Ident<S> {
|
||||
/// Returns an Ident with the given fields
|
||||
///
|
||||
/// # Safety
|
||||
/// This function does not check for the validity of the Ident.
|
||||
/// For a safe version use [`Ident::new`]
|
||||
#[doc(hidden)]
|
||||
pub const fn new_unchecked(string: S, path_start: usize) -> Self {
|
||||
Self { string, path_start }
|
||||
/// The error type created when an [`Ident`] cannot be parsed from a
|
||||
/// string. Contains the string that failed to parse.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Error)]
|
||||
#[error("invalid resource identifier \"{0}\"")]
|
||||
pub struct IdentError(pub String);
|
||||
|
||||
impl<'a> Ident<Cow<'a, str>> {
|
||||
pub fn new(string: impl Into<Cow<'a, str>>) -> Result<Self, IdentError> {
|
||||
parse(string.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> Ident<S> {
|
||||
pub fn new(string: S) -> Result<Self, IdentError<S>> {
|
||||
impl<S> Ident<S> {
|
||||
/// Internal API. Do not use.
|
||||
#[doc(hidden)]
|
||||
pub const fn new_unchecked(string: S) -> Self {
|
||||
Self { string }
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
self.string.as_ref()
|
||||
}
|
||||
|
||||
pub fn as_str_ident(&self) -> Ident<&str>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
Ident {
|
||||
string: self.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> S {
|
||||
self.string
|
||||
}
|
||||
|
||||
/// Returns the namespace part of this resource identifier (the part before
|
||||
/// the colon).
|
||||
pub fn namespace(&self) -> &str
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
self.namespace_and_path().0
|
||||
}
|
||||
|
||||
/// Returns the path part of this resource identifier (the part after the
|
||||
/// colon).
|
||||
pub fn path(&self) -> &str
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
self.namespace_and_path().1
|
||||
}
|
||||
|
||||
pub fn namespace_and_path(&self) -> (&str, &str)
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
self.as_str()
|
||||
.split_once(':')
|
||||
.expect("invalid resource identifier")
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(string: Cow<str>) -> Result<Ident<Cow<str>>, IdentError> {
|
||||
let check_namespace = |s: &str| {
|
||||
!s.is_empty()
|
||||
&& s.chars()
|
||||
.all(|c| matches!(c, 'a'..='z' | '0'..='9' | '_' | '.' | '-'))
|
||||
};
|
||||
|
||||
let check_path = |s: &str| {
|
||||
!s.is_empty()
|
||||
&& s.chars()
|
||||
.all(|c| matches!(c, 'a'..='z' | '0'..='9' | '_' | '.' | '-' | '/'))
|
||||
};
|
||||
|
||||
let str = string.as_ref();
|
||||
|
||||
match str.split_once(':') {
|
||||
match string.split_once(':') {
|
||||
Some((namespace, path)) if check_namespace(namespace) && check_path(path) => {
|
||||
let path_start = namespace.len() + 1;
|
||||
Ok(Self { string, path_start })
|
||||
Ok(Ident { string })
|
||||
}
|
||||
None if check_path(str) => Ok(Self {
|
||||
string,
|
||||
path_start: 0,
|
||||
None if check_path(&string) => Ok(Ident {
|
||||
string: format!("minecraft:{string}").into(),
|
||||
}),
|
||||
_ => Err(IdentError(string)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the namespace part of this resource identifier.
|
||||
///
|
||||
/// If the underlying string does not contain a namespace followed by a
|
||||
/// ':' character, `"minecraft"` is returned.
|
||||
pub fn namespace(&self) -> &str {
|
||||
if self.path_start == 0 {
|
||||
"minecraft"
|
||||
} else {
|
||||
&self.string.as_ref()[..self.path_start - 1]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &str {
|
||||
&self.string.as_ref()[self.path_start..]
|
||||
}
|
||||
|
||||
/// Returns the underlying string as a `str`.
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.string.as_ref()
|
||||
}
|
||||
|
||||
/// Borrows the underlying string and returns it as an `Ident`. This
|
||||
/// operation is infallible and no checks need to be performed.
|
||||
pub fn as_str_ident(&self) -> Ident<&str> {
|
||||
Ident {
|
||||
string: self.string.as_ref(),
|
||||
path_start: self.path_start,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the identifier and returns the underlying string.
|
||||
pub fn into_inner(self) -> S {
|
||||
self.string
|
||||
_ => Err(IdentError(string.into())),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: ?Sized> Ident<&'a S> {
|
||||
/// Converts the underlying string to its owned representation and returns
|
||||
/// it as an `Ident`. This operation is infallible and no checks need to be
|
||||
/// performed.
|
||||
pub fn to_owned_ident(&self) -> Ident<S::Owned>
|
||||
where
|
||||
S: ToOwned,
|
||||
S::Owned: AsRef<str>,
|
||||
{
|
||||
Ident {
|
||||
string: self.string.to_owned(),
|
||||
path_start: self.path_start,
|
||||
impl<S: AsRef<str>> AsRef<str> for Ident<S> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.string.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> AsRef<S> for Ident<S> {
|
||||
fn as_ref(&self) -> &S {
|
||||
&self.string
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Borrow<str>> Borrow<str> for Ident<S> {
|
||||
fn borrow(&self) -> &str {
|
||||
self.string.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ident<String>> for String {
|
||||
fn from(value: Ident<String>) -> Self {
|
||||
value.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<Cow<'a, str>>> for Cow<'a, str> {
|
||||
fn from(value: Ident<Cow<'a, str>>) -> Self {
|
||||
value.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<Cow<'a, str>>> for Ident<String> {
|
||||
fn from(value: Ident<Cow<'a, str>>) -> Self {
|
||||
Self {
|
||||
string: value.string.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<String>> for Ident<Cow<'a, str>> {
|
||||
fn from(value: Ident<String>) -> Self {
|
||||
Self {
|
||||
string: value.string.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<&'a str>> for Ident<Cow<'a, str>> {
|
||||
fn from(value: Ident<&'a str>) -> Self {
|
||||
Ident {
|
||||
string: value.string.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<&'a str>> for Ident<String> {
|
||||
fn from(value: Ident<&'a str>) -> Self {
|
||||
Ident {
|
||||
string: value.string.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Ident<String> {
|
||||
type Err = IdentError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Ident::new(s)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Ident<Cow<'static, str>> {
|
||||
type Err = IdentError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ident::<String>::try_from(s).map(From::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Ident<String> {
|
||||
type Error = IdentError;
|
||||
|
||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||
Ok(Ident::new(value)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Ident<String> {
|
||||
type Error = IdentError;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Ok(Ident::new(value)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Cow<'a, str>> for Ident<String> {
|
||||
type Error = IdentError;
|
||||
|
||||
fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
|
||||
Ok(Ident::new(value)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Ident<Cow<'a, str>> {
|
||||
type Error = IdentError;
|
||||
|
||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<String> for Ident<Cow<'a, str>> {
|
||||
type Error = IdentError;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Cow<'a, str>> for Ident<Cow<'a, str>> {
|
||||
type Error = IdentError;
|
||||
|
||||
fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,121 +252,56 @@ impl<S: fmt::Debug> fmt::Debug for Ident<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<&'a str>> for Ident<String> {
|
||||
fn from(value: Ident<&'a str>) -> Self {
|
||||
value.to_owned_ident()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<&'a str>> for Ident<Box<str>> {
|
||||
fn from(value: Ident<&'a str>) -> Self {
|
||||
Ident {
|
||||
string: value.string.into(),
|
||||
path_start: value.path_start,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<&'a str>> for Ident<Cow<'a, str>> {
|
||||
fn from(value: Ident<&'a str>) -> Self {
|
||||
Ident {
|
||||
string: Cow::Borrowed(value.string),
|
||||
path_start: value.path_start,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<Cow<'a, str>>> for Ident<String> {
|
||||
fn from(value: Ident<Cow<'a, str>>) -> Self {
|
||||
Ident {
|
||||
string: value.string.into_owned(),
|
||||
path_start: value.path_start,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Ident<String> {
|
||||
type Err = IdentError<String>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ident::new(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Ident<String> {
|
||||
type Error = IdentError<String>;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Ident::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<Ident<S>> for String
|
||||
where
|
||||
S: Into<String> + AsRef<str>,
|
||||
{
|
||||
fn from(id: Ident<S>) -> Self {
|
||||
if id.path_start == 0 {
|
||||
format!("minecraft:{}", id.string.as_ref())
|
||||
} else {
|
||||
id.string.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> fmt::Display for Ident<S>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
impl<S: fmt::Display> fmt::Display for Ident<S> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.namespace(), self.path())
|
||||
self.string.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> PartialEq<Ident<T>> for Ident<S>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
T: AsRef<str>,
|
||||
S: PartialEq<T>,
|
||||
{
|
||||
fn eq(&self, other: &Ident<T>) -> bool {
|
||||
self.namespace() == other.namespace() && self.path() == other.path()
|
||||
self.string == other.string
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Eq for Ident<S> where S: AsRef<str> {}
|
||||
|
||||
impl<S, T> PartialOrd<Ident<T>> for Ident<S>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
T: AsRef<str>,
|
||||
S: PartialOrd<T>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Ident<T>) -> Option<Ordering> {
|
||||
(self.namespace(), self.path()).partial_cmp(&(other.namespace(), other.path()))
|
||||
self.string.partial_cmp(&other.string)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Ord for Ident<S>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
(self.namespace(), self.path()).cmp(&(other.namespace(), other.path()))
|
||||
impl<S: Encode> Encode for Ident<S> {
|
||||
fn encode(&self, w: impl Write) -> anyhow::Result<()> {
|
||||
self.as_ref().encode(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Hash for Ident<S>
|
||||
impl<'a, S> Decode<'a> for Ident<S>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
S: Decode<'a>,
|
||||
Ident<S>: TryFrom<S, Error = IdentError>,
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(self.namespace(), self.path()).hash(state);
|
||||
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
|
||||
Ok(Ident::try_from(S::decode(r)?)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Serialize for Ident<T>
|
||||
impl<S> From<Ident<S>> for nbt::Value
|
||||
where
|
||||
T: Serialize,
|
||||
S: Into<nbt::Value>,
|
||||
{
|
||||
fn from(value: Ident<S>) -> Self {
|
||||
value.into_inner().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serialize> Serialize for Ident<T> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
|
@ -261,67 +310,19 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Deserialize<'de> for Ident<T>
|
||||
impl<'de, S> Deserialize<'de> for Ident<S>
|
||||
where
|
||||
T: Deserialize<'de> + AsRef<str>,
|
||||
S: Deserialize<'de>,
|
||||
Ident<S>: TryFrom<S, Error = IdentError>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ident::new(T::deserialize(deserializer)?).map_err(D::Error::custom)
|
||||
Ident::try_from(S::deserialize(deserializer)?).map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Encode> Encode for Ident<S> {
|
||||
fn encode(&self, w: impl Write) -> anyhow::Result<()> {
|
||||
self.string.encode(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Decode<'a> for Ident<S>
|
||||
where
|
||||
S: Decode<'a> + AsRef<str>,
|
||||
{
|
||||
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
|
||||
Ident::new(S::decode(r)?).map_err(|e| anyhow!("{e:#}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<Ident<S>> for nbt::Value
|
||||
where
|
||||
S: Into<nbt::Value>,
|
||||
{
|
||||
fn from(id: Ident<S>) -> Self {
|
||||
id.string.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// The error type created when an [`Ident`] cannot be parsed from a
|
||||
/// string. Contains the offending string.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct IdentError<S>(pub S);
|
||||
|
||||
impl<S> fmt::Debug for IdentError<S>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.debug_tuple("IdentError").field(&self.0.as_ref()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> fmt::Display for IdentError<S>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "invalid resource identifier \"{}\"", self.0.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Error for IdentError<S> where S: AsRef<str> {}
|
||||
|
||||
/// Convenience macro for constructing an [`Ident<String>`] from a format
|
||||
/// string.
|
||||
///
|
||||
|
@ -350,17 +351,12 @@ impl<S> Error for IdentError<S> where S: AsRef<str> {}
|
|||
#[macro_export]
|
||||
macro_rules! ident {
|
||||
($($arg:tt)*) => {{
|
||||
$crate::ident::Ident::new(::std::format!($($arg)*)).unwrap()
|
||||
$crate::ident::Ident::<String>::try_from(::std::format!($($arg)*)).unwrap()
|
||||
}}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hasher;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn check_namespace_and_path() {
|
||||
let id = ident!("namespace:path");
|
||||
|
@ -397,15 +393,4 @@ mod tests {
|
|||
fn equality() {
|
||||
assert_eq!(ident!("minecraft:my.identifier"), ident!("my.identifier"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_hash() {
|
||||
let mut h1 = DefaultHasher::new();
|
||||
ident!("minecraft:my.identifier").hash(&mut h1);
|
||||
|
||||
let mut h2 = DefaultHasher::new();
|
||||
ident!("my.identifier").hash(&mut h2);
|
||||
|
||||
assert_eq!(h1.finish(), h2.finish());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Encode, Decode)]
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub enum AdvancementTabC2s<'a> {
|
||||
OpenedTab { tab_id: Ident<&'a str> },
|
||||
OpenedTab { tab_id: Ident<Cow<'a, str>> },
|
||||
ClosedScreen,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Encode, Decode)]
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct CraftRequestC2s<'a> {
|
||||
pub window_id: i8,
|
||||
pub recipe: Ident<&'a str>,
|
||||
pub recipe: Ident<Cow<'a, str>>,
|
||||
pub make_all: bool,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::raw::RawBytes;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Encode, Decode)]
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct CustomPayloadC2s<'a> {
|
||||
pub channel: Ident<&'a str>,
|
||||
pub channel: Ident<Cow<'a, str>>,
|
||||
pub data: RawBytes<'a>,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Encode, Decode)]
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct RecipeBookDataC2s<'a> {
|
||||
pub recipe_id: Ident<&'a str>,
|
||||
pub recipe_id: Ident<Cow<'a, str>>,
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::block_pos::BlockPos;
|
||||
use crate::ident::Ident;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Encode, Decode)]
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct UpdateJigsawC2s<'a> {
|
||||
pub position: BlockPos,
|
||||
pub name: Ident<&'a str>,
|
||||
pub target: Ident<&'a str>,
|
||||
pub pool: Ident<&'a str>,
|
||||
pub name: Ident<Cow<'a, str>>,
|
||||
pub target: Ident<Cow<'a, str>>,
|
||||
pub pool: Ident<Cow<'a, str>>,
|
||||
pub final_state: &'a str,
|
||||
pub joint_type: &'a str,
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::raw::RawBytes;
|
||||
use crate::var_int::VarInt;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Encode, Decode)]
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct LoginQueryRequestS2c<'a> {
|
||||
pub message_id: VarInt,
|
||||
pub channel: Ident<&'a str>,
|
||||
pub channel: Ident<Cow<'a, str>>,
|
||||
pub data: RawBytes<'a>,
|
||||
}
|
||||
|
|
|
@ -10,16 +10,16 @@ use crate::{Decode, Encode};
|
|||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct AdvancementUpdateS2c<'a> {
|
||||
pub reset: bool,
|
||||
pub advancement_mapping: Vec<(Ident<&'a str>, Advancement<'a>)>,
|
||||
pub identifiers: Vec<Ident<&'a str>>,
|
||||
pub progress_mapping: Vec<(Ident<&'a str>, Vec<AdvancementCriteria<'a>>)>,
|
||||
pub advancement_mapping: Vec<(Ident<Cow<'a, str>>, Advancement<'a>)>,
|
||||
pub identifiers: Vec<Ident<Cow<'a, str>>>,
|
||||
pub progress_mapping: Vec<(Ident<Cow<'a, str>>, Vec<AdvancementCriteria<'a>>)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
|
||||
pub struct Advancement<'a> {
|
||||
pub parent_id: Option<Ident<&'a str>>,
|
||||
pub parent_id: Option<Ident<Cow<'a, str>>>,
|
||||
pub display_data: Option<AdvancementDisplay<'a>>,
|
||||
pub criteria: Vec<(Ident<&'a str>, ())>,
|
||||
pub criteria: Vec<(Ident<Cow<'a, str>>, ())>,
|
||||
pub requirements: Vec<AdvancementRequirements<'a>>,
|
||||
}
|
||||
|
||||
|
@ -35,14 +35,14 @@ pub struct AdvancementDisplay<'a> {
|
|||
pub icon: Option<ItemStack>,
|
||||
pub frame_type: VarInt,
|
||||
pub flags: i32,
|
||||
pub background_texture: Option<Ident<&'a str>>,
|
||||
pub background_texture: Option<Ident<Cow<'a, str>>>,
|
||||
pub x_coord: f32,
|
||||
pub y_coord: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub struct AdvancementCriteria<'a> {
|
||||
pub criterion_identifier: Ident<&'a str>,
|
||||
pub criterion_identifier: Ident<Cow<'a, str>>,
|
||||
/// If present, the criteria has been achieved at the
|
||||
/// time wrapped; time represented as millis since epoch
|
||||
pub criterion_progress: Option<i64>,
|
||||
|
@ -56,7 +56,7 @@ impl Encode for AdvancementDisplay<'_> {
|
|||
self.frame_type.encode(&mut w)?;
|
||||
self.flags.encode(&mut w)?;
|
||||
|
||||
match self.background_texture {
|
||||
match self.background_texture.as_ref() {
|
||||
None => {}
|
||||
Some(texture) => texture.encode(&mut w)?,
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ impl<'a> Decode<'a> for AdvancementDisplay<'a> {
|
|||
let flags = i32::decode(r)?;
|
||||
|
||||
let background_texture = if flags & 1 == 1 {
|
||||
Some(Ident::<&'a str>::decode(r)?)
|
||||
Some(Ident::decode(r)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
|
||||
use anyhow::bail;
|
||||
|
@ -86,10 +87,10 @@ pub enum Parser<'a> {
|
|||
Dimension,
|
||||
GameMode,
|
||||
Time,
|
||||
ResourceOrTag { registry: Ident<&'a str> },
|
||||
ResourceOrTagKey { registry: Ident<&'a str> },
|
||||
Resource { registry: Ident<&'a str> },
|
||||
ResourceKey { registry: Ident<&'a str> },
|
||||
ResourceOrTag { registry: Ident<Cow<'a, str>> },
|
||||
ResourceOrTagKey { registry: Ident<Cow<'a, str>> },
|
||||
Resource { registry: Ident<Cow<'a, str>> },
|
||||
ResourceKey { registry: Ident<Cow<'a, str>> },
|
||||
TemplateMirror,
|
||||
TemplateRotation,
|
||||
Uuid,
|
||||
|
@ -182,12 +183,12 @@ impl<'a> Decode<'a> for Node<'a> {
|
|||
name: <&str>::decode(r)?,
|
||||
parser: Parser::decode(r)?,
|
||||
suggestion: if flags & 0x10 != 0 {
|
||||
Some(match Ident::<&str>::decode(r)?.path() {
|
||||
"ask_server" => Suggestion::AskServer,
|
||||
"all_recipes" => Suggestion::AllRecipes,
|
||||
"available_sounds" => Suggestion::AvailableSounds,
|
||||
"available_biomes" => Suggestion::AvailableBiomes,
|
||||
"summonable_entities" => Suggestion::SummonableEntities,
|
||||
Some(match Ident::<Cow<str>>::decode(r)?.as_str() {
|
||||
"minecraft:ask_server" => Suggestion::AskServer,
|
||||
"minecraft:all_recipes" => Suggestion::AllRecipes,
|
||||
"minecraft:available_sounds" => Suggestion::AvailableSounds,
|
||||
"minecraft:available_biomes" => Suggestion::AvailableBiomes,
|
||||
"minecraft:summonable_entities" => Suggestion::SummonableEntities,
|
||||
other => bail!("unknown command suggestion type of \"{other}\""),
|
||||
})
|
||||
} else {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct CraftFailedResponseS2c<'a> {
|
||||
pub window_id: u8,
|
||||
pub recipe: Ident<&'a str>,
|
||||
pub recipe: Ident<Cow<'a, str>>,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::raw::RawBytes;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Encode, Decode)]
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct CustomPayloadS2c<'a> {
|
||||
pub channel: Ident<&'a str>,
|
||||
pub channel: Ident<Cow<'a, str>>,
|
||||
pub data: RawBytes<'a>,
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::ident::Ident;
|
||||
|
@ -12,7 +14,7 @@ pub struct EntityAttributesS2c<'a> {
|
|||
|
||||
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
|
||||
pub struct AttributeProperty<'a> {
|
||||
pub key: Ident<&'a str>,
|
||||
pub key: Ident<Cow<'a, str>>,
|
||||
pub value: f64,
|
||||
pub modifiers: Vec<AttributeModifier>,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Encode, Decode)]
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct ExplosionS2c<'a> {
|
||||
pub window_id: u8,
|
||||
pub recipe: Ident<&'a str>,
|
||||
pub recipe: Ident<Cow<'a, str>>,
|
||||
pub make_all: bool,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct FeaturesS2c<'a> {
|
||||
pub features: Vec<Ident<&'a str>>,
|
||||
pub features: Vec<Ident<Cow<'a, str>>>,
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@ pub struct GameJoinS2c<'a> {
|
|||
pub game_mode: GameMode,
|
||||
/// Same values as `game_mode` but with -1 to indicate no previous.
|
||||
pub previous_game_mode: i8,
|
||||
pub dimension_names: Vec<Ident<&'a str>>,
|
||||
pub dimension_names: Vec<Ident<Cow<'a, str>>>,
|
||||
pub registry_codec: Cow<'a, Compound>,
|
||||
pub dimension_type_name: Ident<&'a str>,
|
||||
pub dimension_name: Ident<&'a str>,
|
||||
pub dimension_type_name: Ident<Cow<'a, str>>,
|
||||
pub dimension_name: Ident<Cow<'a, str>>,
|
||||
pub hashed_seed: i64,
|
||||
pub max_players: VarInt,
|
||||
pub view_distance: VarInt,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
|
||||
use crate::ident::Ident;
|
||||
|
@ -5,7 +6,7 @@ use crate::types::SoundCategory;
|
|||
use crate::var_int::VarInt;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Encode, Decode)]
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct PlaySoundS2c<'a> {
|
||||
pub id: SoundId<'a>,
|
||||
pub category: SoundCategory,
|
||||
|
@ -15,10 +16,10 @@ pub struct PlaySoundS2c<'a> {
|
|||
pub seed: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum SoundId<'a> {
|
||||
Direct {
|
||||
id: Ident<&'a str>,
|
||||
id: Ident<Cow<'a, str>>,
|
||||
range: Option<f32>,
|
||||
},
|
||||
Reference {
|
||||
|
@ -47,7 +48,7 @@ impl<'a> Decode<'a> for SoundId<'a> {
|
|||
|
||||
if i == 0 {
|
||||
Ok(SoundId::Direct {
|
||||
id: <Ident<&'a str>>::decode(r)?,
|
||||
id: Ident::decode(r)?,
|
||||
range: <Option<f32>>::decode(r)?,
|
||||
})
|
||||
} else {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::types::{GameMode, GlobalPos};
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
|
||||
pub struct PlayerRespawnS2c<'a> {
|
||||
pub dimension_type_name: Ident<&'a str>,
|
||||
pub dimension_name: Ident<&'a str>,
|
||||
pub dimension_type_name: Ident<Cow<'a, str>>,
|
||||
pub dimension_name: Ident<Cow<'a, str>>,
|
||||
pub hashed_seed: u64,
|
||||
pub game_mode: GameMode,
|
||||
pub previous_game_mode: i8,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::{Decode, Encode};
|
||||
|
||||
#[derive(Clone, Debug, Encode, Decode)]
|
||||
pub struct SelectAdvancementsTabS2c<'a> {
|
||||
pub identifier: Option<Ident<&'a str>>,
|
||||
pub identifier: Option<Ident<Cow<'a, str>>>,
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
|
||||
use crate::ident::Ident;
|
||||
|
@ -7,12 +8,12 @@ use crate::{Decode, Encode};
|
|||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct StopSoundS2c<'a> {
|
||||
pub source: Option<SoundCategory>,
|
||||
pub sound: Option<Ident<&'a str>>,
|
||||
pub sound: Option<Ident<Cow<'a, str>>>,
|
||||
}
|
||||
|
||||
impl Encode for StopSoundS2c<'_> {
|
||||
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
|
||||
match (self.source, self.sound) {
|
||||
match (self.source, self.sound.as_ref()) {
|
||||
(Some(source), Some(sound)) => {
|
||||
3i8.encode(&mut w)?;
|
||||
source.encode(&mut w)?;
|
||||
|
@ -38,9 +39,9 @@ impl<'a> Decode<'a> for StopSoundS2c<'a> {
|
|||
let (source, sound) = match i8::decode(r)? {
|
||||
3 => (
|
||||
Some(SoundCategory::decode(r)?),
|
||||
Some(<Ident<&'a str>>::decode(r)?),
|
||||
Some(<Ident<Cow<'a, str>>>::decode(r)?),
|
||||
),
|
||||
2 => (None, Some(<Ident<&'a str>>::decode(r)?)),
|
||||
2 => (None, Some(<Ident<Cow<'a, str>>>::decode(r)?)),
|
||||
1 => (Some(SoundCategory::decode(r)?), None),
|
||||
_ => (None, None),
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
|
||||
use anyhow::{bail, ensure};
|
||||
|
@ -15,14 +16,14 @@ pub struct SynchronizeRecipesS2c<'a> {
|
|||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum Recipe<'a> {
|
||||
CraftingShapeless {
|
||||
recipe_id: Ident<&'a str>,
|
||||
recipe_id: Ident<Cow<'a, str>>,
|
||||
group: &'a str,
|
||||
category: CraftingCategory,
|
||||
ingredients: Vec<Ingredient>,
|
||||
result: Option<ItemStack>,
|
||||
},
|
||||
CraftingShaped {
|
||||
recipe_id: Ident<&'a str>,
|
||||
recipe_id: Ident<Cow<'a, str>>,
|
||||
width: VarInt,
|
||||
height: VarInt,
|
||||
group: &'a str,
|
||||
|
@ -32,11 +33,11 @@ pub enum Recipe<'a> {
|
|||
},
|
||||
CraftingSpecial {
|
||||
kind: SpecialCraftingKind,
|
||||
recipe_id: Ident<&'a str>,
|
||||
recipe_id: Ident<Cow<'a, str>>,
|
||||
category: CraftingCategory,
|
||||
},
|
||||
Smelting {
|
||||
recipe_id: Ident<&'a str>,
|
||||
recipe_id: Ident<Cow<'a, str>>,
|
||||
group: &'a str,
|
||||
category: SmeltCategory,
|
||||
ingredient: Ingredient,
|
||||
|
@ -45,7 +46,7 @@ pub enum Recipe<'a> {
|
|||
cooking_time: VarInt,
|
||||
},
|
||||
Blasting {
|
||||
recipe_id: Ident<&'a str>,
|
||||
recipe_id: Ident<Cow<'a, str>>,
|
||||
group: &'a str,
|
||||
category: SmeltCategory,
|
||||
ingredient: Ingredient,
|
||||
|
@ -54,7 +55,7 @@ pub enum Recipe<'a> {
|
|||
cooking_time: VarInt,
|
||||
},
|
||||
Smoking {
|
||||
recipe_id: Ident<&'a str>,
|
||||
recipe_id: Ident<Cow<'a, str>>,
|
||||
group: &'a str,
|
||||
category: SmeltCategory,
|
||||
ingredient: Ingredient,
|
||||
|
@ -63,7 +64,7 @@ pub enum Recipe<'a> {
|
|||
cooking_time: VarInt,
|
||||
},
|
||||
CampfireCooking {
|
||||
recipe_id: Ident<&'a str>,
|
||||
recipe_id: Ident<Cow<'a, str>>,
|
||||
group: &'a str,
|
||||
category: SmeltCategory,
|
||||
ingredient: Ingredient,
|
||||
|
@ -72,13 +73,13 @@ pub enum Recipe<'a> {
|
|||
cooking_time: VarInt,
|
||||
},
|
||||
Stonecutting {
|
||||
recipe_id: Ident<&'a str>,
|
||||
recipe_id: Ident<Cow<'a, str>>,
|
||||
group: &'a str,
|
||||
ingredient: Ingredient,
|
||||
result: Option<ItemStack>,
|
||||
},
|
||||
Smithing {
|
||||
recipe_id: Ident<&'a str>,
|
||||
recipe_id: Ident<Cow<'a, str>>,
|
||||
base: Ingredient,
|
||||
addition: Ingredient,
|
||||
result: Option<ItemStack>,
|
||||
|
@ -294,16 +295,16 @@ impl<'a> Encode for Recipe<'a> {
|
|||
|
||||
impl<'a> Decode<'a> for Recipe<'a> {
|
||||
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
|
||||
Ok(match Ident::<&str>::decode(r)?.path() {
|
||||
"crafting_shapeless" => Self::CraftingShapeless {
|
||||
Ok(match Ident::<Cow<str>>::decode(r)?.as_str() {
|
||||
"minecraft:crafting_shapeless" => Self::CraftingShapeless {
|
||||
recipe_id: Decode::decode(r)?,
|
||||
group: Decode::decode(r)?,
|
||||
category: Decode::decode(r)?,
|
||||
ingredients: Decode::decode(r)?,
|
||||
result: Decode::decode(r)?,
|
||||
},
|
||||
"crafting_shaped" => {
|
||||
let recipe_id = Ident::<&str>::decode(r)?;
|
||||
"minecraft:crafting_shaped" => {
|
||||
let recipe_id = Ident::decode(r)?;
|
||||
let width = VarInt::decode(r)?.0;
|
||||
let height = VarInt::decode(r)?.0;
|
||||
let group = <&str>::decode(r)?;
|
||||
|
@ -324,7 +325,7 @@ impl<'a> Decode<'a> for Recipe<'a> {
|
|||
result: Decode::decode(r)?,
|
||||
}
|
||||
}
|
||||
"smelting" => Self::Smelting {
|
||||
"minecraft:smelting" => Self::Smelting {
|
||||
recipe_id: Decode::decode(r)?,
|
||||
group: Decode::decode(r)?,
|
||||
category: Decode::decode(r)?,
|
||||
|
@ -333,7 +334,7 @@ impl<'a> Decode<'a> for Recipe<'a> {
|
|||
experience: Decode::decode(r)?,
|
||||
cooking_time: Decode::decode(r)?,
|
||||
},
|
||||
"blasting" => Self::Blasting {
|
||||
"minecraft:blasting" => Self::Blasting {
|
||||
recipe_id: Decode::decode(r)?,
|
||||
group: Decode::decode(r)?,
|
||||
category: Decode::decode(r)?,
|
||||
|
@ -342,7 +343,7 @@ impl<'a> Decode<'a> for Recipe<'a> {
|
|||
experience: Decode::decode(r)?,
|
||||
cooking_time: Decode::decode(r)?,
|
||||
},
|
||||
"smoking" => Self::Smoking {
|
||||
"minecraft:smoking" => Self::Smoking {
|
||||
recipe_id: Decode::decode(r)?,
|
||||
group: Decode::decode(r)?,
|
||||
category: Decode::decode(r)?,
|
||||
|
@ -351,7 +352,7 @@ impl<'a> Decode<'a> for Recipe<'a> {
|
|||
experience: Decode::decode(r)?,
|
||||
cooking_time: Decode::decode(r)?,
|
||||
},
|
||||
"campfire_cooking" => Self::CampfireCooking {
|
||||
"minecraft:campfire_cooking" => Self::CampfireCooking {
|
||||
recipe_id: Decode::decode(r)?,
|
||||
group: Decode::decode(r)?,
|
||||
category: Decode::decode(r)?,
|
||||
|
@ -360,13 +361,13 @@ impl<'a> Decode<'a> for Recipe<'a> {
|
|||
experience: Decode::decode(r)?,
|
||||
cooking_time: Decode::decode(r)?,
|
||||
},
|
||||
"stonecutting" => Self::Stonecutting {
|
||||
"minecraft:stonecutting" => Self::Stonecutting {
|
||||
recipe_id: Decode::decode(r)?,
|
||||
group: Decode::decode(r)?,
|
||||
ingredient: Decode::decode(r)?,
|
||||
result: Decode::decode(r)?,
|
||||
},
|
||||
"smithing" => Self::Smithing {
|
||||
"minecraft:smithing" => Self::Smithing {
|
||||
recipe_id: Decode::decode(r)?,
|
||||
base: Decode::decode(r)?,
|
||||
addition: Decode::decode(r)?,
|
||||
|
@ -374,22 +375,34 @@ impl<'a> Decode<'a> for Recipe<'a> {
|
|||
},
|
||||
other => Self::CraftingSpecial {
|
||||
kind: match other {
|
||||
"crafting_special_armordye" => SpecialCraftingKind::ArmorDye,
|
||||
"crafting_special_bookcloning" => SpecialCraftingKind::BookCloning,
|
||||
"crafting_special_mapcloning" => SpecialCraftingKind::MapCloning,
|
||||
"crafting_special_mapextending" => SpecialCraftingKind::MapExtending,
|
||||
"crafting_special_firework_rocket" => SpecialCraftingKind::FireworkRocket,
|
||||
"crafting_special_firework_star" => SpecialCraftingKind::FireworkStar,
|
||||
"crafting_special_firework_star_fade" => SpecialCraftingKind::FireworkStarFade,
|
||||
"crafting_special_repairitem" => SpecialCraftingKind::RepairItem,
|
||||
"crafting_special_tippedarrow" => SpecialCraftingKind::TippedArrow,
|
||||
"crafting_special_bannerduplicate" => SpecialCraftingKind::BannerDuplicate,
|
||||
"crafting_special_banneraddpattern" => SpecialCraftingKind::BannerAddPattern,
|
||||
"crafting_special_shielddecoration" => SpecialCraftingKind::ShieldDecoration,
|
||||
"crafting_special_shulkerboxcoloring" => {
|
||||
"minecraft:crafting_special_armordye" => SpecialCraftingKind::ArmorDye,
|
||||
"minecraft:crafting_special_bookcloning" => SpecialCraftingKind::BookCloning,
|
||||
"minecraft:crafting_special_mapcloning" => SpecialCraftingKind::MapCloning,
|
||||
"minecraft:crafting_special_mapextending" => SpecialCraftingKind::MapExtending,
|
||||
"minecraft:crafting_special_firework_rocket" => {
|
||||
SpecialCraftingKind::FireworkRocket
|
||||
}
|
||||
"minecraft:crafting_special_firework_star" => SpecialCraftingKind::FireworkStar,
|
||||
"minecraft:crafting_special_firework_star_fade" => {
|
||||
SpecialCraftingKind::FireworkStarFade
|
||||
}
|
||||
"minecraft:crafting_special_repairitem" => SpecialCraftingKind::RepairItem,
|
||||
"minecraft:crafting_special_tippedarrow" => SpecialCraftingKind::TippedArrow,
|
||||
"minecraft:crafting_special_bannerduplicate" => {
|
||||
SpecialCraftingKind::BannerDuplicate
|
||||
}
|
||||
"minecraft:crafting_special_banneraddpattern" => {
|
||||
SpecialCraftingKind::BannerAddPattern
|
||||
}
|
||||
"minecraft:crafting_special_shielddecoration" => {
|
||||
SpecialCraftingKind::ShieldDecoration
|
||||
}
|
||||
"minecraft:crafting_special_shulkerboxcoloring" => {
|
||||
SpecialCraftingKind::ShulkerBoxColoring
|
||||
}
|
||||
"crafting_special_suspiciousstew" => SpecialCraftingKind::SuspiciousStew,
|
||||
"minecraft:crafting_special_suspiciousstew" => {
|
||||
SpecialCraftingKind::SuspiciousStew
|
||||
}
|
||||
_ => bail!("unknown recipe type \"{other}\""),
|
||||
},
|
||||
recipe_id: Decode::decode(r)?,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::ident::Ident;
|
||||
use crate::var_int::VarInt;
|
||||
use crate::{Decode, Encode};
|
||||
|
@ -9,12 +11,12 @@ pub struct SynchronizeTagsS2c<'a> {
|
|||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub struct TagGroup<'a> {
|
||||
pub kind: Ident<&'a str>,
|
||||
pub kind: Ident<Cow<'a, str>>,
|
||||
pub tags: Vec<Tag<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub struct Tag<'a> {
|
||||
pub name: Ident<&'a str>,
|
||||
pub name: Ident<Cow<'a, str>>,
|
||||
pub entries: Vec<VarInt>,
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
|
||||
use anyhow::bail;
|
||||
|
@ -17,12 +18,14 @@ pub struct UnlockRecipesS2c<'a> {
|
|||
pub blast_furnace_recipe_book_filter_active: bool,
|
||||
pub smoker_recipe_book_open: bool,
|
||||
pub smoker_recipe_book_filter_active: bool,
|
||||
pub recipe_ids: Vec<Ident<&'a str>>,
|
||||
pub recipe_ids: Vec<Ident<Cow<'a, str>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum UpdateRecipeBookAction<'a> {
|
||||
Init { recipe_ids: Vec<Ident<&'a str>> },
|
||||
Init {
|
||||
recipe_ids: Vec<Ident<Cow<'a, str>>>,
|
||||
},
|
||||
Add,
|
||||
Remove,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::ident::Ident;
|
||||
use crate::ident_str;
|
||||
use crate::packet::s2c::play::play_sound::SoundId;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/sound.rs"));
|
||||
|
@ -6,7 +7,7 @@ include!(concat!(env!("OUT_DIR"), "/sound.rs"));
|
|||
impl Sound {
|
||||
pub fn to_id(self) -> SoundId<'static> {
|
||||
SoundId::Direct {
|
||||
id: Ident::new(self.to_str()).unwrap(), // TODO: use ident_str.
|
||||
id: self.to_ident().into(),
|
||||
range: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1084,8 +1084,7 @@ mod tests {
|
|||
let txt = Text::storage_nbt(ident!("foo"), "bar", Some(true), Some("baz".into()));
|
||||
let serialized = serde_json::to_string(&txt).unwrap();
|
||||
let deserialized: Text = serde_json::from_str(&serialized).unwrap();
|
||||
let expected =
|
||||
r#"{"storage":"foo","nbt":"bar","interpret":true,"separator":{"text":"baz"}}"#;
|
||||
let expected = r#"{"storage":"minecraft:foo","nbt":"bar","interpret":true,"separator":{"text":"baz"}}"#;
|
||||
assert_eq!(serialized, expected);
|
||||
assert_eq!(txt, deserialized);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Miscellaneous type definitions used in packets.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -61,9 +62,9 @@ pub enum GameMode {
|
|||
Spectator,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub struct GlobalPos<'a> {
|
||||
pub dimension_name: Ident<&'a str>,
|
||||
pub dimension_name: Ident<Cow<'a, str>>,
|
||||
pub position: BlockPos,
|
||||
}
|
||||
|
||||
|
|
|
@ -16,26 +16,22 @@ fn check_path(s: &str) -> bool {
|
|||
|
||||
pub fn ident_str(item: TokenStream) -> Result<TokenStream> {
|
||||
let ident_lit: LitStr = parse2(item)?;
|
||||
let mut ident = &ident_lit.value()[..];
|
||||
let mut ident = ident_lit.value();
|
||||
|
||||
let path_start = match ident.split_once(':') {
|
||||
Some(("minecraft", path)) if check_path(path) => {
|
||||
ident = path;
|
||||
0
|
||||
match ident.split_once(':') {
|
||||
Some((namespace, path)) if check_namespace(namespace) && check_path(path) => {}
|
||||
None if check_path(&ident) => {
|
||||
ident = format!("minecraft:{ident}");
|
||||
}
|
||||
Some((namespace, path)) if check_namespace(namespace) && check_path(path) => {
|
||||
namespace.len() + 1
|
||||
}
|
||||
None if check_path(ident) => 0,
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
ident_lit.span(),
|
||||
"string cannot be parsed as ident",
|
||||
"string cannot be parsed as a resource identifier",
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(quote! {
|
||||
::valence_protocol::ident::Ident::new_unchecked(#ident, #path_start)
|
||||
::valence_protocol::ident::Ident::new_unchecked(#ident)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue