Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ The following permissions can be used to restrict functionality within the plugi
* **coreprotect.consumer** *(default: op)*
Allows access to the CoreProtect consumer command.
 
* **coreprotect.give** *(default: false)*
Allows access to the CoreProtect give command.
 
* **coreprotect.networking** *(default: op)*
Allows access to the CoreProtect networking API.

Expand Down
6 changes: 6 additions & 0 deletions src/main/java/net/coreprotect/command/CommandHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ else if (user.hasPermission("coreprotect.consumer") && corecommand.equals("consu
else if (user.hasPermission("coreprotect.networking") && corecommand.equals("network-debug")) {
permission = true;
}
else if (user.hasPermission("coreprotect.give") && corecommand.equals("give")) {
permission = true;
}
}

if (corecommand.equals("rollback") || corecommand.equals("restore") || corecommand.equals("rb") || corecommand.equals("rs") || corecommand.equals("ro") || corecommand.equals("re")) {
Expand Down Expand Up @@ -120,6 +123,9 @@ else if (corecommand.equals("consumer")) {
else if (corecommand.equals("network-debug")) {
NetworkDebugCommand.runCommand(user, permission, argumentArray);
}
else if (corecommand.equals("give")) {
GiveCommand.runCommand(user, command, permission, argumentArray);
}
else if (corecommand.equals("migrate-db")) {
if (!VersionUtils.validDonationKey()) {
Chat.sendMessage(user, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.DONATION_KEY_REQUIRED));
Expand Down
11 changes: 4 additions & 7 deletions src/main/java/net/coreprotect/command/CommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@
import java.util.Map;
import java.util.Set;

import net.coreprotect.command.parser.*;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;

import net.coreprotect.command.parser.ActionParser;
import net.coreprotect.command.parser.LocationParser;
import net.coreprotect.command.parser.MaterialParser;
import net.coreprotect.command.parser.TimeParser;
import net.coreprotect.command.parser.UserParser;
import net.coreprotect.command.parser.WorldParser;

/**
* Main parser class for CoreProtect commands.
* Delegates to specialized parser classes for specific functionality.
Expand Down Expand Up @@ -326,4 +320,7 @@ private static String timeString(BigDecimal input) {
return input.stripTrailingZeros().toPlainString();
}

protected static Integer parseGivableItemId(String[] inputArguments) {
return GivableItemIdParser.parseGivableItemId(inputArguments);
}
}
40 changes: 40 additions & 0 deletions src/main/java/net/coreprotect/command/GiveCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package net.coreprotect.command;

import net.coreprotect.language.Phrase;
import net.coreprotect.utility.Chat;
import net.coreprotect.utility.ChatMessage;
import net.coreprotect.utility.Color;
import net.coreprotect.utility.ItemUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

public class GiveCommand {
public static void runCommand(CommandSender sender, Command command, boolean permission, String[] args) {
if (!permission) {
Chat.sendMessage(sender, new ChatMessage(Phrase.build(Phrase.NO_PERMISSION)).build());
return;
}

Integer itemId = CommandParser.parseGivableItemId(args);
if (itemId == null) {
Chat.sendMessage(sender, new ChatMessage(Phrase.build(Phrase.MISSING_PARAMETERS, Color.WHITE, "/" + command.getName() + " give <itemId>")).build());
return;
}

ItemStack item = ItemUtils.getGivableItem(itemId);
if (item == null) {
Chat.sendMessage(sender, new ChatMessage(Phrase.build(Phrase.INVALID_ITEM_ID)).build());
return;
}

if (!(sender instanceof Player)) {
Chat.sendMessage(sender, new ChatMessage(Phrase.build(Phrase.ACTION_NOT_SUPPORTED)).build());
return;
}

Player player = (Player) sender;
player.getInventory().addItem(item);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ else if (LookupActions.isInventoryLookup(actions)) {
String dname = StringUtils.nameFilter(blockType.name().toLowerCase(Locale.ROOT), ddata);
byte[] metadata = data[11] == null ? null : data[11].getBytes(StandardCharsets.ISO_8859_1);
String tooltip = ItemUtils.getEnchantments(metadata, dtype, amount);
Integer itemId = ItemUtils.makeGivableItem(ItemUtils.getItemStack(metadata, dtype, amount));

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

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));
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));
PluginChannelListener.getInstance().sendData(player, Integer.parseInt(time), Phrase.LOOKUP_CONTAINER, selector, dplayer, dname, amount, dataX, dataY, dataZ, wid, rbd, true, tag.contains("+"));
}
}
Expand Down Expand Up @@ -389,6 +390,7 @@ else if (daction == ItemTransactionActions.SELL || daction == ItemTransactionAct
if (actions.contains(LookupActions.CONTAINER) || actions.contains(5) || actions.contains(LookupActions.ITEM) || amount > -1) {
byte[] metadata = data[11] == null ? null : data[11].getBytes(StandardCharsets.ISO_8859_1);
String tooltip = ItemUtils.getEnchantments(metadata, dtype, amount);
Integer itemId = ItemUtils.makeGivableItem(ItemUtils.getItemStack(metadata, dtype, amount));

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

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));
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));
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("+"));
}
else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package net.coreprotect.command.parser;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GivableItemIdParser {

protected static final Pattern PATTERN = Pattern.compile("#([0-9]+)");

public static Integer parseGivableItemId(String[] inputArguments) {
for (String argument : inputArguments) {
Matcher matcher = PATTERN.matcher(argument);
if (matcher.find()) {
return Integer.parseInt(matcher.group(1));
}
}
return null;
}
}
1 change: 1 addition & 0 deletions src/main/java/net/coreprotect/language/Language.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public static void loadPhrases() {
phrases.put(Phrase.INVALID_INCLUDE, "\"{0}\" is an invalid block/entity name.");
phrases.put(Phrase.INVALID_INCLUDE_COMBO, "That is an invalid block/entity combination.");
phrases.put(Phrase.INVALID_PARAMETER, "\"{0}\" is not a supported parameter.");
phrases.put(Phrase.INVALID_ITEM_ID, "Please enter a valid item id.");
phrases.put(Phrase.INVALID_RADIUS, "Please enter a valid radius.");
phrases.put(Phrase.INVALID_SELECTION, "{0} selection not found.");
phrases.put(Phrase.INVALID_USERNAME, "\"{0}\" is an invalid username.");
Expand Down
1 change: 1 addition & 0 deletions src/main/java/net/coreprotect/language/Phrase.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public enum Phrase {
INVALID_INCLUDE,
INVALID_INCLUDE_COMBO,
INVALID_PARAMETER,
INVALID_ITEM_ID,
INVALID_RADIUS,
INVALID_SELECTION,
INVALID_USERNAME,
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/net/coreprotect/utility/ChatUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,20 @@ public static String createTooltip(String phrase, String tooltip) {
return message.append(Chat.COMPONENT_TAG_CLOSE).toString();
}

public static String createGiveItemComponent(String phrase, String command, Integer itemId) {
if (itemId == null) {
return "";
}

return Chat.COMPONENT_TAG_OPEN + Chat.COMPONENT_COMMAND + "|/" + command + " give #" + itemId + "|" + phrase + Chat.COMPONENT_TAG_CLOSE;
}

// This theoretically initializes the component code, to prevent gson adapter errors
public static void sendConsoleComponentStartup(ConsoleCommandSender consoleSender, String string) {
Chat.sendComponent(consoleSender, Color.RESET + "[CoreProtect] " + string + Chat.COMPONENT_TAG_OPEN + Chat.COMPONENT_POPUP + "| | " + Chat.COMPONENT_TAG_CLOSE);
}

public static String filterComponent(boolean condition, String component) {
return condition ? component : "";
}
}
42 changes: 27 additions & 15 deletions src/main/java/net/coreprotect/utility/ItemUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,7 @@

import java.io.ByteArrayOutputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -37,6 +28,7 @@
import net.coreprotect.utility.serialize.ItemMetaHandler;

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

private static final Object UNSERIALIZABLE_VALUE = new Object();
private static final Logger LOGGER = Logger.getLogger("CoreProtect");
Expand Down Expand Up @@ -70,6 +62,19 @@ private ItemUtils() {
throw new IllegalStateException("Utility class");
}

public static ItemStack getGivableItem(int id) {
//we can use skip here because it's a linked map from which elements are never removed
return GIVABLE_ITEMS.keySet().stream().skip(id).findFirst().orElse(null);
}

public static Integer makeGivableItem(ItemStack item) {
if (item == null) {
return null;
}

return GIVABLE_ITEMS.computeIfAbsent(item, k -> GIVABLE_ITEMS.size());
}

public static void mergeItems(Material material, ItemStack[] items) {
if (material != null && (material.equals(Material.ARMOR_STAND) || BukkitAdapter.ADAPTER.isItemFrame(material))) {
return;
Expand Down Expand Up @@ -646,14 +651,21 @@ public static ItemMeta deserializeItemMeta(Class<? extends ItemMeta> itemMetaCla

return null;
}
public static String getEnchantments(byte[] metadata, int type, int amount) {

public static ItemStack getItemStack(byte[] metadata, int type, int amount) {
if (metadata == null) {
return "";
return null;
}

ItemStack item = new ItemStack(MaterialUtils.getType(type), amount);
item = (ItemStack) net.coreprotect.database.rollback.Rollback.populateItemStack(item, metadata)[2];
return item;
}

public static String getEnchantments(byte[] metadata, int type, int amount) {
var item = getItemStack(metadata, type, amount);
if (item == null) return "";

String displayName = item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : "";
StringBuilder message = new StringBuilder(Color.ITALIC + displayName + Color.GREY);

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

return message.toString();
}

public static Map<Integer, Object> serializeItemStackLegacy(ItemStack itemStack, String faceData, int slot) {
Map<Integer, Object> result = new HashMap<>();
Map<String, Object> itemMap = serializeItemStack(itemStack, faceData, slot);
Expand Down Expand Up @@ -730,4 +742,4 @@ public static ItemStack unserializeItemStack(Object value) {

return result;
}
}
}
4 changes: 4 additions & 0 deletions src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ permissions:
coreprotect.status: true
coreprotect.consumer: true
coreprotect.networking: true
coreprotect.give: true
coreprotect.co:
description: Has permission to access the CoreProtect /co command
default: true
Expand Down Expand Up @@ -144,3 +145,6 @@ permissions:
coreprotect.networking:
description: Has permission to use the networking API
default: op
coreproctect.give:
description: Has permission to use the give command
default: false