diff --git a/assets/particles.json b/assets/particles.json index c70a8d3dab..694e2fcd5a 100644 --- a/assets/particles.json +++ b/assets/particles.json @@ -1 +1 @@ -["angry_villager","block","block_marker","bubble","cloud","copper_fire_flame","crit","damage_indicator","dragon_breath","dripping_lava","falling_lava","landing_lava","dripping_water","falling_water","dust","dust_color_transition","effect","elder_guardian","enchanted_hit","enchant","end_rod","entity_effect","explosion_emitter","explosion","gust","small_gust","gust_emitter_large","gust_emitter_small","sonic_boom","falling_dust","firework","fishing","flame","infested","cherry_leaves","pale_oak_leaves","tinted_leaves","sculk_soul","sculk_charge","sculk_charge_pop","soul_fire_flame","soul","flash","happy_villager","composter","heart","instant_effect","item","vibration","trail","pause_mob_growth","reset_mob_growth","item_slime","item_cobweb","item_snowball","large_smoke","lava","mycelium","note","poof","portal","rain","smoke","white_smoke","sneeze","spit","squid_ink","sweep_attack","totem_of_undying","underwater","splash","witch","bubble_pop","current_down","bubble_column_up","nautilus","dolphin","campfire_cosy_smoke","campfire_signal_smoke","dripping_honey","falling_honey","landing_honey","falling_nectar","falling_spore_blossom","ash","crimson_spore","warped_spore","spore_blossom_air","dripping_obsidian_tear","falling_obsidian_tear","landing_obsidian_tear","reverse_portal","white_ash","small_flame","snowflake","dripping_dripstone_lava","falling_dripstone_lava","dripping_dripstone_water","falling_dripstone_water","glow_squid_ink","glow","wax_on","wax_off","electric_spark","scrape","shriek","egg_crack","dust_plume","trial_spawner_detection","trial_spawner_detection_ominous","vault_connection","dust_pillar","ominous_spawning","raid_omen","trial_omen","block_crumble","firefly"] \ No newline at end of file +{"angry_villager":[],"block":["state"],"block_marker":["state"],"bubble":[],"cloud":[],"copper_fire_flame":[],"crit":[],"damage_indicator":[],"dragon_breath":["power"],"dripping_lava":[],"falling_lava":[],"landing_lava":[],"dripping_water":[],"falling_water":[],"dust":["color","scale"],"dust_color_transition":["fromColor","toColor","scale"],"effect":["color","power"],"elder_guardian":[],"enchanted_hit":[],"enchant":[],"end_rod":[],"entity_effect":["color"],"explosion_emitter":[],"explosion":[],"gust":[],"small_gust":[],"gust_emitter_large":[],"gust_emitter_small":[],"sonic_boom":[],"falling_dust":["state"],"firework":[],"fishing":[],"flame":[],"infested":[],"cherry_leaves":[],"pale_oak_leaves":[],"tinted_leaves":["color"],"sculk_soul":[],"sculk_charge":["roll"],"sculk_charge_pop":[],"soul_fire_flame":[],"soul":[],"flash":["color"],"happy_villager":[],"composter":[],"heart":[],"instant_effect":["color","power"],"item":["item"],"vibration":["destination","arrivalInTicks"],"trail":["target","color","duration"],"pause_mob_growth":[],"reset_mob_growth":[],"item_slime":[],"item_cobweb":[],"item_snowball":[],"large_smoke":[],"lava":[],"mycelium":[],"note":[],"poof":[],"portal":[],"rain":[],"smoke":[],"white_smoke":[],"sneeze":[],"spit":[],"squid_ink":[],"sweep_attack":[],"totem_of_undying":[],"underwater":[],"splash":[],"witch":[],"bubble_pop":[],"current_down":[],"bubble_column_up":[],"nautilus":[],"dolphin":[],"campfire_cosy_smoke":[],"campfire_signal_smoke":[],"dripping_honey":[],"falling_honey":[],"landing_honey":[],"falling_nectar":[],"falling_spore_blossom":[],"ash":[],"crimson_spore":[],"warped_spore":[],"spore_blossom_air":[],"dripping_obsidian_tear":[],"falling_obsidian_tear":[],"landing_obsidian_tear":[],"reverse_portal":[],"white_ash":[],"small_flame":[],"snowflake":[],"dripping_dripstone_lava":[],"falling_dripstone_lava":[],"dripping_dripstone_water":[],"falling_dripstone_water":[],"glow_squid_ink":[],"glow":[],"wax_on":[],"wax_off":[],"electric_spark":[],"scrape":[],"shriek":["delay"],"egg_crack":[],"dust_plume":[],"trial_spawner_detection":[],"trial_spawner_detection_ominous":[],"vault_connection":[],"dust_pillar":["state"],"ominous_spawning":[],"raid_omen":[],"trial_omen":[],"block_crumble":["state"],"firefly":[]} \ No newline at end of file diff --git a/pumpkin-codegen/src/main.rs b/pumpkin-codegen/src/main.rs index 0386459b91..6cb3aeb5a1 100644 --- a/pumpkin-codegen/src/main.rs +++ b/pumpkin-codegen/src/main.rs @@ -5,7 +5,7 @@ use rayon::prelude::*; use std::{ fs, io::Write, - path::Path, + path::{Path, PathBuf}, process::{Command, Stdio}, }; @@ -65,78 +65,185 @@ mod wit; mod world_event; /// Output directory where all generated Rust source files are written. -pub const OUT_DIR: &str = "../pumpkin-data/src/generated"; +pub const GENERATED_DIR: &str = "../pumpkin-data/src/generated"; +pub const NETWORK_SERIALIZATION_DIR: &str = "../pumpkin-protocol/src/java/client/play"; +pub const PLUGIN_BINDINGS_DIR: &str = "../pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1"; /// Entry point for the code generator. Runs all registered builder functions in parallel -/// and writes their output to [`OUT_DIR`]. pub fn main() { type BuilderFn = fn() -> TokenStream; - fs::create_dir_all(OUT_DIR).expect("Failed to create output directory"); + fs::create_dir_all(GENERATED_DIR).expect("Failed to create output directory"); wit::main(); - let mut build_functions: Vec<(BuilderFn, &str)> = vec![ - (advancement::build, "advancement.rs"), - (packet::build, "packet.rs"), - (screen::build, "screen.rs"), - (particle::build, "particle.rs"), - (sound::build, "sound.rs"), - (meta_data_type::build, "meta_data_type.rs"), - (tracked_data::build, "tracked_data.rs"), - (chunk_status::build, "chunk_status.rs"), - (game_event::build, "game_event.rs"), - (game_rules::build, "game_rules.rs"), - (registry::build, "registry.rs"), - (dimension::build, "dimension.rs"), - (translations::build, "translation.rs"), - (jukebox_song::build, "jukebox_song.rs"), - (sound_category::build, "sound_category.rs"), - (entity_pose::build, "entity_pose.rs"), - (scoreboard_slot::build, "scoreboard_slot.rs"), - (world_event::build, "world_event.rs"), - (entity_type::build, "entity_type.rs"), - (statistic::build, "statistic.rs"), - (noise_parameter::build, "noise_parameter.rs"), - (biome::build, "biome.rs"), - (damage_type::build, "damage_type.rs"), - (message_type::build, "message_type.rs"), - (spawn_egg::build, "spawn_egg.rs"), - (block::build, "block.rs"), - (item::build, "item.rs"), - (structures::build, "structures.rs"), - (chunk_gen_settings::build, "chunk_gen_settings.rs"), - (fluid::build, "fluid.rs"), - (entity_status::build, "entity_status.rs"), - (tag::build, "tag.rs"), - (noise_router::build, "noise_router.rs"), - (villager::build, "villager.rs"), + let mut build_functions: Vec<(BuilderFn, PathBuf)> = vec![ + (advancement::build, Path::new(GENERATED_DIR).join("advancement.rs")), + (packet::build, Path::new(GENERATED_DIR).join("packet.rs")), + (screen::build, Path::new(GENERATED_DIR).join("screen.rs")), + ( + particle::build, + Path::new(GENERATED_DIR).join("particle.rs"), + ), + ( + particle::build_serialization, + Path::new(NETWORK_SERIALIZATION_DIR).join("particle_data.rs"), + ), + ( + particle::build_wit_bindings, + Path::new(PLUGIN_BINDINGS_DIR).join("particle.rs"), + ), + (sound::build, Path::new(GENERATED_DIR).join("sound.rs")), + ( + meta_data_type::build, + Path::new(GENERATED_DIR).join("meta_data_type.rs"), + ), + ( + tracked_data::build, + Path::new(GENERATED_DIR).join("tracked_data.rs"), + ), + ( + chunk_status::build, + Path::new(GENERATED_DIR).join("chunk_status.rs"), + ), + ( + game_event::build, + Path::new(GENERATED_DIR).join("game_event.rs"), + ), + ( + game_rules::build, + Path::new(GENERATED_DIR).join("game_rules.rs"), + ), + ( + registry::build, + Path::new(GENERATED_DIR).join("registry.rs"), + ), + ( + dimension::build, + Path::new(GENERATED_DIR).join("dimension.rs"), + ), + ( + translations::build, + Path::new(GENERATED_DIR).join("translation.rs"), + ), + ( + jukebox_song::build, + Path::new(GENERATED_DIR).join("jukebox_song.rs"), + ), + ( + sound_category::build, + Path::new(GENERATED_DIR).join("sound_category.rs"), + ), + ( + entity_pose::build, + Path::new(GENERATED_DIR).join("entity_pose.rs"), + ), + ( + scoreboard_slot::build, + Path::new(GENERATED_DIR).join("scoreboard_slot.rs"), + ), + ( + world_event::build, + Path::new(GENERATED_DIR).join("world_event.rs"), + ), + ( + entity_type::build, + Path::new(GENERATED_DIR).join("entity_type.rs"), + ), + ( + noise_parameter::build, + Path::new(GENERATED_DIR).join("noise_parameter.rs"), + ), + (biome::build, Path::new(GENERATED_DIR).join("biome.rs")), + ( + damage_type::build, + Path::new(GENERATED_DIR).join("damage_type.rs"), + ), + ( + message_type::build, + Path::new(GENERATED_DIR).join("message_type.rs"), + ), + ( + spawn_egg::build, + Path::new(GENERATED_DIR).join("spawn_egg.rs"), + ), + (block::build, Path::new(GENERATED_DIR).join("block.rs")), + (item::build, Path::new(GENERATED_DIR).join("item.rs")), + ( + structures::build, + Path::new(GENERATED_DIR).join("structures.rs"), + ), + ( + chunk_gen_settings::build, + Path::new(GENERATED_DIR).join("chunk_gen_settings.rs"), + ), + (fluid::build, Path::new(GENERATED_DIR).join("fluid.rs")), + ( + entity_status::build, + Path::new(GENERATED_DIR).join("entity_status.rs"), + ), + (tag::build, Path::new(GENERATED_DIR).join("tag.rs")), + ( + noise_router::build, + Path::new(GENERATED_DIR).join("noise_router.rs"), + ), + ( + villager::build, + Path::new(GENERATED_DIR).join("villager.rs"), + ), ( flower_pot_transformations::build, - "flower_pot_transformations.rs", + Path::new(GENERATED_DIR).join("flower_pot_transformations.rs"), ), ( composter_increase_chance::build, - "composter_increase_chance.rs", - ), - (recipes::build, "recipes.rs"), - (enchantments::build, "enchantment.rs"), - (fuels::build, "fuels.rs"), - (data_component::build, "data_component.rs"), - (attributes::build, "attributes.rs"), - (effect::build, "effect.rs"), - (potion::build, "potion.rs"), - (potion_brewing::build, "potion_brewing.rs"), - (recipe_remainder::build, "recipe_remainder.rs"), - (placed_feature::build_enum, "placed_feature.rs"), - (placed_feature::build, "placed_features_generated.rs"), - (configured_feature::build_enum, "configured_feature.rs"), + Path::new(GENERATED_DIR).join("composter_increase_chance.rs"), + ), + (recipes::build, Path::new(GENERATED_DIR).join("recipes.rs")), + ( + enchantments::build, + Path::new(GENERATED_DIR).join("enchantment.rs"), + ), + (fuels::build, Path::new(GENERATED_DIR).join("fuels.rs")), + ( + data_component::build, + Path::new(GENERATED_DIR).join("data_component.rs"), + ), + ( + attributes::build, + Path::new(GENERATED_DIR).join("attributes.rs"), + ), + (effect::build, Path::new(GENERATED_DIR).join("effect.rs")), + (potion::build, Path::new(GENERATED_DIR).join("potion.rs")), + ( + potion_brewing::build, + Path::new(GENERATED_DIR).join("potion_brewing.rs"), + ), + ( + recipe_remainder::build, + Path::new(GENERATED_DIR).join("recipe_remainder.rs"), + ), + ( + placed_feature::build_enum, + Path::new(GENERATED_DIR).join("placed_feature.rs"), + ), + ( + placed_feature::build, + Path::new(GENERATED_DIR).join("placed_features_generated.rs"), + ), + ( + configured_feature::build_enum, + Path::new(GENERATED_DIR).join("configured_feature.rs"), + ), ( configured_feature::build, - "configured_features_generated.rs", + Path::new(GENERATED_DIR).join("configured_features_generated.rs"), + ), + (carver::build, Path::new(GENERATED_DIR).join("carver.rs")), + ( + chest_loot::build, + Path::new(GENERATED_DIR).join("chest_loot.rs"), ), - (carver::build, "carver.rs"), - (chest_loot::build, "chest_loot.rs"), ]; build_functions.extend(remap::build()); @@ -149,27 +256,41 @@ pub fn main() { build_functions .into_iter() .filter(|(_, file)| { - let stem = file.trim_end_matches(".rs"); - filters.iter().any(|f| f == stem || f == *file) + let stem = file.file_stem().and_then(|s| s.to_str()); + + let full_path_str = file.to_str(); + + filters + .iter() + .any(|f| Some(f.as_str()) == stem || Some(f.as_str()) == full_path_str) }) .collect() }; build_functions.par_iter().for_each(|(build_fn, file)| { - println!("Parsing {}", file); + println!("Parsing {:#?}", file.file_name().unwrap()); + + output_code(build_fn(), file); + }); + println!("Done") +} - let raw_code = build_fn().to_string(); +/// Writes the generated code with a warning and proper format +/// +/// # Arguments +/// - `token_stream` - The code to write. +/// - `file` - The file to write to. +pub fn output_code(token_stream: TokenStream, file: &PathBuf) { + let raw_code = token_stream.to_string(); - let header = "/* This file is generated. Do not edit manually. */\n"; + let header = "/* This file is generated. Do not edit manually. */\n"; - let final_code = format_code(&raw_code).map_or_else( - |_| format!("{header}{raw_code}"), - |formatted| format!("{header}{formatted}"), - ); + let final_code = format_code(&raw_code).map_or_else( + |_| format!("{header}{raw_code}"), + |formatted| format!("{header}{formatted}"), + ); - write_generated_file(&final_code, file); - }); - println!("Done") + write_generated_file(&final_code, file); } /// Converts a slice of strings into a `TokenStream` of PascalCase enum variants. @@ -193,10 +314,8 @@ pub fn array_to_tokenstream(array: &[String]) -> TokenStream { /// /// # Arguments /// - `new_code` – The formatted source code string to write. -/// - `out_file` – The filename (relative to [`OUT_DIR`]) to write into. -pub fn write_generated_file(new_code: &str, out_file: &str) { - let path = Path::new(OUT_DIR).join(out_file); - +/// - `path` – The file to write to. +pub fn write_generated_file(new_code: &str, path: &PathBuf) { if path.exists() && let Ok(existing_code) = fs::read_to_string(&path) && existing_code == new_code diff --git a/pumpkin-codegen/src/particle.rs b/pumpkin-codegen/src/particle.rs index 6ce1837fe6..fb30601f57 100644 --- a/pumpkin-codegen/src/particle.rs +++ b/pumpkin-codegen/src/particle.rs @@ -1,63 +1,291 @@ use std::fs; -use heck::ToPascalCase; +use heck::{ToSnakeCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; +use serde_json::Value; -use crate::array_to_tokenstream; - -/// Generates the `TokenStream` for the `Particle` enum and its `from_name`/`to_name` methods. +/// Generates the `TokenStream` for the Particles pub fn build() -> TokenStream { - let particle: Vec = + let mut particles: serde_json::Map = serde_json::from_str(&fs::read_to_string("../assets/particles.json").unwrap()) .expect("Failed to parse particles.json"); - let variants = array_to_tokenstream(&particle); - let type_from_name = &particle + + // TODO: Handle vibration and item particles + // These particles has some complicated data + // For now we will skip these particle + // They will still be a particle, just without the fields + particles.insert("vibration".to_string(), Value::Array(vec![])); + particles.insert("item".to_string(), Value::Array(vec![])); + + let structs = particles .iter() - .map(|particle| { - let id = &particle; - let name = format_ident!("{}", particle.to_pascal_case()); + .map(|(name, fields)| { + let fields = fields.as_array().unwrap(); + let upper_name_str = name.to_upper_camel_case(); + let upper_name = format_ident!("{}", upper_name_str); - quote! { - #id => Some(Self::#name), + let struct_fields = fields.iter().map(|f| { + let f = f.as_str().unwrap(); + + let field_name = format_ident!("{}", f.to_snake_case()); + let field_type = match_field_type_helper(f); + quote! { pub #field_name: #field_type } + }); + + let new_params = fields.iter().map(|f| { + let f = f.as_str().unwrap(); + + let field_name = format_ident!("{}", f.to_snake_case()); + let field_type = match_field_type_helper(f); + quote! { #field_name: #field_type } + }); + + let new_inits = fields.iter().map(|f| { + let f = f.as_str().unwrap(); + + let field = format_ident!("{}", f.to_snake_case()); + quote! { #field } + }); + + if fields.is_empty() { + quote! { + #[derive(Clone, Copy, Debug)] + pub struct #upper_name; + } + } else { + quote! { + #[derive(Clone, Copy, Debug)] + pub struct #upper_name { + #( #struct_fields ),* + } + + impl #upper_name { + pub fn new(#( #new_params ),*) -> Self { + Self { + #( #new_inits ),* + } + } + } + } } }) .collect::(); - let type_to_name = &particle - .iter() - .map(|particle| { - let id = &particle; - let name = format_ident!("{}", particle.to_pascal_case()); + quote! { + use pumpkin_util::math::vector3::Vector3; + + #structs + } +} + +fn match_field_type_helper(field_name: &str) -> TokenStream { + match field_name { + "state" | "color" | "fromColor" | "toColor" | "duration" | "delay" => quote! { i32 }, + "power" | "scale" | "roll" => quote! { f32 }, + "target" => quote! { Vector3 }, + _ => unreachable!("Unknown field name: {field_name}"), + } +} + +// Particle is special, because with the particle we need to generate the proper networking code +/// Generates the code for network serialiation for particles +pub fn build_serialization() -> TokenStream { + let mut particles: serde_json::Map = + serde_json::from_str(&fs::read_to_string("../assets/particles.json").unwrap()) + .expect("Failed to parse particles.json"); + + // TODO: Handle vibration and item particles + // These particles has some complicated data + // For now we will skip these particle + // They will still be a particle, just without the fields + particles.insert("vibration".to_string(), Value::Array(vec![])); + particles.insert("item".to_string(), Value::Array(vec![])); + + let particle_serialization = particles.iter().enumerate().map(|(id, (name, fields))| { + let fields = fields.as_array().unwrap(); + let upper_name_str = name.to_upper_camel_case(); + let upper_name = format_ident!("{}", upper_name_str); + + let id = id as i32; + let is_simple_particle = fields.is_empty(); + + if is_simple_particle { quote! { - Self::#name => #id, + impl SerializeParticleData for #upper_name { + fn id(&self) -> i32 { + #id + } + } } - }) - .collect::(); + } else { + let serialization = fields + .iter() + .map(|f| match f.as_str().unwrap() { + "state" => quote! { write.write_var_int(&VarInt(self.state))?; }, + "power" => quote! { write.write_f32_be(self.power)?; }, + "color" => quote! { write.write_i32_be(self.color)?; }, + "scale" => quote! { write.write_f32_be(self.scale)?; }, + "fromColor" => quote! { write.write_i32_be(self.from_color)?; }, + "toColor" => quote! { write.write_i32_be(self.to_color)?; }, + "roll" => quote! { write.write_f32_be(self.roll)?; }, + "target" => quote! { + write.write_f64_be(self.target.x)?; + write.write_f64_be(self.target.y)?; + write.write_f64_be(self.target.z)?; + }, + "duration" => quote! { write.write_var_int(&VarInt(self.duration))?; }, + "delay" => quote! { write.write_var_int(&VarInt(self.delay))?; }, + _ => unreachable!("Unknown field name: {f}"), + }) + .collect::(); + + quote! { + impl SerializeParticleData for #upper_name { + fn id(&self) -> i32 { + #id + } + + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + #serialization + Ok(initial_len - write.len()) + } + } + } + } + }); + quote! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub enum Particle { - #variants + use std::fmt::Debug; + + #[allow(clippy::wildcard_imports)] + use pumpkin_data::particle::*; + + use crate::{codec::var_int::VarInt, ser::{NetworkWriteExt, WritingError}}; + + pub trait SerializeParticleData: Send + Sync + Debug { + fn id(&self) -> i32; + + fn to_bytes(&self, _write: &mut [u8]) -> Result { + Ok(0) + } + } + + impl SerializeParticleData for &T { + fn id(&self) -> i32 { + (**self).id() + } + fn to_bytes(&self, write: &mut [u8]) -> Result { + (**self).to_bytes(write) + } } - impl Particle { - #[doc = r" Try to parse a `Particle` from a resource location string."] - #[must_use] - #[allow(clippy::too_many_lines)] - pub fn from_name(name: &str) -> Option { - match name { - #type_from_name - _ => None + #( #particle_serialization )* + } +} + +/// Builds the rust side of the WIT bindings code +pub fn build_wit_bindings() -> TokenStream { + let mut particles: serde_json::Map = + serde_json::from_str(&fs::read_to_string("../assets/particles.json").unwrap()) + .expect("Failed to parse particles.json"); + + // TODO: Handle vibration and item particles + // These particles has some complicated data + // For now we will skip these particle + // They will still be a particle, just without the fields + particles.insert("vibration".to_string(), Value::Array(vec![])); + particles.insert("item".to_string(), Value::Array(vec![])); + + let mut new_impls = vec![]; + + let spawn_functions = particles.iter().map(|(name, fields)| { + let fields = fields.as_array().unwrap(); + let camel_case = format_ident!("{}", name.to_upper_camel_case()); + let snake_case = format_ident!("{}", name.to_snake_case()); + let particle_type: syn::Path = syn::parse_str(&format!("pumpkin_data::particle::{}", camel_case)).unwrap(); + let spawn_function_name = format_ident!("spawn_{}_particle", snake_case); + + let mut function_params = vec![]; + let function_params_types: Vec<_> = fields.iter().map(|f| { + let f = f.as_str().unwrap(); + let field_type = if f != "position" && f != "target" { + match_field_type_helper(f) + } else { + quote!{Position} + }; + let f = format_ident!("{}", f.to_snake_case()); + + if f == "target" { + function_params.push(quote! {#f.into()}); + } else { + function_params.push(quote! {#f}); + } + + quote! { + #f: #field_type + } + }).collect(); + + if !fields.is_empty() { + let resource_type = format_ident!("{}ParticleResource", camel_case); + let host_impl_name = format_ident!("Host{}", camel_case); + + new_impls.push(quote! { + pub type #resource_type = WasmResource<#particle_type>; + impl #host_impl_name for PluginHostState { + async fn new(&mut self, #( #function_params_types ),*) -> wasmtime::Result> { + let provider = #particle_type::new(#( #function_params ),*); + let resource = self.resource_table.push(#resource_type { provider })?; + + Ok(Resource::new_own(resource.rep())) + } + + async fn drop(&mut self, rep: Resource<#camel_case>) -> wasmtime::Result<()> { + self.resource_table + .delete::<#resource_type>(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } + } + }); + + quote! { + async fn #spawn_function_name(&mut self, world: Resource, position: Position, offset: Offset, max_speed: f32, particle_count: i32, particle: Resource<#camel_case>) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + + world.provider.spawn_particle(position.into(), offset.into(), max_speed, particle_count, self.resource_table.get::<#resource_type>(&Resource::new_own(particle.rep()))?.provider); + + Ok(()) } } + } else { + quote! { + async fn #spawn_function_name(&mut self, world: Resource, position: Position, offset: Offset, max_speed: f32, particle_count: i32) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; - #[must_use] - #[allow(clippy::too_many_lines)] - pub const fn to_name(&self) -> &'static str { - match self { - #type_to_name + world.provider.spawn_particle(position.into(), offset.into(), max_speed, particle_count, #particle_type); + + Ok(()) } } } + }); + + quote! { + use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::Position; + #[allow(clippy::wildcard_imports)] + use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::particles::*; + use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::World; + #[allow(clippy::wildcard_imports)] + use crate::plugin::loader::wasm::wasm_host::state::*; + use wasmtime::component::Resource; + + impl Host for PluginHostState { + #( #spawn_functions )* + } + + #( #new_impls )* } } diff --git a/pumpkin-codegen/src/remap/mod.rs b/pumpkin-codegen/src/remap/mod.rs index 523c095459..c53a255636 100644 --- a/pumpkin-codegen/src/remap/mod.rs +++ b/pumpkin-codegen/src/remap/mod.rs @@ -1,7 +1,9 @@ +use std::path::{Path, PathBuf}; + use proc_macro2::TokenStream; use pumpkin_nbt::compound::NbtCompound; -use crate::version::JavaMinecraftVersion; +use crate::{GENERATED_DIR, version::JavaMinecraftVersion}; mod block_state; mod entity_id; @@ -10,12 +12,24 @@ mod sound_id; /// Returns the list of remap builder functions paired with their output file names. #[allow(clippy::type_complexity)] -pub fn build() -> Vec<(fn() -> TokenStream, &'static str)> { +pub fn build() -> Vec<(fn() -> TokenStream, PathBuf)> { vec![ - (block_state::build, "block_state_remap.rs"), - (entity_id::build, "entity_id_remap.rs"), - (item_id::build, "item_id_remap.rs"), - (sound_id::build, "sound_id_remap.rs"), + ( + block_state::build, + Path::new(GENERATED_DIR).join("block_state_remap.rs"), + ), + ( + entity_id::build, + Path::new(GENERATED_DIR).join("entity_id_remap.rs"), + ), + ( + item_id::build, + Path::new(GENERATED_DIR).join("item_id_remap.rs"), + ), + ( + sound_id::build, + Path::new(GENERATED_DIR).join("sound_id_remap.rs"), + ), ] } diff --git a/pumpkin-codegen/src/wit/particle.rs b/pumpkin-codegen/src/wit/particle.rs index 5bfacfeaef..3675e8d5ef 100644 --- a/pumpkin-codegen/src/wit/particle.rs +++ b/pumpkin-codegen/src/wit/particle.rs @@ -1,12 +1,24 @@ +use heck::{ToKebabCase, ToSnakeCase}; use semver::Version; +use serde_json::Value; use std::fs; -use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind}; +use wit_encoder::{ + Interface, InterfaceItem, Package, PackageName, ResourceFunc, StandaloneFunc, Type, TypeDef, + Use, +}; pub fn build() -> String { - let particles: Vec = + let mut particles: serde_json::Map = serde_json::from_str(&fs::read_to_string("../assets/particles.json").unwrap()) .expect("Failed to parse particles.json"); + // TODO: Handle vibration and item particles + // These particles has some complicated data + // For now we will skip these particle + // They will still be a particle, just without the fields + particles.insert("vibration".to_string(), Value::Array(vec![])); + particles.insert("item".to_string(), Value::Array(vec![])); + let mut package = Package::new(PackageName::new( "pumpkin", "plugin", @@ -14,16 +26,79 @@ pub fn build() -> String { )); let mut interface = Interface::new("particles"); - let mut particle_enum = Enum::empty(); - for particle in particles { - // WIT uses kebab-case for variants usually, but let's see if we should keep them as is - // Minecraft particles are already kebab-case or underscore. - // WIT prefers kebab-case. - particle_enum.case(particle.replace('_', "-")); + let mut use_item_stack = Use::new("item-stack"); + use_item_stack.item("item-stack", None); + interface.use_(use_item_stack); + + let mut use_position = Use::new("common"); + use_position.item("position", None); + interface.use_(use_position); + + let mut use_position = Use::new("world"); + use_position.item("world", None); + interface.use_(use_position); + + let offset_type = TypeDef::type_("offset", Type::tuple(vec![Type::F32, Type::F32, Type::F32])); + interface.type_def(offset_type); + + for (particle_name, fields) in particles { + let fields = fields.as_array().unwrap(); + let mut spawn_function = + StandaloneFunc::new(format!("spawn-{}-particle", particle_name.to_kebab_case())); + + spawn_function + .params_mut() + .push("world", Type::borrow("%world")); + spawn_function + .params_mut() + .push("pos", Type::named("position")); + spawn_function + .params_mut() + .push("offset", Type::named("offset")); + spawn_function.params_mut().push("max-speed", Type::F32); + spawn_function + .params_mut() + .push("particle-count", Type::S32); + if !fields.is_empty() { + spawn_function + .params_mut() + .push("particle", Type::named(particle_name.to_kebab_case())); + } + spawn_function.set_docs(Some(format!( + "Spawns `{}` particles at the specified position.", + particle_name.to_snake_case() + ))); + + interface.function(spawn_function); + + // There is no need to declare a particle, if it has no fields + // We will directly generate the spawn particles function for these particle types + if !fields.is_empty() { + let mut constructor = ResourceFunc::constructor(); + + for field in fields { + let field = field.as_str().unwrap(); + constructor + .params_mut() + .push(field.to_kebab_case(), match_field_type_helper(&field)); + } + + interface.item(InterfaceItem::TypeDef(TypeDef::resource( + particle_name.to_kebab_case(), + vec![constructor], + ))); + } } - interface.type_def(TypeDef::new("particle", TypeDefKind::Enum(particle_enum))); package.interface(interface); - package.to_string() } + +fn match_field_type_helper(field_name: &str) -> Type { + match field_name { + "state" | "color" | "fromColor" | "toColor" | "duration" | "delay" => Type::S32, + "power" | "scale" | "roll" => Type::F32, + "target" => Type::named("position"), + _ => unreachable!("Unknown field name: {field_name}"), + } +} diff --git a/pumpkin-codegen/src/wit/utils.rs b/pumpkin-codegen/src/wit/utils.rs index 4bb4337413..e0c4728d76 100644 --- a/pumpkin-codegen/src/wit/utils.rs +++ b/pumpkin-codegen/src/wit/utils.rs @@ -18,9 +18,10 @@ pub fn map_type(ty: &Type) -> WitType { "u16" | "i16" => WitType::S32, "Option" => { if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments - && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { - return WitType::option(map_type(inner_ty)); - } + && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() + { + return WitType::option(map_type(inner_ty)); + } WitType::String } "Box" => { @@ -32,15 +33,16 @@ pub fn map_type(ty: &Type) -> WitType { } "Vec" => { if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments - && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { - // Check if the inner type is u8 - if let Type::Path(tp) = inner_ty - && tp.path.segments.last().unwrap().ident == "u8" - { - return WitType::list(WitType::U8); - } - return WitType::list(map_type(inner_ty)); + && let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() + { + // Check if the inner type is u8 + if let Type::Path(tp) = inner_ty + && tp.path.segments.last().unwrap().ident == "u8" + { + return WitType::list(WitType::U8); } + return WitType::list(map_type(inner_ty)); + } WitType::String } "Vector3" | "BlockPos" => { diff --git a/pumpkin-data/src/generated/particle.rs b/pumpkin-data/src/generated/particle.rs index c9aea38d61..bbabe1e692 100644 --- a/pumpkin-data/src/generated/particle.rs +++ b/pumpkin-data/src/generated/particle.rs @@ -1,371 +1,363 @@ /* This file is generated. Do not edit manually. */ -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Particle { - AngryVillager, - Block, - BlockMarker, - Bubble, - Cloud, - CopperFireFlame, - Crit, - DamageIndicator, - DragonBreath, - DrippingLava, - FallingLava, - LandingLava, - DrippingWater, - FallingWater, - Dust, - DustColorTransition, - Effect, - ElderGuardian, - EnchantedHit, - Enchant, - EndRod, - EntityEffect, - ExplosionEmitter, - Explosion, - Gust, - SmallGust, - GustEmitterLarge, - GustEmitterSmall, - SonicBoom, - FallingDust, - Firework, - Fishing, - Flame, - Infested, - CherryLeaves, - PaleOakLeaves, - TintedLeaves, - SculkSoul, - SculkCharge, - SculkChargePop, - SoulFireFlame, - Soul, - Flash, - HappyVillager, - Composter, - Heart, - InstantEffect, - Item, - Vibration, - Trail, - PauseMobGrowth, - ResetMobGrowth, - ItemSlime, - ItemCobweb, - ItemSnowball, - LargeSmoke, - Lava, - Mycelium, - Note, - Poof, - Portal, - Rain, - Smoke, - WhiteSmoke, - Sneeze, - Spit, - SquidInk, - SweepAttack, - TotemOfUndying, - Underwater, - Splash, - Witch, - BubblePop, - CurrentDown, - BubbleColumnUp, - Nautilus, - Dolphin, - CampfireCosySmoke, - CampfireSignalSmoke, - DrippingHoney, - FallingHoney, - LandingHoney, - FallingNectar, - FallingSporeBlossom, - Ash, - CrimsonSpore, - WarpedSpore, - SporeBlossomAir, - DrippingObsidianTear, - FallingObsidianTear, - LandingObsidianTear, - ReversePortal, - WhiteAsh, - SmallFlame, - Snowflake, - DrippingDripstoneLava, - FallingDripstoneLava, - DrippingDripstoneWater, - FallingDripstoneWater, - GlowSquidInk, - Glow, - WaxOn, - WaxOff, - ElectricSpark, - Scrape, - Shriek, - EggCrack, - DustPlume, - TrialSpawnerDetection, - TrialSpawnerDetectionOminous, - VaultConnection, - DustPillar, - OminousSpawning, - RaidOmen, - TrialOmen, - BlockCrumble, - Firefly, -} -impl Particle { - #[doc = r" Try to parse a `Particle` from a resource location string."] - #[must_use] - #[allow(clippy::too_many_lines)] - pub fn from_name(name: &str) -> Option { - match name { - "angry_villager" => Some(Self::AngryVillager), - "block" => Some(Self::Block), - "block_marker" => Some(Self::BlockMarker), - "bubble" => Some(Self::Bubble), - "cloud" => Some(Self::Cloud), - "copper_fire_flame" => Some(Self::CopperFireFlame), - "crit" => Some(Self::Crit), - "damage_indicator" => Some(Self::DamageIndicator), - "dragon_breath" => Some(Self::DragonBreath), - "dripping_lava" => Some(Self::DrippingLava), - "falling_lava" => Some(Self::FallingLava), - "landing_lava" => Some(Self::LandingLava), - "dripping_water" => Some(Self::DrippingWater), - "falling_water" => Some(Self::FallingWater), - "dust" => Some(Self::Dust), - "dust_color_transition" => Some(Self::DustColorTransition), - "effect" => Some(Self::Effect), - "elder_guardian" => Some(Self::ElderGuardian), - "enchanted_hit" => Some(Self::EnchantedHit), - "enchant" => Some(Self::Enchant), - "end_rod" => Some(Self::EndRod), - "entity_effect" => Some(Self::EntityEffect), - "explosion_emitter" => Some(Self::ExplosionEmitter), - "explosion" => Some(Self::Explosion), - "gust" => Some(Self::Gust), - "small_gust" => Some(Self::SmallGust), - "gust_emitter_large" => Some(Self::GustEmitterLarge), - "gust_emitter_small" => Some(Self::GustEmitterSmall), - "sonic_boom" => Some(Self::SonicBoom), - "falling_dust" => Some(Self::FallingDust), - "firework" => Some(Self::Firework), - "fishing" => Some(Self::Fishing), - "flame" => Some(Self::Flame), - "infested" => Some(Self::Infested), - "cherry_leaves" => Some(Self::CherryLeaves), - "pale_oak_leaves" => Some(Self::PaleOakLeaves), - "tinted_leaves" => Some(Self::TintedLeaves), - "sculk_soul" => Some(Self::SculkSoul), - "sculk_charge" => Some(Self::SculkCharge), - "sculk_charge_pop" => Some(Self::SculkChargePop), - "soul_fire_flame" => Some(Self::SoulFireFlame), - "soul" => Some(Self::Soul), - "flash" => Some(Self::Flash), - "happy_villager" => Some(Self::HappyVillager), - "composter" => Some(Self::Composter), - "heart" => Some(Self::Heart), - "instant_effect" => Some(Self::InstantEffect), - "item" => Some(Self::Item), - "vibration" => Some(Self::Vibration), - "trail" => Some(Self::Trail), - "pause_mob_growth" => Some(Self::PauseMobGrowth), - "reset_mob_growth" => Some(Self::ResetMobGrowth), - "item_slime" => Some(Self::ItemSlime), - "item_cobweb" => Some(Self::ItemCobweb), - "item_snowball" => Some(Self::ItemSnowball), - "large_smoke" => Some(Self::LargeSmoke), - "lava" => Some(Self::Lava), - "mycelium" => Some(Self::Mycelium), - "note" => Some(Self::Note), - "poof" => Some(Self::Poof), - "portal" => Some(Self::Portal), - "rain" => Some(Self::Rain), - "smoke" => Some(Self::Smoke), - "white_smoke" => Some(Self::WhiteSmoke), - "sneeze" => Some(Self::Sneeze), - "spit" => Some(Self::Spit), - "squid_ink" => Some(Self::SquidInk), - "sweep_attack" => Some(Self::SweepAttack), - "totem_of_undying" => Some(Self::TotemOfUndying), - "underwater" => Some(Self::Underwater), - "splash" => Some(Self::Splash), - "witch" => Some(Self::Witch), - "bubble_pop" => Some(Self::BubblePop), - "current_down" => Some(Self::CurrentDown), - "bubble_column_up" => Some(Self::BubbleColumnUp), - "nautilus" => Some(Self::Nautilus), - "dolphin" => Some(Self::Dolphin), - "campfire_cosy_smoke" => Some(Self::CampfireCosySmoke), - "campfire_signal_smoke" => Some(Self::CampfireSignalSmoke), - "dripping_honey" => Some(Self::DrippingHoney), - "falling_honey" => Some(Self::FallingHoney), - "landing_honey" => Some(Self::LandingHoney), - "falling_nectar" => Some(Self::FallingNectar), - "falling_spore_blossom" => Some(Self::FallingSporeBlossom), - "ash" => Some(Self::Ash), - "crimson_spore" => Some(Self::CrimsonSpore), - "warped_spore" => Some(Self::WarpedSpore), - "spore_blossom_air" => Some(Self::SporeBlossomAir), - "dripping_obsidian_tear" => Some(Self::DrippingObsidianTear), - "falling_obsidian_tear" => Some(Self::FallingObsidianTear), - "landing_obsidian_tear" => Some(Self::LandingObsidianTear), - "reverse_portal" => Some(Self::ReversePortal), - "white_ash" => Some(Self::WhiteAsh), - "small_flame" => Some(Self::SmallFlame), - "snowflake" => Some(Self::Snowflake), - "dripping_dripstone_lava" => Some(Self::DrippingDripstoneLava), - "falling_dripstone_lava" => Some(Self::FallingDripstoneLava), - "dripping_dripstone_water" => Some(Self::DrippingDripstoneWater), - "falling_dripstone_water" => Some(Self::FallingDripstoneWater), - "glow_squid_ink" => Some(Self::GlowSquidInk), - "glow" => Some(Self::Glow), - "wax_on" => Some(Self::WaxOn), - "wax_off" => Some(Self::WaxOff), - "electric_spark" => Some(Self::ElectricSpark), - "scrape" => Some(Self::Scrape), - "shriek" => Some(Self::Shriek), - "egg_crack" => Some(Self::EggCrack), - "dust_plume" => Some(Self::DustPlume), - "trial_spawner_detection" => Some(Self::TrialSpawnerDetection), - "trial_spawner_detection_ominous" => Some(Self::TrialSpawnerDetectionOminous), - "vault_connection" => Some(Self::VaultConnection), - "dust_pillar" => Some(Self::DustPillar), - "ominous_spawning" => Some(Self::OminousSpawning), - "raid_omen" => Some(Self::RaidOmen), - "trial_omen" => Some(Self::TrialOmen), - "block_crumble" => Some(Self::BlockCrumble), - "firefly" => Some(Self::Firefly), - _ => None, +use pumpkin_util::math::vector3::Vector3; +#[derive(Clone, Copy, Debug)] +pub struct AngryVillager; +#[derive(Clone, Copy, Debug)] +pub struct Block { + pub state: i32, +} +impl Block { + pub fn new(state: i32) -> Self { + Self { state } + } +} +#[derive(Clone, Copy, Debug)] +pub struct BlockMarker { + pub state: i32, +} +impl BlockMarker { + pub fn new(state: i32) -> Self { + Self { state } + } +} +#[derive(Clone, Copy, Debug)] +pub struct Bubble; +#[derive(Clone, Copy, Debug)] +pub struct Cloud; +#[derive(Clone, Copy, Debug)] +pub struct CopperFireFlame; +#[derive(Clone, Copy, Debug)] +pub struct Crit; +#[derive(Clone, Copy, Debug)] +pub struct DamageIndicator; +#[derive(Clone, Copy, Debug)] +pub struct DragonBreath { + pub power: f32, +} +impl DragonBreath { + pub fn new(power: f32) -> Self { + Self { power } + } +} +#[derive(Clone, Copy, Debug)] +pub struct DrippingLava; +#[derive(Clone, Copy, Debug)] +pub struct FallingLava; +#[derive(Clone, Copy, Debug)] +pub struct LandingLava; +#[derive(Clone, Copy, Debug)] +pub struct DrippingWater; +#[derive(Clone, Copy, Debug)] +pub struct FallingWater; +#[derive(Clone, Copy, Debug)] +pub struct Dust { + pub color: i32, + pub scale: f32, +} +impl Dust { + pub fn new(color: i32, scale: f32) -> Self { + Self { color, scale } + } +} +#[derive(Clone, Copy, Debug)] +pub struct DustColorTransition { + pub from_color: i32, + pub to_color: i32, + pub scale: f32, +} +impl DustColorTransition { + pub fn new(from_color: i32, to_color: i32, scale: f32) -> Self { + Self { + from_color, + to_color, + scale, } } - #[must_use] - #[allow(clippy::too_many_lines)] - pub const fn to_name(&self) -> &'static str { - match self { - Self::AngryVillager => "angry_villager", - Self::Block => "block", - Self::BlockMarker => "block_marker", - Self::Bubble => "bubble", - Self::Cloud => "cloud", - Self::CopperFireFlame => "copper_fire_flame", - Self::Crit => "crit", - Self::DamageIndicator => "damage_indicator", - Self::DragonBreath => "dragon_breath", - Self::DrippingLava => "dripping_lava", - Self::FallingLava => "falling_lava", - Self::LandingLava => "landing_lava", - Self::DrippingWater => "dripping_water", - Self::FallingWater => "falling_water", - Self::Dust => "dust", - Self::DustColorTransition => "dust_color_transition", - Self::Effect => "effect", - Self::ElderGuardian => "elder_guardian", - Self::EnchantedHit => "enchanted_hit", - Self::Enchant => "enchant", - Self::EndRod => "end_rod", - Self::EntityEffect => "entity_effect", - Self::ExplosionEmitter => "explosion_emitter", - Self::Explosion => "explosion", - Self::Gust => "gust", - Self::SmallGust => "small_gust", - Self::GustEmitterLarge => "gust_emitter_large", - Self::GustEmitterSmall => "gust_emitter_small", - Self::SonicBoom => "sonic_boom", - Self::FallingDust => "falling_dust", - Self::Firework => "firework", - Self::Fishing => "fishing", - Self::Flame => "flame", - Self::Infested => "infested", - Self::CherryLeaves => "cherry_leaves", - Self::PaleOakLeaves => "pale_oak_leaves", - Self::TintedLeaves => "tinted_leaves", - Self::SculkSoul => "sculk_soul", - Self::SculkCharge => "sculk_charge", - Self::SculkChargePop => "sculk_charge_pop", - Self::SoulFireFlame => "soul_fire_flame", - Self::Soul => "soul", - Self::Flash => "flash", - Self::HappyVillager => "happy_villager", - Self::Composter => "composter", - Self::Heart => "heart", - Self::InstantEffect => "instant_effect", - Self::Item => "item", - Self::Vibration => "vibration", - Self::Trail => "trail", - Self::PauseMobGrowth => "pause_mob_growth", - Self::ResetMobGrowth => "reset_mob_growth", - Self::ItemSlime => "item_slime", - Self::ItemCobweb => "item_cobweb", - Self::ItemSnowball => "item_snowball", - Self::LargeSmoke => "large_smoke", - Self::Lava => "lava", - Self::Mycelium => "mycelium", - Self::Note => "note", - Self::Poof => "poof", - Self::Portal => "portal", - Self::Rain => "rain", - Self::Smoke => "smoke", - Self::WhiteSmoke => "white_smoke", - Self::Sneeze => "sneeze", - Self::Spit => "spit", - Self::SquidInk => "squid_ink", - Self::SweepAttack => "sweep_attack", - Self::TotemOfUndying => "totem_of_undying", - Self::Underwater => "underwater", - Self::Splash => "splash", - Self::Witch => "witch", - Self::BubblePop => "bubble_pop", - Self::CurrentDown => "current_down", - Self::BubbleColumnUp => "bubble_column_up", - Self::Nautilus => "nautilus", - Self::Dolphin => "dolphin", - Self::CampfireCosySmoke => "campfire_cosy_smoke", - Self::CampfireSignalSmoke => "campfire_signal_smoke", - Self::DrippingHoney => "dripping_honey", - Self::FallingHoney => "falling_honey", - Self::LandingHoney => "landing_honey", - Self::FallingNectar => "falling_nectar", - Self::FallingSporeBlossom => "falling_spore_blossom", - Self::Ash => "ash", - Self::CrimsonSpore => "crimson_spore", - Self::WarpedSpore => "warped_spore", - Self::SporeBlossomAir => "spore_blossom_air", - Self::DrippingObsidianTear => "dripping_obsidian_tear", - Self::FallingObsidianTear => "falling_obsidian_tear", - Self::LandingObsidianTear => "landing_obsidian_tear", - Self::ReversePortal => "reverse_portal", - Self::WhiteAsh => "white_ash", - Self::SmallFlame => "small_flame", - Self::Snowflake => "snowflake", - Self::DrippingDripstoneLava => "dripping_dripstone_lava", - Self::FallingDripstoneLava => "falling_dripstone_lava", - Self::DrippingDripstoneWater => "dripping_dripstone_water", - Self::FallingDripstoneWater => "falling_dripstone_water", - Self::GlowSquidInk => "glow_squid_ink", - Self::Glow => "glow", - Self::WaxOn => "wax_on", - Self::WaxOff => "wax_off", - Self::ElectricSpark => "electric_spark", - Self::Scrape => "scrape", - Self::Shriek => "shriek", - Self::EggCrack => "egg_crack", - Self::DustPlume => "dust_plume", - Self::TrialSpawnerDetection => "trial_spawner_detection", - Self::TrialSpawnerDetectionOminous => "trial_spawner_detection_ominous", - Self::VaultConnection => "vault_connection", - Self::DustPillar => "dust_pillar", - Self::OminousSpawning => "ominous_spawning", - Self::RaidOmen => "raid_omen", - Self::TrialOmen => "trial_omen", - Self::BlockCrumble => "block_crumble", - Self::Firefly => "firefly", +} +#[derive(Clone, Copy, Debug)] +pub struct Effect { + pub color: i32, + pub power: f32, +} +impl Effect { + pub fn new(color: i32, power: f32) -> Self { + Self { color, power } + } +} +#[derive(Clone, Copy, Debug)] +pub struct ElderGuardian; +#[derive(Clone, Copy, Debug)] +pub struct EnchantedHit; +#[derive(Clone, Copy, Debug)] +pub struct Enchant; +#[derive(Clone, Copy, Debug)] +pub struct EndRod; +#[derive(Clone, Copy, Debug)] +pub struct EntityEffect { + pub color: i32, +} +impl EntityEffect { + pub fn new(color: i32) -> Self { + Self { color } + } +} +#[derive(Clone, Copy, Debug)] +pub struct ExplosionEmitter; +#[derive(Clone, Copy, Debug)] +pub struct Explosion; +#[derive(Clone, Copy, Debug)] +pub struct Gust; +#[derive(Clone, Copy, Debug)] +pub struct SmallGust; +#[derive(Clone, Copy, Debug)] +pub struct GustEmitterLarge; +#[derive(Clone, Copy, Debug)] +pub struct GustEmitterSmall; +#[derive(Clone, Copy, Debug)] +pub struct SonicBoom; +#[derive(Clone, Copy, Debug)] +pub struct FallingDust { + pub state: i32, +} +impl FallingDust { + pub fn new(state: i32) -> Self { + Self { state } + } +} +#[derive(Clone, Copy, Debug)] +pub struct Firework; +#[derive(Clone, Copy, Debug)] +pub struct Fishing; +#[derive(Clone, Copy, Debug)] +pub struct Flame; +#[derive(Clone, Copy, Debug)] +pub struct Infested; +#[derive(Clone, Copy, Debug)] +pub struct CherryLeaves; +#[derive(Clone, Copy, Debug)] +pub struct PaleOakLeaves; +#[derive(Clone, Copy, Debug)] +pub struct TintedLeaves { + pub color: i32, +} +impl TintedLeaves { + pub fn new(color: i32) -> Self { + Self { color } + } +} +#[derive(Clone, Copy, Debug)] +pub struct SculkSoul; +#[derive(Clone, Copy, Debug)] +pub struct SculkCharge { + pub roll: f32, +} +impl SculkCharge { + pub fn new(roll: f32) -> Self { + Self { roll } + } +} +#[derive(Clone, Copy, Debug)] +pub struct SculkChargePop; +#[derive(Clone, Copy, Debug)] +pub struct SoulFireFlame; +#[derive(Clone, Copy, Debug)] +pub struct Soul; +#[derive(Clone, Copy, Debug)] +pub struct Flash { + pub color: i32, +} +impl Flash { + pub fn new(color: i32) -> Self { + Self { color } + } +} +#[derive(Clone, Copy, Debug)] +pub struct HappyVillager; +#[derive(Clone, Copy, Debug)] +pub struct Composter; +#[derive(Clone, Copy, Debug)] +pub struct Heart; +#[derive(Clone, Copy, Debug)] +pub struct InstantEffect { + pub color: i32, + pub power: f32, +} +impl InstantEffect { + pub fn new(color: i32, power: f32) -> Self { + Self { color, power } + } +} +#[derive(Clone, Copy, Debug)] +pub struct Item; +#[derive(Clone, Copy, Debug)] +pub struct Vibration; +#[derive(Clone, Copy, Debug)] +pub struct Trail { + pub target: Vector3, + pub color: i32, + pub duration: i32, +} +impl Trail { + pub fn new(target: Vector3, color: i32, duration: i32) -> Self { + Self { + target, + color, + duration, } } } +#[derive(Clone, Copy, Debug)] +pub struct PauseMobGrowth; +#[derive(Clone, Copy, Debug)] +pub struct ResetMobGrowth; +#[derive(Clone, Copy, Debug)] +pub struct ItemSlime; +#[derive(Clone, Copy, Debug)] +pub struct ItemCobweb; +#[derive(Clone, Copy, Debug)] +pub struct ItemSnowball; +#[derive(Clone, Copy, Debug)] +pub struct LargeSmoke; +#[derive(Clone, Copy, Debug)] +pub struct Lava; +#[derive(Clone, Copy, Debug)] +pub struct Mycelium; +#[derive(Clone, Copy, Debug)] +pub struct Note; +#[derive(Clone, Copy, Debug)] +pub struct Poof; +#[derive(Clone, Copy, Debug)] +pub struct Portal; +#[derive(Clone, Copy, Debug)] +pub struct Rain; +#[derive(Clone, Copy, Debug)] +pub struct Smoke; +#[derive(Clone, Copy, Debug)] +pub struct WhiteSmoke; +#[derive(Clone, Copy, Debug)] +pub struct Sneeze; +#[derive(Clone, Copy, Debug)] +pub struct Spit; +#[derive(Clone, Copy, Debug)] +pub struct SquidInk; +#[derive(Clone, Copy, Debug)] +pub struct SweepAttack; +#[derive(Clone, Copy, Debug)] +pub struct TotemOfUndying; +#[derive(Clone, Copy, Debug)] +pub struct Underwater; +#[derive(Clone, Copy, Debug)] +pub struct Splash; +#[derive(Clone, Copy, Debug)] +pub struct Witch; +#[derive(Clone, Copy, Debug)] +pub struct BubblePop; +#[derive(Clone, Copy, Debug)] +pub struct CurrentDown; +#[derive(Clone, Copy, Debug)] +pub struct BubbleColumnUp; +#[derive(Clone, Copy, Debug)] +pub struct Nautilus; +#[derive(Clone, Copy, Debug)] +pub struct Dolphin; +#[derive(Clone, Copy, Debug)] +pub struct CampfireCosySmoke; +#[derive(Clone, Copy, Debug)] +pub struct CampfireSignalSmoke; +#[derive(Clone, Copy, Debug)] +pub struct DrippingHoney; +#[derive(Clone, Copy, Debug)] +pub struct FallingHoney; +#[derive(Clone, Copy, Debug)] +pub struct LandingHoney; +#[derive(Clone, Copy, Debug)] +pub struct FallingNectar; +#[derive(Clone, Copy, Debug)] +pub struct FallingSporeBlossom; +#[derive(Clone, Copy, Debug)] +pub struct Ash; +#[derive(Clone, Copy, Debug)] +pub struct CrimsonSpore; +#[derive(Clone, Copy, Debug)] +pub struct WarpedSpore; +#[derive(Clone, Copy, Debug)] +pub struct SporeBlossomAir; +#[derive(Clone, Copy, Debug)] +pub struct DrippingObsidianTear; +#[derive(Clone, Copy, Debug)] +pub struct FallingObsidianTear; +#[derive(Clone, Copy, Debug)] +pub struct LandingObsidianTear; +#[derive(Clone, Copy, Debug)] +pub struct ReversePortal; +#[derive(Clone, Copy, Debug)] +pub struct WhiteAsh; +#[derive(Clone, Copy, Debug)] +pub struct SmallFlame; +#[derive(Clone, Copy, Debug)] +pub struct Snowflake; +#[derive(Clone, Copy, Debug)] +pub struct DrippingDripstoneLava; +#[derive(Clone, Copy, Debug)] +pub struct FallingDripstoneLava; +#[derive(Clone, Copy, Debug)] +pub struct DrippingDripstoneWater; +#[derive(Clone, Copy, Debug)] +pub struct FallingDripstoneWater; +#[derive(Clone, Copy, Debug)] +pub struct GlowSquidInk; +#[derive(Clone, Copy, Debug)] +pub struct Glow; +#[derive(Clone, Copy, Debug)] +pub struct WaxOn; +#[derive(Clone, Copy, Debug)] +pub struct WaxOff; +#[derive(Clone, Copy, Debug)] +pub struct ElectricSpark; +#[derive(Clone, Copy, Debug)] +pub struct Scrape; +#[derive(Clone, Copy, Debug)] +pub struct Shriek { + pub delay: i32, +} +impl Shriek { + pub fn new(delay: i32) -> Self { + Self { delay } + } +} +#[derive(Clone, Copy, Debug)] +pub struct EggCrack; +#[derive(Clone, Copy, Debug)] +pub struct DustPlume; +#[derive(Clone, Copy, Debug)] +pub struct TrialSpawnerDetection; +#[derive(Clone, Copy, Debug)] +pub struct TrialSpawnerDetectionOminous; +#[derive(Clone, Copy, Debug)] +pub struct VaultConnection; +#[derive(Clone, Copy, Debug)] +pub struct DustPillar { + pub state: i32, +} +impl DustPillar { + pub fn new(state: i32) -> Self { + Self { state } + } +} +#[derive(Clone, Copy, Debug)] +pub struct OminousSpawning; +#[derive(Clone, Copy, Debug)] +pub struct RaidOmen; +#[derive(Clone, Copy, Debug)] +pub struct TrialOmen; +#[derive(Clone, Copy, Debug)] +pub struct BlockCrumble { + pub state: i32, +} +impl BlockCrumble { + pub fn new(state: i32) -> Self { + Self { state } + } +} +#[derive(Clone, Copy, Debug)] +pub struct Firefly; diff --git a/pumpkin-plugin-api/src/lib.rs b/pumpkin-plugin-api/src/lib.rs index 849e46ba72..58198c542f 100644 --- a/pumpkin-plugin-api/src/lib.rs +++ b/pumpkin-plugin-api/src/lib.rs @@ -53,8 +53,7 @@ pub use wit::pumpkin::plugin::{ context::{Context, Server}, entity, entity_types::EntityType, - gui, i18n, java_dialogs, java_packets, particles, permission, player, scoreboard, server, text, - world, + gui, i18n, java_dialogs, java_packets, permission, player, scoreboard, server, text, world, }; pub mod java_dialog { diff --git a/pumpkin-plugin-wit b/pumpkin-plugin-wit index 9e87f503d0..4ef033ab88 160000 --- a/pumpkin-plugin-wit +++ b/pumpkin-plugin-wit @@ -1 +1 @@ -Subproject commit 9e87f503d03644569117906cf0cc7f88dfa36194 +Subproject commit 4ef033ab884db28a33a94fe75b1109829d0933a6 diff --git a/pumpkin-protocol/src/java/client/play/mod.rs b/pumpkin-protocol/src/java/client/play/mod.rs index e005b096da..bce998ebf5 100644 --- a/pumpkin-protocol/src/java/client/play/mod.rs +++ b/pumpkin-protocol/src/java/client/play/mod.rs @@ -47,6 +47,7 @@ mod multi_block_update; mod open_screen; mod open_sign_editor; mod particle; +mod particle_data; mod ping_response; mod player_abilities; mod player_action; @@ -151,6 +152,7 @@ pub use multi_block_update::*; pub use open_screen::*; pub use open_sign_editor::*; pub use particle::*; +pub use particle_data::*; pub use ping_response::*; pub use player_abilities::*; pub use player_action::*; diff --git a/pumpkin-protocol/src/java/client/play/particle_data.rs b/pumpkin-protocol/src/java/client/play/particle_data.rs new file mode 100644 index 0000000000..643aa5c005 --- /dev/null +++ b/pumpkin-protocol/src/java/client/play/particle_data.rs @@ -0,0 +1,696 @@ +/* This file is generated. Do not edit manually. */ +use crate::{ + codec::var_int::VarInt, + ser::{NetworkWriteExt, WritingError}, +}; +#[allow(clippy::wildcard_imports)] +use pumpkin_data::particle::*; +use std::fmt::Debug; +pub trait SerializeParticleData: Send + Sync + Debug { + fn id(&self) -> i32; + fn to_bytes(&self, _write: &mut [u8]) -> Result { + Ok(0) + } +} +impl SerializeParticleData for &T { + fn id(&self) -> i32 { + (**self).id() + } + fn to_bytes(&self, write: &mut [u8]) -> Result { + (**self).to_bytes(write) + } +} +impl SerializeParticleData for AngryVillager { + fn id(&self) -> i32 { + 0i32 + } +} +impl SerializeParticleData for Block { + fn id(&self) -> i32 { + 1i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_var_int(&VarInt(self.state))?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for BlockMarker { + fn id(&self) -> i32 { + 2i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_var_int(&VarInt(self.state))?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for Bubble { + fn id(&self) -> i32 { + 3i32 + } +} +impl SerializeParticleData for Cloud { + fn id(&self) -> i32 { + 4i32 + } +} +impl SerializeParticleData for CopperFireFlame { + fn id(&self) -> i32 { + 5i32 + } +} +impl SerializeParticleData for Crit { + fn id(&self) -> i32 { + 6i32 + } +} +impl SerializeParticleData for DamageIndicator { + fn id(&self) -> i32 { + 7i32 + } +} +impl SerializeParticleData for DragonBreath { + fn id(&self) -> i32 { + 8i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_f32_be(self.power)?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for DrippingLava { + fn id(&self) -> i32 { + 9i32 + } +} +impl SerializeParticleData for FallingLava { + fn id(&self) -> i32 { + 10i32 + } +} +impl SerializeParticleData for LandingLava { + fn id(&self) -> i32 { + 11i32 + } +} +impl SerializeParticleData for DrippingWater { + fn id(&self) -> i32 { + 12i32 + } +} +impl SerializeParticleData for FallingWater { + fn id(&self) -> i32 { + 13i32 + } +} +impl SerializeParticleData for Dust { + fn id(&self) -> i32 { + 14i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_i32_be(self.color)?; + write.write_f32_be(self.scale)?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for DustColorTransition { + fn id(&self) -> i32 { + 15i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_i32_be(self.from_color)?; + write.write_i32_be(self.to_color)?; + write.write_f32_be(self.scale)?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for Effect { + fn id(&self) -> i32 { + 16i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_i32_be(self.color)?; + write.write_f32_be(self.power)?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for ElderGuardian { + fn id(&self) -> i32 { + 17i32 + } +} +impl SerializeParticleData for EnchantedHit { + fn id(&self) -> i32 { + 18i32 + } +} +impl SerializeParticleData for Enchant { + fn id(&self) -> i32 { + 19i32 + } +} +impl SerializeParticleData for EndRod { + fn id(&self) -> i32 { + 20i32 + } +} +impl SerializeParticleData for EntityEffect { + fn id(&self) -> i32 { + 21i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_i32_be(self.color)?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for ExplosionEmitter { + fn id(&self) -> i32 { + 22i32 + } +} +impl SerializeParticleData for Explosion { + fn id(&self) -> i32 { + 23i32 + } +} +impl SerializeParticleData for Gust { + fn id(&self) -> i32 { + 24i32 + } +} +impl SerializeParticleData for SmallGust { + fn id(&self) -> i32 { + 25i32 + } +} +impl SerializeParticleData for GustEmitterLarge { + fn id(&self) -> i32 { + 26i32 + } +} +impl SerializeParticleData for GustEmitterSmall { + fn id(&self) -> i32 { + 27i32 + } +} +impl SerializeParticleData for SonicBoom { + fn id(&self) -> i32 { + 28i32 + } +} +impl SerializeParticleData for FallingDust { + fn id(&self) -> i32 { + 29i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_var_int(&VarInt(self.state))?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for Firework { + fn id(&self) -> i32 { + 30i32 + } +} +impl SerializeParticleData for Fishing { + fn id(&self) -> i32 { + 31i32 + } +} +impl SerializeParticleData for Flame { + fn id(&self) -> i32 { + 32i32 + } +} +impl SerializeParticleData for Infested { + fn id(&self) -> i32 { + 33i32 + } +} +impl SerializeParticleData for CherryLeaves { + fn id(&self) -> i32 { + 34i32 + } +} +impl SerializeParticleData for PaleOakLeaves { + fn id(&self) -> i32 { + 35i32 + } +} +impl SerializeParticleData for TintedLeaves { + fn id(&self) -> i32 { + 36i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_i32_be(self.color)?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for SculkSoul { + fn id(&self) -> i32 { + 37i32 + } +} +impl SerializeParticleData for SculkCharge { + fn id(&self) -> i32 { + 38i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_f32_be(self.roll)?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for SculkChargePop { + fn id(&self) -> i32 { + 39i32 + } +} +impl SerializeParticleData for SoulFireFlame { + fn id(&self) -> i32 { + 40i32 + } +} +impl SerializeParticleData for Soul { + fn id(&self) -> i32 { + 41i32 + } +} +impl SerializeParticleData for Flash { + fn id(&self) -> i32 { + 42i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_i32_be(self.color)?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for HappyVillager { + fn id(&self) -> i32 { + 43i32 + } +} +impl SerializeParticleData for Composter { + fn id(&self) -> i32 { + 44i32 + } +} +impl SerializeParticleData for Heart { + fn id(&self) -> i32 { + 45i32 + } +} +impl SerializeParticleData for InstantEffect { + fn id(&self) -> i32 { + 46i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_i32_be(self.color)?; + write.write_f32_be(self.power)?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for Item { + fn id(&self) -> i32 { + 47i32 + } +} +impl SerializeParticleData for Vibration { + fn id(&self) -> i32 { + 48i32 + } +} +impl SerializeParticleData for Trail { + fn id(&self) -> i32 { + 49i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_f64_be(self.target.x)?; + write.write_f64_be(self.target.y)?; + write.write_f64_be(self.target.z)?; + write.write_i32_be(self.color)?; + write.write_var_int(&VarInt(self.duration))?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for PauseMobGrowth { + fn id(&self) -> i32 { + 50i32 + } +} +impl SerializeParticleData for ResetMobGrowth { + fn id(&self) -> i32 { + 51i32 + } +} +impl SerializeParticleData for ItemSlime { + fn id(&self) -> i32 { + 52i32 + } +} +impl SerializeParticleData for ItemCobweb { + fn id(&self) -> i32 { + 53i32 + } +} +impl SerializeParticleData for ItemSnowball { + fn id(&self) -> i32 { + 54i32 + } +} +impl SerializeParticleData for LargeSmoke { + fn id(&self) -> i32 { + 55i32 + } +} +impl SerializeParticleData for Lava { + fn id(&self) -> i32 { + 56i32 + } +} +impl SerializeParticleData for Mycelium { + fn id(&self) -> i32 { + 57i32 + } +} +impl SerializeParticleData for Note { + fn id(&self) -> i32 { + 58i32 + } +} +impl SerializeParticleData for Poof { + fn id(&self) -> i32 { + 59i32 + } +} +impl SerializeParticleData for Portal { + fn id(&self) -> i32 { + 60i32 + } +} +impl SerializeParticleData for Rain { + fn id(&self) -> i32 { + 61i32 + } +} +impl SerializeParticleData for Smoke { + fn id(&self) -> i32 { + 62i32 + } +} +impl SerializeParticleData for WhiteSmoke { + fn id(&self) -> i32 { + 63i32 + } +} +impl SerializeParticleData for Sneeze { + fn id(&self) -> i32 { + 64i32 + } +} +impl SerializeParticleData for Spit { + fn id(&self) -> i32 { + 65i32 + } +} +impl SerializeParticleData for SquidInk { + fn id(&self) -> i32 { + 66i32 + } +} +impl SerializeParticleData for SweepAttack { + fn id(&self) -> i32 { + 67i32 + } +} +impl SerializeParticleData for TotemOfUndying { + fn id(&self) -> i32 { + 68i32 + } +} +impl SerializeParticleData for Underwater { + fn id(&self) -> i32 { + 69i32 + } +} +impl SerializeParticleData for Splash { + fn id(&self) -> i32 { + 70i32 + } +} +impl SerializeParticleData for Witch { + fn id(&self) -> i32 { + 71i32 + } +} +impl SerializeParticleData for BubblePop { + fn id(&self) -> i32 { + 72i32 + } +} +impl SerializeParticleData for CurrentDown { + fn id(&self) -> i32 { + 73i32 + } +} +impl SerializeParticleData for BubbleColumnUp { + fn id(&self) -> i32 { + 74i32 + } +} +impl SerializeParticleData for Nautilus { + fn id(&self) -> i32 { + 75i32 + } +} +impl SerializeParticleData for Dolphin { + fn id(&self) -> i32 { + 76i32 + } +} +impl SerializeParticleData for CampfireCosySmoke { + fn id(&self) -> i32 { + 77i32 + } +} +impl SerializeParticleData for CampfireSignalSmoke { + fn id(&self) -> i32 { + 78i32 + } +} +impl SerializeParticleData for DrippingHoney { + fn id(&self) -> i32 { + 79i32 + } +} +impl SerializeParticleData for FallingHoney { + fn id(&self) -> i32 { + 80i32 + } +} +impl SerializeParticleData for LandingHoney { + fn id(&self) -> i32 { + 81i32 + } +} +impl SerializeParticleData for FallingNectar { + fn id(&self) -> i32 { + 82i32 + } +} +impl SerializeParticleData for FallingSporeBlossom { + fn id(&self) -> i32 { + 83i32 + } +} +impl SerializeParticleData for Ash { + fn id(&self) -> i32 { + 84i32 + } +} +impl SerializeParticleData for CrimsonSpore { + fn id(&self) -> i32 { + 85i32 + } +} +impl SerializeParticleData for WarpedSpore { + fn id(&self) -> i32 { + 86i32 + } +} +impl SerializeParticleData for SporeBlossomAir { + fn id(&self) -> i32 { + 87i32 + } +} +impl SerializeParticleData for DrippingObsidianTear { + fn id(&self) -> i32 { + 88i32 + } +} +impl SerializeParticleData for FallingObsidianTear { + fn id(&self) -> i32 { + 89i32 + } +} +impl SerializeParticleData for LandingObsidianTear { + fn id(&self) -> i32 { + 90i32 + } +} +impl SerializeParticleData for ReversePortal { + fn id(&self) -> i32 { + 91i32 + } +} +impl SerializeParticleData for WhiteAsh { + fn id(&self) -> i32 { + 92i32 + } +} +impl SerializeParticleData for SmallFlame { + fn id(&self) -> i32 { + 93i32 + } +} +impl SerializeParticleData for Snowflake { + fn id(&self) -> i32 { + 94i32 + } +} +impl SerializeParticleData for DrippingDripstoneLava { + fn id(&self) -> i32 { + 95i32 + } +} +impl SerializeParticleData for FallingDripstoneLava { + fn id(&self) -> i32 { + 96i32 + } +} +impl SerializeParticleData for DrippingDripstoneWater { + fn id(&self) -> i32 { + 97i32 + } +} +impl SerializeParticleData for FallingDripstoneWater { + fn id(&self) -> i32 { + 98i32 + } +} +impl SerializeParticleData for GlowSquidInk { + fn id(&self) -> i32 { + 99i32 + } +} +impl SerializeParticleData for Glow { + fn id(&self) -> i32 { + 100i32 + } +} +impl SerializeParticleData for WaxOn { + fn id(&self) -> i32 { + 101i32 + } +} +impl SerializeParticleData for WaxOff { + fn id(&self) -> i32 { + 102i32 + } +} +impl SerializeParticleData for ElectricSpark { + fn id(&self) -> i32 { + 103i32 + } +} +impl SerializeParticleData for Scrape { + fn id(&self) -> i32 { + 104i32 + } +} +impl SerializeParticleData for Shriek { + fn id(&self) -> i32 { + 105i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_var_int(&VarInt(self.delay))?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for EggCrack { + fn id(&self) -> i32 { + 106i32 + } +} +impl SerializeParticleData for DustPlume { + fn id(&self) -> i32 { + 107i32 + } +} +impl SerializeParticleData for TrialSpawnerDetection { + fn id(&self) -> i32 { + 108i32 + } +} +impl SerializeParticleData for TrialSpawnerDetectionOminous { + fn id(&self) -> i32 { + 109i32 + } +} +impl SerializeParticleData for VaultConnection { + fn id(&self) -> i32 { + 110i32 + } +} +impl SerializeParticleData for DustPillar { + fn id(&self) -> i32 { + 111i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_var_int(&VarInt(self.state))?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for OminousSpawning { + fn id(&self) -> i32 { + 112i32 + } +} +impl SerializeParticleData for RaidOmen { + fn id(&self) -> i32 { + 113i32 + } +} +impl SerializeParticleData for TrialOmen { + fn id(&self) -> i32 { + 114i32 + } +} +impl SerializeParticleData for BlockCrumble { + fn id(&self) -> i32 { + 115i32 + } + fn to_bytes(&self, mut write: &mut [u8]) -> Result { + let initial_len = write.len(); + write.write_var_int(&VarInt(self.state))?; + Ok(initial_len - write.len()) + } +} +impl SerializeParticleData for Firefly { + fn id(&self) -> i32 { + 116i32 + } +} diff --git a/pumpkin/src/block/blocks/sponge.rs b/pumpkin/src/block/blocks/sponge.rs index ed9101cd88..99f2c4b677 100644 --- a/pumpkin/src/block/blocks/sponge.rs +++ b/pumpkin/src/block/blocks/sponge.rs @@ -1,3 +1,4 @@ +use pumpkin_data::particle; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::vector3::Vector3; use std::collections::{HashSet, VecDeque}; @@ -6,7 +7,6 @@ use std::sync::Arc; use crate::block::{BlockBehaviour, BlockFuture, OnNeighborUpdateArgs, PlacedArgs}; use pumpkin_data::Block; use pumpkin_data::dimension::Dimension; -use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; @@ -137,7 +137,7 @@ impl BlockBehaviour for WetSpongeBlock { Vector3::new(0.25, 0.0, 0.25), 0.01, 16, - Particle::Cloud, + particle::Cloud, ); } }) diff --git a/pumpkin/src/command/args/mod.rs b/pumpkin/src/command/args/mod.rs index f168b07bf1..1cf4b943fa 100644 --- a/pumpkin/src/command/args/mod.rs +++ b/pumpkin/src/command/args/mod.rs @@ -5,9 +5,10 @@ use bounded_num::{NotInBounds, Number}; use pumpkin_data::Enchantment; use pumpkin_data::damage::DamageType; use pumpkin_data::effect::StatusEffect; -use pumpkin_data::particle::Particle; use pumpkin_data::sound::SoundCategory; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ + ArgumentType, CommandSuggestion, SerializeParticleData, SuggestionProviders, +}; use pumpkin_util::Difficulty; use pumpkin_util::text::TextComponent; use pumpkin_util::{ @@ -126,7 +127,7 @@ pub enum Arg<'a> { BlockPredicate(&'a str), BossbarColor(BossbarColor), BossbarStyle(BossbarDivisions), - Particle(Particle), + Particle(Arc), Msg(String), TextComponent(TextComponent), Time(i32), diff --git a/pumpkin/src/command/args/resource/particle.rs b/pumpkin/src/command/args/resource/particle.rs index 70fcc5a633..b9b88cb33f 100644 --- a/pumpkin/src/command/args/resource/particle.rs +++ b/pumpkin/src/command/args/resource/particle.rs @@ -1,5 +1,25 @@ -use pumpkin_data::particle::Particle; -use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; +use std::sync::Arc; + +use pumpkin_data::particle::{ + AngryVillager, Ash, Bubble, BubbleColumnUp, BubblePop, CampfireCosySmoke, CampfireSignalSmoke, + CherryLeaves, Cloud, Composter, CopperFireFlame, CrimsonSpore, Crit, CurrentDown, + DamageIndicator, Dolphin, DrippingDripstoneLava, DrippingDripstoneWater, DrippingHoney, + DrippingLava, DrippingObsidianTear, DrippingWater, DustPlume, EggCrack, ElderGuardian, + ElectricSpark, Enchant, EnchantedHit, EndRod, Explosion, ExplosionEmitter, + FallingDripstoneLava, FallingDripstoneWater, FallingHoney, FallingLava, FallingNectar, + FallingObsidianTear, FallingSporeBlossom, FallingWater, Firefly, Firework, Fishing, Flame, + Glow, GlowSquidInk, Gust, GustEmitterLarge, GustEmitterSmall, HappyVillager, Heart, Infested, + ItemCobweb, ItemSlime, ItemSnowball, LandingHoney, LandingLava, LandingObsidianTear, + LargeSmoke, Lava, Mycelium, Nautilus, Note, OminousSpawning, PaleOakLeaves, PauseMobGrowth, + Poof, Portal, RaidOmen, Rain, ResetMobGrowth, ReversePortal, Scrape, SculkChargePop, SculkSoul, + SmallFlame, SmallGust, Smoke, Sneeze, Snowflake, SonicBoom, Soul, SoulFireFlame, Spit, Splash, + SporeBlossomAir, SquidInk, SweepAttack, TotemOfUndying, TrialOmen, TrialSpawnerDetection, + TrialSpawnerDetectionOminous, Underwater, VaultConnection, Vibration, WarpedSpore, WaxOff, + WaxOn, WhiteAsh, WhiteSmoke, Witch, +}; +use pumpkin_protocol::java::client::play::{ + ArgumentType, SerializeParticleData, SuggestionProviders, +}; use crate::command::{ CommandSender, @@ -37,10 +57,7 @@ impl ArgumentConsumer for ParticleArgumentConsumer { let result: Option> = name_opt.map_or_else( || None, - |name| { - Particle::from_name(name.strip_prefix("minecraft:").unwrap_or(name)) - .map(Arg::Particle) - }, + |name| from_name(name.strip_prefix("minecraft:").unwrap_or(name)).map(Arg::Particle), ); Box::pin(async move { result }) @@ -54,12 +71,120 @@ impl DefaultNameArgConsumer for ParticleArgumentConsumer { } impl<'a> FindArg<'a> for ParticleArgumentConsumer { - type Data = &'a Particle; + type Data = Arc; fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result { match args.get(name) { - Some(Arg::Particle(data)) => Ok(data), + Some(Arg::Particle(data)) => Ok(data.clone()), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), } } } + +#[allow(clippy::too_many_lines)] +#[must_use] +pub fn from_name(name: &str) -> Option> { + match name { + "angry_villager" => Some(Arc::new(AngryVillager)), + "bubble" => Some(Arc::new(Bubble)), + "cloud" => Some(Arc::new(Cloud)), + "copper_fire_flame" => Some(Arc::new(CopperFireFlame)), + "crit" => Some(Arc::new(Crit)), + "damage_indicator" => Some(Arc::new(DamageIndicator)), + "dripping_lava" => Some(Arc::new(DrippingLava)), + "falling_lava" => Some(Arc::new(FallingLava)), + "landing_lava" => Some(Arc::new(LandingLava)), + "dripping_water" => Some(Arc::new(DrippingWater)), + "falling_water" => Some(Arc::new(FallingWater)), + "elder_guardian" => Some(Arc::new(ElderGuardian)), + "enchanted_hit" => Some(Arc::new(EnchantedHit)), + "enchant" => Some(Arc::new(Enchant)), + "end_rod" => Some(Arc::new(EndRod)), + "explosion_emitter" => Some(Arc::new(ExplosionEmitter)), + "explosion" => Some(Arc::new(Explosion)), + "gust" => Some(Arc::new(Gust)), + "small_gust" => Some(Arc::new(SmallGust)), + "gust_emitter_large" => Some(Arc::new(GustEmitterLarge)), + "gust_emitter_small" => Some(Arc::new(GustEmitterSmall)), + "sonic_boom" => Some(Arc::new(SonicBoom)), + "firework" => Some(Arc::new(Firework)), + "fishing" => Some(Arc::new(Fishing)), + "flame" => Some(Arc::new(Flame)), + "infested" => Some(Arc::new(Infested)), + "cherry_leaves" => Some(Arc::new(CherryLeaves)), + "pale_oak_leaves" => Some(Arc::new(PaleOakLeaves)), + "sculk_soul" => Some(Arc::new(SculkSoul)), + "sculk_charge_pop" => Some(Arc::new(SculkChargePop)), + "soul_fire_flame" => Some(Arc::new(SoulFireFlame)), + "soul" => Some(Arc::new(Soul)), + "happy_villager" => Some(Arc::new(HappyVillager)), + "composter" => Some(Arc::new(Composter)), + "heart" => Some(Arc::new(Heart)), + "vibration" => Some(Arc::new(Vibration)), + "pause_mob_growth" => Some(Arc::new(PauseMobGrowth)), + "reset_mob_growth" => Some(Arc::new(ResetMobGrowth)), + "item_slime" => Some(Arc::new(ItemSlime)), + "item_cobweb" => Some(Arc::new(ItemCobweb)), + "item_snowball" => Some(Arc::new(ItemSnowball)), + "large_smoke" => Some(Arc::new(LargeSmoke)), + "lava" => Some(Arc::new(Lava)), + "mycelium" => Some(Arc::new(Mycelium)), + "note" => Some(Arc::new(Note)), + "poof" => Some(Arc::new(Poof)), + "portal" => Some(Arc::new(Portal)), + "rain" => Some(Arc::new(Rain)), + "smoke" => Some(Arc::new(Smoke)), + "white_smoke" => Some(Arc::new(WhiteSmoke)), + "sneeze" => Some(Arc::new(Sneeze)), + "spit" => Some(Arc::new(Spit)), + "squid_ink" => Some(Arc::new(SquidInk)), + "sweep_attack" => Some(Arc::new(SweepAttack)), + "totem_of_undying" => Some(Arc::new(TotemOfUndying)), + "underwater" => Some(Arc::new(Underwater)), + "splash" => Some(Arc::new(Splash)), + "witch" => Some(Arc::new(Witch)), + "bubble_pop" => Some(Arc::new(BubblePop)), + "current_down" => Some(Arc::new(CurrentDown)), + "bubble_column_up" => Some(Arc::new(BubbleColumnUp)), + "nautilus" => Some(Arc::new(Nautilus)), + "dolphin" => Some(Arc::new(Dolphin)), + "campfire_cosy_smoke" => Some(Arc::new(CampfireCosySmoke)), + "campfire_signal_smoke" => Some(Arc::new(CampfireSignalSmoke)), + "dripping_honey" => Some(Arc::new(DrippingHoney)), + "falling_honey" => Some(Arc::new(FallingHoney)), + "landing_honey" => Some(Arc::new(LandingHoney)), + "falling_nectar" => Some(Arc::new(FallingNectar)), + "falling_spore_blossom" => Some(Arc::new(FallingSporeBlossom)), + "ash" => Some(Arc::new(Ash)), + "crimson_spore" => Some(Arc::new(CrimsonSpore)), + "warped_spore" => Some(Arc::new(WarpedSpore)), + "spore_blossom_air" => Some(Arc::new(SporeBlossomAir)), + "dripping_obsidian_tear" => Some(Arc::new(DrippingObsidianTear)), + "falling_obsidian_tear" => Some(Arc::new(FallingObsidianTear)), + "landing_obsidian_tear" => Some(Arc::new(LandingObsidianTear)), + "reverse_portal" => Some(Arc::new(ReversePortal)), + "white_ash" => Some(Arc::new(WhiteAsh)), + "small_flame" => Some(Arc::new(SmallFlame)), + "snowflake" => Some(Arc::new(Snowflake)), + "dripping_dripstone_lava" => Some(Arc::new(DrippingDripstoneLava)), + "falling_dripstone_lava" => Some(Arc::new(FallingDripstoneLava)), + "dripping_dripstone_water" => Some(Arc::new(DrippingDripstoneWater)), + "falling_dripstone_water" => Some(Arc::new(FallingDripstoneWater)), + "glow_squid_ink" => Some(Arc::new(GlowSquidInk)), + "glow" => Some(Arc::new(Glow)), + "wax_on" => Some(Arc::new(WaxOn)), + "wax_off" => Some(Arc::new(WaxOff)), + "electric_spark" => Some(Arc::new(ElectricSpark)), + "scrape" => Some(Arc::new(Scrape)), + "egg_crack" => Some(Arc::new(EggCrack)), + "dust_plume" => Some(Arc::new(DustPlume)), + "trial_spawner_detection" => Some(Arc::new(TrialSpawnerDetection)), + "trial_spawner_detection_ominous" => Some(Arc::new(TrialSpawnerDetectionOminous)), + "vault_connection" => Some(Arc::new(VaultConnection)), + "ominous_spawning" => Some(Arc::new(OminousSpawning)), + "raid_omen" => Some(Arc::new(RaidOmen)), + "trial_omen" => Some(Arc::new(TrialOmen)), + "firefly" => Some(Arc::new(Firefly)), + _ => None, + } +} diff --git a/pumpkin/src/command/commands/particle.rs b/pumpkin/src/command/commands/particle.rs index dbbf44177c..67139862f6 100644 --- a/pumpkin/src/command/commands/particle.rs +++ b/pumpkin/src/command/commands/particle.rs @@ -70,7 +70,7 @@ impl CommandExecutor for Executor { } }; - world.spawn_particle(pos, delta, speed, count, *particle); + world.spawn_particle(pos, delta, speed, count, &*particle); sender .send_message(TextComponent::translate_cross( diff --git a/pumpkin/src/entity/area_effect_cloud.rs b/pumpkin/src/entity/area_effect_cloud.rs index 53f568196e..32dfeee2e3 100644 --- a/pumpkin/src/entity/area_effect_cloud.rs +++ b/pumpkin/src/entity/area_effect_cloud.rs @@ -5,7 +5,8 @@ use crate::{ entity::{Entity, EntityBase, EntityBaseFuture, NBTStorage}, server::Server, }; -use pumpkin_data::effect::StatusEffect; +use pumpkin_data::{effect::StatusEffect, particle}; +use pumpkin_protocol::java::client::play::SerializeParticleData; use pumpkin_util::math::boundingbox::BoundingBox; use pumpkin_util::math::vector3::Vector3; @@ -167,7 +168,7 @@ impl EntityBase for AreaEffectCloudEntity { let meta = ParticleMeta { particle_id: pumpkin_protocol::codec::var_int::VarInt( - pumpkin_data::particle::Particle::EntityEffect as i32, + particle::EntityEffect::new(color).id(), ), data: &data_bytes, }; diff --git a/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs b/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs index a2a576595f..85f8802a93 100644 --- a/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs +++ b/pumpkin/src/entity/boss/ender_dragon/phase/dying.rs @@ -2,7 +2,7 @@ use super::EnderDragonPhase; use crate::entity::boss::ender_dragon::{DEATH_TIMER_MAX, EnderDragonEntity}; use crate::entity::experience_orb::ExperienceOrbEntity; use futures::future::BoxFuture; -use pumpkin_data::particle::Particle; +use pumpkin_data::particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::math::vector3::Vector3; @@ -49,7 +49,7 @@ impl super::Phase for DyingPhase { Vector3::new(0.0, 0.0, 0.0), 0.0, 1, - Particle::ExplosionEmitter, + particle::ExplosionEmitter, ); } diff --git a/pumpkin/src/entity/combat.rs b/pumpkin/src/entity/combat.rs index f85295ee29..3e974f22c9 100644 --- a/pumpkin/src/entity/combat.rs +++ b/pumpkin/src/entity/combat.rs @@ -2,7 +2,7 @@ use std::sync::atomic::Ordering; use crate::entity::EntityBase; use pumpkin_data::{ - particle::Particle, + particle, sound::{Sound, SoundCategory}, }; use pumpkin_util::math::vector3::Vector3; @@ -86,7 +86,7 @@ pub fn spawn_sweep_particle(attacker_entity: &Entity, world: &World, pos: &Vecto Vector3::new(0.0, 0.0, 0.0), 0.0, 0, - Particle::SweepAttack, + particle::SweepAttack, ); } diff --git a/pumpkin/src/entity/decoration/armor_stand.rs b/pumpkin/src/entity/decoration/armor_stand.rs index f51c8e5d7b..b8c79679a5 100644 --- a/pumpkin/src/entity/decoration/armor_stand.rs +++ b/pumpkin/src/entity/decoration/armor_stand.rs @@ -5,12 +5,12 @@ use crate::entity::{ }; use crossbeam::atomic::AtomicCell; use pumpkin_data::item_stack::ItemStack; +use pumpkin_data::particle; use pumpkin_data::{ damage::DamageType, data_component_impl::{EquipmentSlot, EquipmentType}, entity::EntityStatus, item::Item, - particle::Particle, sound::{Sound, SoundCategory}, }; use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; @@ -214,7 +214,6 @@ impl ArmorStandEntity { } /// Spawns break particles at the armor stand's position. - // TODO: use oak plank block particles like vanilla (requires block state data in particle system) fn spawn_break_particles(entity: &Entity) { let world = entity.world.load(); let pos = entity.pos.load(); @@ -227,7 +226,7 @@ impl ArmorStandEntity { Vector3::new(width / 4.0, height / 4.0, width / 4.0), 0.05, 10, - Particle::Poof, + particle::Block::new(pumpkin_data::Block::OAK_PLANKS.default_state.id as i32), ); } } diff --git a/pumpkin/src/entity/mob/enderman.rs b/pumpkin/src/entity/mob/enderman.rs index c64530721c..03e16ab7b3 100644 --- a/pumpkin/src/entity/mob/enderman.rs +++ b/pumpkin/src/entity/mob/enderman.rs @@ -5,7 +5,7 @@ use std::sync::{ use crate::entity::attributes::Modifier; use crate::entity::attributes::ModifierOperation; -use pumpkin_data::attributes::Attributes; +use pumpkin_data::{attributes::Attributes, particle}; use std::sync::LazyLock; use uuid::Uuid; @@ -16,7 +16,6 @@ use pumpkin_data::{ entity::EntityType, item::Item, meta_data_type::MetaDataType, - particle::Particle, sound::{Sound, SoundCategory}, tag, tag::Taggable, @@ -214,7 +213,7 @@ impl EndermanEntity { Vector3::new(0.0, 0.0, 0.0), 0.0, 128, - Particle::Portal, + particle::Portal, ); world.play_sound(Sound::EntityEndermanTeleport, SoundCategory::Hostile, pos); } diff --git a/pumpkin/src/entity/passive/chicken.rs b/pumpkin/src/entity/passive/chicken.rs index 05597b292a..e45c7a3813 100644 --- a/pumpkin/src/entity/passive/chicken.rs +++ b/pumpkin/src/entity/passive/chicken.rs @@ -3,10 +3,9 @@ use std::sync::{ atomic::{AtomicI32, Ordering, Ordering::Relaxed}, }; -use pumpkin_data::item_stack::ItemStack; -use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::{entity::EntityType, item::Item}; +use pumpkin_data::{item_stack::ItemStack, particle}; use pumpkin_util::math::vector3::Vector3; use rand::RngExt; @@ -132,7 +131,7 @@ impl Mob for ChickenEntity { Vector3::new(0.5, 0.5, 0.5), 1.0, 7, - Particle::Heart, + particle::Heart, ); world.play_sound( Sound::EntityChickenAmbient, diff --git a/pumpkin/src/entity/passive/cow.rs b/pumpkin/src/entity/passive/cow.rs index dd537a6419..d353f7cc0a 100644 --- a/pumpkin/src/entity/passive/cow.rs +++ b/pumpkin/src/entity/passive/cow.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Weak}; use pumpkin_data::item_stack::ItemStack; -use pumpkin_data::particle::Particle; +use pumpkin_data::particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::{entity::EntityType, item::Item}; use pumpkin_util::math::vector3::Vector3; @@ -93,7 +93,7 @@ impl Mob for CowEntity { Vector3::new(0.5, 0.5, 0.5), 1.0, 7, - Particle::Heart, + particle::Heart, ); world.play_sound( Sound::EntityCowAmbient, diff --git a/pumpkin/src/entity/passive/pig.rs b/pumpkin/src/entity/passive/pig.rs index 4fbaf337c9..0204bb7402 100644 --- a/pumpkin/src/entity/passive/pig.rs +++ b/pumpkin/src/entity/passive/pig.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Weak}; use pumpkin_data::item_stack::ItemStack; -use pumpkin_data::particle::Particle; +use pumpkin_data::particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::{entity::EntityType, item::Item}; use pumpkin_util::math::vector3::Vector3; @@ -98,7 +98,7 @@ impl Mob for PigEntity { Vector3::new(0.5, 0.5, 0.5), 1.0, 7, - Particle::Heart, + particle::Heart, ); world.play_sound( Sound::EntityPigAmbient, diff --git a/pumpkin/src/entity/passive/sheep.rs b/pumpkin/src/entity/passive/sheep.rs index ef21d3a2a3..c581e6ce03 100644 --- a/pumpkin/src/entity/passive/sheep.rs +++ b/pumpkin/src/entity/passive/sheep.rs @@ -4,7 +4,8 @@ use std::sync::{ }; use pumpkin_data::{ - entity::EntityType, item::Item, meta_data_type::MetaDataType, tracked_data::TrackedData, + entity::EntityType, item::Item, meta_data_type::MetaDataType, particle, + tracked_data::TrackedData, }; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::java::client::play::Metadata; @@ -22,7 +23,6 @@ use crate::entity::{ }; use pumpkin_data::item_stack::ItemStack; -use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::math::vector3::Vector3; @@ -161,7 +161,7 @@ impl Mob for SheepEntity { Vector3::new(0.5, 0.5, 0.5), 1.0, 7, - Particle::Heart, + particle::Heart, ); world.play_sound( Sound::EntitySheepAmbient, diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index b66c4066e1..4c840c38fe 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -41,7 +41,6 @@ use pumpkin_data::data_component_impl::{EquipmentSlot, EquippableImpl, ToolImpl, use pumpkin_data::effect::StatusEffect; use pumpkin_data::entity::{EntityPose, EntityStatus, EntityType}; use pumpkin_data::item_stack::ItemStack; -use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::statistic::StatisticCategory; use pumpkin_data::tag::Taggable; @@ -1744,7 +1743,8 @@ impl Player { offset: Vector3, max_speed: f32, particle_count: i32, - particle: Particle, + id: VarInt, + data: &[u8], ) { self.client.try_enqueue_packet(&CParticle::new( false, @@ -1753,8 +1753,8 @@ impl Player { offset, max_speed, particle_count, - VarInt(particle as i32), - &[], + id, + data, )); } diff --git a/pumpkin/src/entity/projectile/arrow.rs b/pumpkin/src/entity/projectile/arrow.rs index 145e60ffee..aef33463d8 100644 --- a/pumpkin/src/entity/projectile/arrow.rs +++ b/pumpkin/src/entity/projectile/arrow.rs @@ -11,7 +11,7 @@ use crate::{ use pumpkin_data::damage::DamageType; use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; -use pumpkin_data::particle::Particle; +use pumpkin_data::particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_protocol::IdOr; use pumpkin_protocol::java::client::play::CEntityVelocity; @@ -250,7 +250,7 @@ impl EntityBase for ArrowEntity { Vector3::new(0.0f32, 0.0f32, 0.0f32), 0.0, 1, - Particle::Crit, + particle::Crit, ); } diff --git a/pumpkin/src/entity/projectile/ender_pearl.rs b/pumpkin/src/entity/projectile/ender_pearl.rs index 3d69faf484..2a88abe834 100644 --- a/pumpkin/src/entity/projectile/ender_pearl.rs +++ b/pumpkin/src/entity/projectile/ender_pearl.rs @@ -11,7 +11,7 @@ use crate::{ }; use pumpkin_data::damage::DamageType; use pumpkin_data::entity::EntityStatus; -use pumpkin_data::particle::Particle; +use pumpkin_data::particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_util::math::vector3::Vector3; @@ -123,7 +123,7 @@ impl EntityBase for EnderPearlEntity { Vector3::new(speed.x as f32, speed.y as f32, speed.z as f32), 1.0, 1, - Particle::Portal, + particle::Portal, ); } diff --git a/pumpkin/src/entity/projectile/fishing_bobber.rs b/pumpkin/src/entity/projectile/fishing_bobber.rs index 44666380b1..9219791e45 100644 --- a/pumpkin/src/entity/projectile/fishing_bobber.rs +++ b/pumpkin/src/entity/projectile/fishing_bobber.rs @@ -10,6 +10,7 @@ use crate::{ }; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::meta_data_type::MetaDataType; +use pumpkin_data::particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tracked_data::TrackedData; use pumpkin_protocol::java::client::play::Metadata; @@ -131,7 +132,7 @@ impl FishingBobberEntity { Vector3::new(0.1f32, 0.1f32, 0.1f32), 0.0, 5, - pumpkin_data::particle::Particle::Bubble, + particle::Bubble, ); } } else { diff --git a/pumpkin/src/entity/projectile/shulker_bullet.rs b/pumpkin/src/entity/projectile/shulker_bullet.rs index c9e99e57e2..70b3591750 100644 --- a/pumpkin/src/entity/projectile/shulker_bullet.rs +++ b/pumpkin/src/entity/projectile/shulker_bullet.rs @@ -5,7 +5,7 @@ use crossbeam::atomic::AtomicCell; use pumpkin_data::damage::DamageType; use pumpkin_data::effect::StatusEffect; use pumpkin_data::entity::EntityType; -use pumpkin_data::particle::Particle; +use pumpkin_data::particle; use pumpkin_data::potion::Effect; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_protocol::java::client::play::{CEntityPositionSync, CEntityVelocity}; @@ -320,7 +320,7 @@ impl EntityBase for ShulkerBulletEntity { Vector3::new(0.2, 0.2, 0.2), 0.0, 2, - Particle::Explosion, + particle::Explosion, ); entity.remove().await; true @@ -356,7 +356,7 @@ impl EntityBase for ShulkerBulletEntity { Vector3::new(0.2, 0.2, 0.2), 0.0, 2, - Particle::Explosion, + particle::Explosion, ); entity.remove().await; } @@ -461,7 +461,7 @@ impl EntityBase for ShulkerBulletEntity { Vector3::new(0.2, 0.2, 0.2), 0.0, 2, - Particle::Explosion, + particle::Explosion, ); entity.remove().await; } @@ -540,7 +540,7 @@ impl EntityBase for ShulkerBulletEntity { Vector3::new(0.2, 0.2, 0.2), 0.0, 2, - Particle::Explosion, + particle::Explosion, ); entity.remove().await; break; diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/args.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/args.rs index a713d3ffd5..787d03af4e 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/args.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/args.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, sync::Arc}; +use pumpkin_protocol::java::client::play::SerializeParticleData; use pumpkin_util::text::TextComponent; use crate::{command::tree::CommandTree, entity::player::Player}; @@ -24,7 +25,7 @@ pub enum OwnedArg { BlockPredicate(String), BossbarColor(crate::world::bossbar::BossbarColor), BossbarStyle(crate::world::bossbar::BossbarDivisions), - Particle(pumpkin_data::particle::Particle), + Particle(Arc), Msg(String), TextComponent(TextComponent), Time(i32), @@ -66,7 +67,7 @@ impl OwnedArg { Arg::BlockPredicate(s) => Self::BlockPredicate(s.to_string()), Arg::BossbarColor(c) => Self::BossbarColor(*c), Arg::BossbarStyle(s) => Self::BossbarStyle(*s), - Arg::Particle(p) => Self::Particle(*p), + Arg::Particle(p) => Self::Particle(p.clone()), Arg::Msg(m) => Self::Msg(m.clone()), Arg::TextComponent(t) => Self::TextComponent(t.clone()), Arg::Time(t) => Self::Time(*t), diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/mod.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/mod.rs index cf7f25107a..12fd7a78ef 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/mod.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/mod.rs @@ -20,6 +20,7 @@ pub mod i18n; pub mod item_stack; pub mod java_dialogs; pub mod logging; +pub mod particle; pub mod permission; pub mod player; pub mod recipe; @@ -34,7 +35,7 @@ bindgen!({ path: "../pumpkin-plugin-wit/v0.1", world: "plugin", imports: { default: async | trappable }, - exports: { default: async | trappable}, + exports: { default: async | trappable }, }); impl pumpkin::plugin::java_packets::Host for PluginHostState {} diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/particle.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/particle.rs new file mode 100644 index 0000000000..156d52d179 --- /dev/null +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/particle.rs @@ -0,0 +1,2432 @@ +/* This file is generated. Do not edit manually. */ +#[allow(clippy::wildcard_imports)] +use crate::plugin::loader::wasm::wasm_host::state::*; +use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::Position; +#[allow(clippy::wildcard_imports)] +use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::particles::*; +use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::World; +use wasmtime::component::Resource; +impl Host for PluginHostState { + async fn spawn_angry_villager_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::AngryVillager, + ); + Ok(()) + } + async fn spawn_block_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_block_marker_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_bubble_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Bubble, + ); + Ok(()) + } + async fn spawn_cloud_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Cloud, + ); + Ok(()) + } + async fn spawn_copper_fire_flame_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::CopperFireFlame, + ); + Ok(()) + } + async fn spawn_crit_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Crit, + ); + Ok(()) + } + async fn spawn_damage_indicator_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::DamageIndicator, + ); + Ok(()) + } + async fn spawn_dragon_breath_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_dripping_lava_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::DrippingLava, + ); + Ok(()) + } + async fn spawn_falling_lava_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::FallingLava, + ); + Ok(()) + } + async fn spawn_landing_lava_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::LandingLava, + ); + Ok(()) + } + async fn spawn_dripping_water_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::DrippingWater, + ); + Ok(()) + } + async fn spawn_falling_water_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::FallingWater, + ); + Ok(()) + } + async fn spawn_dust_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_dust_color_transition_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_effect_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_elder_guardian_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::ElderGuardian, + ); + Ok(()) + } + async fn spawn_enchanted_hit_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::EnchantedHit, + ); + Ok(()) + } + async fn spawn_enchant_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Enchant, + ); + Ok(()) + } + async fn spawn_end_rod_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::EndRod, + ); + Ok(()) + } + async fn spawn_entity_effect_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_explosion_emitter_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::ExplosionEmitter, + ); + Ok(()) + } + async fn spawn_explosion_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Explosion, + ); + Ok(()) + } + async fn spawn_gust_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Gust, + ); + Ok(()) + } + async fn spawn_small_gust_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::SmallGust, + ); + Ok(()) + } + async fn spawn_gust_emitter_large_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::GustEmitterLarge, + ); + Ok(()) + } + async fn spawn_gust_emitter_small_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::GustEmitterSmall, + ); + Ok(()) + } + async fn spawn_sonic_boom_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::SonicBoom, + ); + Ok(()) + } + async fn spawn_falling_dust_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_firework_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Firework, + ); + Ok(()) + } + async fn spawn_fishing_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Fishing, + ); + Ok(()) + } + async fn spawn_flame_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Flame, + ); + Ok(()) + } + async fn spawn_infested_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Infested, + ); + Ok(()) + } + async fn spawn_cherry_leaves_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::CherryLeaves, + ); + Ok(()) + } + async fn spawn_pale_oak_leaves_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::PaleOakLeaves, + ); + Ok(()) + } + async fn spawn_tinted_leaves_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_sculk_soul_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::SculkSoul, + ); + Ok(()) + } + async fn spawn_sculk_charge_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_sculk_charge_pop_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::SculkChargePop, + ); + Ok(()) + } + async fn spawn_soul_fire_flame_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::SoulFireFlame, + ); + Ok(()) + } + async fn spawn_soul_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Soul, + ); + Ok(()) + } + async fn spawn_flash_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_happy_villager_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::HappyVillager, + ); + Ok(()) + } + async fn spawn_composter_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Composter, + ); + Ok(()) + } + async fn spawn_heart_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Heart, + ); + Ok(()) + } + async fn spawn_instant_effect_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_item_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Item, + ); + Ok(()) + } + async fn spawn_vibration_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Vibration, + ); + Ok(()) + } + async fn spawn_trail_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_pause_mob_growth_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::PauseMobGrowth, + ); + Ok(()) + } + async fn spawn_reset_mob_growth_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::ResetMobGrowth, + ); + Ok(()) + } + async fn spawn_item_slime_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::ItemSlime, + ); + Ok(()) + } + async fn spawn_item_cobweb_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::ItemCobweb, + ); + Ok(()) + } + async fn spawn_item_snowball_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::ItemSnowball, + ); + Ok(()) + } + async fn spawn_large_smoke_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::LargeSmoke, + ); + Ok(()) + } + async fn spawn_lava_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Lava, + ); + Ok(()) + } + async fn spawn_mycelium_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Mycelium, + ); + Ok(()) + } + async fn spawn_note_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Note, + ); + Ok(()) + } + async fn spawn_poof_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Poof, + ); + Ok(()) + } + async fn spawn_portal_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Portal, + ); + Ok(()) + } + async fn spawn_rain_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Rain, + ); + Ok(()) + } + async fn spawn_smoke_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Smoke, + ); + Ok(()) + } + async fn spawn_white_smoke_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::WhiteSmoke, + ); + Ok(()) + } + async fn spawn_sneeze_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Sneeze, + ); + Ok(()) + } + async fn spawn_spit_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Spit, + ); + Ok(()) + } + async fn spawn_squid_ink_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::SquidInk, + ); + Ok(()) + } + async fn spawn_sweep_attack_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::SweepAttack, + ); + Ok(()) + } + async fn spawn_totem_of_undying_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::TotemOfUndying, + ); + Ok(()) + } + async fn spawn_underwater_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Underwater, + ); + Ok(()) + } + async fn spawn_splash_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Splash, + ); + Ok(()) + } + async fn spawn_witch_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Witch, + ); + Ok(()) + } + async fn spawn_bubble_pop_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::BubblePop, + ); + Ok(()) + } + async fn spawn_current_down_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::CurrentDown, + ); + Ok(()) + } + async fn spawn_bubble_column_up_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::BubbleColumnUp, + ); + Ok(()) + } + async fn spawn_nautilus_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Nautilus, + ); + Ok(()) + } + async fn spawn_dolphin_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Dolphin, + ); + Ok(()) + } + async fn spawn_campfire_cosy_smoke_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::CampfireCosySmoke, + ); + Ok(()) + } + async fn spawn_campfire_signal_smoke_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::CampfireSignalSmoke, + ); + Ok(()) + } + async fn spawn_dripping_honey_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::DrippingHoney, + ); + Ok(()) + } + async fn spawn_falling_honey_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::FallingHoney, + ); + Ok(()) + } + async fn spawn_landing_honey_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::LandingHoney, + ); + Ok(()) + } + async fn spawn_falling_nectar_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::FallingNectar, + ); + Ok(()) + } + async fn spawn_falling_spore_blossom_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::FallingSporeBlossom, + ); + Ok(()) + } + async fn spawn_ash_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Ash, + ); + Ok(()) + } + async fn spawn_crimson_spore_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::CrimsonSpore, + ); + Ok(()) + } + async fn spawn_warped_spore_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::WarpedSpore, + ); + Ok(()) + } + async fn spawn_spore_blossom_air_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::SporeBlossomAir, + ); + Ok(()) + } + async fn spawn_dripping_obsidian_tear_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::DrippingObsidianTear, + ); + Ok(()) + } + async fn spawn_falling_obsidian_tear_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::FallingObsidianTear, + ); + Ok(()) + } + async fn spawn_landing_obsidian_tear_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::LandingObsidianTear, + ); + Ok(()) + } + async fn spawn_reverse_portal_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::ReversePortal, + ); + Ok(()) + } + async fn spawn_white_ash_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::WhiteAsh, + ); + Ok(()) + } + async fn spawn_small_flame_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::SmallFlame, + ); + Ok(()) + } + async fn spawn_snowflake_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Snowflake, + ); + Ok(()) + } + async fn spawn_dripping_dripstone_lava_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::DrippingDripstoneLava, + ); + Ok(()) + } + async fn spawn_falling_dripstone_lava_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::FallingDripstoneLava, + ); + Ok(()) + } + async fn spawn_dripping_dripstone_water_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::DrippingDripstoneWater, + ); + Ok(()) + } + async fn spawn_falling_dripstone_water_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::FallingDripstoneWater, + ); + Ok(()) + } + async fn spawn_glow_squid_ink_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::GlowSquidInk, + ); + Ok(()) + } + async fn spawn_glow_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Glow, + ); + Ok(()) + } + async fn spawn_wax_on_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::WaxOn, + ); + Ok(()) + } + async fn spawn_wax_off_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::WaxOff, + ); + Ok(()) + } + async fn spawn_electric_spark_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::ElectricSpark, + ); + Ok(()) + } + async fn spawn_scrape_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Scrape, + ); + Ok(()) + } + async fn spawn_shriek_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_egg_crack_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::EggCrack, + ); + Ok(()) + } + async fn spawn_dust_plume_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::DustPlume, + ); + Ok(()) + } + async fn spawn_trial_spawner_detection_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::TrialSpawnerDetection, + ); + Ok(()) + } + async fn spawn_trial_spawner_detection_ominous_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::TrialSpawnerDetectionOminous, + ); + Ok(()) + } + async fn spawn_vault_connection_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::VaultConnection, + ); + Ok(()) + } + async fn spawn_dust_pillar_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_ominous_spawning_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::OminousSpawning, + ); + Ok(()) + } + async fn spawn_raid_omen_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::RaidOmen, + ); + Ok(()) + } + async fn spawn_trial_omen_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::TrialOmen, + ); + Ok(()) + } + async fn spawn_block_crumble_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + particle: Resource, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + self.resource_table + .get::(&Resource::new_own(particle.rep()))? + .provider, + ); + Ok(()) + } + async fn spawn_firefly_particle( + &mut self, + world: Resource, + position: Position, + offset: Offset, + max_speed: f32, + particle_count: i32, + ) -> wasmtime::Result<()> { + let world = self.get_world_res(&world)?; + world.provider.spawn_particle( + position.into(), + offset.into(), + max_speed, + particle_count, + pumpkin_data::particle::Firefly, + ); + Ok(()) + } +} +pub type BlockParticleResource = WasmResource; +impl HostBlock for PluginHostState { + async fn new(&mut self, state: i32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::Block::new(state); + let resource = self + .resource_table + .push(BlockParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type BlockMarkerParticleResource = WasmResource; +impl HostBlockMarker for PluginHostState { + async fn new(&mut self, state: i32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::BlockMarker::new(state); + let resource = self + .resource_table + .push(BlockMarkerParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type DragonBreathParticleResource = WasmResource; +impl HostDragonBreath for PluginHostState { + async fn new(&mut self, power: f32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::DragonBreath::new(power); + let resource = self + .resource_table + .push(DragonBreathParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type DustParticleResource = WasmResource; +impl HostDust for PluginHostState { + async fn new(&mut self, color: i32, scale: f32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::Dust::new(color, scale); + let resource = self + .resource_table + .push(DustParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type DustColorTransitionParticleResource = + WasmResource; +impl HostDustColorTransition for PluginHostState { + async fn new( + &mut self, + from_color: i32, + to_color: i32, + scale: f32, + ) -> wasmtime::Result> { + let provider = + pumpkin_data::particle::DustColorTransition::new(from_color, to_color, scale); + let resource = self + .resource_table + .push(DustColorTransitionParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type EffectParticleResource = WasmResource; +impl HostEffect for PluginHostState { + async fn new(&mut self, color: i32, power: f32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::Effect::new(color, power); + let resource = self + .resource_table + .push(EffectParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type EntityEffectParticleResource = WasmResource; +impl HostEntityEffect for PluginHostState { + async fn new(&mut self, color: i32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::EntityEffect::new(color); + let resource = self + .resource_table + .push(EntityEffectParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type FallingDustParticleResource = WasmResource; +impl HostFallingDust for PluginHostState { + async fn new(&mut self, state: i32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::FallingDust::new(state); + let resource = self + .resource_table + .push(FallingDustParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type TintedLeavesParticleResource = WasmResource; +impl HostTintedLeaves for PluginHostState { + async fn new(&mut self, color: i32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::TintedLeaves::new(color); + let resource = self + .resource_table + .push(TintedLeavesParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type SculkChargeParticleResource = WasmResource; +impl HostSculkCharge for PluginHostState { + async fn new(&mut self, roll: f32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::SculkCharge::new(roll); + let resource = self + .resource_table + .push(SculkChargeParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type FlashParticleResource = WasmResource; +impl HostFlash for PluginHostState { + async fn new(&mut self, color: i32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::Flash::new(color); + let resource = self + .resource_table + .push(FlashParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type InstantEffectParticleResource = WasmResource; +impl HostInstantEffect for PluginHostState { + async fn new(&mut self, color: i32, power: f32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::InstantEffect::new(color, power); + let resource = self + .resource_table + .push(InstantEffectParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type TrailParticleResource = WasmResource; +impl HostTrail for PluginHostState { + async fn new( + &mut self, + target: Position, + color: i32, + duration: i32, + ) -> wasmtime::Result> { + let provider = pumpkin_data::particle::Trail::new(target.into(), color, duration); + let resource = self + .resource_table + .push(TrailParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type ShriekParticleResource = WasmResource; +impl HostShriek for PluginHostState { + async fn new(&mut self, delay: i32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::Shriek::new(delay); + let resource = self + .resource_table + .push(ShriekParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type DustPillarParticleResource = WasmResource; +impl HostDustPillar for PluginHostState { + async fn new(&mut self, state: i32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::DustPillar::new(state); + let resource = self + .resource_table + .push(DustPillarParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} +pub type BlockCrumbleParticleResource = WasmResource; +impl HostBlockCrumble for PluginHostState { + async fn new(&mut self, state: i32) -> wasmtime::Result> { + let provider = pumpkin_data::particle::BlockCrumble::new(state); + let resource = self + .resource_table + .push(BlockCrumbleParticleResource { provider })?; + Ok(Resource::new_own(resource.rep())) + } + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + self.resource_table + .delete::(Resource::new_own(rep.rep())) + .map_err(wasmtime::Error::from)?; + Ok(()) + } +} diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs index cd65d98f9a..79eee606b2 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs @@ -83,7 +83,7 @@ pub(crate) const fn to_wit_bounding_box( // --- Trapping Helpers --- impl PluginHostState { - fn get_world_res(&self, res: &Resource) -> wasmtime::Result<&WorldResource> { + pub(super) fn get_world_res(&self, res: &Resource) -> wasmtime::Result<&WorldResource> { self.resource_table .get::(&Resource::new_own(res.rep())) .map_err(wasmtime::Error::from) @@ -193,7 +193,6 @@ impl PluginHostState { } impl pumpkin::plugin::world::Host for PluginHostState {} -impl pumpkin::plugin::particles::Host for PluginHostState {} impl pumpkin::plugin::sounds::Host for PluginHostState {} impl pumpkin::plugin::world::HostWorld for PluginHostState { @@ -500,34 +499,6 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState { Ok(()) } - async fn spawn_particle( - &mut self, - world: Resource, - particle: pumpkin::plugin::particles::Particle, - pos: pumpkin::plugin::common::Position, - offset: pumpkin::plugin::common::Position, - max_speed: f32, - count: i32, - ) -> wasmtime::Result<()> { - let world_ref = self.get_world_res(&world)?; - let particle_name = format!("{particle:?}").to_lowercase().replace('_', "-"); - let particle_data = pumpkin_data::particle::Particle::from_name(&particle_name) - .ok_or_else(|| wasmtime::Error::msg(format!("Unknown particle: {particle_name}")))?; - - world_ref.provider.spawn_particle( - pumpkin_util::math::vector3::Vector3::new(pos.0, pos.1, pos.2), - pumpkin_util::math::vector3::Vector3::new( - offset.0 as f32, - offset.1 as f32, - offset.2 as f32, - ), - max_speed, - count, - particle_data, - ); - Ok(()) - } - async fn create_explosion( &mut self, world: Resource, diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index e1c65865d5..6f351de659 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -65,12 +65,11 @@ use pumpkin_data::{ entity::{EntityStatus, EntityType}, fluid::Fluid, item_stack::ItemStack, - particle::Particle, sound::{Sound, SoundCategory}, sound_id_remap::remap_sound_id_for_version, world::{RAW, WorldEvent}, }; -use pumpkin_data::{BlockDirection, BlockState, translation}; +use pumpkin_data::{BlockDirection, BlockState, particle, translation}; use pumpkin_inventory::crafting::recipe_provider::RecipeProvider; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed}; @@ -79,7 +78,7 @@ use pumpkin_protocol::bedrock::client::start_game::{CStartGame, ServerTelemetryD use pumpkin_protocol::bedrock::frame_set::FrameSet; use pumpkin_protocol::java::client::play::{ CBlockUpdate, CChunkBatchEnd, CChunkBatchStart, CChunkData, CDisguisedChatMessage, CExplosion, - CRespawn, CSetBlockDestroyStage, CWorldEvent, + CRespawn, CSetBlockDestroyStage, CWorldEvent, SerializeParticleData, }; use pumpkin_protocol::java::client::play::{ CPlayerSpawnPosition, CRecipeBookAdd, CRecipeBookSettings, CSystemChatMessage, @@ -752,16 +751,27 @@ impl World { Self::broadcast_java_grouped(packet, recipients_by_version); } - pub fn spawn_particle( + pub fn spawn_particle( &self, position: Vector3, offset: Vector3, max_speed: f32, particle_count: i32, - particle: Particle, + particle: P, ) { + let id = VarInt(particle.id()); + let mut data = [0u8; 512]; + let size = particle.to_bytes(&mut data).unwrap(); + for player in self.players.load().iter() { - player.spawn_particle(position, offset, max_speed, particle_count, particle); + player.spawn_particle( + position, + offset, + max_speed, + particle_count, + id, + &data[..size], + ); } } @@ -2947,9 +2957,9 @@ impl World { let explosion = Explosion::new(power, position); let block_count = explosion.explode(self).await; let particle = if power < 2.0 { - Particle::Explosion + particle::Explosion.id() } else { - Particle::ExplosionEmitter + particle::ExplosionEmitter.id() }; for player in self.players.load().iter() { let mut sound_id = Sound::EntityGenericExplode as u16; @@ -2967,7 +2977,7 @@ impl World { power, block_count as i32, None, - VarInt(particle as i32), + VarInt(particle), sound.clone(), )) .await;