Add Summon Command

Also refactor some things:

- Remove cmd_ prefix from commands
- Remove arg_ prefix from arguments
This commit is contained in:
Alexander Medvedev 2025-01-26 20:06:20 +01:00
parent 69cb3bb4f0
commit 2fdf0777f6
57 changed files with 351 additions and 196 deletions

View File

@ -40,8 +40,20 @@ pub(crate) fn build() -> TokenStream {
})
.collect::<TokenStream>();
let type_from_name = json
.iter()
.map(|sound| {
let id = &sound.0;
let name = ident(sound.0.to_pascal_case());
quote! {
#id => Some(Self::#name),
}
})
.collect::<TokenStream>();
quote! {
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum EntityType {
#variants
@ -54,6 +66,13 @@ pub(crate) fn build() -> TokenStream {
_ => None
}
}
pub fn from_name(name: &str) -> Option<Self> {
match name {
#type_from_name
_ => None
}
}
}
}
}

View File

@ -76,7 +76,6 @@ async-trait = "0.1.83"
# plugins
libloading = "0.8.5"
oneshot = "0.1.8"
[build-dependencies]
git-version = "0.3.9"
# This makes it so the entire project doesn't recompile on each build on linux.

View File

@ -49,8 +49,8 @@ impl ArgumentConsumer for BlockArgumentConsumer {
}
impl DefaultNameArgConsumer for BlockArgumentConsumer {
fn default_name(&self) -> String {
"block".to_string()
fn default_name(&self) -> &'static str {
"block"
}
}

View File

@ -62,8 +62,8 @@ impl ArgumentConsumer for BossbarColorArgumentConsumer {
}
impl DefaultNameArgConsumer for BossbarColorArgumentConsumer {
fn default_name(&self) -> String {
"color".to_string()
fn default_name(&self) -> &'static str {
"color"
}
}

View File

@ -66,8 +66,8 @@ impl ArgumentConsumer for BossbarStyleArgumentConsumer {
}
impl DefaultNameArgConsumer for BossbarStyleArgumentConsumer {
fn default_name(&self) -> String {
"style".to_string()
fn default_name(&self) -> &'static str {
"style"
}
}

View File

@ -238,8 +238,8 @@ impl<T: ToFromNumber> DefaultNameArgConsumer for BoundedNumArgumentConsumer<T>
where
Self: ArgumentConsumer,
{
fn default_name(&self) -> String {
fn default_name(&self) -> &'static str {
// setting a single default name for all BoundedNumArgumentConsumer variants is probably a bad idea since it would lead to confusion
self.name.expect("Only use *_default variants of methods with a BoundedNumArgumentConsumer that has a name.").to_string()
self.name.expect("Only use *_default variants of methods with a BoundedNumArgumentConsumer that has a name.")
}
}

View File

@ -65,8 +65,8 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer {
}
impl DefaultNameArgConsumer for CommandTreeArgumentConsumer {
fn default_name(&self) -> String {
"cmd".to_string()
fn default_name(&self) -> &'static str {
"cmd"
}
}

View File

@ -10,7 +10,7 @@ use crate::entity::player::Player;
use crate::server::Server;
use super::super::args::ArgumentConsumer;
use super::arg_players::PlayersArgumentConsumer;
use super::players::PlayersArgumentConsumer;
use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser};
/// todo: implement (currently just calls [`super::arg_player::PlayerArgumentConsumer`])
@ -55,8 +55,8 @@ impl ArgumentConsumer for EntitiesArgumentConsumer {
}
impl DefaultNameArgConsumer for EntitiesArgumentConsumer {
fn default_name(&self) -> String {
"targets".to_string()
fn default_name(&self) -> &'static str {
"targets"
}
}

View File

@ -77,8 +77,8 @@ impl ArgumentConsumer for EntityArgumentConsumer {
}
impl DefaultNameArgConsumer for EntityArgumentConsumer {
fn default_name(&self) -> String {
"target".to_string()
fn default_name(&self) -> &'static str {
"target"
}
}

View File

@ -57,8 +57,8 @@ impl ArgumentConsumer for GamemodeArgumentConsumer {
}
impl DefaultNameArgConsumer for GamemodeArgumentConsumer {
fn default_name(&self) -> String {
"gamemode".to_string()
fn default_name(&self) -> &'static str {
"gamemode"
}
}

View File

@ -47,8 +47,8 @@ impl ArgumentConsumer for ItemArgumentConsumer {
}
impl DefaultNameArgConsumer for ItemArgumentConsumer {
fn default_name(&self) -> String {
"item".to_string()
fn default_name(&self) -> &'static str {
"item"
}
}

View File

@ -55,8 +55,8 @@ impl ArgumentConsumer for MsgArgConsumer {
}
impl DefaultNameArgConsumer for MsgArgConsumer {
fn default_name(&self) -> String {
"msg".to_string()
fn default_name(&self) -> &'static str {
"msg"
}
}

View File

@ -1,7 +1,7 @@
use std::{collections::HashMap, hash::Hash, sync::Arc};
use arg_bounded_num::{NotInBounds, Number};
use async_trait::async_trait;
use bounded_num::{NotInBounds, Number};
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
use pumpkin_util::text::TextComponent;
use pumpkin_util::{
@ -17,27 +17,28 @@ use super::{
use crate::world::bossbar::{BossbarColor, BossbarDivisions};
use crate::{entity::player::Player, server::Server};
pub mod arg_block;
pub mod arg_bool;
pub mod arg_bossbar_color;
pub mod arg_bossbar_style;
pub mod arg_bounded_num;
pub mod arg_command;
pub mod arg_entities;
pub mod arg_entity;
pub mod arg_gamemode;
pub mod arg_item;
pub mod arg_message;
pub mod arg_players;
pub mod arg_position_2d;
pub mod arg_position_3d;
pub mod arg_position_block;
pub mod arg_resource_location;
pub mod arg_rotation;
pub mod arg_simple;
pub mod arg_sound;
pub mod arg_textcomponent;
pub mod block;
pub mod bool;
pub mod bossbar_color;
pub mod bossbar_style;
pub mod bounded_num;
pub mod command;
mod coordinate;
pub mod entities;
pub mod entity;
pub mod gamemode;
pub mod item;
pub mod message;
pub mod players;
pub mod position_2d;
pub mod position_3d;
pub mod position_block;
pub mod resource_location;
pub mod rotation;
pub mod simple;
pub mod sound;
pub mod summonable_entities;
pub mod textcomponent;
/// see [`crate::commands::tree_builder::argument`]
#[async_trait]
@ -68,7 +69,7 @@ pub trait GetClientSideArgParser {
}
pub trait DefaultNameArgConsumer: ArgumentConsumer {
fn default_name(&self) -> String;
fn default_name(&self) -> &str;
}
#[derive(Clone)]
@ -120,7 +121,7 @@ pub(crate) trait FindArgDefaultName<'a, T> {
impl<'a, T, C: FindArg<'a, Data = T> + DefaultNameArgConsumer> FindArgDefaultName<'a, T> for C {
fn find_arg_default_name(&self, args: &'a ConsumedArgs) -> Result<T, CommandError> {
C::find_arg(args, &self.default_name())
C::find_arg(args, self.default_name())
}
}

View File

@ -71,8 +71,8 @@ impl ArgumentConsumer for PlayersArgumentConsumer {
}
impl DefaultNameArgConsumer for PlayersArgumentConsumer {
fn default_name(&self) -> String {
"player".to_string()
fn default_name(&self) -> &'static str {
"player"
}
}

View File

@ -71,8 +71,8 @@ impl MaybeRelativePosition2D {
}
impl DefaultNameArgConsumer for Position2DArgumentConsumer {
fn default_name(&self) -> String {
"pos2d".to_string()
fn default_name(&self) -> &'static str {
"pos"
}
}

View File

@ -74,8 +74,8 @@ impl MaybeRelativePosition3D {
}
impl DefaultNameArgConsumer for Position3DArgumentConsumer {
fn default_name(&self) -> String {
"pos".to_string()
fn default_name(&self) -> &'static str {
"pos"
}
}

View File

@ -75,8 +75,8 @@ impl MaybeRelativeBlockPos {
}
impl DefaultNameArgConsumer for BlockPosArgumentConsumer {
fn default_name(&self) -> String {
"block_pos".to_string()
fn default_name(&self) -> &'static str {
"block_pos"
}
}

View File

@ -58,8 +58,8 @@ impl ArgumentConsumer for ResourceLocationArgumentConsumer {
}
impl DefaultNameArgConsumer for ResourceLocationArgumentConsumer {
fn default_name(&self) -> String {
"id".to_string()
fn default_name(&self) -> &'static str {
"id"
}
}

View File

@ -59,8 +59,8 @@ impl ArgumentConsumer for RotationArgumentConsumer {
}
impl DefaultNameArgConsumer for RotationArgumentConsumer {
fn default_name(&self) -> String {
"rotation".to_string()
fn default_name(&self) -> &'static str {
"rotation"
}
}

View File

@ -47,8 +47,8 @@ impl ArgumentConsumer for SoundArgumentConsumer {
}
impl DefaultNameArgConsumer for SoundArgumentConsumer {
fn default_name(&self) -> String {
"available_sounds".to_string()
fn default_name(&self) -> &'static str {
"available_sounds"
}
}

View File

@ -0,0 +1,72 @@
use async_trait::async_trait;
use pumpkin_data::entity::EntityType;
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
use crate::{command::dispatcher::CommandError, server::Server};
use super::{
super::{
args::{ArgumentConsumer, RawArgs},
CommandSender,
},
Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
};
pub(crate) struct SummonableEntitiesArgumentConsumer;
impl GetClientSideArgParser for SummonableEntitiesArgumentConsumer {
fn get_client_side_parser(&self) -> ArgumentType {
ArgumentType::ResourceLocation
}
fn get_client_side_suggestion_type_override(&self) -> Option<SuggestionProviders> {
Some(SuggestionProviders::SummonableEntities)
}
}
#[async_trait]
impl ArgumentConsumer for SummonableEntitiesArgumentConsumer {
async fn consume<'a>(
&'a self,
_sender: &CommandSender<'a>,
_server: &'a Server,
args: &mut RawArgs<'a>,
) -> Option<Arg<'a>> {
let s = args.pop()?;
Some(Arg::Block(s))
}
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 SummonableEntitiesArgumentConsumer {
fn default_name(&self) -> &'static str {
"summonable_entities"
}
}
impl<'a> FindArg<'a> for SummonableEntitiesArgumentConsumer {
type Data = EntityType;
fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::Block(name)) => EntityType::from_name(&name.replace("minecraft:", ""))
.map_or_else(
|| {
Err(CommandError::GeneralCommandIssue(format!(
"Entity {name} does not exist."
)))
},
Result::Ok,
),
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),
}
}
}

View File

@ -1,14 +1,14 @@
use crate::command::args::arg_bool::BoolArgConsumer;
use crate::command::args::arg_bossbar_color::BossbarColorArgumentConsumer;
use crate::command::args::arg_bossbar_style::BossbarStyleArgumentConsumer;
use crate::command::args::arg_bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::arg_players::PlayersArgumentConsumer;
use crate::command::args::arg_resource_location::ResourceLocationArgumentConsumer;
use crate::command::args::bool::BoolArgConsumer;
use crate::command::args::bossbar_color::BossbarColorArgumentConsumer;
use crate::command::args::bossbar_style::BossbarStyleArgumentConsumer;
use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::players::PlayersArgumentConsumer;
use crate::command::args::resource_location::ResourceLocationArgumentConsumer;
use crate::command::args::{ConsumedArgs, DefaultNameArgConsumer, FindArg, FindArgDefaultName};
use crate::command::dispatcher::CommandError;
use crate::command::args::arg_textcomponent::TextComponentArgConsumer;
use crate::command::args::textcomponent::TextComponentArgConsumer;
use crate::command::tree::CommandTree;
use crate::command::tree_builder::{argument, argument_default_name, literal};
use crate::command::{CommandExecutor, CommandSender};

View File

@ -5,7 +5,7 @@ use pumpkin_inventory::Container;
use pumpkin_util::text::color::NamedColor;
use pumpkin_util::text::TextComponent;
use crate::command::args::arg_entities::EntitiesArgumentConsumer;
use crate::command::args::entities::EntitiesArgumentConsumer;
use crate::command::args::{Arg, ConsumedArgs};
use crate::command::tree::CommandTree;
use crate::command::tree_builder::{argument, require};

View File

@ -1,6 +1,6 @@
use crate::{
command::{
args::{arg_players::PlayersArgumentConsumer, Arg, ConsumedArgs},
args::{players::PlayersArgumentConsumer, Arg, ConsumedArgs},
tree::CommandTree,
tree_builder::argument,
CommandError, CommandExecutor, CommandSender,

View File

@ -1,5 +1,5 @@
use crate::command::args::arg_block::BlockArgumentConsumer;
use crate::command::args::arg_position_block::BlockPosArgumentConsumer;
use crate::command::args::block::BlockArgumentConsumer;
use crate::command::args::position_block::BlockPosArgumentConsumer;
use crate::command::args::{ConsumedArgs, FindArg};
use crate::command::tree::CommandTree;
use crate::command::tree_builder::{argument, literal};

View File

@ -1,11 +1,11 @@
use async_trait::async_trait;
use crate::command::args::arg_gamemode::GamemodeArgumentConsumer;
use crate::command::args::gamemode::GamemodeArgumentConsumer;
use crate::command::args::GetCloned;
use crate::TextComponent;
use crate::command::args::arg_players::PlayersArgumentConsumer;
use crate::command::args::players::PlayersArgumentConsumer;
use crate::command::args::{Arg, ConsumedArgs};
use crate::command::dispatcher::CommandError;

View File

@ -2,9 +2,9 @@ use async_trait::async_trait;
use pumpkin_util::text::color::{Color, NamedColor};
use pumpkin_util::text::TextComponent;
use crate::command::args::arg_bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::arg_item::ItemArgumentConsumer;
use crate::command::args::arg_players::PlayersArgumentConsumer;
use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::item::ItemArgumentConsumer;
use crate::command::args::players::PlayersArgumentConsumer;
use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName};
use crate::command::tree::CommandTree;
use crate::command::tree_builder::{argument, argument_default_name};

View File

@ -3,8 +3,8 @@ use pumpkin_util::text::click::ClickEvent;
use pumpkin_util::text::color::{Color, NamedColor};
use pumpkin_util::text::TextComponent;
use crate::command::args::arg_bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::arg_command::CommandTreeArgumentConsumer;
use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::command::CommandTreeArgumentConsumer;
use crate::command::args::{Arg, ConsumedArgs, FindArgDefaultName};
use crate::command::dispatcher::CommandError;
use crate::command::dispatcher::CommandError::InvalidConsumption;

View File

@ -2,8 +2,8 @@ use async_trait::async_trait;
use pumpkin_util::text::color::NamedColor;
use pumpkin_util::text::TextComponent;
use crate::command::args::arg_message::MsgArgConsumer;
use crate::command::args::arg_players::PlayersArgumentConsumer;
use crate::command::args::message::MsgArgConsumer;
use crate::command::args::players::PlayersArgumentConsumer;
use crate::command::args::{Arg, ConsumedArgs};
use crate::command::tree::CommandTree;
use crate::command::tree_builder::argument;

View File

@ -2,7 +2,7 @@ use async_trait::async_trait;
use pumpkin_util::text::color::NamedColor;
use pumpkin_util::text::TextComponent;
use crate::command::args::arg_entities::EntitiesArgumentConsumer;
use crate::command::args::entities::EntitiesArgumentConsumer;
use crate::command::args::{Arg, ConsumedArgs};
use crate::command::tree::CommandTree;
use crate::command::tree_builder::{argument, require};

View File

@ -3,7 +3,7 @@ use pumpkin_data::world::EMOTE_COMMAND;
use pumpkin_util::text::TextComponent;
use crate::command::{
args::{arg_message::MsgArgConsumer, Arg, ConsumedArgs},
args::{message::MsgArgConsumer, Arg, ConsumedArgs},
tree::CommandTree,
tree_builder::argument,
CommandError, CommandExecutor, CommandSender,

View File

@ -1,25 +1,26 @@
pub mod cmd_bossbar;
pub mod cmd_clear;
pub mod cmd_deop;
pub mod cmd_fill;
pub mod cmd_gamemode;
pub mod cmd_give;
pub mod cmd_help;
pub mod cmd_kick;
pub mod cmd_kill;
pub mod cmd_list;
pub mod cmd_me;
pub mod cmd_op;
pub mod cmd_playsound;
pub mod cmd_plugin;
pub mod cmd_plugins;
pub mod cmd_pumpkin;
pub mod cmd_say;
pub mod cmd_seed;
pub mod cmd_setblock;
pub mod cmd_stop;
pub mod cmd_teleport;
pub mod cmd_time;
pub mod cmd_title;
pub mod cmd_transfer;
pub mod cmd_worldborder;
pub mod bossbar;
pub mod clear;
pub mod deop;
pub mod fill;
pub mod gamemode;
pub mod give;
pub mod help;
pub mod kick;
pub mod kill;
pub mod list;
pub mod me;
pub mod op;
pub mod playsound;
pub mod plugin;
pub mod plugins;
pub mod pumpkin;
pub mod say;
pub mod seed;
pub mod setblock;
pub mod stop;
pub mod summon;
pub mod teleport;
pub mod time;
pub mod title;
pub mod transfer;
pub mod worldborder;

View File

@ -1,6 +1,6 @@
use crate::{
command::{
args::{arg_players::PlayersArgumentConsumer, Arg, ConsumedArgs},
args::{players::PlayersArgumentConsumer, Arg, ConsumedArgs},
tree::CommandTree,
tree_builder::argument,
CommandError, CommandExecutor, CommandSender,

View File

@ -3,7 +3,7 @@ use pumpkin_data::sound::SoundCategory;
use rand::{thread_rng, Rng};
use crate::command::{
args::{arg_sound::SoundArgumentConsumer, ConsumedArgs, FindArg},
args::{sound::SoundArgumentConsumer, ConsumedArgs, FindArg},
tree::CommandTree,
tree_builder::argument,
CommandError, CommandExecutor, CommandSender,

View File

@ -6,7 +6,7 @@ use pumpkin_util::{
use crate::{
command::{
args::{arg_simple::SimpleArgConsumer, Arg, ConsumedArgs},
args::{simple::SimpleArgConsumer, Arg, ConsumedArgs},
tree::CommandTree,
tree_builder::{argument, literal, require},
CommandError, CommandExecutor, CommandSender,

View File

@ -3,7 +3,7 @@ use pumpkin_data::world::SAY_COMMAND;
use pumpkin_util::text::TextComponent;
use crate::command::{
args::{arg_message::MsgArgConsumer, Arg, ConsumedArgs},
args::{message::MsgArgConsumer, Arg, ConsumedArgs},
tree::CommandTree,
tree_builder::argument,
CommandError, CommandExecutor, CommandSender,

View File

@ -36,19 +36,17 @@ impl CommandExecutor for PumpkinExecutor {
let seed = (seed as i64).to_string();
sender
.send_message(
TextComponent::text("Seed: [")
.add_child(
TextComponent::text(seed.clone())
.hover_event(HoverEvent::show_text(TextComponent::translate(
Cow::from("chat.copy.click"),
vec![],
)))
.click_event(ClickEvent::CopyToClipboard(Cow::from(seed)))
.color_named(NamedColor::Green),
)
.add_child(TextComponent::text("]")),
)
.send_message(TextComponent::translate(
"commands.seed.success",
[TextComponent::text(seed.clone())
.hover_event(HoverEvent::show_text(TextComponent::translate(
Cow::from("chat.copy.click"),
vec![],
)))
.click_event(ClickEvent::CopyToClipboard(Cow::from(seed)))
.color_named(NamedColor::Green)]
.into(),
))
.await;
Ok(())
}

View File

@ -1,8 +1,8 @@
use async_trait::async_trait;
use pumpkin_util::text::TextComponent;
use crate::command::args::arg_block::BlockArgumentConsumer;
use crate::command::args::arg_position_block::BlockPosArgumentConsumer;
use crate::command::args::block::BlockArgumentConsumer;
use crate::command::args::position_block::BlockPosArgumentConsumer;
use crate::command::args::{ConsumedArgs, FindArg};
use crate::command::tree::CommandTree;
use crate::command::tree_builder::{argument, literal};

View File

@ -0,0 +1,64 @@
use async_trait::async_trait;
use pumpkin_util::text::TextComponent;
use crate::{
command::{
args::{
position_3d::Position3DArgumentConsumer,
summonable_entities::SummonableEntitiesArgumentConsumer, ConsumedArgs, FindArg,
},
tree::CommandTree,
tree_builder::argument,
CommandError, CommandExecutor, CommandSender,
},
entity::mob,
};
const NAMES: [&str; 1] = ["summon"];
const DESCRIPTION: &str = "Spawns a Entity at position.";
const ARG_ENTITY: &str = "entity";
const ARG_POS: &str = "pos";
struct SummonExecutor;
#[async_trait]
impl CommandExecutor for SummonExecutor {
async fn execute<'a>(
&self,
sender: &mut CommandSender<'a>,
server: &crate::server::Server,
args: &ConsumedArgs<'a>,
) -> Result<(), CommandError> {
let entity = SummonableEntitiesArgumentConsumer::find_arg(args, ARG_ENTITY)?;
let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS);
// 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 (mob, uuid) = mob::from_type(entity, server, pos, player.world()).await;
player
.world()
.broadcast_packet_all(&mob.living_entity.entity.create_spawn_packet(uuid))
.await;
sender
.send_message(TextComponent::translate(
"commands.summon.success",
[TextComponent::text(format!("{entity:?}"))].into(),
))
.await;
}
Ok(())
}
}
pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_ENTITY, SummonableEntitiesArgumentConsumer)
.execute(SummonExecutor)
.then(argument(ARG_POS, Position3DArgumentConsumer).execute(SummonExecutor)),
// TODO: Add NBT
)
}

View File

@ -2,10 +2,10 @@ use async_trait::async_trait;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::text::TextComponent;
use crate::command::args::arg_entities::EntitiesArgumentConsumer;
use crate::command::args::arg_entity::EntityArgumentConsumer;
use crate::command::args::arg_position_3d::Position3DArgumentConsumer;
use crate::command::args::arg_rotation::RotationArgumentConsumer;
use crate::command::args::entities::EntitiesArgumentConsumer;
use crate::command::args::entity::EntityArgumentConsumer;
use crate::command::args::position_3d::Position3DArgumentConsumer;
use crate::command::args::rotation::RotationArgumentConsumer;
use crate::command::args::ConsumedArgs;
use crate::command::args::FindArg;
use crate::command::tree::CommandTree;

View File

@ -2,7 +2,7 @@ use async_trait::async_trait;
use pumpkin_util::text::color::{Color, NamedColor};
use pumpkin_util::text::TextComponent;
use crate::command::args::arg_bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::FindArgDefaultName;
use crate::command::tree_builder::{argument_default_name, literal};
use crate::command::{

View File

@ -5,7 +5,7 @@ use pumpkin_util::text::TextComponent;
use crate::{
command::{
args::{
arg_players::PlayersArgumentConsumer, arg_textcomponent::TextComponentArgConsumer, Arg,
players::PlayersArgumentConsumer, textcomponent::TextComponentArgConsumer, Arg,
ConsumedArgs, FindArg,
},
tree::CommandTree,

View File

@ -4,9 +4,9 @@ use pumpkin_protocol::codec::var_int::VarInt;
use pumpkin_util::text::color::{Color, NamedColor};
use pumpkin_util::text::TextComponent;
use crate::command::args::arg_bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::arg_players::PlayersArgumentConsumer;
use crate::command::args::arg_simple::SimpleArgConsumer;
use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::players::PlayersArgumentConsumer;
use crate::command::args::simple::SimpleArgConsumer;
use crate::command::args::{Arg, FindArgDefaultName};
use crate::command::dispatcher::CommandError::{InvalidConsumption, InvalidRequirement};
use crate::command::tree_builder::{argument, argument_default_name, require};

View File

@ -10,9 +10,8 @@ use pumpkin_util::{
use crate::{
command::{
args::{
arg_bounded_num::BoundedNumArgumentConsumer,
arg_position_2d::Position2DArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer,
FindArgDefaultName,
bounded_num::BoundedNumArgumentConsumer, position_2d::Position2DArgumentConsumer,
ConsumedArgs, DefaultNameArgConsumer, FindArgDefaultName,
},
tree::CommandTree,
tree_builder::{argument_default_name, literal},

View File

@ -1,8 +1,8 @@
use std::fmt;
use std::sync::Arc;
use crate::command::commands::cmd_seed;
use crate::command::commands::{cmd_bossbar, cmd_transfer};
use crate::command::commands::seed;
use crate::command::commands::{bossbar, transfer};
use crate::command::dispatcher::CommandDispatcher;
use crate::entity::player::Player;
use crate::server::Server;
@ -10,9 +10,8 @@ use crate::world::World;
use args::ConsumedArgs;
use async_trait::async_trait;
use commands::{
cmd_clear, cmd_deop, cmd_fill, cmd_gamemode, cmd_give, cmd_help, cmd_kick, cmd_kill, cmd_list,
cmd_me, cmd_op, cmd_playsound, cmd_plugin, cmd_plugins, cmd_pumpkin, cmd_say, cmd_setblock,
cmd_stop, cmd_teleport, cmd_time, cmd_title, cmd_worldborder,
clear, deop, fill, gamemode, give, help, kick, kill, list, me, op, playsound, plugin, plugins,
pumpkin, say, setblock, stop, summon, teleport, time, title, worldborder,
};
use dispatcher::CommandError;
use pumpkin_util::math::vector3::Vector3;
@ -112,31 +111,32 @@ impl CommandSender<'_> {
pub fn default_dispatcher() -> CommandDispatcher {
let mut dispatcher = CommandDispatcher::default();
dispatcher.register(cmd_pumpkin::init_command_tree(), PermissionLvl::Zero);
dispatcher.register(cmd_bossbar::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_say::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_gamemode::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_stop::init_command_tree(), PermissionLvl::Four);
dispatcher.register(cmd_help::init_command_tree(), PermissionLvl::Zero);
dispatcher.register(cmd_kill::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_kick::init_command_tree(), PermissionLvl::Three);
dispatcher.register(cmd_plugin::init_command_tree(), PermissionLvl::Three);
dispatcher.register(cmd_plugins::init_command_tree(), PermissionLvl::Three);
dispatcher.register(cmd_worldborder::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_teleport::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_time::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_give::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_list::init_command_tree(), PermissionLvl::Zero);
dispatcher.register(cmd_clear::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_setblock::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_seed::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_transfer::init_command_tree(), PermissionLvl::Zero);
dispatcher.register(cmd_fill::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_op::init_command_tree(), PermissionLvl::Three);
dispatcher.register(cmd_deop::init_command_tree(), PermissionLvl::Three);
dispatcher.register(cmd_me::init_command_tree(), PermissionLvl::Zero);
dispatcher.register(cmd_playsound::init_command_tree(), PermissionLvl::Two);
dispatcher.register(cmd_title::init_command_tree(), PermissionLvl::Two);
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(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(teleport::init_command_tree(), PermissionLvl::Two);
dispatcher.register(time::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(setblock::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(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(title::init_command_tree(), PermissionLvl::Two);
dispatcher.register(summon::init_command_tree(), PermissionLvl::Two);
dispatcher
}

View File

@ -168,7 +168,7 @@ pub fn argument_default_name(
) -> NonLeafNodeBuilder {
NonLeafNodeBuilder {
node_type: NodeType::Argument {
name: consumer.default_name(),
name: consumer.default_name().to_string(),
consumer: Arc::new(consumer),
},
child_nodes: Vec::new(),

View File

@ -1,7 +1,6 @@
use std::sync::Arc;
use pumpkin_data::entity::EntityType;
use pumpkin_protocol::{client::play::CSpawnEntity, codec::var_int::VarInt};
use pumpkin_util::math::vector3::Vector3;
use tokio::sync::Mutex;
use uuid::Uuid;
@ -61,25 +60,4 @@ impl MobEntity {
pub async fn goal<T: Goal + 'static>(&self, goal: T) {
self.goals.lock().await.push((Arc::new(goal), false));
}
pub fn create_spawn_entity_packet(&self, uuid: Uuid) -> CSpawnEntity {
let e = &self.living_entity.entity;
let entity_loc = e.pos.load();
let entity_vel = e.velocity.load();
CSpawnEntity::new(
VarInt(e.entity_id),
uuid,
VarInt((e.entity_type) as i32),
entity_loc.x,
entity_loc.y,
entity_loc.z,
e.pitch.load(),
e.yaw.load(),
e.yaw.load(), // todo: head_yaw and yaw are swapped, find out why
0.into(),
entity_vel.x as f32,
entity_vel.y as f32,
entity_vel.z as f32,
)
}
}

View File

@ -6,7 +6,9 @@ use crossbeam::atomic::AtomicCell;
use pumpkin_data::entity::{EntityPose, EntityType};
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
use pumpkin_protocol::{
client::play::{CHeadRot, CSetEntityMetadata, CTeleportEntity, CUpdateEntityRot, Metadata},
client::play::{
CHeadRot, CSetEntityMetadata, CSpawnEntity, CTeleportEntity, CUpdateEntityRot, Metadata,
},
codec::var_int::VarInt,
};
use pumpkin_util::math::{
@ -17,6 +19,7 @@ use pumpkin_util::math::{
vector3::Vector3,
wrap_degrees,
};
use uuid::Uuid;
use crate::world::World;
@ -206,6 +209,26 @@ impl Entity {
self.world.remove_entity(self).await;
}
pub fn create_spawn_packet(&self, uuid: Uuid) -> CSpawnEntity {
let entity_loc = self.pos.load();
let entity_vel = self.velocity.load();
CSpawnEntity::new(
VarInt(self.entity_id),
uuid,
VarInt((self.entity_type) as i32),
entity_loc.x,
entity_loc.y,
entity_loc.z,
self.pitch.load(),
self.yaw.load(),
self.head_yaw.load(), // todo: head_yaw and yaw are swapped, find out why
0.into(),
entity_vel.x as f32,
entity_vel.y as f32,
entity_vel.z as f32,
)
}
/// Applies knockback to the entity, following vanilla Minecraft's mechanics.
///
/// This function calculates the entity's new velocity based on the specified knockback strength and direction.

View File

@ -538,7 +538,7 @@ impl Player {
let mobs = world.current_living_mobs.lock().await.clone();
for (uuid, mob) in mobs {
self.client
.send_packet(&mob.create_spawn_entity_packet(uuid))
.send_packet(&mob.living_entity.entity.create_spawn_packet(uuid))
.await;
}
}

View File

@ -1095,12 +1095,13 @@ impl Player {
// create rotation like Vanilla
let yaw = wrap_degrees(rand::random::<f32>() * 360.0) % 360.0;
let world = self.world();
// create new mob and uuid based on spawn egg id
let (mob, uuid) = mob::from_type(
EntityType::from_raw(*spawn_item_id).unwrap(),
server,
pos,
self.world(),
world,
)
.await;
@ -1108,8 +1109,8 @@ impl Player {
mob.living_entity.entity.set_rotation(yaw, 0.0);
// broadcast new mob to all players
server
.broadcast_packet_all(&mob.create_spawn_entity_packet(uuid))
world
.broadcast_packet_all(&mob.living_entity.entity.create_spawn_packet(uuid))
.await;
// TODO: send/configure additional commands/data based on type of entity (horse, slime, etc)