Add Particle command
This commit is contained in:
parent
af0573d9b8
commit
06334ec658
@ -55,12 +55,15 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
.expect("Failed to parse damage_type.json");
|
.expect("Failed to parse damage_type.json");
|
||||||
|
|
||||||
let mut constants = Vec::new();
|
let mut constants = Vec::new();
|
||||||
let mut enum_variants = Vec::new();
|
let mut type_from_name = TokenStream::new();
|
||||||
|
|
||||||
for (name, entry) in damage_types {
|
for (name, entry) in damage_types {
|
||||||
let const_ident = format_ident!("{}", name.to_shouty_snake_case());
|
let const_ident = format_ident!("{}", name.to_shouty_snake_case());
|
||||||
|
let resource_name = name.to_lowercase();
|
||||||
|
|
||||||
enum_variants.push(const_ident.clone());
|
type_from_name.extend(quote! {
|
||||||
|
#resource_name => Some(Self::#const_ident),
|
||||||
|
});
|
||||||
|
|
||||||
let data = &entry.components;
|
let data = &entry.components;
|
||||||
let death_message_type = match &data.death_message_type {
|
let death_message_type = match &data.death_message_type {
|
||||||
@ -100,15 +103,6 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let type_name_pairs = enum_variants.iter().map(|variant| {
|
|
||||||
let name = variant.to_string();
|
|
||||||
let name_lowercase = name.to_lowercase();
|
|
||||||
let resource_name = format!("minecraft:{}", name_lowercase);
|
|
||||||
quote! {
|
|
||||||
#resource_name => Some(Self::#variant)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct DamageType {
|
pub struct DamageType {
|
||||||
@ -150,7 +144,7 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
#[doc = r" Try to parse a damage type from a resource location string"]
|
#[doc = r" Try to parse a damage type from a resource location string"]
|
||||||
pub fn from_name(name: &str) -> Option<Self> {
|
pub fn from_name(name: &str) -> Option<Self> {
|
||||||
match name {
|
match name {
|
||||||
#(#type_name_pairs,)*
|
#type_from_name
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
use heck::ToPascalCase;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
use crate::array_to_tokenstream;
|
use crate::array_to_tokenstream;
|
||||||
|
|
||||||
@ -9,11 +10,47 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
let particle: Vec<String> = serde_json::from_str(include_str!("../../assets/particles.json"))
|
let particle: Vec<String> = serde_json::from_str(include_str!("../../assets/particles.json"))
|
||||||
.expect("Failed to parse particles.json");
|
.expect("Failed to parse particles.json");
|
||||||
let variants = array_to_tokenstream(&particle);
|
let variants = array_to_tokenstream(&particle);
|
||||||
|
let type_from_name = &particle
|
||||||
|
.iter()
|
||||||
|
.map(|particle| {
|
||||||
|
let id = &particle;
|
||||||
|
let name = format_ident!("{}", particle.to_pascal_case());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#id => Some(Self::#name),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<TokenStream>();
|
||||||
|
let type_to_name = &particle
|
||||||
|
.iter()
|
||||||
|
.map(|particle| {
|
||||||
|
let id = &particle;
|
||||||
|
let name = format_ident!("{}", particle.to_pascal_case());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
Self::#name => #id,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<TokenStream>();
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum Particle {
|
pub enum Particle {
|
||||||
#variants
|
#variants
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Particle {
|
||||||
|
#[doc = r" Try to parse Particle from a resource location string"]
|
||||||
|
pub fn from_name(name: &str) -> Option<Self> {
|
||||||
|
match name {
|
||||||
|
#type_from_name
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const fn to_name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
#type_to_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,11 @@ use crate::VarInt;
|
|||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[client_packet(PLAY_LEVEL_PARTICLES)]
|
#[client_packet(PLAY_LEVEL_PARTICLES)]
|
||||||
pub struct CParticle<'a> {
|
pub struct CParticle<'a> {
|
||||||
|
force_spawn: bool,
|
||||||
/// If true, particle distance increases from 256 to 65536.
|
/// If true, particle distance increases from 256 to 65536.
|
||||||
long_distance: bool,
|
important: bool,
|
||||||
position: Vector3<f64>,
|
position: Vector3<f64>,
|
||||||
offset: Vector3<f64>,
|
offset: Vector3<f32>,
|
||||||
max_speed: f32,
|
max_speed: f32,
|
||||||
particle_count: i32,
|
particle_count: i32,
|
||||||
pariticle_id: VarInt,
|
pariticle_id: VarInt,
|
||||||
@ -19,17 +20,20 @@ pub struct CParticle<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CParticle<'a> {
|
impl<'a> CParticle<'a> {
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
long_distance: bool,
|
force_spawn: bool,
|
||||||
|
important: bool,
|
||||||
position: Vector3<f64>,
|
position: Vector3<f64>,
|
||||||
offset: Vector3<f64>,
|
offset: Vector3<f32>,
|
||||||
max_speed: f32,
|
max_speed: f32,
|
||||||
particle_count: i32,
|
particle_count: i32,
|
||||||
pariticle_id: VarInt,
|
pariticle_id: VarInt,
|
||||||
data: &'a [u8],
|
data: &'a [u8],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
long_distance,
|
force_spawn,
|
||||||
|
important,
|
||||||
position,
|
position,
|
||||||
offset,
|
offset,
|
||||||
max_speed,
|
max_speed,
|
||||||
|
|||||||
@ -52,7 +52,7 @@ impl ArgumentConsumer for DamageTypeArgumentConsumer {
|
|||||||
|
|
||||||
impl DefaultNameArgConsumer for DamageTypeArgumentConsumer {
|
impl DefaultNameArgConsumer for DamageTypeArgumentConsumer {
|
||||||
fn default_name(&self) -> &'static str {
|
fn default_name(&self) -> &'static str {
|
||||||
"damageType"
|
"damage_type"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use std::{collections::HashMap, hash::Hash, sync::Arc};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bounded_num::{NotInBounds, Number};
|
use bounded_num::{NotInBounds, Number};
|
||||||
use pumpkin_data::damage::DamageType;
|
use pumpkin_data::damage::DamageType;
|
||||||
|
use pumpkin_data::particle::Particle;
|
||||||
use pumpkin_data::sound::SoundCategory;
|
use pumpkin_data::sound::SoundCategory;
|
||||||
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||||
use pumpkin_util::text::TextComponent;
|
use pumpkin_util::text::TextComponent;
|
||||||
@ -32,6 +33,7 @@ pub mod entity;
|
|||||||
pub mod gamemode;
|
pub mod gamemode;
|
||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
|
pub mod particle;
|
||||||
pub mod players;
|
pub mod players;
|
||||||
pub mod position_2d;
|
pub mod position_2d;
|
||||||
pub mod position_3d;
|
pub mod position_3d;
|
||||||
@ -93,6 +95,7 @@ pub enum Arg<'a> {
|
|||||||
Block(&'a str),
|
Block(&'a str),
|
||||||
BossbarColor(BossbarColor),
|
BossbarColor(BossbarColor),
|
||||||
BossbarStyle(BossbarDivisions),
|
BossbarStyle(BossbarDivisions),
|
||||||
|
Particle(Particle),
|
||||||
Msg(String),
|
Msg(String),
|
||||||
TextComponent(TextComponent),
|
TextComponent(TextComponent),
|
||||||
Time(i32),
|
Time(i32),
|
||||||
|
|||||||
68
pumpkin/src/command/args/particle.rs
Normal file
68
pumpkin/src/command/args/particle.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use pumpkin_data::particle::Particle;
|
||||||
|
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
|
||||||
|
|
||||||
|
use crate::command::{
|
||||||
|
args::{Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser},
|
||||||
|
dispatcher::CommandError,
|
||||||
|
tree::RawArgs,
|
||||||
|
CommandSender,
|
||||||
|
};
|
||||||
|
use crate::server::Server;
|
||||||
|
|
||||||
|
pub struct ParticleArgumentConsumer;
|
||||||
|
|
||||||
|
impl GetClientSideArgParser for ParticleArgumentConsumer {
|
||||||
|
fn get_client_side_parser(&self) -> ArgumentType {
|
||||||
|
ArgumentType::Resource {
|
||||||
|
identifier: "particle_type",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_client_side_suggestion_type_override(&self) -> Option<SuggestionProviders> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl ArgumentConsumer for ParticleArgumentConsumer {
|
||||||
|
async fn consume<'a>(
|
||||||
|
&'a self,
|
||||||
|
_sender: &CommandSender<'a>,
|
||||||
|
_server: &'a Server,
|
||||||
|
args: &mut RawArgs<'a>,
|
||||||
|
) -> Option<Arg<'a>> {
|
||||||
|
let name = args.pop()?;
|
||||||
|
|
||||||
|
// Create a static damage type first
|
||||||
|
let particle = Particle::from_name(&name.replace("minecraft:", ""))?;
|
||||||
|
// Find matching static damage type from values array
|
||||||
|
Some(Arg::Particle(particle))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn suggest<'a>(
|
||||||
|
&'a self,
|
||||||
|
_sender: &CommandSender<'a>,
|
||||||
|
_server: &'a Server,
|
||||||
|
_input: &'a str,
|
||||||
|
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DefaultNameArgConsumer for ParticleArgumentConsumer {
|
||||||
|
fn default_name(&self) -> &'static str {
|
||||||
|
"particle_type"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FindArg<'a> for ParticleArgumentConsumer {
|
||||||
|
type Data = &'a Particle;
|
||||||
|
|
||||||
|
fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
|
||||||
|
match args.get(name) {
|
||||||
|
Some(Arg::Particle(data)) => Ok(data),
|
||||||
|
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,7 @@ pub mod msg;
|
|||||||
pub mod op;
|
pub mod op;
|
||||||
pub mod pardon;
|
pub mod pardon;
|
||||||
pub mod pardonip;
|
pub mod pardonip;
|
||||||
|
pub mod particle;
|
||||||
pub mod playsound;
|
pub mod playsound;
|
||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
pub mod plugins;
|
pub mod plugins;
|
||||||
|
|||||||
92
pumpkin/src/command/commands/particle.rs
Normal file
92
pumpkin/src/command/commands/particle.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use pumpkin_util::{math::vector3::Vector3, text::TextComponent};
|
||||||
|
|
||||||
|
use crate::command::{
|
||||||
|
args::{
|
||||||
|
bounded_num::BoundedNumArgumentConsumer, particle::ParticleArgumentConsumer,
|
||||||
|
position_3d::Position3DArgumentConsumer, ConsumedArgs, FindArg,
|
||||||
|
},
|
||||||
|
tree::{builder::argument, CommandTree},
|
||||||
|
CommandError, CommandExecutor, CommandSender,
|
||||||
|
};
|
||||||
|
const NAMES: [&str; 1] = ["particle"];
|
||||||
|
|
||||||
|
const DESCRIPTION: &str = "Spawns a Particle at position.";
|
||||||
|
|
||||||
|
const ARG_NAME: &str = "name";
|
||||||
|
|
||||||
|
const ARG_POS: &str = "pos";
|
||||||
|
const ARG_DELTA: &str = "delta";
|
||||||
|
const ARG_SPEED: &str = "speed";
|
||||||
|
const ARG_COUNT: &str = "count";
|
||||||
|
|
||||||
|
struct ParticleExecutor;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl CommandExecutor for ParticleExecutor {
|
||||||
|
async fn execute<'a>(
|
||||||
|
&self,
|
||||||
|
sender: &mut CommandSender<'a>,
|
||||||
|
_server: &crate::server::Server,
|
||||||
|
args: &ConsumedArgs<'a>,
|
||||||
|
) -> Result<(), CommandError> {
|
||||||
|
let particle = ParticleArgumentConsumer::find_arg(args, ARG_NAME)?;
|
||||||
|
let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS);
|
||||||
|
let delta = Position3DArgumentConsumer::find_arg(args, ARG_DELTA);
|
||||||
|
let speed = BoundedNumArgumentConsumer::<f32>::find_arg(args, ARG_SPEED);
|
||||||
|
let count = BoundedNumArgumentConsumer::<i32>::find_arg(args, ARG_COUNT);
|
||||||
|
|
||||||
|
// TODO: Make this work in console
|
||||||
|
if let Some(player) = sender.as_player() {
|
||||||
|
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
|
||||||
|
let delta = delta.unwrap_or(Vector3::new(0.0, 0.0, 0.0));
|
||||||
|
let delta: Vector3<f32> = Vector3::new(delta.x as f32, delta.y as f32, delta.z as f32);
|
||||||
|
let speed = speed.unwrap_or(Ok(0.0)).unwrap_or(0.0);
|
||||||
|
let count = count.unwrap_or(Ok(0)).unwrap_or(0);
|
||||||
|
|
||||||
|
player
|
||||||
|
.world()
|
||||||
|
.await
|
||||||
|
.spawn_particle(pos, delta, speed, count, *particle)
|
||||||
|
.await;
|
||||||
|
sender
|
||||||
|
.send_message(TextComponent::translate(
|
||||||
|
"commands.particle.success",
|
||||||
|
[TextComponent::text(format!("{particle:?}"))].into(),
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_command_tree() -> CommandTree {
|
||||||
|
CommandTree::new(NAMES, DESCRIPTION).then(
|
||||||
|
argument(ARG_NAME, ParticleArgumentConsumer)
|
||||||
|
.execute(ParticleExecutor)
|
||||||
|
.then(
|
||||||
|
argument(ARG_POS, Position3DArgumentConsumer)
|
||||||
|
.execute(ParticleExecutor)
|
||||||
|
.then(
|
||||||
|
argument(ARG_DELTA, Position3DArgumentConsumer)
|
||||||
|
.execute(ParticleExecutor)
|
||||||
|
.then(
|
||||||
|
argument(
|
||||||
|
ARG_SPEED,
|
||||||
|
BoundedNumArgumentConsumer::<f32>::new().min(0.0),
|
||||||
|
)
|
||||||
|
.execute(ParticleExecutor)
|
||||||
|
.then(
|
||||||
|
argument(
|
||||||
|
ARG_COUNT,
|
||||||
|
BoundedNumArgumentConsumer::<i32>::new().min(0),
|
||||||
|
)
|
||||||
|
.execute(ParticleExecutor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// TODO: Add NBT
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -11,8 +11,8 @@ use args::ConsumedArgs;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use commands::{
|
use commands::{
|
||||||
ban, banip, banlist, clear, damage, deop, experience, fill, gamemode, give, help, kick, kill,
|
ban, banip, banlist, clear, damage, deop, experience, fill, gamemode, give, help, kick, kill,
|
||||||
list, me, msg, op, pardon, pardonip, playsound, plugin, plugins, pumpkin, say, setblock, stop,
|
list, me, msg, op, pardon, pardonip, particle, playsound, plugin, plugins, pumpkin, say,
|
||||||
summon, teleport, time, title, weather, worldborder,
|
setblock, stop, summon, teleport, time, title, weather, worldborder,
|
||||||
};
|
};
|
||||||
use dispatcher::CommandError;
|
use dispatcher::CommandError;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
@ -110,41 +110,46 @@ impl CommandSender<'_> {
|
|||||||
pub fn default_dispatcher() -> CommandDispatcher {
|
pub fn default_dispatcher() -> CommandDispatcher {
|
||||||
let mut dispatcher = CommandDispatcher::default();
|
let mut dispatcher = CommandDispatcher::default();
|
||||||
|
|
||||||
|
// Zerp
|
||||||
dispatcher.register(pumpkin::init_command_tree(), PermissionLvl::Zero);
|
dispatcher.register(pumpkin::init_command_tree(), PermissionLvl::Zero);
|
||||||
dispatcher.register(bossbar::init_command_tree(), PermissionLvl::Two);
|
|
||||||
dispatcher.register(say::init_command_tree(), PermissionLvl::Two);
|
|
||||||
dispatcher.register(gamemode::init_command_tree(), PermissionLvl::Two);
|
|
||||||
dispatcher.register(stop::init_command_tree(), PermissionLvl::Four);
|
|
||||||
dispatcher.register(help::init_command_tree(), PermissionLvl::Zero);
|
dispatcher.register(help::init_command_tree(), PermissionLvl::Zero);
|
||||||
|
dispatcher.register(list::init_command_tree(), PermissionLvl::Zero);
|
||||||
|
dispatcher.register(transfer::init_command_tree(), PermissionLvl::Zero);
|
||||||
|
dispatcher.register(me::init_command_tree(), PermissionLvl::Zero);
|
||||||
|
dispatcher.register(msg::init_command_tree(), PermissionLvl::Zero);
|
||||||
|
// Two
|
||||||
dispatcher.register(kill::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(kill::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(kick::init_command_tree(), PermissionLvl::Three);
|
|
||||||
dispatcher.register(plugin::init_command_tree(), PermissionLvl::Three);
|
|
||||||
dispatcher.register(plugins::init_command_tree(), PermissionLvl::Three);
|
|
||||||
dispatcher.register(worldborder::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(worldborder::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(teleport::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(teleport::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(time::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(time::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(give::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(give::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(list::init_command_tree(), PermissionLvl::Zero);
|
|
||||||
dispatcher.register(clear::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(clear::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(setblock::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(setblock::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(seed::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(seed::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(transfer::init_command_tree(), PermissionLvl::Zero);
|
|
||||||
dispatcher.register(fill::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(fill::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(op::init_command_tree(), PermissionLvl::Three);
|
|
||||||
dispatcher.register(deop::init_command_tree(), PermissionLvl::Three);
|
|
||||||
dispatcher.register(me::init_command_tree(), PermissionLvl::Zero);
|
|
||||||
dispatcher.register(playsound::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(playsound::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(title::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(title::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(summon::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(summon::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(msg::init_command_tree(), PermissionLvl::Zero);
|
dispatcher.register(experience::init_command_tree(), PermissionLvl::Two);
|
||||||
|
dispatcher.register(weather::init_command_tree(), PermissionLvl::Two);
|
||||||
|
dispatcher.register(particle::init_command_tree(), PermissionLvl::Two);
|
||||||
|
dispatcher.register(damage::init_command_tree(), PermissionLvl::Two);
|
||||||
|
dispatcher.register(bossbar::init_command_tree(), PermissionLvl::Two);
|
||||||
|
dispatcher.register(say::init_command_tree(), PermissionLvl::Two);
|
||||||
|
dispatcher.register(gamemode::init_command_tree(), PermissionLvl::Two);
|
||||||
|
// Three
|
||||||
|
dispatcher.register(op::init_command_tree(), PermissionLvl::Three);
|
||||||
|
dispatcher.register(deop::init_command_tree(), PermissionLvl::Three);
|
||||||
|
dispatcher.register(kick::init_command_tree(), PermissionLvl::Three);
|
||||||
|
dispatcher.register(plugin::init_command_tree(), PermissionLvl::Three);
|
||||||
|
dispatcher.register(plugins::init_command_tree(), PermissionLvl::Three);
|
||||||
dispatcher.register(ban::init_command_tree(), PermissionLvl::Three);
|
dispatcher.register(ban::init_command_tree(), PermissionLvl::Three);
|
||||||
dispatcher.register(banip::init_command_tree(), PermissionLvl::Three);
|
dispatcher.register(banip::init_command_tree(), PermissionLvl::Three);
|
||||||
dispatcher.register(banlist::init_command_tree(), PermissionLvl::Three);
|
dispatcher.register(banlist::init_command_tree(), PermissionLvl::Three);
|
||||||
dispatcher.register(pardon::init_command_tree(), PermissionLvl::Three);
|
dispatcher.register(pardon::init_command_tree(), PermissionLvl::Three);
|
||||||
dispatcher.register(pardonip::init_command_tree(), PermissionLvl::Three);
|
dispatcher.register(pardonip::init_command_tree(), PermissionLvl::Three);
|
||||||
dispatcher.register(experience::init_command_tree(), PermissionLvl::Two);
|
// Four
|
||||||
dispatcher.register(weather::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(stop::init_command_tree(), PermissionLvl::Four);
|
||||||
dispatcher.register(damage::init_command_tree(), PermissionLvl::Two);
|
|
||||||
|
|
||||||
dispatcher
|
dispatcher
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,7 @@ use pumpkin_data::{
|
|||||||
particle::Particle,
|
particle::Particle,
|
||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
};
|
};
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{client::play::CEntityVelocity, codec::var_int::VarInt};
|
||||||
client::play::{CEntityVelocity, CParticle},
|
|
||||||
codec::var_int::VarInt,
|
|
||||||
};
|
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use pumpkin_world::item::ItemStack;
|
use pumpkin_world::item::ItemStack;
|
||||||
|
|
||||||
@ -93,19 +90,16 @@ pub async fn spawn_sweep_particle(attacker_entity: &Entity, world: &World, pos:
|
|||||||
let e = f64::from((yaw.to_radians()).cos());
|
let e = f64::from((yaw.to_radians()).cos());
|
||||||
|
|
||||||
let scale = 0.5;
|
let scale = 0.5;
|
||||||
// TODO: use entity height
|
let body_y = pos.y + f64::from(attacker_entity.height()) * scale;
|
||||||
let body_y = pos.y * 2.0 * scale;
|
|
||||||
|
|
||||||
world
|
world
|
||||||
.broadcast_packet_all(&CParticle::new(
|
.spawn_particle(
|
||||||
false,
|
|
||||||
Vector3::new(pos.x + d, body_y, pos.z + e),
|
Vector3::new(pos.x + d, body_y, pos.z + e),
|
||||||
Vector3::new(0.0, 0.0, 0.0),
|
Vector3::new(0.0, 0.0, 0.0),
|
||||||
0.0,
|
0.0,
|
||||||
0,
|
0,
|
||||||
VarInt(Particle::SweepAttack as i32), // sweep
|
Particle::SweepAttack,
|
||||||
&[],
|
)
|
||||||
))
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -273,6 +273,13 @@ impl Entity {
|
|||||||
entity_vel,
|
entity_vel,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
pub fn width(&self) -> f32 {
|
||||||
|
self.bounding_box_size.load().width
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> f32 {
|
||||||
|
self.bounding_box_size.load().height
|
||||||
|
}
|
||||||
|
|
||||||
/// Applies knockback to the entity, following vanilla Minecraft's mechanics.
|
/// Applies knockback to the entity, following vanilla Minecraft's mechanics.
|
||||||
///
|
///
|
||||||
|
|||||||
@ -14,6 +14,7 @@ use pumpkin_data::{
|
|||||||
damage::DamageType,
|
damage::DamageType,
|
||||||
entity::EntityType,
|
entity::EntityType,
|
||||||
item::Operation,
|
item::Operation,
|
||||||
|
particle::Particle,
|
||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
};
|
};
|
||||||
use pumpkin_inventory::player::PlayerInventory;
|
use pumpkin_inventory::player::PlayerInventory;
|
||||||
@ -22,9 +23,9 @@ use pumpkin_protocol::{
|
|||||||
bytebuf::packet::Packet,
|
bytebuf::packet::Packet,
|
||||||
client::play::{
|
client::play::{
|
||||||
CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus, CGameEvent, CHurtAnimation,
|
CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus, CGameEvent, CHurtAnimation,
|
||||||
CKeepAlive, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition,
|
CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate,
|
||||||
CRespawn, CSetExperience, CSetHealth, CSubtitle, CSystemChatMessage, CTitleText,
|
CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle, CSystemChatMessage,
|
||||||
CUnloadChunk, GameEvent, MetaDataType, PlayerAction,
|
CTitleText, CUnloadChunk, GameEvent, MetaDataType, PlayerAction,
|
||||||
},
|
},
|
||||||
server::play::{
|
server::play::{
|
||||||
SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd,
|
SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd,
|
||||||
@ -385,6 +386,28 @@ impl Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn spawn_particle(
|
||||||
|
&self,
|
||||||
|
position: Vector3<f64>,
|
||||||
|
offset: Vector3<f32>,
|
||||||
|
max_speed: f32,
|
||||||
|
particle_count: i32,
|
||||||
|
pariticle: Particle,
|
||||||
|
) {
|
||||||
|
self.client
|
||||||
|
.send_packet(&CParticle::new(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
position,
|
||||||
|
offset,
|
||||||
|
max_speed,
|
||||||
|
particle_count,
|
||||||
|
VarInt(pariticle as i32),
|
||||||
|
&[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn play_sound(
|
pub async fn play_sound(
|
||||||
&self,
|
&self,
|
||||||
sound_id: u16,
|
sound_id: u16,
|
||||||
|
|||||||
@ -23,6 +23,7 @@ use border::Worldborder;
|
|||||||
use pumpkin_config::BasicConfiguration;
|
use pumpkin_config::BasicConfiguration;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
entity::EntityType,
|
entity::EntityType,
|
||||||
|
particle::Particle,
|
||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
world::WorldEvent,
|
world::WorldEvent,
|
||||||
};
|
};
|
||||||
@ -187,6 +188,22 @@ impl World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn spawn_particle(
|
||||||
|
&self,
|
||||||
|
position: Vector3<f64>,
|
||||||
|
offset: Vector3<f32>,
|
||||||
|
max_speed: f32,
|
||||||
|
particle_count: i32,
|
||||||
|
pariticle: Particle,
|
||||||
|
) {
|
||||||
|
let players = self.players.lock().await;
|
||||||
|
for (_, player) in players.iter() {
|
||||||
|
player
|
||||||
|
.spawn_particle(position, offset, max_speed, particle_count, pariticle)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn play_sound(&self, sound: Sound, category: SoundCategory, position: &Vector3<f64>) {
|
pub async fn play_sound(&self, sound: Sound, category: SoundCategory, position: &Vector3<f64>) {
|
||||||
self.play_sound_raw(sound as u16, category, position, 1.0, 1.0)
|
self.play_sound_raw(sound as u16, category, position, 1.0, 1.0)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user