diff --git a/pumpkin-codegen/src/remap/mod.rs b/pumpkin-codegen/src/remap/mod.rs index 523c09545..c5a2211d1 100644 --- a/pumpkin-codegen/src/remap/mod.rs +++ b/pumpkin-codegen/src/remap/mod.rs @@ -6,6 +6,7 @@ use crate::version::JavaMinecraftVersion; mod block_state; mod entity_id; mod item_id; +mod particle_id; mod sound_id; /// Returns the list of remap builder functions paired with their output file names. @@ -15,6 +16,7 @@ pub fn build() -> Vec<(fn() -> TokenStream, &'static str)> { (block_state::build, "block_state_remap.rs"), (entity_id::build, "entity_id_remap.rs"), (item_id::build, "item_id_remap.rs"), + (particle_id::build, "particle_id_remap.rs"), (sound_id::build, "sound_id_remap.rs"), ] } diff --git a/pumpkin-codegen/src/remap/particle_id.rs b/pumpkin-codegen/src/remap/particle_id.rs new file mode 100644 index 000000000..b435fb90b --- /dev/null +++ b/pumpkin-codegen/src/remap/particle_id.rs @@ -0,0 +1,125 @@ +use proc_macro2::{Literal, TokenStream}; +use quote::{format_ident, quote}; + +use crate::remap::{MappingNode, ParsedMappings, Remapper}; +use crate::version::JavaMinecraftVersion; + +/// Generates the `TokenStream` for per-version particle ID remap tables and the +/// `remap_particle_id_for_version` function. +pub fn build() -> TokenStream { + let node_1_20_5 = MappingNode { + version: JavaMinecraftVersion::V_1_20_5, + value: "../assets/viabackwards/data/mappings-1.21to1.20.5.nbt", + child: None, + }; + let node_1_21 = MappingNode { + version: JavaMinecraftVersion::V_1_21, + value: "../assets/viabackwards/data/mappings-1.21.2to1.21.nbt", + child: Some(&node_1_20_5), + }; + let node_1_21_2 = MappingNode { + version: JavaMinecraftVersion::V_1_21_2, + value: "../assets/viabackwards/data/mappings-1.21.4to1.21.2.nbt", + child: Some(&node_1_21), + }; + let node_1_21_4 = MappingNode { + version: JavaMinecraftVersion::V_1_21_4, + value: "../assets/viabackwards/data/mappings-1.21.5to1.21.4.nbt", + child: Some(&node_1_21_2), + }; + let node_1_21_5 = MappingNode { + version: JavaMinecraftVersion::V_1_21_5, + value: "../assets/viabackwards/data/mappings-1.21.6to1.21.5.nbt", + child: Some(&node_1_21_4), + }; + let node_1_21_6 = MappingNode { + version: JavaMinecraftVersion::V_1_21_6, + value: "../assets/viabackwards/data/mappings-1.21.7to1.21.6.nbt", + child: Some(&node_1_21_5), + }; + let node_1_21_7 = MappingNode { + version: JavaMinecraftVersion::V_1_21_7, + value: "../assets/viabackwards/data/mappings-1.21.9to1.21.7.nbt", + child: Some(&node_1_21_6), + }; + let node_1_21_9 = MappingNode { + version: JavaMinecraftVersion::V_1_21_9, + value: "../assets/viabackwards/data/mappings-1.21.11to1.21.9.nbt", + child: Some(&node_1_21_7), + }; + let node_1_21_11 = MappingNode { + version: JavaMinecraftVersion::V_1_21_11, + value: "../assets/viabackwards/data/mappings-26.1to1.21.11.nbt", + child: Some(&node_1_21_9), + }; + let node_26_1 = MappingNode { + version: JavaMinecraftVersion::V_26_1, + value: "../assets/viabackwards/data/mappings-26.2to26.1.nbt", + child: Some(&node_1_21_11), + }; + let remapper: Remapper<_, Option>> = Remapper { + version: JavaMinecraftVersion::V_26_2, + remapper: |first, second| match (first, second) { + (Some(first), Some(second)) => Some( + first + .iter() + .map(|id| second.get(usize::from(*id)).copied().unwrap_or(0)) + .collect(), + ), + (None, Some(second)) => Some( + (0..second.len()) + .map(|id| second.get(id).copied().unwrap_or(0)) + .collect(), + ), + (Some(first), None) => Some(first.clone()), + _ => None, + }, + serializer: |&file| { + ParsedMappings::parse_mapping_file(file, "particles") + .map(|mappings| mappings.to_u16(file)) + }, + }; + + let all_mappings = remapper.process(&node_26_1); + let mut static_values = TokenStream::new(); + let mut match_arms = TokenStream::new(); + for (ver, mapping) in &all_mappings { + let ident = format_ident!( + "{}", + format!("PARTICLE_ID_REMAP_{:?}_TO_{:?}", remapper.version, ver).to_uppercase() + ); + let mapping_tokens: Vec<_> = mapping + .as_ref() + .unwrap() + .iter() + .copied() + .map(Literal::u16_unsuffixed) + .collect(); + static_values.extend(quote! { + const #ident: &[u16] = &[#(#mapping_tokens),*]; + }); + match_arms.extend(quote! { + #ver => #ident + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id), + }); + } + + quote! { + use pumpkin_util::version::JavaMinecraftVersion; + + #static_values + + #[must_use] + pub fn remap_particle_id_for_version( + particle_id: u16, + version: JavaMinecraftVersion, + ) -> u16 { + match version { + #match_arms + _ => particle_id, + } + } + } +} diff --git a/pumpkin-data/Cargo.toml b/pumpkin-data/Cargo.toml index 2797c72bb..ef40c9558 100644 --- a/pumpkin-data/Cargo.toml +++ b/pumpkin-data/Cargo.toml @@ -106,6 +106,7 @@ block = [] item_id_remap = [] entity_id_remap = [] sound_id_remap = [] +particle_id_remap = [] potion_brewing = [] [dependencies] diff --git a/pumpkin-data/src/generated/particle_id_remap.rs b/pumpkin-data/src/generated/particle_id_remap.rs new file mode 100644 index 000000000..db7013dfd --- /dev/null +++ b/pumpkin-data/src/generated/particle_id_remap.rs @@ -0,0 +1,144 @@ +/* This file is generated. Do not edit manually. */ +use pumpkin_util::version::JavaMinecraftVersion; +const PARTICLE_ID_REMAP_V_26_2_TO_V_1_20_5: &[u16] = &[ + 0, 1, 2, 3, 68, 71, 4, 4, 0, 0, 0, 4, 37, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 33, 41, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 20, 40, 40, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 1, 78, 46, +]; +const PARTICLE_ID_REMAP_V_26_2_TO_V_1_21: &[u16] = &[ + 0, 1, 2, 3, 68, 71, 4, 4, 0, 0, 0, 4, 37, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 33, 41, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 20, 40, 40, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 1, 78, 46, +]; +const PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_2: &[u16] = &[ + 0, 1, 2, 3, 69, 72, 4, 4, 0, 0, 0, 4, 37, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 33, 41, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 40, 40, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 79, 47, +]; +const PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_4: &[u16] = &[ + 0, 1, 2, 3, 70, 73, 4, 4, 0, 0, 0, 4, 38, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 42, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 41, 41, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 80, 48, +]; +const PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_5: &[u16] = &[ + 0, 1, 2, 3, 71, 74, 4, 4, 0, 0, 0, 4, 39, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 42, 42, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 49, +]; +const PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_6: &[u16] = &[ + 0, 1, 2, 3, 71, 74, 4, 4, 0, 0, 0, 4, 39, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 42, 42, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 49, +]; +const PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_7: &[u16] = &[ + 0, 1, 2, 3, 71, 74, 4, 4, 0, 0, 0, 4, 39, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 42, 42, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 49, +]; +const PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_9: &[u16] = &[ + 0, 1, 2, 3, 72, 75, 4, 4, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 43, 43, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 50, +]; +const PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_11: &[u16] = &[ + 0, 1, 2, 3, 72, 75, 4, 4, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 43, 43, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 50, +]; +const PARTICLE_ID_REMAP_V_26_2_TO_V_26_1: &[u16] = &[ + 0, 1, 2, 3, 74, 77, 4, 4, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 52, +]; +#[must_use] +pub fn remap_particle_id_for_version(particle_id: u16, version: JavaMinecraftVersion) -> u16 { + match version { + pumpkin_util::version::JavaMinecraftVersion::V_1_20_5 => { + PARTICLE_ID_REMAP_V_26_2_TO_V_1_20_5 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id) + } + pumpkin_util::version::JavaMinecraftVersion::V_1_21 => PARTICLE_ID_REMAP_V_26_2_TO_V_1_21 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id), + pumpkin_util::version::JavaMinecraftVersion::V_1_21_2 => { + PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_2 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id) + } + pumpkin_util::version::JavaMinecraftVersion::V_1_21_4 => { + PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_4 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id) + } + pumpkin_util::version::JavaMinecraftVersion::V_1_21_5 => { + PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_5 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id) + } + pumpkin_util::version::JavaMinecraftVersion::V_1_21_6 => { + PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_6 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id) + } + pumpkin_util::version::JavaMinecraftVersion::V_1_21_7 => { + PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_7 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id) + } + pumpkin_util::version::JavaMinecraftVersion::V_1_21_9 => { + PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_9 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id) + } + pumpkin_util::version::JavaMinecraftVersion::V_1_21_11 => { + PARTICLE_ID_REMAP_V_26_2_TO_V_1_21_11 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id) + } + pumpkin_util::version::JavaMinecraftVersion::V_26_1 => PARTICLE_ID_REMAP_V_26_2_TO_V_26_1 + .get(usize::from(particle_id)) + .copied() + .unwrap_or(particle_id), + _ => particle_id, + } +} diff --git a/pumpkin-data/src/lib.rs b/pumpkin-data/src/lib.rs index 630927fe5..777e976a1 100644 --- a/pumpkin-data/src/lib.rs +++ b/pumpkin-data/src/lib.rs @@ -250,6 +250,11 @@ pub mod entity_id_remap; #[path = "generated/sound_id_remap.rs"] pub mod sound_id_remap; +#[cfg(feature = "particle_id_remap")] +#[rustfmt::skip] +#[path = "generated/particle_id_remap.rs"] +pub mod particle_id_remap; + #[cfg(feature = "bedrock_creative")] #[rustfmt::skip] #[path = "generated/bedrock_creative.rs"] diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index 60b05f628..828538c10 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -13,7 +13,7 @@ query = [] [dependencies] pumpkin-nbt.workspace = true -pumpkin-data = { workspace = true, features = ["packet", "item_id_remap", "entity_id_remap", "sound_id_remap", "recipes", "bedrock_creative"] } +pumpkin-data = { workspace = true, features = ["packet", "item_id_remap", "entity_id_remap", "sound_id_remap", "particle_id_remap", "recipes", "bedrock_creative"] } pumpkin-macros.workspace = true pumpkin-world.workspace = true pumpkin-util.workspace = true diff --git a/pumpkin-protocol/src/java/client/play/entity_metadata.rs b/pumpkin-protocol/src/java/client/play/entity_metadata.rs index 0c7817af0..0c4c04c40 100644 --- a/pumpkin-protocol/src/java/client/play/entity_metadata.rs +++ b/pumpkin-protocol/src/java/client/play/entity_metadata.rs @@ -13,6 +13,8 @@ use crate::{ ser::{NetworkWriteExt, WritingError}, }; +use super::particle::particle_id_for_version; + pub trait MetadataSerializer { fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError>; } @@ -138,6 +140,22 @@ impl Metadata { return Ok(()); } + if self.r#type == MetaDataType::PARTICLE { + let mut serialized_value = Vec::new(); + self.value.write_metadata(&mut serialized_value)?; + + let mut cursor = Cursor::new(serialized_value); + let particle_id = VarInt::decode(&mut cursor).map_err(|e| { + WritingError::Message(format!("Failed to decode particle metadata: {e}")) + })?; + writer.write_var_int(&particle_id_for_version(particle_id, *version))?; + + let remainder_start = cursor.position() as usize; + let inner = cursor.into_inner(); + writer.write_slice(&inner[remainder_start..])?; + return Ok(()); + } + self.value.write_metadata(&mut writer)?; Ok(()) @@ -264,3 +282,71 @@ impl MetadataSerializer for crate::codec::optional_int::OptionalInt { writer.write_var_int(&VarInt(val)) } } + +#[cfg(test)] +mod tests { + use std::io::{Cursor, Read}; + + use pumpkin_data::{ + meta_data_type::MetaDataType, particle::Particle, tracked_data::TrackedData, + }; + use pumpkin_util::version::JavaMinecraftVersion; + + use crate::{VarInt, ser::NetworkWriteExt}; + + use super::{Metadata, MetadataSerializer}; + + struct ParticleMetadata { + particle_id: VarInt, + data: [u8; 4], + } + + impl MetadataSerializer for ParticleMetadata { + fn write_metadata( + &self, + writer: &mut impl std::io::Write, + ) -> Result<(), crate::WritingError> { + writer.write_var_int(&self.particle_id)?; + writer.write_slice(&self.data) + } + } + + fn encoded_particle(version: JavaMinecraftVersion) -> (VarInt, Vec) { + let particle_data = [0x12, 0x34, 0x56, 0x78]; + let metadata = Metadata::new( + TrackedData::PARTICLE, + MetaDataType::PARTICLE, + ParticleMetadata { + particle_id: VarInt(Particle::ExplosionEmitter as i32), + data: particle_data, + }, + ); + let mut bytes = Vec::new(); + metadata.write(&mut bytes, &version).unwrap(); + + assert_eq!(bytes[0], TrackedData::PARTICLE.get(&version)); + assert_eq!(bytes[1], MetaDataType::PARTICLE.id(version) as u8); + + let mut cursor = Cursor::new(&bytes[2..]); + let particle_id = VarInt::decode(&mut cursor).unwrap(); + let mut remainder = Vec::new(); + cursor.read_to_end(&mut remainder).unwrap(); + (particle_id, remainder) + } + + #[test] + fn particle_metadata_id_remaps_for_1_21_11() { + let (particle_id, data) = encoded_particle(JavaMinecraftVersion::V_1_21_11); + + assert_eq!(particle_id, VarInt(22)); + assert_eq!(data, [0x12, 0x34, 0x56, 0x78]); + } + + #[test] + fn particle_metadata_id_stays_latest_for_26_2() { + let (particle_id, data) = encoded_particle(JavaMinecraftVersion::V_26_2); + + assert_eq!(particle_id, VarInt(29)); + assert_eq!(data, [0x12, 0x34, 0x56, 0x78]); + } +} diff --git a/pumpkin-protocol/src/java/client/play/explode.rs b/pumpkin-protocol/src/java/client/play/explode.rs index d710e2a13..835c4dcbc 100644 --- a/pumpkin-protocol/src/java/client/play/explode.rs +++ b/pumpkin-protocol/src/java/client/play/explode.rs @@ -7,6 +7,8 @@ use crate::ser::NetworkWriteExt; use crate::{IdOr, SoundEvent, codec::var_int::VarInt}; use pumpkin_util::version::JavaMinecraftVersion; +use super::particle::particle_id_for_version; + /// Notifies the client that an explosion has occurred. /// /// This is a high-level packet that handles the visual, auditory, and physical @@ -60,7 +62,7 @@ impl ClientPacket for CExplosion { fn write_packet_data( &self, mut write: impl std::io::Write, - _version: &JavaMinecraftVersion, + version: &JavaMinecraftVersion, ) -> Result<(), crate::ser::WritingError> { write.write_f64_be(self.center.x)?; write.write_f64_be(self.center.y)?; @@ -73,7 +75,8 @@ impl ClientPacket for CExplosion { w.write_f64_be(k.z)?; Ok(()) })?; - write.write_var_int(&self.particle)?; + let particle = particle_id_for_version(self.particle, *version); + write.write_var_int(&particle)?; match &self.sound { IdOr::Id(id) => write.write_var_int(&VarInt((*id + 1) as i32))?, IdOr::Value(event) => { @@ -86,3 +89,48 @@ impl ClientPacket for CExplosion { Ok(()) } } + +#[cfg(test)] +mod tests { + use std::io::{Cursor, Seek, SeekFrom}; + + use pumpkin_data::particle::Particle; + use pumpkin_util::{math::vector3::Vector3, version::JavaMinecraftVersion}; + + use crate::{ClientPacket, IdOr, VarInt}; + + use super::CExplosion; + + fn encoded_particle_id(version: JavaMinecraftVersion) -> VarInt { + let packet = CExplosion::new( + Vector3::new(0.0, 0.0, 0.0), + 4.0, + 0, + None, + VarInt(Particle::ExplosionEmitter as i32), + IdOr::Id(0), + ); + let mut bytes = Vec::new(); + packet.write_packet_data(&mut bytes, &version).unwrap(); + + let mut cursor = Cursor::new(bytes); + cursor.seek(SeekFrom::Start(33)).unwrap(); + VarInt::decode(&mut cursor).unwrap() + } + + #[test] + fn explosion_particle_id_remaps_for_1_21_11() { + assert_eq!( + encoded_particle_id(JavaMinecraftVersion::V_1_21_11), + VarInt(22) + ); + } + + #[test] + fn explosion_particle_id_stays_latest_for_26_2() { + assert_eq!( + encoded_particle_id(JavaMinecraftVersion::V_26_2), + VarInt(29) + ); + } +} diff --git a/pumpkin-protocol/src/java/client/play/particle.rs b/pumpkin-protocol/src/java/client/play/particle.rs index 0695cc41c..b06769305 100644 --- a/pumpkin-protocol/src/java/client/play/particle.rs +++ b/pumpkin-protocol/src/java/client/play/particle.rs @@ -1,6 +1,8 @@ use std::io::Write; -use pumpkin_data::packet::clientbound::PLAY_LEVEL_PARTICLES; +use pumpkin_data::{ + packet::clientbound::PLAY_LEVEL_PARTICLES, particle_id_remap::remap_particle_id_for_version, +}; use pumpkin_macros::java_packet; use pumpkin_util::{math::vector3::Vector3, version::JavaMinecraftVersion}; @@ -64,11 +66,23 @@ impl<'a> CParticle<'a> { } } +pub(super) fn particle_id_for_version( + particle_id: VarInt, + version: JavaMinecraftVersion, +) -> VarInt { + u16::try_from(particle_id.0).map_or(particle_id, |particle_id| { + VarInt(i32::from(remap_particle_id_for_version( + particle_id, + version, + ))) + }) +} + impl ClientPacket for CParticle<'_> { fn write_packet_data( &self, write: impl Write, - _version: &JavaMinecraftVersion, + version: &JavaMinecraftVersion, ) -> Result<(), WritingError> { let mut write = write; @@ -85,8 +99,55 @@ impl ClientPacket for CParticle<'_> { write.write_f32_be(self.max_speed)?; write.write_i32_be(self.particle_count)?; - write.write_var_int(&self.particle_id)?; + write.write_var_int(&particle_id_for_version(self.particle_id, *version))?; write.write_all(self.data).map_err(WritingError::IoError) } } + +#[cfg(test)] +mod tests { + use std::io::{Cursor, Seek, SeekFrom}; + + use pumpkin_data::particle::Particle; + use pumpkin_util::{math::vector3::Vector3, version::JavaMinecraftVersion}; + + use crate::{ClientPacket, VarInt}; + + use super::CParticle; + + fn encoded_particle_id(version: JavaMinecraftVersion) -> VarInt { + let packet = CParticle::new( + false, + false, + Vector3::new(0.0, 0.0, 0.0), + Vector3::new(0.0, 0.0, 0.0), + 0.0, + 1, + VarInt(Particle::ExplosionEmitter as i32), + &[], + ); + let mut bytes = Vec::new(); + packet.write_packet_data(&mut bytes, &version).unwrap(); + + let mut cursor = Cursor::new(bytes); + cursor.seek(SeekFrom::Start(46)).unwrap(); + VarInt::decode(&mut cursor).unwrap() + } + + #[test] + fn particle_id_remaps_for_1_21_11() { + assert_eq!( + encoded_particle_id(JavaMinecraftVersion::V_1_21_11), + VarInt(22) + ); + } + + #[test] + fn particle_id_stays_latest_for_26_2() { + assert_eq!( + encoded_particle_id(JavaMinecraftVersion::V_26_2), + VarInt(29) + ); + } +}