Added Stop Command
This commit is contained in:
parent
7a4b46f51b
commit
081bdbbbaa
@ -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;
|
||||||
|
|
||||||
@ -10,11 +11,48 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
serde_json::from_str(include_str!("../../assets/sound_category.json"))
|
serde_json::from_str(include_str!("../../assets/sound_category.json"))
|
||||||
.expect("Failed to parse sound_category.json");
|
.expect("Failed to parse sound_category.json");
|
||||||
let variants = array_to_tokenstream(&sound_categories);
|
let variants = array_to_tokenstream(&sound_categories);
|
||||||
|
let type_from_name = &sound_categories
|
||||||
|
.iter()
|
||||||
|
.map(|sound| {
|
||||||
|
let id = &sound.to_lowercase();
|
||||||
|
let name = format_ident!("{}", sound.to_pascal_case());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#id => Some(Self::#name),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<TokenStream>();
|
||||||
|
|
||||||
|
let type_to_name = &sound_categories
|
||||||
|
.iter()
|
||||||
|
.map(|sound| {
|
||||||
|
let id = &sound.to_lowercase();
|
||||||
|
let name = format_ident!("{}", sound.to_pascal_case());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
Self::#name => #id,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<TokenStream>();
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum SoundCategory {
|
pub enum SoundCategory {
|
||||||
#variants
|
#variants
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SoundCategory {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,6 +62,7 @@ mod set_time;
|
|||||||
mod set_title;
|
mod set_title;
|
||||||
mod sound_effect;
|
mod sound_effect;
|
||||||
mod spawn_entity;
|
mod spawn_entity;
|
||||||
|
mod stop_sound;
|
||||||
mod store_cookie;
|
mod store_cookie;
|
||||||
mod subtitle;
|
mod subtitle;
|
||||||
mod system_chat_message;
|
mod system_chat_message;
|
||||||
@ -141,6 +142,7 @@ pub use set_time::*;
|
|||||||
pub use set_title::*;
|
pub use set_title::*;
|
||||||
pub use sound_effect::*;
|
pub use sound_effect::*;
|
||||||
pub use spawn_entity::*;
|
pub use spawn_entity::*;
|
||||||
|
pub use stop_sound::*;
|
||||||
pub use store_cookie::*;
|
pub use store_cookie::*;
|
||||||
pub use subtitle::*;
|
pub use subtitle::*;
|
||||||
pub use system_chat_message::*;
|
pub use system_chat_message::*;
|
||||||
|
|||||||
45
pumpkin-protocol/src/client/play/stop_sound.rs
Normal file
45
pumpkin-protocol/src/client/play/stop_sound.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use crate::bytebuf::ByteBufMut;
|
||||||
|
use crate::codec::var_int::VarInt;
|
||||||
|
use crate::{ClientPacket, codec::identifier::Identifier};
|
||||||
|
use pumpkin_data::{packet::clientbound::PLAY_STOP_SOUND, sound::SoundCategory};
|
||||||
|
use pumpkin_macros::packet;
|
||||||
|
|
||||||
|
#[packet(PLAY_STOP_SOUND)]
|
||||||
|
pub struct CStopSound {
|
||||||
|
sound_id: Option<Identifier>,
|
||||||
|
category: Option<SoundCategory>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CStopSound {
|
||||||
|
pub fn new(sound_id: Option<Identifier>, category: Option<SoundCategory>) -> Self {
|
||||||
|
Self { sound_id, category }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientPacket for CStopSound {
|
||||||
|
fn write(&self, bytebuf: &mut impl bytes::BufMut) {
|
||||||
|
const NO_CATEGORY_NO_SOUND: u8 = 0;
|
||||||
|
const CATEGORY_ONLY: u8 = 1;
|
||||||
|
const SOUND_ONLY: u8 = 2;
|
||||||
|
const CATEGORY_AND_SOUND: u8 = 3;
|
||||||
|
|
||||||
|
match (self.category, &self.sound_id) {
|
||||||
|
(Some(category), Some(sound_id)) => {
|
||||||
|
bytebuf.put_u8(CATEGORY_AND_SOUND);
|
||||||
|
bytebuf.put_var_int(&VarInt(category as i32));
|
||||||
|
bytebuf.put_identifier(sound_id);
|
||||||
|
}
|
||||||
|
(Some(category), None) => {
|
||||||
|
bytebuf.put_u8(CATEGORY_ONLY);
|
||||||
|
bytebuf.put_var_int(&VarInt(category as i32));
|
||||||
|
}
|
||||||
|
(None, Some(sound_id)) => {
|
||||||
|
bytebuf.put_u8(SOUND_ONLY);
|
||||||
|
bytebuf.put_identifier(sound_id);
|
||||||
|
}
|
||||||
|
(None, None) => {
|
||||||
|
bytebuf.put_u8(NO_CATEGORY_NO_SOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -39,14 +39,15 @@ impl ArgumentConsumer for SoundCategoryArgumentConsumer {
|
|||||||
let category = match s.to_lowercase().as_str() {
|
let category = match s.to_lowercase().as_str() {
|
||||||
"master" => Some(SoundCategory::Master), // Default category, affects all sounds
|
"master" => Some(SoundCategory::Master), // Default category, affects all sounds
|
||||||
"music" => Some(SoundCategory::Music), // Background music
|
"music" => Some(SoundCategory::Music), // Background music
|
||||||
|
// i don't use SoundCategory::from_name because its is record and not records :c
|
||||||
"record" => Some(SoundCategory::Records), // Music discs
|
"record" => Some(SoundCategory::Records), // Music discs
|
||||||
"weather" => Some(SoundCategory::Weather), // Rain, thunder
|
"weather" => Some(SoundCategory::Weather), // Rain, thunder
|
||||||
"block" => Some(SoundCategory::Blocks), // Block sounds
|
"block" => Some(SoundCategory::Blocks), // Block sounds
|
||||||
"hostile" => Some(SoundCategory::Hostile), // Hostile mob sounds
|
"hostile" => Some(SoundCategory::Hostile), // Hostile mob sounds
|
||||||
"neutral" => Some(SoundCategory::Neutral), // Neutral mob sounds
|
"neutral" => Some(SoundCategory::Neutral), // Neutral mob sounds
|
||||||
"player" => Some(SoundCategory::Players), // Player sounds
|
"player" => Some(SoundCategory::Players), // Player sounds
|
||||||
"ambient" => Some(SoundCategory::Ambient), // Ambient environment
|
"ambient" => Some(SoundCategory::Ambient), // Ambient environment
|
||||||
"voice" => Some(SoundCategory::Voice), // Voice/speech
|
"voice" => Some(SoundCategory::Voice), // Voice/speech
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ mod say;
|
|||||||
mod seed;
|
mod seed;
|
||||||
mod setblock;
|
mod setblock;
|
||||||
mod stop;
|
mod stop;
|
||||||
|
mod stopsound;
|
||||||
mod summon;
|
mod summon;
|
||||||
mod teleport;
|
mod teleport;
|
||||||
mod time;
|
mod time;
|
||||||
@ -73,6 +74,7 @@ pub fn default_dispatcher() -> CommandDispatcher {
|
|||||||
dispatcher.register(bossbar::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(bossbar::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(say::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(say::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(gamemode::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(gamemode::init_command_tree(), PermissionLvl::Two);
|
||||||
|
dispatcher.register(stopsound::init_command_tree(), PermissionLvl::Two);
|
||||||
dispatcher.register(defaultgamemode::init_command_tree(), PermissionLvl::Two);
|
dispatcher.register(defaultgamemode::init_command_tree(), PermissionLvl::Two);
|
||||||
// Three
|
// Three
|
||||||
dispatcher.register(op::init_command_tree(), PermissionLvl::Three);
|
dispatcher.register(op::init_command_tree(), PermissionLvl::Three);
|
||||||
|
|||||||
84
pumpkin/src/command/commands/stopsound.rs
Normal file
84
pumpkin/src/command/commands/stopsound.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use crate::command::{
|
||||||
|
CommandExecutor, CommandSender,
|
||||||
|
args::{
|
||||||
|
ConsumedArgs, FindArg, players::PlayersArgumentConsumer, sound::SoundArgumentConsumer,
|
||||||
|
sound_category::SoundCategoryArgumentConsumer,
|
||||||
|
},
|
||||||
|
dispatcher::CommandError,
|
||||||
|
tree::{CommandTree, builder::argument},
|
||||||
|
};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use pumpkin_protocol::codec::identifier::Identifier;
|
||||||
|
use pumpkin_util::text::TextComponent;
|
||||||
|
|
||||||
|
const NAMES: [&str; 1] = ["stopsound"];
|
||||||
|
const DESCRIPTION: &str = "Stops a currently playing sound.";
|
||||||
|
|
||||||
|
const ARG_TARGETS: &str = "targets";
|
||||||
|
const ARG_SOURCE: &str = "source";
|
||||||
|
const ARG_SOUND: &str = "sound";
|
||||||
|
|
||||||
|
pub struct Executor;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl CommandExecutor for Executor {
|
||||||
|
async fn execute<'a>(
|
||||||
|
&self,
|
||||||
|
sender: &mut CommandSender<'a>,
|
||||||
|
_server: &crate::server::Server,
|
||||||
|
args: &ConsumedArgs<'a>,
|
||||||
|
) -> Result<(), CommandError> {
|
||||||
|
let targets = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)?;
|
||||||
|
|
||||||
|
let mut category = SoundCategoryArgumentConsumer::find_arg(args, ARG_SOURCE);
|
||||||
|
let mut sound = SoundArgumentConsumer::find_arg(args, ARG_SOUND);
|
||||||
|
|
||||||
|
for target in targets {
|
||||||
|
target
|
||||||
|
.stop_sound(
|
||||||
|
sound
|
||||||
|
.as_mut()
|
||||||
|
.cloned()
|
||||||
|
.map(|s| Identifier::vanilla(s.to_name()))
|
||||||
|
.ok(),
|
||||||
|
category.as_mut().map(|s| **s).ok(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
let text = match (category, sound) {
|
||||||
|
(Ok(c), Ok(s)) => TextComponent::translate(
|
||||||
|
"commands.stopsound.success.source.sound",
|
||||||
|
[
|
||||||
|
TextComponent::text(s.to_name()),
|
||||||
|
TextComponent::text(c.to_name()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(Ok(c), Err(_)) => TextComponent::translate(
|
||||||
|
"commands.stopsound.success.source.any",
|
||||||
|
[TextComponent::text(c.to_name())],
|
||||||
|
),
|
||||||
|
(Err(_), Ok(s)) => TextComponent::translate(
|
||||||
|
"commands.stopsound.success.sourceless.sound",
|
||||||
|
[TextComponent::text(s.to_name())],
|
||||||
|
),
|
||||||
|
(Err(_), Err(_)) => {
|
||||||
|
TextComponent::translate("commands.stopsound.success.sourceless.any", [])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sender.send_message(text).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_command_tree() -> CommandTree {
|
||||||
|
CommandTree::new(NAMES, DESCRIPTION).then(
|
||||||
|
argument(ARG_TARGETS, PlayersArgumentConsumer)
|
||||||
|
.execute(Executor)
|
||||||
|
.then(
|
||||||
|
argument(ARG_SOURCE, SoundCategoryArgumentConsumer)
|
||||||
|
.execute(Executor)
|
||||||
|
.then(argument(ARG_SOUND, SoundArgumentConsumer).execute(Executor)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -26,9 +26,11 @@ use pumpkin_protocol::{
|
|||||||
client::play::{
|
client::play::{
|
||||||
CAcknowledgeBlockChange, CActionBar, CCombatDeath, CDisguisedChatMessage, CGameEvent,
|
CAcknowledgeBlockChange, CActionBar, CCombatDeath, CDisguisedChatMessage, CGameEvent,
|
||||||
CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate,
|
CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate,
|
||||||
CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle, CSystemChatMessage,
|
CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CStopSound, CSubtitle,
|
||||||
CTitleText, CUnloadChunk, CUpdateMobEffect, GameEvent, MetaDataType, PlayerAction,
|
CSystemChatMessage, CTitleText, CUnloadChunk, CUpdateMobEffect, GameEvent, MetaDataType,
|
||||||
|
PlayerAction,
|
||||||
},
|
},
|
||||||
|
codec::identifier::Identifier,
|
||||||
server::play::{
|
server::play::{
|
||||||
SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd,
|
SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd,
|
||||||
SCommandSuggestion, SConfirmTeleport, SInteract, SPickItemFromBlock, SPlayerAbilities,
|
SCommandSuggestion, SConfirmTeleport, SInteract, SPickItemFromBlock, SPlayerAbilities,
|
||||||
@ -416,6 +418,18 @@ impl Player {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stops a sound playing on the client.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `sound_id`: An optional `Identifier` specifying the sound to stop. If `None`, all sounds in the specified category (if any) will be stopped.
|
||||||
|
/// * `category`: An optional `SoundCategory` specifying the sound category to stop. If `None`, all sounds with the specified identifier (if any) will be stopped.
|
||||||
|
pub async fn stop_sound(&self, sound_id: Option<Identifier>, category: Option<SoundCategory>) {
|
||||||
|
self.client
|
||||||
|
.send_packet(&CStopSound::new(sound_id, category))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn await_cancel(&self) {
|
pub async fn await_cancel(&self) {
|
||||||
self.cancel_tasks.notified().await;
|
self.cancel_tasks.notified().await;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user