Implement kits placing items in specific slots

Everytime you create a kit with gear (armor and offhand) equipped, the
gear is placed in their respective slots once you retrieve the kit. For
example, if you create the kit with a golden helmet equipped, it will be
placed in your helmet slot when retrieving the kit.

Co-authored-by: Catarina Freire <catarina.xie.freire@tecnico.ulisboa.pt>
This commit is contained in:
Tiago Farinha 2024-05-06 16:07:45 +01:00
parent cde7184da3
commit 18758ba1cf
5 changed files with 218 additions and 64 deletions

View File

@ -26,6 +26,7 @@ import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static com.earth2me.essentials.I18n.tlLiteral;
@ -127,10 +128,10 @@ public class Kit {
@Deprecated
public List<String> getItems(final User user) throws Exception {
return getItems();
return getBasicItems();
}
public List<String> getItems() throws Exception {
public List<String> getBasicItems() throws Exception {
if (kit == null) {
throw new TranslatableException("kitNotFound");
}
@ -154,14 +155,46 @@ public class Kit {
}
}
public boolean expandItems(final User user) throws Exception {
return expandItems(user, getItems(user));
public List<String> getGearItems() throws Exception {
if (kit == null) {
throw new TranslatableException("kitNotFound");
}
try {
final List<String> itemList = new ArrayList<>();
final String[] gearConfigName = {"boots", "leggings", "chestplate", "helmet", "offhand"};
for (String itemName : gearConfigName) {
final Object item = kit.get(itemName);
if (item == null) {
itemList.add(null);
continue;
}
if (item instanceof String) {
itemList.add(item.toString());
continue;
}
throw new Exception("Invalid kit item: " + item.toString());
}
return itemList;
} catch (final Exception e) {
ess.getLogger().log(Level.WARNING, "Error parsing kit " + kitName + ": " + e.getMessage());
throw new TranslatableException(e,"kitError2");
}
}
public boolean expandItems(final User user, final List<String> items) throws Exception {
public boolean expandItems(final User user) throws Exception {
return expandItems(user, getItems(user), getGearItems());
}
public boolean expandItems(final User user, final List<String> items, final List<String> gearItems) throws Exception {
try {
final IText input = new SimpleTextInput(items);
final IText output = new KeywordReplacer(input, user.getSource(), ess, true, true);
final IText basicInput = new SimpleTextInput(items);
final IText basicOutput = new KeywordReplacer(basicInput, user.getSource(), ess, true, true);
final List<String> nonNullGearItems = gearItems.stream().filter(is -> is != null).collect(Collectors.toList());
final IText gearInput = new SimpleTextInput(nonNullGearItems);
final IText gearOutput = new KeywordReplacer(gearInput, user.getSource(), ess, true, true);
final KitClaimEvent event = new KitClaimEvent(user, this);
Bukkit.getPluginManager().callEvent(event);
@ -173,58 +206,29 @@ public class Kit {
final boolean allowUnsafe = ess.getSettings().allowUnsafeEnchantments();
final boolean autoEquip = ess.getSettings().isKitAutoEquip();
final List<ItemStack> itemList = new ArrayList<>();
final List<ItemStack> gearList = new ArrayList<>();
final List<String> commandQueue = new ArrayList<>();
final List<String> moneyQueue = new ArrayList<>();
final String currencySymbol = ess.getSettings().getCurrencySymbol().isEmpty() ? "$" : ess.getSettings().getCurrencySymbol();
for (final String kitItem : output.getLines()) {
if (kitItem.startsWith("$") || kitItem.startsWith(currencySymbol)) {
moneyQueue.add(NumberUtil.sanitizeCurrencyString(kitItem, ess));
continue;
}
if (kitItem.startsWith("/")) {
String command = kitItem.substring(1);
final String name = user.getName();
command = command.replace("{player}", name);
commandQueue.add(command);
continue;
}
final ItemStack stack;
if (kitItem.startsWith("@")) {
if (ess.getSerializationProvider() == null) {
ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("kitError3", kitName, user.getName())));
continue;
}
stack = ess.getSerializationProvider().deserializeItem(Base64Coder.decodeLines(kitItem.substring(1)));
} else {
final String[] parts = kitItem.split(" +");
final ItemStack parseStack = ess.getItemDb().get(parts[0], parts.length > 1 ? Integer.parseInt(parts[1]) : 1);
if (parseStack.getType() == Material.AIR) {
continue;
}
final MetaItemStack metaStack = new MetaItemStack(parseStack);
if (parts.length > 2) {
// We pass a null sender here because kits should not do perm checks
metaStack.parseStringMeta(null, allowUnsafe, parts, 2, ess);
}
stack = metaStack.getItemStack();
}
itemList.add(stack);
}
populateKitLists(user, basicOutput, moneyQueue, commandQueue, itemList, allowUnsafe, currencySymbol);
populateKitLists(user, gearOutput, moneyQueue, commandQueue, gearList, allowUnsafe, currencySymbol);
final int maxStackSize = user.isAuthorized("essentials.oversizedstacks") ? ess.getSettings().getOversizedStackSize() : 0;
final boolean isDropItemsIfFull = ess.getSettings().isDropItemsIfFull();
final KitPreExpandItemsEvent itemsEvent = new KitPreExpandItemsEvent(user, kitName, itemList);
final List<ItemStack> totalItems = new ArrayList<>(itemList);
totalItems.addAll(gearList.stream().filter(is -> is != null).collect(Collectors.toList()));
final KitPreExpandItemsEvent itemsEvent = new KitPreExpandItemsEvent(user, kitName, totalItems);
Bukkit.getPluginManager().callEvent(itemsEvent);
final List<Integer> nullGearItemsIndexes = findNullIndexes(gearItems);
final ItemStack[] gearArray = addNullIndexes(gearList, nullGearItemsIndexes).toArray(new ItemStack[0]);
final List<ItemStack> leftovers = Inventories.addGear(user.getBase(), gearArray);
itemList.addAll(leftovers);
final ItemStack[] itemArray = itemList.toArray(new ItemStack[0]);
if (!isDropItemsIfFull && !Inventories.hasSpace(user.getBase(), maxStackSize, autoEquip, itemArray)) {
@ -274,4 +278,76 @@ public class Kit {
}
return true;
}
private void populateKitLists(User user, IText output, List<String> moneyQueue, List<String> commandQueue, List<ItemStack> itemList, boolean allowUnsafe, String currencySymbol) throws Exception {
for (final String kitItem : output.getLines()) {
if (kitItem.startsWith(currencySymbol)) {
moneyQueue.add(NumberUtil.sanitizeCurrencyString(kitItem, ess));
continue;
}
if (kitItem.startsWith("/")) {
String command = kitItem.substring(1);
final String name = user.getName();
command = command.replace("{player}", name);
commandQueue.add(command);
continue;
}
final ItemStack stack = parseItemStack(kitItem, user, allowUnsafe);
if (stack == null) {
continue;
}
itemList.add(stack);
}
}
private ItemStack parseItemStack(String kitItem, User user, boolean allowUnsafe) throws Exception {
if (kitItem.startsWith("@")) {
if (ess.getSerializationProvider() == null) {
ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("kitError3", kitName, user.getName())));
return null;
}
return ess.getSerializationProvider().deserializeItem(Base64Coder.decodeLines(kitItem.substring(1)));
} else {
final String[] parts = kitItem.split(" +");
final ItemStack parseStack = ess.getItemDb().get(parts[0], parts.length > 1 ? Integer.parseInt(parts[1]) : 1);
if (parseStack.getType() == Material.AIR) {
return null;
}
final MetaItemStack metaStack = new MetaItemStack(parseStack);
if (parts.length > 2) {
// We pass a null sender here because kits should not do perm checks
metaStack.parseStringMeta(null, allowUnsafe, parts, 2, ess);
}
return metaStack.getItemStack();
}
}
private List<Integer> findNullIndexes(List<String> list) {
final List<Integer> nullIndexes = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == null) {
nullIndexes.add(i);
}
}
return nullIndexes;
}
private List<ItemStack> addNullIndexes(List<ItemStack> list, List<Integer> nullIndexes) {
final List<ItemStack> newList = new ArrayList<>(list);
for (int nullIndex : nullIndexes) {
newList.add(nullIndex, null);
}
return newList;
}
}

View File

@ -112,11 +112,20 @@ public class Kits implements IConf {
return null;
}
public void addKit(String name, final List<String> lines, final long delay) {
public void addKit(String name, final List<String> basicLines, final List<String> gearLines, final long delay) {
name = name.replace('.', '_').replace('/', '_').toLowerCase(Locale.ENGLISH);
// Will overwrite but w/e
rootConfig.setProperty("kits." + name + ".delay", delay);
rootConfig.setProperty("kits." + name + ".items", lines);
rootConfig.setProperty("kits." + name + ".items", basicLines);
final String[] gearConfigName = {"boots", "leggings", "chestplate", "helmet", "offhand"};
for (int i = 0; i < gearLines.size(); i++) {
final String gearLine = gearLines.get(i);
if (gearLine != null) {
rootConfig.setProperty("kits." + name + "." + gearConfigName[i], gearLine);
}
}
parseKits();
rootConfig.save();
}

View File

@ -39,8 +39,10 @@ public class Commandcreatekit extends EssentialsCommand {
// Command handler will auto fail if this fails.
final long delay = Long.parseLong(args[1]);
final String kitname = args[0];
final ItemStack[] items = Inventories.getInventory(user.getBase(), true);
final ItemStack[] items = Inventories.getInventoryBasicContents(user.getBase());
final ItemStack[] gear = Inventories.getInventoryGear(user.getBase());
final List<String> list = new ArrayList<>();
final List<String> gearList = new ArrayList<>();
boolean useSerializationProvider = ess.getSettings().isUseBetterKits();
@ -50,26 +52,43 @@ public class Commandcreatekit extends EssentialsCommand {
}
for (ItemStack is : items) {
if (is != null && is.getType() != null && is.getType() != Material.AIR) {
final String serialized;
if (useSerializationProvider) {
serialized = "@" + Base64Coder.encodeLines(ess.getSerializationProvider().serializeItem(is));
} else {
serialized = ess.getItemDb().serialize(is);
}
final String serialized = serializeItem(is, useSerializationProvider);
if (serialized != null) {
list.add(serialized);
}
}
int gearItemsAmount = 0;
for (ItemStack is : gear) {
gearItemsAmount = is == null ? gearItemsAmount : gearItemsAmount + 1;
gearList.add(serializeItem(is, useSerializationProvider));
}
// Some users might want to directly write to config knowing the consequences. *shrug*
if (!ess.getSettings().isPastebinCreateKit()) {
ess.getKits().addKit(kitname, list, delay);
user.sendTl("createdKit", kitname, list.size(), delay);
ess.getKits().addKit(kitname, list, gearList, delay);
user.sendTl("createdKit", kitname, list.size() + gearItemsAmount, delay);
} else {
uploadPaste(user.getSource(), kitname, delay, list);
uploadPaste(user.getSource(), kitname, delay, list, gearList);
}
}
private void uploadPaste(final CommandSource sender, final String kitName, final long delay, final List<String> list) {
private String serializeItem(ItemStack is, boolean useSerializationProvider) {
if (is != null && is.getType() != null && is.getType() != Material.AIR) {
final String serialized;
if (useSerializationProvider) {
serialized = "@" + Base64Coder.encodeLines(ess.getSerializationProvider().serializeItem(is));
} else {
serialized = ess.getItemDb().serialize(is);
}
return serialized;
}
return null;
}
private void uploadPaste(final CommandSource sender, final String kitName, final long delay, final List<String> list, final List<String> gearList) {
ess.runTaskAsynchronously(() -> {
try {
final StringWriter sw = new StringWriter();
@ -79,6 +98,14 @@ public class Commandcreatekit extends EssentialsCommand {
config.node("kits", kitName, "delay").set(delay);
config.node("kits", kitName, "items").set(list);
final String[] gearConfigName = {"boots", "leggings", "chestplate", "helmet", "offhand"};
for (int i = 0; i < gearList.size(); i++) {
final String gearLine = gearList.get(i);
if (gearLine != null) {
config.node("kits", kitName, gearConfigName[i]).set(gearList.get(i));
}
}
sw.append("# Copy the kit code below into the kits section in your config.yml file\n");
loader.save(config);

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
public class Commandshowkit extends EssentialsCommand {
public Commandshowkit() {
@ -22,7 +23,11 @@ public class Commandshowkit extends EssentialsCommand {
for (final String kitName : args[0].toLowerCase(Locale.ENGLISH).split(",")) {
user.sendTl("kitContains", kitName);
for (final String s : new Kit(kitName, ess).getItems()) {
final Kit kit = new Kit(kitName, ess);
for (final String s : kit.getBasicItems()) {
user.sendTl("kitItem", s);
}
for (final String s : kit.getGearItems().stream().filter(is -> is != null).collect(Collectors.toList())) {
user.sendTl("kitItem", s);
}
}

View File

@ -21,6 +21,7 @@ public final class Inventories {
private static final int BOOT_SLOT = 36;
private static final boolean HAS_OFFHAND = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_9_R01);
private static final int INVENTORY_SIZE = HAS_OFFHAND ? 41 : 40;
private static final int BASIC_INVENTORY_SIZE = 36;
private Inventories() {
}
@ -132,6 +133,24 @@ public final class Inventories {
return true;
}
public static List<ItemStack> addGear(final Player player, final ItemStack... items) {
final List<ItemStack> leftovers = new ArrayList<>();
for (int i = 0; i < items.length; i++) {
if (items[i] != null) {
final ItemStack playerCurrentGear = player.getInventory().getItem(BASIC_INVENTORY_SIZE + i);
if (!isEmpty(playerCurrentGear)) {
leftovers.add(items[i]);
}
player.getInventory().setItem(BASIC_INVENTORY_SIZE + i, items[i]);
}
}
return leftovers;
}
public static Map<Integer, ItemStack> addItem(final Player player, final ItemStack... items) {
return addItem(player, 0, false, items);
}
@ -212,6 +231,24 @@ public final class Inventories {
return items;
}
public static ItemStack[] getInventoryBasicContents(final Player player) {
final ItemStack[] items = new ItemStack[BASIC_INVENTORY_SIZE];
for (int i = 0; i < items.length; i++) {
items[i] = player.getInventory().getItem(i);
}
return items;
}
public static ItemStack[] getInventoryGear(final Player player) {
final ItemStack[] items = new ItemStack[INVENTORY_SIZE - BASIC_INVENTORY_SIZE];
for (int i = 0; i < items.length; i++) {
items[i] = player.getInventory().getItem(BASIC_INVENTORY_SIZE + i);
}
return items;
}
public static void removeItemExact(final Player player, final ItemStack toRemove, final boolean includeArmor) {
removeItems(player, itemStack -> itemStack.equals(toRemove), includeArmor);
}