Skip to content

Commit

Permalink
Add /give Command (#234)
Browse files Browse the repository at this point in the history
* add prototype for /give command

* fix /give namespace

* fix accidentally replaced text

* remove dbg! statements

* replace block_registry with item_registry

* sync added items with client

* warn!(..) => log::warn!(..)

---------

Co-authored-by: user622628252416 <[email protected]>
  • Loading branch information
user622628252416 and user622628252416 authored Nov 7, 2024
1 parent 41e71a8 commit 16dabf1
Show file tree
Hide file tree
Showing 16 changed files with 470 additions and 74 deletions.
7 changes: 7 additions & 0 deletions pumpkin-inventory/src/player.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::slice::IterMut;
use std::sync::atomic::AtomicU32;

use crate::container_click::MouseClick;
Expand Down Expand Up @@ -25,6 +26,8 @@ impl Default for PlayerInventory {
}

impl PlayerInventory {
pub const CONTAINER_ID: i8 = 0;

pub fn new() -> Self {
Self {
crafting: [None; 4],
Expand Down Expand Up @@ -134,6 +137,10 @@ impl PlayerInventory {
slots.push(&mut self.offhand);
slots
}

pub fn iter_items_mut(&mut self) -> IterMut<Option<ItemStack>> {
self.items.iter_mut()
}
}

impl Container for PlayerInventory {
Expand Down
7 changes: 7 additions & 0 deletions pumpkin-protocol/src/slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,10 @@ impl From<Option<&ItemStack>> for Slot {
item.map(Slot::from).unwrap_or(Slot::empty())
}
}

impl From<&Option<ItemStack>> for Slot {
fn from(item: &Option<ItemStack>) -> Self {
item.map(|stack| Self::from(&stack))
.unwrap_or(Slot::empty())
}
}
14 changes: 9 additions & 5 deletions pumpkin-world/src/item/item_registry.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
use std::{collections::HashMap, sync::LazyLock};
use std::sync::LazyLock;

use serde::Deserialize;

const ITEMS_JSON: &str = include_str!("../../../assets/items.json");

pub static ITEMS: LazyLock<HashMap<String, Item>> = LazyLock::new(|| {
pub static ITEMS: LazyLock<Vec<Item>> = LazyLock::new(|| {
serde_json::from_str(ITEMS_JSON).expect("Could not parse items.json registry.")
});

pub fn get_item<'a>(name: &str) -> Option<&'a Item> {
ITEMS.iter().find(|item| item.name == name)
}

#[expect(dead_code)]
#[derive(Deserialize, Clone, Debug)]
pub struct Item {
id: u16,
name: String,
pub id: u16,
pub name: String,
translation_key: String,
max_stack: i8,
pub max_stack: i8,
max_durability: u16,
break_sound: String,
food: Option<FoodComponent>,
Expand Down
11 changes: 10 additions & 1 deletion pumpkin-world/src/item/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod item_categories;
mod item_registry;
pub mod item_registry;
pub use item_registry::ITEMS;
#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
Expand All @@ -24,3 +24,12 @@ impl PartialEq for ItemStack {
self.item_id == other.item_id
}
}

impl ItemStack {
pub fn new(item_count: u8, item_id: u16) -> Self {
Self {
item_count,
item_id,
}
}
}
104 changes: 62 additions & 42 deletions pumpkin/src/command/args/arg_bounded_num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ use super::super::args::ArgumentConsumer;
use super::{Arg, DefaultNameArgConsumer, FindArg};

/// Consumes a single generic num, but only if it's in bounds.
pub(crate) struct BoundedNumArgumentConsumer<T: ArgNum> {
pub(crate) struct BoundedNumArgumentConsumer<T: ToFromNumber> {
min_inclusive: Option<T>,
max_inclusive: Option<T>,
name: Option<&'static str>,
}

#[async_trait]
impl<T: ArgNum> ArgumentConsumer for BoundedNumArgumentConsumer<T> {
impl<T: ToFromNumber> ArgumentConsumer for BoundedNumArgumentConsumer<T> {
async fn consume<'a>(
&self,
_src: &CommandSender<'a>,
Expand All @@ -30,39 +30,59 @@ impl<T: ArgNum> ArgumentConsumer for BoundedNumArgumentConsumer<T> {

if let Some(max) = self.max_inclusive {
if x > max {
return None;
return Some(Arg::Num(Err(())));
}
}

if let Some(min) = self.min_inclusive {
if x < min {
return None;
return Some(Arg::Num(Err(())));
}
}

Some(x.to_arg())
Some(Arg::Num(Ok(x.to_number())))
}
}

impl<'a, T: ArgNum> FindArg<'a> for BoundedNumArgumentConsumer<T> {
type Data = T;
impl<'a, T: ToFromNumber> FindArg<'a> for BoundedNumArgumentConsumer<T> {
type Data = Result<T, NotInBounds>;

fn find_arg(args: &super::ConsumedArgs, name: &str) -> Result<Self::Data, InvalidTreeError> {
match args.get(name) {
Some(arg) => match T::from_arg(arg) {
Some(x) => Ok(x),
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
name.to_string(),
))),
},
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
let Some(Arg::Num(result)) = args.get(name) else {
return Err(InvalidTreeError::InvalidConsumptionError(Some(
name.to_string(),
))),
}
)));
};

let data: Self::Data = match result {
Ok(num) => {
if let Some(x) = T::from_number(num) {
Ok(x)
} else {
return Err(InvalidTreeError::InvalidConsumptionError(Some(
name.to_string(),
)));
}
}
Err(()) => Err(()),
};

Ok(data)
}
}

impl<T: ArgNum> BoundedNumArgumentConsumer<T> {
pub(crate) type NotInBounds = ();

#[derive(Clone, Copy)]
pub(crate) enum Number {
F64(f64),
F32(f32),
I32(i32),
#[allow(unused)]
U32(u32),
}

impl<T: ToFromNumber> BoundedNumArgumentConsumer<T> {
pub(crate) const fn new() -> Self {
Self {
min_inclusive: None,
Expand All @@ -88,64 +108,64 @@ impl<T: ArgNum> BoundedNumArgumentConsumer<T> {
}
}

pub(crate) trait ArgNum: PartialOrd + Copy + Send + Sync + FromStr {
fn to_arg<'a>(self) -> Arg<'a>;
fn from_arg(arg: &Arg<'_>) -> Option<Self>;
pub(crate) trait ToFromNumber: PartialOrd + Copy + Send + Sync + FromStr {
fn to_number(self) -> Number;
fn from_number(arg: &Number) -> Option<Self>;
}

impl ArgNum for f64 {
fn to_arg<'a>(self) -> Arg<'a> {
Arg::F64(self)
impl ToFromNumber for f64 {
fn to_number(self) -> Number {
Number::F64(self)
}

fn from_arg(arg: &Arg<'_>) -> Option<Self> {
fn from_number(arg: &Number) -> Option<Self> {
match arg {
Arg::F64(x) => Some(*x),
Number::F64(x) => Some(*x),
_ => None,
}
}
}

impl ArgNum for f32 {
fn to_arg<'a>(self) -> Arg<'a> {
Arg::F32(self)
impl ToFromNumber for f32 {
fn to_number(self) -> Number {
Number::F32(self)
}

fn from_arg(arg: &Arg<'_>) -> Option<Self> {
fn from_number(arg: &Number) -> Option<Self> {
match arg {
Arg::F32(x) => Some(*x),
Number::F32(x) => Some(*x),
_ => None,
}
}
}

impl ArgNum for i32 {
fn to_arg<'a>(self) -> Arg<'a> {
Arg::I32(self)
impl ToFromNumber for i32 {
fn to_number(self) -> Number {
Number::I32(self)
}

fn from_arg(arg: &Arg<'_>) -> Option<Self> {
fn from_number(arg: &Number) -> Option<Self> {
match arg {
Arg::I32(x) => Some(*x),
Number::I32(x) => Some(*x),
_ => None,
}
}
}

impl ArgNum for u32 {
fn to_arg<'a>(self) -> Arg<'a> {
Arg::U32(self)
impl ToFromNumber for u32 {
fn to_number(self) -> Number {
Number::U32(self)
}

fn from_arg(arg: &Arg<'_>) -> Option<Self> {
fn from_number(arg: &Number) -> Option<Self> {
match arg {
Arg::U32(x) => Some(*x),
Number::U32(x) => Some(*x),
_ => None,
}
}
}

impl<T: ArgNum> DefaultNameArgConsumer for BoundedNumArgumentConsumer<T> {
impl<T: ToFromNumber> DefaultNameArgConsumer for BoundedNumArgumentConsumer<T> {
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.")
Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/command/args/arg_entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::entity::player::Player;
use crate::server::Server;

use super::super::args::ArgumentConsumer;
use super::arg_player::PlayersArgumentConsumer;
use super::arg_players::PlayersArgumentConsumer;
use super::{Arg, DefaultNameArgConsumer, FindArg};

/// todo: implement (currently just calls [`super::arg_player::PlayerArgumentConsumer`])
Expand Down
60 changes: 60 additions & 0 deletions pumpkin/src/command/args/arg_item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use async_trait::async_trait;

use crate::{command::dispatcher::InvalidTreeError, server::Server};

use super::{
super::{
args::{ArgumentConsumer, RawArgs},
CommandSender,
},
Arg, DefaultNameArgConsumer, FindArg,
};

pub(crate) struct ItemArgumentConsumer;

#[async_trait]
impl ArgumentConsumer for ItemArgumentConsumer {
async fn consume<'a>(
&self,
_sender: &CommandSender<'a>,
_server: &'a Server,
args: &mut RawArgs<'a>,
) -> Option<Arg<'a>> {
let s = args.pop()?;

let name = if s.contains(':') {
s.to_string()
} else {
format!("minecraft:{s}")
};

// todo: get an actual item
Some(Arg::Item(name))
}
}

impl DefaultNameArgConsumer for ItemArgumentConsumer {
fn default_name(&self) -> &'static str {
"item"
}

fn get_argument_consumer(&self) -> &dyn ArgumentConsumer {
self
}
}

impl<'a> FindArg<'a> for ItemArgumentConsumer {
type Data = &'a str;

fn find_arg(
args: &'a super::ConsumedArgs,
name: &'a str,
) -> Result<Self::Data, InvalidTreeError> {
match args.get(name) {
Some(Arg::Item(name)) => Ok(name),
_ => Err(InvalidTreeError::InvalidConsumptionError(Some(
name.to_string(),
))),
}
}
}
File renamed without changes.
11 changes: 5 additions & 6 deletions pumpkin/src/command/args/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{collections::HashMap, hash::Hash, sync::Arc};

use arg_bounded_num::{NotInBounds, Number};
use async_trait::async_trait;
use pumpkin_core::{
math::{vector2::Vector2, vector3::Vector3},
Expand All @@ -19,8 +20,9 @@ pub(crate) mod arg_command;
pub(crate) mod arg_entities;
pub(crate) mod arg_entity;
pub(crate) mod arg_gamemode;
pub(crate) mod arg_item;
pub(crate) mod arg_message;
pub(crate) mod arg_player;
pub(crate) mod arg_players;
pub(crate) mod arg_position_2d;
pub(crate) mod arg_position_3d;
pub(crate) mod arg_rotation;
Expand Down Expand Up @@ -55,12 +57,9 @@ pub(crate) enum Arg<'a> {
Rotation(f32, f32),
GameMode(GameMode),
CommandTree(&'a CommandTree<'a>),
Item(String),
Msg(String),
F64(f64),
F32(f32),
I32(i32),
#[allow(unused)]
U32(u32),
Num(Result<Number, NotInBounds>),
#[allow(unused)]
Simple(String),
}
Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/command/commands/cmd_gamemode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::command::args::GetCloned;

use crate::TextComponent;

use crate::command::args::arg_player::PlayersArgumentConsumer;
use crate::command::args::arg_players::PlayersArgumentConsumer;

use crate::command::args::{Arg, ConsumedArgs};
use crate::command::dispatcher::InvalidTreeError;
Expand Down
Loading

0 comments on commit 16dabf1

Please sign in to comment.