Skip to content

Commit 2291e0d

Browse files
MrSteppyLeonard SteppyIntelli
authored
#810 Add option to give looked up items into inventory (#811)
* Add givable items to ItemUtils * Improve performance of givable items methods * Add ItemUtils#getItemStack * Add ChatUtils#createGiveItemComponent * Fix ChatUtils#createGiveItemComponent not returning empty String when no id is given * Add 'give item' components to StandardLookupThread * Add GivableItemIdParser * Add CommandParser#parseGivableItemId * Add Phrase#INVALID_ITEM_ID * Add GiveCommand * Update permissions.md * Fix itemId parsing * Improve performance of givable items methods * Update coreprotect.give permission to be false by default * Only show give component if player has permission to use the command --------- Co-authored-by: Leonard Steppy <leonard.steppy@outlook.de> Co-authored-by: Intelli <6790859+Intelli@users.noreply.github.com>
1 parent a8aeeca commit 2291e0d

11 files changed

Lines changed: 121 additions & 24 deletions

File tree

docs/permissions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ The following permissions can be used to restrict functionality within the plugi
3939
* **coreprotect.consumer** *(default: op)*
4040
Allows access to the CoreProtect consumer command.
4141
&nbsp;
42+
* **coreprotect.give** *(default: false)*
43+
Allows access to the CoreProtect give command.
44+
&nbsp;
4245
* **coreprotect.networking** *(default: op)*
4346
Allows access to the CoreProtect networking API.
4447

src/main/java/net/coreprotect/command/CommandHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ else if (user.hasPermission("coreprotect.consumer") && corecommand.equals("consu
7676
else if (user.hasPermission("coreprotect.networking") && corecommand.equals("network-debug")) {
7777
permission = true;
7878
}
79+
else if (user.hasPermission("coreprotect.give") && corecommand.equals("give")) {
80+
permission = true;
81+
}
7982
}
8083

8184
if (corecommand.equals("rollback") || corecommand.equals("restore") || corecommand.equals("rb") || corecommand.equals("rs") || corecommand.equals("ro") || corecommand.equals("re")) {
@@ -120,6 +123,9 @@ else if (corecommand.equals("consumer")) {
120123
else if (corecommand.equals("network-debug")) {
121124
NetworkDebugCommand.runCommand(user, permission, argumentArray);
122125
}
126+
else if (corecommand.equals("give")) {
127+
GiveCommand.runCommand(user, command, permission, argumentArray);
128+
}
123129
else if (corecommand.equals("migrate-db")) {
124130
if (!VersionUtils.validDonationKey()) {
125131
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DONATION_KEY_REQUIRED));

src/main/java/net/coreprotect/command/CommandParser.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,11 @@
55
import java.util.Map;
66
import java.util.Set;
77

8+
import net.coreprotect.command.parser.*;
89
import org.bukkit.Location;
910
import org.bukkit.Material;
1011
import org.bukkit.command.CommandSender;
1112

12-
import net.coreprotect.command.parser.ActionParser;
13-
import net.coreprotect.command.parser.LocationParser;
14-
import net.coreprotect.command.parser.MaterialParser;
15-
import net.coreprotect.command.parser.TimeParser;
16-
import net.coreprotect.command.parser.UserParser;
17-
import net.coreprotect.command.parser.WorldParser;
18-
1913
/**
2014
* Main parser class for CoreProtect commands.
2115
* Delegates to specialized parser classes for specific functionality.
@@ -326,4 +320,7 @@ private static String timeString(BigDecimal input) {
326320
return input.stripTrailingZeros().toPlainString();
327321
}
328322

323+
protected static Integer parseGivableItemId(String[] inputArguments) {
324+
return GivableItemIdParser.parseGivableItemId(inputArguments);
325+
}
329326
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package net.coreprotect.command;
2+
3+
import net.coreprotect.language.Phrase;
4+
import net.coreprotect.utility.Chat;
5+
import net.coreprotect.utility.ChatMessage;
6+
import net.coreprotect.utility.Color;
7+
import net.coreprotect.utility.ItemUtils;
8+
import org.bukkit.command.Command;
9+
import org.bukkit.command.CommandSender;
10+
import org.bukkit.entity.Player;
11+
import org.bukkit.inventory.ItemStack;
12+
13+
public class GiveCommand {
14+
public static void runCommand(CommandSender sender, Command command, boolean permission, String[] args) {
15+
if (!permission) {
16+
Chat.sendMessage(sender, new ChatMessage(Phrase.build(Phrase.NO_PERMISSION)).build());
17+
return;
18+
}
19+
20+
Integer itemId = CommandParser.parseGivableItemId(args);
21+
if (itemId == null) {
22+
Chat.sendMessage(sender, new ChatMessage(Phrase.build(Phrase.MISSING_PARAMETERS, Color.WHITE, "/" + command.getName() + " give <itemId>")).build());
23+
return;
24+
}
25+
26+
ItemStack item = ItemUtils.getGivableItem(itemId);
27+
if (item == null) {
28+
Chat.sendMessage(sender, new ChatMessage(Phrase.build(Phrase.INVALID_ITEM_ID)).build());
29+
return;
30+
}
31+
32+
if (!(sender instanceof Player)) {
33+
Chat.sendMessage(sender, new ChatMessage(Phrase.build(Phrase.ACTION_NOT_SUPPORTED)).build());
34+
return;
35+
}
36+
37+
Player player = (Player) sender;
38+
player.getInventory().addItem(item);
39+
}
40+
}

src/main/java/net/coreprotect/command/lookup/StandardLookupThread.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ else if (LookupActions.isInventoryLookup(actions)) {
292292
String dname = StringUtils.nameFilter(blockType.name().toLowerCase(Locale.ROOT), ddata);
293293
byte[] metadata = data[11] == null ? null : data[11].getBytes(StandardCharsets.ISO_8859_1);
294294
String tooltip = ItemUtils.getEnchantments(metadata, dtype, amount);
295+
Integer itemId = ItemUtils.makeGivableItem(ItemUtils.getItemStack(metadata, dtype, amount));
295296

296297
String selector = Selector.FIRST;
297298
String tag = Color.WHITE + "-";
@@ -320,7 +321,7 @@ else if (daction == ItemTransactionActions.SELL || daction == ItemTransactionAct
320321
tag = (daction == ItemTransactionActions.REMOVE ? Color.GREEN + "+" : Color.RED + "-");
321322
}
322323

323-
Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(Phrase.LOOKUP_CONTAINER, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, "x" + amount, ChatUtils.createTooltip(Color.DARK_AQUA + rbd + dname, tooltip) + Color.WHITE, selector));
324+
Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(Phrase.LOOKUP_CONTAINER, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, "x" + amount, ChatUtils.createTooltip(Color.DARK_AQUA + rbd + dname, tooltip) + ChatUtils.filterComponent(player.hasPermission("coreprotect.give"), ChatUtils.createGiveItemComponent(Color.GREY + "(↓)", command.getName(), itemId)) + Color.WHITE, selector));
324325
PluginChannelListener.getInstance().sendData(player, Integer.parseInt(time), Phrase.LOOKUP_CONTAINER, selector, dplayer, dname, amount, dataX, dataY, dataZ, wid, rbd, true, tag.contains("+"));
325326
}
326327
}
@@ -389,6 +390,7 @@ else if (daction == ItemTransactionActions.SELL || daction == ItemTransactionAct
389390
if (actions.contains(LookupActions.CONTAINER) || actions.contains(5) || actions.contains(LookupActions.ITEM) || amount > -1) {
390391
byte[] metadata = data[11] == null ? null : data[11].getBytes(StandardCharsets.ISO_8859_1);
391392
String tooltip = ItemUtils.getEnchantments(metadata, dtype, amount);
393+
Integer itemId = ItemUtils.makeGivableItem(ItemUtils.getItemStack(metadata, dtype, amount));
392394

393395
if (daction == ItemTransactionActions.DROP || daction == ItemTransactionActions.PICKUP) {
394396
phrase = Phrase.LOOKUP_ITEM; // {picked up|dropped}
@@ -415,7 +417,7 @@ else if (daction == ItemTransactionActions.THROW || daction == ItemTransactionAc
415417
action = "a:container";
416418
}
417419

418-
Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(phrase, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, "x" + amount, ChatUtils.createTooltip(Color.DARK_AQUA + rbd + dname, tooltip) + Color.WHITE, selector));
420+
Chat.sendComponent(player, timeago + " " + tag + " " + Phrase.build(phrase, Color.DARK_AQUA + rbd + dplayer + Color.WHITE + rbd, "x" + amount, ChatUtils.createTooltip(Color.DARK_AQUA + rbd + dname, tooltip) + ChatUtils.filterComponent(player.hasPermission("coreprotect.give"), ChatUtils.createGiveItemComponent(Color.GREY + "(↓)", command.getName(), itemId)) + Color.WHITE, selector));
419421
PluginChannelListener.getInstance().sendData(player, Integer.parseInt(time), phrase, selector, dplayer, dname, (tag.contains("+") ? 1 : -1), dataX, dataY, dataZ, wid, rbd, action.contains("container"), tag.contains("+"));
420422
}
421423
else {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package net.coreprotect.command.parser;
2+
3+
import java.util.regex.Matcher;
4+
import java.util.regex.Pattern;
5+
6+
public class GivableItemIdParser {
7+
8+
protected static final Pattern PATTERN = Pattern.compile("#([0-9]+)");
9+
10+
public static Integer parseGivableItemId(String[] inputArguments) {
11+
for (String argument : inputArguments) {
12+
Matcher matcher = PATTERN.matcher(argument);
13+
if (matcher.find()) {
14+
return Integer.parseInt(matcher.group(1));
15+
}
16+
}
17+
return null;
18+
}
19+
}

src/main/java/net/coreprotect/language/Language.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ public static void loadPhrases() {
127127
phrases.put(Phrase.INVALID_INCLUDE, "\"{0}\" is an invalid block/entity name.");
128128
phrases.put(Phrase.INVALID_INCLUDE_COMBO, "That is an invalid block/entity combination.");
129129
phrases.put(Phrase.INVALID_PARAMETER, "\"{0}\" is not a supported parameter.");
130+
phrases.put(Phrase.INVALID_ITEM_ID, "Please enter a valid item id.");
130131
phrases.put(Phrase.INVALID_RADIUS, "Please enter a valid radius.");
131132
phrases.put(Phrase.INVALID_SELECTION, "{0} selection not found.");
132133
phrases.put(Phrase.INVALID_USERNAME, "\"{0}\" is an invalid username.");

src/main/java/net/coreprotect/language/Phrase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public enum Phrase {
110110
INVALID_INCLUDE,
111111
INVALID_INCLUDE_COMBO,
112112
INVALID_PARAMETER,
113+
INVALID_ITEM_ID,
113114
INVALID_RADIUS,
114115
INVALID_SELECTION,
115116
INVALID_USERNAME,

src/main/java/net/coreprotect/utility/ChatUtils.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,20 @@ public static String createTooltip(String phrase, String tooltip) {
185185
return message.append(Chat.COMPONENT_TAG_CLOSE).toString();
186186
}
187187

188+
public static String createGiveItemComponent(String phrase, String command, Integer itemId) {
189+
if (itemId == null) {
190+
return "";
191+
}
192+
193+
return Chat.COMPONENT_TAG_OPEN + Chat.COMPONENT_COMMAND + "|/" + command + " give #" + itemId + "|" + phrase + Chat.COMPONENT_TAG_CLOSE;
194+
}
195+
188196
// This theoretically initializes the component code, to prevent gson adapter errors
189197
public static void sendConsoleComponentStartup(ConsoleCommandSender consoleSender, String string) {
190198
Chat.sendComponent(consoleSender, Color.RESET + "[CoreProtect] " + string + Chat.COMPONENT_TAG_OPEN + Chat.COMPONENT_POPUP + "| | " + Chat.COMPONENT_TAG_CLOSE);
191199
}
200+
201+
public static String filterComponent(boolean condition, String component) {
202+
return condition ? component : "";
203+
}
192204
}

src/main/java/net/coreprotect/utility/ItemUtils.java

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,7 @@
22

33
import java.io.ByteArrayOutputStream;
44
import java.lang.reflect.Array;
5-
import java.util.ArrayList;
6-
import java.util.Arrays;
7-
import java.util.Collections;
8-
import java.util.HashMap;
9-
import java.util.LinkedHashMap;
10-
import java.util.LinkedHashSet;
11-
import java.util.List;
12-
import java.util.Locale;
13-
import java.util.Map;
14-
import java.util.Set;
5+
import java.util.*;
156
import java.util.logging.Logger;
167
import java.util.regex.Matcher;
178
import java.util.regex.Pattern;
@@ -37,6 +28,7 @@
3728
import net.coreprotect.utility.serialize.ItemMetaHandler;
3829

3930
public class ItemUtils {
31+
private static final Map<ItemStack, Integer> GIVABLE_ITEMS = Collections.synchronizedMap(new LinkedHashMap<>());
4032

4133
private static final Object UNSERIALIZABLE_VALUE = new Object();
4234
private static final Logger LOGGER = Logger.getLogger("CoreProtect");
@@ -70,6 +62,19 @@ private ItemUtils() {
7062
throw new IllegalStateException("Utility class");
7163
}
7264

65+
public static ItemStack getGivableItem(int id) {
66+
//we can use skip here because it's a linked map from which elements are never removed
67+
return GIVABLE_ITEMS.keySet().stream().skip(id).findFirst().orElse(null);
68+
}
69+
70+
public static Integer makeGivableItem(ItemStack item) {
71+
if (item == null) {
72+
return null;
73+
}
74+
75+
return GIVABLE_ITEMS.computeIfAbsent(item, k -> GIVABLE_ITEMS.size());
76+
}
77+
7378
public static void mergeItems(Material material, ItemStack[] items) {
7479
if (material != null && (material.equals(Material.ARMOR_STAND) || BukkitAdapter.ADAPTER.isItemFrame(material))) {
7580
return;
@@ -646,14 +651,21 @@ public static ItemMeta deserializeItemMeta(Class<? extends ItemMeta> itemMetaCla
646651

647652
return null;
648653
}
649-
650-
public static String getEnchantments(byte[] metadata, int type, int amount) {
654+
655+
public static ItemStack getItemStack(byte[] metadata, int type, int amount) {
651656
if (metadata == null) {
652-
return "";
657+
return null;
653658
}
654659

655660
ItemStack item = new ItemStack(MaterialUtils.getType(type), amount);
656661
item = (ItemStack) net.coreprotect.database.rollback.Rollback.populateItemStack(item, metadata)[2];
662+
return item;
663+
}
664+
665+
public static String getEnchantments(byte[] metadata, int type, int amount) {
666+
var item = getItemStack(metadata, type, amount);
667+
if (item == null) return "";
668+
657669
String displayName = item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : "";
658670
StringBuilder message = new StringBuilder(Color.ITALIC + displayName + Color.GREY);
659671

@@ -675,7 +687,7 @@ else if (!enchantments.isEmpty()) {
675687

676688
return message.toString();
677689
}
678-
690+
679691
public static Map<Integer, Object> serializeItemStackLegacy(ItemStack itemStack, String faceData, int slot) {
680692
Map<Integer, Object> result = new HashMap<>();
681693
Map<String, Object> itemMap = serializeItemStack(itemStack, faceData, slot);
@@ -730,4 +742,4 @@ public static ItemStack unserializeItemStack(Object value) {
730742

731743
return result;
732744
}
733-
}
745+
}

0 commit comments

Comments
 (0)