Skip to content

Commit bf7663b

Browse files
feat: add amount to /give (#525)
Co-authored-by: Andrew Gazelka <andrew.gazelka@gmail.com>
1 parent c85f961 commit bf7663b

File tree

2 files changed

+121
-27
lines changed

2 files changed

+121
-27
lines changed

events/proof-of-concept/src/module/command.rs

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,21 @@ use std::borrow::Cow;
22

33
use flecs_ecs::prelude::*;
44
use hyperion::{
5+
chat,
56
egress::player_join::{PlayerListActions, PlayerListEntry, PlayerListS2c},
67
net::{Compose, NetworkStreamRef},
78
simulation::{
89
blocks::Blocks,
9-
command::{add_command, get_root_command, Command, Parser},
10-
event, Health, InGameName, Position, Uuid,
10+
command::{add_command, cmd_with, get_root_command, Command, Parser},
11+
event, Health, IgnMap, InGameName, Position, Uuid,
1112
},
1213
storage::EventQueue,
1314
system_registry::SystemId,
1415
uuid,
16+
valence_ident::ident,
1517
valence_protocol::{
1618
self,
1719
game_mode::OptGameMode,
18-
ident,
1920
math::IVec3,
2021
nbt,
2122
packets::play::{
@@ -43,9 +44,26 @@ pub fn add_to_tree(world: &World) {
4344
// add to tree
4445
add_command(world, Command::literal("team"), root_command);
4546
add_command(world, Command::literal("zombie"), root_command);
46-
add_command(world, Command::literal("give"), root_command);
4747
add_command(world, Command::literal("upgrade"), root_command);
4848

49+
cmd_with(world, "give", |scope| {
50+
scope.argument_with(
51+
"player",
52+
Parser::Entity {
53+
single: true,
54+
only_players: true,
55+
},
56+
|scope| {
57+
scope.argument_with("", Parser::ItemStack, |scope| {
58+
scope.argument("count", Parser::Integer {
59+
min: Some(1),
60+
max: None,
61+
});
62+
});
63+
},
64+
);
65+
});
66+
4967
let speed = add_command(world, Command::literal("speed"), root_command);
5068
add_command(
5169
world,
@@ -101,6 +119,7 @@ struct CommandContext<'a> {
101119
inventory: &'a mut PlayerInventory,
102120
level: &'a mut Level,
103121
health: &'a mut Health,
122+
ign_map: &'a IgnMap,
104123
}
105124

106125
fn process_command(command: &ParsedCommand, context: &mut CommandContext<'_>) {
@@ -109,7 +128,11 @@ fn process_command(command: &ParsedCommand, context: &mut CommandContext<'_>) {
109128
ParsedCommand::Team => handle_team_command(context),
110129
ParsedCommand::Zombie => handle_zombie_command(context),
111130
ParsedCommand::Dirt { x, y, z } => handle_dirt_command(*x, *y, *z, context),
112-
ParsedCommand::Give => handle_give_command(context),
131+
ParsedCommand::Give {
132+
username,
133+
item,
134+
count,
135+
} => handle_give_command(username, item, *count, context),
113136
ParsedCommand::Upgrade => handle_upgrade_command(context),
114137
ParsedCommand::Stats(stat, amount) => handle_stats(*stat, *amount, context),
115138
ParsedCommand::Health(amount) => handle_health_command(*amount, context),
@@ -351,24 +374,39 @@ fn handle_stats(stat: Stat, amount: f32, context: &CommandContext<'_>) {
351374
});
352375
}
353376

354-
fn handle_give_command(context: &mut CommandContext<'_>) {
355-
let mut blue_wool_nbt = nbt::Compound::new();
377+
fn handle_give_command(username: &str, item_name: &str, count: i8, context: &CommandContext<'_>) {
378+
let Some(item) = ItemKind::from_str(item_name) else {
379+
let packet = chat!("Unknown item '{item_name:?}'");
380+
context
381+
.compose
382+
.unicast(&packet, context.stream, context.system_id, context.world)
383+
.unwrap();
384+
return;
385+
};
386+
387+
let Some(player) = context.ign_map.get(username) else {
388+
let chat = chat!("Player {username} does not exist");
389+
context
390+
.compose
391+
.unicast(&chat, context.stream, context.system_id, context.world)
392+
.unwrap();
393+
return;
394+
};
356395

357-
let can_place_on = [
358-
"minecraft:stone",
359-
"minecraft:dirt",
360-
"minecraft:grass_block",
361-
"minecraft:blue_wool",
362-
]
363-
.into_iter()
364-
.map(std::convert::Into::into)
365-
.collect();
396+
context
397+
.world
398+
.entity_from_id(*player)
399+
.get::<&mut PlayerInventory>(|inventory| {
400+
inventory.try_add_item(ItemStack::new(item, count, None));
401+
});
366402

367-
blue_wool_nbt.insert("CanPlaceOn", nbt::List::String(can_place_on));
403+
let name = item.to_str();
368404

405+
let packet = chat!("Gave {count} [{name}] to {username}");
369406
context
370-
.inventory
371-
.try_add_item(ItemStack::new(ItemKind::BlueWool, 4, Some(blue_wool_nbt)));
407+
.compose
408+
.unicast(&packet, context.stream, context.system_id, context.world)
409+
.unwrap();
372410
}
373411

374412
fn handle_dirt_command(x: i32, y: i32, z: i32, context: &mut CommandContext<'_>) {
@@ -603,9 +641,9 @@ impl Module for CommandModule {
603641

604642
let system_id = SystemId(8);
605643

606-
system!("handle_poc_events_player", world, &Compose($), &mut EventQueue<event::Command<'_>>($), &mut Blocks($))
644+
system!("handle_poc_events_player", world, &Compose($), &mut EventQueue<event::Command<'_>>($), &mut Blocks($), &IgnMap($))
607645
.multi_threaded()
608-
.each_iter(move |it: TableIter<'_, false>, _, (compose, event_queue, mc)| {
646+
.each_iter(move |it: TableIter<'_, false>, _, (compose, event_queue, mc, ign_map)| {
609647
let span = trace_span!("handle_poc_events_player");
610648
let _enter = span.enter();
611649

@@ -644,6 +682,7 @@ impl Module for CommandModule {
644682
level,
645683
health,
646684
position,
685+
ign_map,
647686
};
648687
process_command(&command, &mut context);
649688
},

events/proof-of-concept/src/module/command/parse.rs

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use nom::{
22
branch::alt,
3-
bytes::complete::{tag, take_until},
3+
bytes::complete::{tag, take_until, take_while1},
44
character::complete::space1,
5-
combinator::{map, map_res},
5+
combinator::{map, map_res, opt},
66
number::complete::float,
77
sequence::preceded,
88
IResult, Parser,
@@ -21,13 +21,33 @@ pub enum ParsedCommand {
2121
Speed(f32),
2222
Team,
2323
Zombie,
24-
Dirt { x: i32, y: i32, z: i32 },
25-
Give,
24+
Dirt {
25+
x: i32,
26+
y: i32,
27+
z: i32,
28+
},
29+
Give {
30+
username: String,
31+
item: String,
32+
count: i8,
33+
},
2634
Upgrade,
2735
Stats(Stat, f32),
2836
Health(f32),
2937
TpHere,
30-
Tp { x: f32, y: f32, z: f32 },
38+
Tp {
39+
x: f32,
40+
y: f32,
41+
z: f32,
42+
},
43+
}
44+
45+
fn is_valid_player_char(c: char) -> bool {
46+
c.is_alphanumeric() || c == '_'
47+
}
48+
49+
fn space1_str(input: &str) -> IResult<&str, &str> {
50+
space1::<&str, nom::error::Error<&str>>(input)
3151
}
3252

3353
fn parse_speed(input: &str) -> IResult<&str, ParsedCommand> {
@@ -62,7 +82,23 @@ fn parse_dirt(input: &str) -> IResult<&str, ParsedCommand> {
6282
}
6383

6484
fn parse_give(input: &str) -> IResult<&str, ParsedCommand> {
65-
map(tag("give"), |_| ParsedCommand::Give).parse(input)
85+
map(
86+
(
87+
tag("give "),
88+
take_while1(is_valid_player_char),
89+
preceded(
90+
space1_str,
91+
preceded(opt(tag("minecraft:")), take_while1(is_valid_player_char)),
92+
),
93+
opt(preceded(space1_str, nom::character::complete::i8)),
94+
),
95+
|(_, username, item, count)| ParsedCommand::Give {
96+
username: username.to_string(),
97+
item: item.to_string(),
98+
count: count.unwrap_or(1),
99+
},
100+
)
101+
.parse(input)
66102
}
67103

68104
fn parse_upgrade(input: &str) -> IResult<&str, ParsedCommand> {
@@ -124,3 +160,22 @@ pub fn command(input: &str) -> IResult<&str, ParsedCommand> {
124160
))
125161
.parse(input)
126162
}
163+
164+
#[cfg(test)]
165+
mod tests {
166+
use super::*;
167+
168+
#[test]
169+
fn test_parse_give_command() {
170+
let input = "give Cuz_Im_Clicks minecraft:dirt 64";
171+
let result = parse_give(input);
172+
assert!(result.is_ok());
173+
}
174+
175+
#[test]
176+
fn test_parse_give_command_no_minecraft() {
177+
let input = "give Cuz_Im_Clicks acacia_button 64";
178+
let result = parse_give(input);
179+
assert!(result.is_ok());
180+
}
181+
}

0 commit comments

Comments
 (0)